Mrz 26

Dieses Tutorial beschreibt, wie man mit PHP in kürzester Zeit einen SOAP WebService erstellt, der mit Persistenz umgehen kann und WSDL-Dateien selber generiert.

Wenn es in PHP-Projekten um Client-Server-Kommunikation, ins Besondere um RPC, geht, lässt man sich schnell dazu verleiten “mal eben” was eigenes zu bauen, obwohl es doch einige Standards für RPC gibt. Viele – genau wie ich auch – scheuen den Aufwand sich in Monster wie SOAP oder das etwas schlankere XML-RPC einzuarbeiten. Mit den richtigen Tool’s jedoch können PHP-Klassen jedoch mit geringstem Mehraufwand über SOAP zugänglich gemacht werden.

PHP mit aktiviertem SOAP

PHP mit aktiviertem SOAP

Die Einzige Vorraussetzung ist einen Webserver mit PHP5.0.3 oder aktueller und aktiviertem SOAP.

Leider ist die native SOAP-Implementation von PHP jedoch relativ kryptisch und bietet momentan keine Möglichkeit WSDL Dateien zu generieren.  Die Web Services Description Language ist eine plattform-, programmiersprachen- und protokollunabhängige Beschreibungssprache für Netzwerkdienste (Web Services) zum Austausch von Nachrichten auf Basis von XML. Über diese WSDL-Dateien können dann Client-Stubs, z.B. in .NET komplett generiert werden.

Abhilfe schafft da der Webservice-Helper [nachfolgend WSH genannt] von jool.nl. Der WSH nimmt einem jegliche Arbeit ab, um Klassen und Objekte über SOAP bereitzustellen.Zusätzlich generiert der WSH automatisch WSDL-Dateien. Diese WSDL’s können dann, wie gesagt zum generieren von Clients in beliebigen Sprachen benutzt werden.

Also den WSH herunterladen und entpacken. Standardmäßig kann der WSH auf Klassen zugreifen die in den folgenden Unterordnern liegen:

  • lib/data_objects/Klassenname.class.php
  • lib/soap/Klassenname.class.php
  • lib/Klassenname.class.php

Der Dateiname muss sich aus dem Klassennamen und “.class.php” zusammensetzten. Es ist jedoch auch möglich, Klassen aus anderen Ordnern bereitzusetellen. Dazu muss die Autoload-Funktion in der common.php angepasst  werden.

  1. /** autoload functie voor PHP5 */
  2. function __autoload($classname) {
  3.         if(file_exists("lib/data_objects/$classname.class.php"))
  4.                 include("lib/data_objects/$classname.class.php");
  5.         elseif(file_exists("lib/soap/$classname.class.php"))
  6.                 include("lib/soap/$classname.class.php");
  7.         elseif(file_exists("lib/$classname.class.php"))
  8.                 include("lib/$classname.class.php");
  9. }

Die dort definierten Suchpfade und auch Dateinamen (.class.php) können nach belieben geändert werden. Ist der Zugriff konfiguriert, müssen die Klassen noch in der config.php freigegeben werden.

config.php

  1. /* All the allowed webservice classes */
  2. $WSClasses = array(
  3.         "BeispielKlasse",
  4.         "TestKlasse",
  5.         "Klasse"
  6. );
  7.  
  8. /* The classmap associative array. When you want to allow objects as a parameter for
  9.  * your webservice method. ie. saveObject($object). By default $object will now be
  10.  * a stdClass, but when you add a classname defined in the type description in the @param
  11.  * documentation tag and add your class to the classmap below, the object will be of the
  12.  * given type. Requires PHP 5.0.3+
  13.  */
  14. $WSStructures = array(
  15.         "TestDatentyp" => "TestDatentyp"
  16. );

Alle Klassen, die über SOAP betreitgestellt werden sollen müssen im Array $WSClasses aufgeführt werden. Damit wird sichergestellt, dass über die autoload Funktion keine ungewollten Dateien aufgerufen werden. Objekte bzw. strukturierte Datentypen, die als Paramer oder Rückgabewert einer Funktion genutzt werden, sollten noch im Array $WSStructures aufgeführt werden, damit man einen typisierten Austausch dieser Daten realisieren kann.

Typisierte Daten in PHP?! Ja genau. Damit die ganze Geschichte funktioniert muss der PHP Code Dokumentiert/Typisiert werden. Die Syntax der Dokumentation ist analog der von phpDocumentor, jedoch wird nur ein Teil der Schlüsselwörter benutzt. Jeder Parameter und der Rückgabewert einer Methode muss Typisiert werden. Das geschieht durch die Schlüsselwörter @param und @return um Doc-Block (/**  */). Dazu ein kleines Beispiel:

BeispielKlasse.class.php

  1. <?php
  2.  
  3. /**
  4.   * Beispiel einer Dokumentierten Klasse
  5.   */
  6. class BeispielKlasse{
  7.  
  8.         /**
  9.          * Funktion mit mehreren Parametern und ohne Rückgabewerte
  10.          * Aufbau: @return datentyp|void
  11.          * Aufbau: @param datentyp $parametername Beschreibung
  12.          * @return void
  13.          * @param sting $text Ein übergebener String
  14.          * @param int $number Ein Integer
  15.          * @param boolean $something Ein boolean-Wahrheitswert
  16.          *
  17.          */
  18.         public function foo($text, $number, $something) {
  19.         }
  20.  
  21.         /**
  22.           * Funktion mit einfachem Rückgabewert
  23.           * @return string
  24.           */
  25.         public function helloWorld() {
  26.                 return "Hallo Welt";
  27.         }
  28.  
  29.         /**
  30.           * Funktion die ein Array eines komplexen Datentyps zurückgibt
  31.           * @return TestDatentyp[]
  32.           */
  33.         public function helloWorld() {
  34.                 $objects[] = new TestDatentyp();
  35.                 $objects[] = new TestDatentyp();
  36.                 $objects[] = new TestDatentyp();
  37.                 $objects[] = new TestDatentyp();
  38.                 return $objects;
  39.         }
  40. }
  41. ?>
  42.  
  43. Natürlich muss der genutzte Datentyp "TestDatentyp" auch definiert und ebenfalls typisiert werden:

TestDatentyp.class.php

  1. <?php
  2.  
  3. /**
  4.   * Beispiel eines strukturierten Datentyps mit zwei Attributen.
  5.   */
  6. class TestDatentyp{
  7.         /**
  8.          * Name einer Person
  9.          * @var string
  10.          */
  11.         public $name;
  12.  
  13.         /**
  14.          * Alter einer Person
  15.          * @var int
  16.          */
  17.         public $age;
  18.  
  19.         /**
  20.          * Geschlecht einer Person
  21.          * @var boolean
  22.          */
  23.         public $gender;
  24.  
  25.         /**
  26.          * Name einer Person
  27.          * @var string
  28.          */
  29.         public $name;
  30.  
  31.         //…weitere, auch strukturierte Attribute / Funktionen möglich
  32. }
  33. ?>

Sind alle Klassen geschrieben ist der WebService fertig. Denn der WSH sorgt nun dafür, dass alle Methoden, die public sind, über SOAP bereit gestellt werden. Die passende WSDL zum WebService gibts automatisch dazu. Unter folgendem Pfad kann man die WDSL beziehen:

http://deinserver/service.php?class=BeispielKlasse&wsdl

Mit dieser WSDL-Datei kann man nun auf einfachste Weise Client-Stubs generieren. Ein Tutorial dazu werde ich die Tage mal verfassen.

Bei der Arbeit mit diesem PHP WebService wird man auf zwei größere Hürden stoßen:

  1. Persistenz
  2. Codeänderungen

Zu 1: Die PHP-SOAP-Engine kennt zwei Persistenz-Modi: SOAP_PERSISTENCE_SESSION und SOAP_PERSISTENCE_REQUEST. Der SOAP_PERSISTENCE_SESSION-Modus ist der schönere von beiden ;) , PHP-Objekte werden mit all Ihren Attributen in einer Session gespeichert und sind somit über mehrere Aufrufe persistent. Da PHP Cookies nutzt um die Session-ID an den Client zu übermitteln, muss sichergestellt sein, dass der SOAP-Client dies auch unterstützt.
Der SOAP_PERSISTENCE_REQUEST-Modus hingegen deaktiviert die Persistenz und versetzt den WebService in den Amnesie-Modus :D
Leider ist die Persistenz im WSH standardmäßig deaktiviert. Um die szu ändern muss folgende Codezeile in der service.php ersetzt werden:

  1. $WSHelper->setPersistence(SOAP_PERSISTENCE_REQUEST);

ersetzten durch

  1. $WSHelper->setPersistence(SOAP_PERSISTENCE_SESSION);

Zu 2: Damit die ganze WebService Sache einigermaßen performant bleibt, arbeiten sowohl der WSH alsauch die nativen PHP-Methoden mit Caches. Damit äußere Strukturänderungen an den über SOAP bereitgestellten Klassen angewandt werdem müssen der WSDL-Cache und der PHP-Cache gelöscht werden. Die WSDL-Dateien befinden sich im Unterordner ‘wsdl’ vom WSH. Bei geändertem Code einfach alle Datien löschen, damit diese neu generiert werden können. Dazu sollte man noch den WSDL-Cache von PHP löschen. Dieser befindet sich im tmp-Ordner des Servers (/tmp). Alle Dateien die mit ‘wsdl-’ beginnen löschen – dann klappts auch mit den Änderungen

In diesem Sinne, viel Erfolg. Fragen bitte als Kommentar posten!

  • Facebook
  • Twitter
  • del.icio.us
  • Google Bookmarks
  • Digg

written by gk \\ tags: , , , , ,


3 Responses to “PHP-WebService mittels SOAP – Tutorial”

  1. 1. Scott Deagan Says:

    What are the native types that WSH can use to generate the WSDL? So far I’ve seen:

    @var string
    @var int
    @var boolean

    What about “@var date”? Perhaps this isn’t included because of different binary representations of date? Is it simply safer to use @var string?

    Any help really appreciated – thanks :)

  2. 2. gk Says:

    Hey Scott,
    I only used string, int and booloean so far, but I am sure there is no native date type. You could exchange a date over a unix-timestamp (int) or build your own one. This could looke like:

    class date{
      @var int
      public $day;
    
      @var int
      public $month;
    
      @var int
      public $year;
    }
    
  3. 3. Christian Says:

    hi,

    ich beschäftige mich auch grad mit webservices via php.
    Soap ist echt klasse.
    Ich wollt jetzt gern mit der Session-Variante arbeiten.
    Aber ganz versteh ich das nicht.

    Gerade der Teil mit dem Cookie setzte und wiederherstellen der Session wäre interessant, doch dieser fehlt hier total.
    Das mit setPercistence steht in jedem tut, aber das was man braucht um die session erfolgreich auf dem server fortzusetzten fehlt immer.
    Wäre schön hier etwas dazu zu finden ;)

    GC

Leave a Reply