Das Soap Protokoll, XML und PHP – eine Einführung


Das Soap (Simple Object Access Protocol) ist eine Netzwerkprotokoll, mit dessen Hilfe Daten zwischen Systemen ausgetauscht und Remote Procedure Calls durchgeführt werden können (Wikipedia).

Meistens werden Nachrichten im XML Format mittels eines HTTP Post-Request im Body zwischen einem Client und einem Server gesendet. Für das Nachrichtenformat wird kein Format vorgegeben, sondern nur ein Framework zur Verfügung gestellt. Verwandte System sind REST und Cobra. Alle Systeme dienen zur Entkopplung/Abstraktion und Interoperabilität von Applikationen und dienen als Middleware zwischen verschiedenen Applikationen, die unterschiedliche Programmiersprachen oder Technologien verwenden können.

Die Vorteile werden durch leicht erhöhten Rechenaufwand und erhöhtem Datenvolumen erkauft.

Der Aufbau einer XML Soap Nachricht

Eine minimale SOAP-Nachricht besteht aus einem Envelope genannten Element, welchem ein lokaler Name zugewiesen werden muss. Kind dieses Elements muss ein Body-Element sein. Optional kann zuvor ein Header-Element stehen. In diesem können Meta-Informationen, beispielsweise zum Routing, zur Verschlüsselung oder zu Transaktionsidentifizierung, untergebracht werden. Im Body-Element sind die eigentlichen Nutzdaten untergebracht.

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    <s:Header>
    </s:Header>
    <s:Body>
    </s:Body>
</s:Envelope>

Der Aufbau der WSDL Datei (Web Services Description Language)

Die Wsdl Datei dient zur Beschriebung der Schnittstelle.

Beispiel:

<message name="newTermValues">
   <part name="term" type="xs:string"/>
   <part name="value" type="xs:string"/>
 </message>
<message name="getTermResponse">
   <part name="value" type="xs:string"/>
 </message>
 <portType name="glossaryTerms">
   <operation name="setTerm">
     <input name="newTerm" message="newTermValues"/>
     <output name="outputTerm" message="getTermMessage"/>
   </operation> </portType >

Hier wird eine  Webservice (portType) namens glossaryTerms definiert, der eine Operation namens setTerm bereitstellt, die als Input einen Parameter vom Typ newTermValue namens newTerm, der wiederum aus zwei Strings besteht: term und value. Als Antwort bekommt man vom Werbserice der einen Wert namens getTermResponse, der aus einem String namens value beinhaltet.

 Programmierung eines Soap Servers und zugriff mittels eines Soap Clients

Der Soap Server liefert liefert dem Soap Client alle Posts mit Hilfe der Operaton getPosts($num).

Soap Server: soap_server.php

// SOAP config array
$soap_config = array(
    'encoding' => 'UTF-8',
    'uri' => 'testNamespace'
);

// Don't cache WSDL files, mandatory for development
ini_set('soap.wsdl_cache_enabled', '0');

class MySoapServer
{

    public function getPosts($num = 10)
    {
        $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
        );
    }
}

// Run SOAP server
$soapserver = new SoapServer(null, $soap_config);
$soapserver->setClass('MySoapServer');
$soapserver->handle();

Soap Client: soap_client.php

// Create new SOAP client
$soap_config = array(
    'encoding' => 'UTF-8',
    'location' => 'http://localhost/test/soap/soap_server.php', //adjust this URL
    'uri' => 'testNamespace',
);
$soap_client = new SoapClient(null, $soap_config);
if (is_object($soap_client)) {
    $soap_result = $soap_client->getPosts(10);
    // Debug output
    echo "<pre>";
    print_r($soap_result);
    echo "</pre>";
} else {
    throw new Exception('SOAP Server not available');
}

Eine Wsdl Datei kann man zusätzlich mit der Library PHP wsdl generator erstellen, ist aber für das Ausführen des Beispiel nicht notwendig.

 Debugging Soap Requests

Der Client Call

Der Client Call kann mittels der __getLastRequest() Methode des SoapClients ausgegeben werden, wichtig ist, dass der Parameter trace auf 1 gesetzt wird, sonst gibt es kein Ergbenis.

// Create new SOAP client
$soap_config = array(
    'encoding' => 'UTF-8',
    'location' => 'http://localhost/test/soap/soap_server.php',
    'uri' => 'testNamespace',
    'trace'=>1
);
$soap_client = new SoapClient(null, $soap_config);
if (is_object($soap_client)) {

    $soap_result = $soap_client->getPosts(10);
    echo  $soap_client->__getLastRequest() ;
} else {
    throw new Exception('SOAP Server not available');
}

Ausgabe:

<soap-env:envelope soap-env:encodingstyle="http://schemas.xmlsoap.org/soap/encoding/"
                   xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   xmlns:ns1="testNamespace" xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
    <soap-env:body>
        <ns1:getposts>
            <param0 xsi:type="xsd:int">10</param0>
        </ns1:getposts>
    </soap-env:body>
</soap-env:envelope>

DIE Server Response

Mit der Funktion __getLastResponse() kann man die Antwort vom Server im XML Format ausgeben lassen:

<soap-env:envelope soap-env:encodingstyle="http://schemas.xmlsoap.org/soap/encoding/"
                   xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="http://xml.apache.org/xml-soap"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:ns1="testNamespace" xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
    <soap-env:body>
        <ns1:getpostsresponse>
            <return xsi:type="ns2:Map">
                <item>
                    <key xsi:type="xsd:string">return</key>
                    <value xsi:type="xsd:boolean">true</value>
                </item>
                <item>
                    <key xsi:type="xsd:string">error</key>
                    <value xsi:type="xsd:string"></value>
                </item>
                <item>
                    <key xsi:type="xsd:string">posts</key>
                    <value xsi:type="SOAP-ENC:Array" soap-enc:arraytype="ns2:Map[4]">
                        <item xsi:type="ns2:Map">
                            <item>
                                <key xsi:type="xsd:string">title</key>
                                <value xsi:type="xsd:string">Demo Post 1</value>
                            </item>
                            <item>
                                <key xsi:type="xsd:string">body</key>
                                <value xsi:type="xsd:string">Demo content 1</value>
                            </item>
                        </item>
                        <item xsi:type="ns2:Map">
                            <item>
                                <key xsi:type="xsd:string">title</key>
                                <value xsi:type="xsd:string">Demo Post 2</value>
                            </item>
                            <item>
                                <key xsi:type="xsd:string">body</key>
                                <value xsi:type="xsd:string">Demo content 2</value>
                            </item>
                        </item>
                        <item xsi:type="ns2:Map">
                            <item>
                                <key xsi:type="xsd:string">title</key>
                                <value xsi:type="xsd:string">Demo Post 3</value>
                            </item>
                            <item>
                                <key xsi:type="xsd:string">body</key>
                                <value xsi:type="xsd:string">Demo content 3</value>
                            </item>
                        </item>
                        <item xsi:type="ns2:Map">
                            <item>
                                <key xsi:type="xsd:string">title</key>
                                <value xsi:type="xsd:string">Demo Post 4</value>
                            </item>
                            <item>
                                <key xsi:type="xsd:string">body</key>
                                <value xsi:type="xsd:string">Demo content 4</value>
                            </item>
                        </item>
                    </value>
                </item>
                <item>
                    <key xsi:type="xsd:string">posts_count</key>
                    <value xsi:type="xsd:int">0</value>
                </item>
            </return>
        </ns1:getpostsresponse>
    </soap-env:body>
</soap-env:envelope>