Wer für seine eigene Anwendung eine Web-API anbieten möchte, hat vielfältige Möglichkeiten dies zu realisieren. Per einfachem HTTP GET oder POST Request und einer Rückgabe als XML/RSS/CSV/JSON/whatever, oder – was ich etwas eleganter finde – per SOAP. Dabei ist das Entwickeln eines eigenen SOAP Dienstes mit PHP gar nicht so kompliziert, wie vielleicht vermutet. Zumindest finde ich SOAP schöner als die weitere Alternative XML-RPC.
Aber nun zur Definition von SOAP (Quelle: Wikipedia): SOAP (ursprünglich für Simple Object Access Protocol) ist ein Netzwerkprotokoll, mit dessen Hilfe Daten zwischen Systemen ausgetauscht und Remote Procedure Calls durchgeführt werden können. SOAP stützt sich auf andere Standards: XML zur Repräsentation der Daten und Internet-Protokolle der Transport- und Anwendungsschicht (vgl. TCP/IP-Referenzmodell) zur Übertragung der Nachrichten. Die gängigste Kombination ist SOAP über HTTP und TCP.
In der Praxis bedeutet das folgendes: In einer XML Datei (WSDL) werden Nachrichten-Typen definiert und an Operationen gebunden. Dabei gibt es Input-Nachrichten und Output-Nachrichten, sprich Übergaben und Rückgaben. Die WSDL Datei (WSDL = Web Services Description Language) wird dabei vom Client, als auch vom Server genutzt und regelt so die Kommunikation dieser beider Stellen.
Die Vorgehensweise um einen neuen Web-Service zu erstellen ist bei mir immer zuerst die WSDL zu erzeugen, danach die Operationen im Server abzubilden, um diese anschließend mit dem Client zu testen. Eine Beispiel WSDL könnte z.B. so aussehen:
<?xml version="1.0" encoding="UTF-8"?> <definitions name="TheGeek" targetNamespace="http://thegeek.de/wp-soap/thegeek.wsdl" xmlns:tns="http://thegeek.de/wp-soap/thegeek.wsdl" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <!-- GET LATEST POSTS --> <message name="RequestGetPostsIn"> <part name="apikey" type="xsd:string"/> <part name="num" type="xsd:integer"/> </message> <message name="RequestGetPostsOut"> <part name="returncode" type="xsd:integer"/> <part name="error" type="xsd:string"/> <part name="posts" type="xsd:struct"/> <part name="posts_count" type="xsd:integer"/> </message> <portType name="RequestGetPosts_PortType"> <operation name="GetPosts"> <input message="tns:RequestGetPostsIn"/> <output message="tns:RequestGetPostsOut"/> </operation> </portType> <binding name="GetPosts_Binding" type="tns:RequestGetPosts_PortType"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="GetPosts"> <soap:operation soapAction="GetPosts"/> <input> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:thegeek-getpostsservice" use="encoded"/> </input> <output> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:thegeek-getpostsservice" use="encoded"/> </output> </operation> </binding> <service name="GetPosts_Service"> <documentation>Return latest posts</documentation> <port binding="tns:GetPosts_Binding" name="GetPosts_Port"> <soap:address location="http://thegeek.de/wp-soap/index.php"/> </port> </service> </definitions>
Diese WSDL definiert eine einfache Funktion “GetPosts” mit einer eingehenden Nachricht und einer ausgehenden Nachricht. Für die einzelnen Felder der Nachrichten sind entsprechende Typen definiert. Nachdem die Nachrichten an die Operation “GetPosts” gebunden wurden, wird mit dieser Operation ein Soap-Service definiert. Ein Service besteht also aus einem Binding, welches wiederum aus eine Input- und/oder Output-Message besteht. Das klingt nun zugegeben ziemlich kompliziert, ist aber in der Praxis wirklich einfach nachzuvollziehen, wenn wir uns mal den Server-Quellcode zu dieser WSDL Definition ansehen.
<?php
// SOAP config array
$soap_config = array( 'encoding' => 'UTF-8' );
// Don't cache WSDL files, mandatory for development
ini_set( 'soap.wsdl_cache_enabled', '0' );
class thegeek_soapserver
{
function __construct( $parameters )
{
}
public function GetPosts( $apikey = '', $num = 10 )
{
if( $apikey == 'demoapikey' )
{
$posts = array( array( 'title' => 'Demo Post 1', 'body' => 'Demo content 1' ),
array( 'title' => 'Demo Post 2', 'body' => 'Demo content 2' ),
array( 'title' => 'Demo Post 3', 'body' => 'Demo content 3' ),
array( 'title' => 'Demo Post 4', 'body' => 'Demo content 4' )
);
return array( 'return' => true,
'error' => '',
'posts' => $posts,
'posts_count' => 0
);
}
// API KEY FAIL
else
{
return array( 'return' => false,
'error' => 'API KEY INVALID',
'posts' => array(),
'posts_count' => 0
);
}
}
}
// Run SOAP server
$soapserver = new SoapServer( 'http://thegeek.de/wp-soap/thegeek.wsdl', $soap_config );
$soapserver -> setClass( 'thegeek_soapserver', 'some parameters for the constructor (example only...)' );
$soapserver -> handle();
?>
Nachdem die Soap-Config für die PHP Klasse “SoapServer” definiert und das caching von WSDL Dateien per “ini_set” ausgeschaltet wurde, folgt unsere eigene Klasse, die den Web-Service abbildet. Der Konstruktor kann wie gewohnt definiert werden, auch beim Initialisieren unserer Klasse über die PHP Klasse “SoapServer”, können beliebige Parameter übergeben werden. Übrigens sind auch extends problemlos möglich.
Dann findet sich in der Klasse eine Methode, welche unseren SoapService “GetPosts” abbildet, als Übergabeparameter sind die Variablen unserer Input-Nachricht angegeben. Wie die Variablennamen in der Methode gewählt werden ist egal, aber die Methode muss genau so heißen, wie in der WSDL angegeben (soap:operation soapAction=”GetPosts”). Der Code in der Methode dient natürlich nur der Verdeutlichung, wie Rückgaben erfolgen, nämlich als assoziatives Array, welches die in der WSDL definierte Output-Nachricht abbildet.
Um unsere Server-Klasse dann zu initialisieren, wird die PHP Klasse “SoapServer” mit entsprechenden Parametern (Web-Pfad zur WSDL, SoapConfig) aufgerufen. Danach wird mit Hilfe der Methode “setClass” unsere Klasse als Handler ausgewählt und der Server letztendlich mit der Methode “handle” konstruiert.
Fehlt eigentlich nur noch ein kleiner Test-Client:
<?php
// Create new SOAP client
$soap_client = new SoapClient( 'http://thegeek.de/wp-soap/thegeek.wsdl', array( 'encoding' => 'UTF-8' ) );
if( is_object( $soap_client ) )
{
$soap_result = $soap_client -> GetPosts( 'demoapikey', 10 );
// Debug output
echo "<pre>";
print_r( $soap_result );
echo "</pre>";
}
else die( 'SOAP Server not available' );
?>
Der Client ist ziemlich problemlos umzusetzen: Einfach mit der PHP Klasse “SoapClient” ein Client-Objekt erzeugen (Web-Pfad zur WSDL übergeben) und die Methoden (= Operationen) wie in der WSDL definiert nutzen. Obiges Beispiel gibt folgendes aus:
Array
(
[returncode] =>
[error] =>
[posts] => Array
(
[0] => Array
(
[title] => Demo Post 1
[body] => Demo content 1
)
[1] => Array
(
[title] => Demo Post 2
[body] => Demo content 2
)
[2] => Array
(
[title] => Demo Post 3
[body] => Demo content 3
)
[3] => Array
(
[title] => Demo Post 4
[body] => Demo content 4
)
)
[posts_count] => 0
)
So schwierig ist das mit SOAP und PHP also nicht. Wichtig ist allerdings noch: PHP muss mit der Option “–enable-soap” kompiliert werden, um die SOAP Extension nutzen zu können. Alternativ kann aber auch das SOAP PEAR Paket genutzt oder auf die Klasse nuSoap zurückgegriffen werden.
Wer selbst mal von seinem Webspace aus testen will: http://thegeek.de/wp-soap/thegeek.wsdl
Wer Fragen hat: Comments are open.
