Webservices (WSDL) mit PHP

Gepostet von am Jan 7, 2010 in PHP, Programmierung, Software | 4 Kommentare

Beim Thema Web­ser­vices mit PHP schei­den sich wohl die Geis­ter. Nach dem ich aus aktu­el­lem Anlass einige Biblio­the­ken getes­tet habe, bin ich zu sehr unter­schied­li­chen Erkennt­nis­sen gelangt. Wäh­rend einige Biblio­the­ken einen sehr schö­nen Ansatz haben und dem Gedan­ken der ein­fa­chen Hand­ha­bung nach­kom­men, funk­tio­nie­ren ledig­lich andere Biblio­the­ken die nicht unbe­dingt Benut­zer­freund­lich aber dafür feh­ler­frei agie­ren können.

Fol­gende Biblio­the­ken wur­den getestet:

  • PHP2WSDL1
  • WSDL­Do­cu­ment2
  • NuSoap3

Posi­tiv her­vor­zu­he­ben ist, dass jede Biblio­thek unter der GNU Les­ser Gene­ral Public License ver­öf­fent­licht wurde und somit frei für die eigene und kom­mer­zi­elle Ver­wen­dung zur Ver­fü­gung steht.

Aus­gangs­ba­sis für alle getes­te­ten Biblio­the­ken sind die fol­gen­den 3 Klas­sen. Ers­tere ist die soge­nannte Funk­ti­ons­klasse. Sie beinhal­tet die Klasse und Metho­den die als Ser­vice zur Ver­fü­gung gestellt wer­den sol­len. Als Bei­spiel wurde eine Klasse auf­be­rei­tet, wel­che die Grund­re­chen­ar­ten zur Ver­fü­gung stellt. Als Überg­a­be­pa­ra­me­ter wer­den jeweils zwei Zah­len vom Daten­typ float ver­langt. Der Rück­ga­be­wert ist in die­sem Bei­spiel eben­falls eine Zahl vom Typ float.

Mathematics.class.php

/**
 * Service class documentation.
 */
class Mathematics {

    /**
     * Addition of two numbers.
     *
     * @param float x
     * @param float y
     * @return float
     */
    public function add($x, $y) {
    	return $x + $y;
    }

    /**
     * Subtraction of two numbers.
     *
     * @param float x
     * @param float y
     * @return float
     */
    public function minus($x, $y) {
    	return $x - $y;
    }

    /**
     * Multiply of two numbers.
     *
     * @param float x
     * @param float y
     * @return float
     */
    public function multiply($x, $y) {
    	return $x * $y;
    }

    /**
     * Divide of two numbers.
     *
     * @param float x
     * @param float y
     * @return float
     */
    public function divide($x, $y) {
    	return $x / $y;
    }
}

Bei der zwei­ten Klasse han­delt es sich um die SoapClient.php. Diese wird als Test-Klasse gese­hen und prüft die zu Ver­fü­gung ste­hen­den Metho­den. Diese muss bei der Ver­wen­dung von unter­schied­li­chen Biblio­the­ken auch ent­spre­chend abge­än­dert wer­den. Aber im gro­ßen und gan­zen ändert sich nur die URL zur Ser­ver­klasse. Eben­falls kann es nötig sein diverse Biblio­the­ken vor­her einzubinden.

ServerSoap.php

<?php

ini_set('soap.wsdl_cache_enabled', "0");

try {
	// create the client instance
	$client = new SoapClient('http://localhost/SoapServer.php');

	$baseMetrics = array(
		'add' => '+',
		'minus' => '-',
		'multiply' => '*',
		'divide' => '/'
	);
	$format = '%.2f %s %.2f = %.2f <br />';

	foreach($baseMetrics as $key => $value) {
		// call the SOAP method
		$x = random(); $y = random();
		$result = $client->__soapCall($key, array('x' => $x, 'y' => $y));
		printf($format, $x, $value, $y, $result);
	}

} catch (SoapFault $fault) {
	echo '<h2>Fault</h2><pre>';
	print_r($fault);
	echo '</pre>';
}

// seed with microseconds
function random() {
   list($usec, $sec) = explode(' ', microtime());
   mt_srand((float) $sec + ( (float) $usec * 100000));
   return mt_rand(1,9) / pi();
}

/*
echo 'Request: <xmp>'.$client->request.'</xmp>';
echo 'Response: <xmp>'.$client->response.'</xmp>';
echo 'Debug log: <pre>'.$client->debug_str.'</pre>';
*/
?>

Die ServerSoap-Klasse soll an die­ser Stelle nur grob erklärt wer­den. Begin­nend mit einem ini_set wird die Cache-Funktion von Soap außer Kraft gesetzt. Zu Test­zwe­cken ist dies zwin­gend erfor­der­lich. Im Pro­duk­tiv­ein­satz muss diese dann nicht mehr initi­iert wer­den. Inner­halb einer try-catch Anwei­sung wird der eigent­li­che Pro­zess abge­ar­bei­tet. Hier­für wird ein Soap­Cli­ent initia­li­siert, wel­che als ers­tes Argu­ment die URL vom Soap­Ser­ver trägt. Ab dem zwei­ten Argu­ment wird ein Array mit Optio­nen erwar­tet, auf die an die­ser Stelle aber nicht wei­ter ein­ge­gan­gen wird. In einem ers­ten Array wer­den alle Funk­ti­ons­na­men fest­ge­hal­ten die getes­tet wer­den sol­len. Dabei han­delt es sich um die Grund­re­chen­ar­ten aus der Mathematics.class.php. Diese wer­den nach ein­an­der abge­ar­bei­tet. Mit dem client-Objekt wird ein soge­nann­ter __soapCall gestar­tet. Das erste Argu­ment trägt den Namen der Methode. Das zweite Argu­ment trägt ein Array mit den jewei­li­gen Überg­a­be­pa­ra­me­tern die ver­ar­bei­tet wer­den sol­len. Abschlie­ßend ist nur noch zu sagen das die Zah­len, zum tes­ten der Metho­den, zufäl­lig gene­riert wer­den. Hier­für wurde eine eigene random-Methode imple­men­tiert. Für fana­ti­sche Debug­ger wurde ganz zum Schluss die­ser Klasse einige Aus­ga­ben getä­tigt. Hier nun aus­kom­men­tiert, da diese nicht mit jeder Biblio­thek kom­pa­ti­bel sind.

All­ge­mein wird für jede Grund­re­chen­art ein Request gestar­tet. Und in Form einer Glei­chung auf dem Bild­schirm aus­ge­ge­ben. Die Rück­gabe stellt das Ergeb­nis der jewei­li­gen Glei­chung dar. Wie es nun mit den jewei­li­gen Soap­Ser­vern aus­sieht, wird mit den fol­gen­den, getes­te­ten Biblio­the­ken erklärt.

PHP2WSDL

Die­ses doch recht junge Pro­jekt wurde vom Rumä­nen Dra­gos Pro­tung ver­öf­fent­licht. Eine recht schlanke Biblio­thek mit gerade mal 3 Biblio­theks­klas­sen, ermög­licht es dem Anwen­der, aus einer beste­hen­den und doku­men­tier­ten PHP-Klasse ein WSDL-Dokument zu gene­rie­ren. Der dahin­ter ste­hende Gedanke bezieht sich auf die Arbeits­er­leich­te­rung, der zu regis­trie­ren­den Klas­sen und Metho­den. Bei der Ver­wen­dung die­ser Biblio­thek ent­fal­len diese Schritte. Der Anwen­der hat ledig­lich eine wohl doku­men­tiere PHP-Klasse vor­zu­le­gen und muss sich um nichts ande­res mehr kümmern.

So schön dies auch klin­gen mag, birgt diese Biblio­thek, in der Ver­sion 1.0, eine schier von Form­feh­lern. So muss bereits bei der ers­ten Ver­wen­dung klei­nere Anpas­sung an den Sour­cen vor­ge­nom­men wer­den. Auch die auto­ma­ti­sche Gene­rie­rung von Varia­blen läuft nicht feh­ler­frei ab. So wer­den zum Bei­spiel an den bereits ver­wen­de­ten Varia­blen, einer Methode in einer Klasse, fort­lau­fende Zah­len ange­hängt. Das ist natür­lich unvor­teil­haft, wenn das Script selbst die Doku­men­ta­ti­ons­va­ria­blen und die Überg­a­be­pa­ra­me­ter der Methode ver­gleicht und dem­ent­spre­chende Daten­ty­pen zuweist. Diese wer­den beim Anhän­gen von fort­lau­fen­den Zah­len nicht mehr rich­tig erkannt und erhal­ten einen unbe­kann­ten Daten­typ im WSDL-Dokument.

Zum aktu­el­len Zeit­punkt (Januar 2010) ist im haus­ei­ge­nen Sup­port­fo­rum nach­zu­le­sen das der Ent­wick­ler an der nächs­ten Ver­sion (2.0) arbei­tet, die sol­che Feh­ler einer­seits besei­ti­gen soll und neue Fea­tures und Unter­stüt­zun­gen für die Doku­men­ta­tion und Daten­ty­pen von PHP bereit hal­ten soll. Es bleibt abzu­war­ten ob hier­aus noch was wer­den wird, da diese Mel­dung bereits meh­rere Monate alt ist.

So, oder so ähnlich kann dies­be­züg­lich die SoapServer-Klasse aus­se­hen. Eine ver­nüf­tige Doku­men­ta­tion habe ich lei­der nicht gefun­den. Daher habe ich aus­kom­men­tierte Funk­ti­ons­auf­rufe im fol­gen­dem Bei­spiel belas­sen. Die Pfade sind dem­ent­spre­chend abzuändern.

SoapServer.php

<?php

ini_set('soap.wsdl_cache_enabled', "0");

require_once("../WSDLCreator.php");
//header("Content-Type: application/xml");

$test = new WSDLCreator("Mathematics", "http://localhost/php2wsdl/SoapServer.php");
//$test->includeMethodsDocumentation(false);

$test->addFile(Mathematics.class.php");
$test->setClassesGeneralURL("http://localhost/php2wsdl/example");

$test->addURLToClass("Mathematics", "http://localhost/php2wsdl/Mathematics.class.php");
$test->addURLToTypens("XMLCreator", "http://localhost/php2wsdl");

$test->createWSDL();

$test->printWSDL(true); // print with headers
//print $test->getWSDL();
//$test->downloadWSDL();
//$test->saveWSDL(dirname(__FILE__)."/test.wsdl", false);
?>

Ein unbe­streit­ba­rer Vor­teil ist die ein­fa­che Inte­grie­rung von meh­re­ren Dateien und Klas­sen. Es muss ledig­lich mit­telst add­File eine neue Funk­ti­ons­bi­blio­thek hin­zu­ge­fügt wer­den. Der Rest wird vom Script selbst erle­digt. Aus den bereits genann­ten Nach­tei­len kommt beschwe­rend hinzu das ich mit­telst die­ser Biblio­thek noch nie ein Response erhal­ten habe. Die­ser war bis­her immer NULL.

WSDL­Do­cu­ment

Bei die­ser Biblio­thek han­delt es sich ledig­lich um eine Biblio­thek die ein WSDL-Dokument gene­riert. Es ver­folgt den glei­chen Gedan­ken wie die PHP2WSDL-Bibliothek. Nur mit dem gra­vie­ren­den Unter­schied das diese feh­ler­frei funk­tio­niert. Daten­ty­pen wer­den aus der Doku­men­ta­tion rich­tig erkannt und in einem WSDL-Dokument auf­be­rei­tet. Wie auch beim Vor­gän­ger sind hier eben­falls Kom­plexe Daten­ty­pen mög­lich. Dazu gehö­ren unter ande­rem ver­schach­telte Arrays. Die Ein­bin­dung des Scripts, habe ich ein­fachs­hal­ber gleich in der Funk­ti­ons­klasse Mathe­ma­tics erle­digt. Dazu ist fol­gen­der Quell­code in der Klasse Mathematics.class.php unter­zu­brin­gen. Beim Auf­ruf erhält man dann das WSDL-Dokument.

header ("content-type: text/xml");
require_once('WSDLDocument.php');
$wsdl = new WSDLDocument('Mathematics','urn:Mathematics');
echo $wsdl->saveXML();

Den­noch sei gesagt, das die­ses Doku­ment nach einer alten Spe­zi­fi­ka­tion arbei­tet. Eine ent­spre­chende Anpas­sung ist aber rela­tiv zügig rea­li­siert. Was lei­der nicht funk­tio­niert ist die Ansteue­rung des Ser­vice selbst. Es wird hier­bei wirk­lich nur ein XML-Dokument ausgegeben.

NuSoap

Nicht Per­fekt, aber eine der am erfolg­reichs­ten getes­te­ten Biblio­the­ken, ist NuSoap. Diese kommt mit einer rela­ti­ven gro­ßen Anzahl an Klas­sen daher und rea­li­siert das gene­rie­ren von WSDL-Dokumenten. Lei­der wird hier nicht der Gedanke des Par­sens von PHP-Klassen ver­folgt. Wie es von PHP-Soap bereits bekannt ist, müs­sen Metho­den regis­triert und zusätz­lich in der Ser­ver­Soap geschrie­ben wer­den. Da man dies eigent­lich nur ein­mal macht, kann man die­sen Mehr­auf­wand aller­dings ver­nach­läs­si­gen. Eine Mathematics.class.php wird hier­bei nicht benötigt.

SoapServer.php

<?php
// Pull in the NuSOAP code
require_once('lib/nusoap.php');
// Create the server instance
$server = new soap_server();
// Initialize WSDL support
$server->configureWSDL('Mathematics', 'urn:Mathematics');
// Register the method to expose
$server->register('add',
	array('x' => 'xsd:float', 'y' => 'xsd:float'),
	array('return' => 'xsd:float'),
	'urn:Mathematics',
	'urn:Mathematics#add',
	'rpc', 'encoded', 'Addition of two numbers.');
$server->register('minus',
	array('x' => 'xsd:float', 'y' => 'xsd:float'),
	array('return' => 'xsd:float'),
	'urn:Mathematics',
	'urn:Mathematics#minus',
	'rpc', 'encoded', 'Subtraction of two numbers.');
$server->register('multiply',
	array('x' => 'xsd:float', 'y' => 'xsd:float'),
	array('return' => 'xsd:float'),
	'urn:Mathematics',
	'urn:Mathematics#multiply',
	'rpc', 'encoded', 'Multiply of two numbers.');
$server->register('divide',
	array('x' => 'xsd:float', 'y' => 'xsd:float'),
	array('return' => 'xsd:float'),
	'urn:Mathematics',
	'urn:Mathematics#divide',
	'rpc', 'encoded', 'Divide of two numbers.');
// Define the method as a PHP function
function add($x, $y) { return ($x + $y); }
function minus($x, $y) { return ($x - $y); }
function multiply($x, $y) { return ($x * $y); }
function divide($x, $y) { return ($x / $y); }
// Use the request to (try to) invoke the service
$HTTP_RAW_POST_DATA = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
$server->service($HTTP_RAW_POST_DATA);
?>

Wie man sieht, ist die Server-Klasse etwas grö­ßer gera­ten, als die bis­he­ri­gen Metho­den. Das WSDL-Dokument, wird ohne Frage rich­tig gene­riert. Ein Abzug in der B-Note gibt es für die dop­pelte Defi­nie­rung der Metho­den. Ein­mal für die Regis­trie­rung für den Soap­Ser­ver und die eigent­li­che Bereit­stel­lung. Eben­falls zu beach­ten ist die Ein­bin­dung der nusoap.php-Biblio­thek die hier­bei in einem Unter­ord­ner namens lib liegt.

Ein gro­ßer Plus­punkt ist die Auf­be­rei­tung einer „Start­seite” des Soap­Ser­vers. Gra­fisch, mit­telst CSS, wer­den alle Funk­tio­nen und des­sen regis­trierte Doku­men­ta­tion ange­zeigt. Ein wirk­lich tol­les Fea­ture, was viel­leicht die gene­relle Dokumnta­tion abneh­men kann. Das eigent­li­che WSDL-Dokument lässt sich mit dem Para­ma­ter „?wsdl” errei­chen und muss auch die URL beim Soap­Cli­ent sein.

Diese Biblio­thek wurde als erste voll­stän­dig und feh­ler­frei getes­tet und ist somit auch für den all­ge­mei­nen Ein­satz zu emp­feh­len. Schade nur das es keine Gene­rie­rung aus der PHP-Dokumentation ermög­licht. Aber was nicht ist kann ja noch werden.

Fazit

NuSoap hat sich her­aus­ra­gend geschla­gen. Mit einem klei­nen Mehr­auf­wand, Metho­den zu regis­trie­ren und zusätz­lich zu beschrei­ben, erle­digt es die Auf­gabe der Gene­rie­rung des WSDL-Dokuments und die Abfrage der Ser­vices sehr gewis­sen­haft und konnte keine Feh­ler in der Ver­ar­bei­tung vor­wei­sen. Für Begin­ner eige­nen sich den­noch Biblio­the­ken wie PHP2WSDL und WSDL­Do­cu­ment. Um ein Gefühl für die Mate­rie zu erhal­ten sollte man sich auch vor­her mit dem eigent­li­chen Auf­bau von WSDL-Dokumenten beschäf­tigt haben.

  1. http://sourceforge.net/projects/php2wsdl []
  2. http://code.google.com/p/wsdldocument []
  3. PHP -5: http://sourceforge.net/projects/nusoap
    PHP +5: http://code.google.com/p/nusoap-for-php5 []

4 Antworten : “Webservices (WSDL) mit PHP

  1. Her­aus­ra­gend!!! Vie­len Dank für die Infos. Auch ich habe meh­rere Mög­lich­kei­ten ver­sucht und mich aus eben die­sen Grün­den für nusoap ent­schie­den. Bin nun froh auch von einer ande­ren Stelle in mei­ner Ent­schei­dung bestärkt zu werden.

    Eine Sache aber hat mich ver­wun­dert:
    $server->register(‚multiply‘,
    22 array(‚x‘ => ‚xsd:float‘, ‚y‘ => ‚xsd:float‘),
    23 array(‚return‘ => ‚xsd:float‘),
    24 ‚urn:Mathematics‘,
    25 ‚urn:Mathematics#multiply‘,
    26 ‚rpc‘, ‚encoded‘, ‚Mul­ti­ply of two num­bers.‘);
    und der Auf­ruf:
    $client->__soapCall($key, array(‚x‘ => $x, ‚y‘ => $y));
    funk­tio­niert bei mir nicht. Gehen würde
    $client->__soapCall($key, $x,$y);
    eine Idee wes­halb? Ich bekomme ansons­ten nur eine 1 für den ers­ten an die Funk­tion über­ge­be­nen Para­me­ter. Ist die­ser nicht gerade float oder int son­dern string lau­tet die­ser Array. gettype($x) ver­rät mir dann, dass Array eben­falls nur ein String ist.

  2. EDIT: (Evtl. hin­zu­fü­gen)
    Zudem kann ich den Ser­ver nicht in Betrieb neh­men, wenn das Return nicht als soapval(‚return‘, ‚xsd:float‘, ($x * $y)) defi­niert wurde

  3. php2wsdl finde ich ja ehr­lich gesagt nicht so gut. Du kannst auch die Reflec­tion API nut­zen um die WSDL zu gen­eie­ren. Wie das funk­tio­niert gibts hier zu lesen: http://www.easy-coding.de/wiki/php/php-soap-server-mit-wsdl-und-api-schluessel.html

  4. Frank Gottschlich sagt:

    php2wsdl ist auch nur eine getes­tete Biblio­thek. Das diese für den Pro­duk­tiv­ein­satz unge­eig­net ist, geht hof­fent­lich aus dem Bei­trag her­vor. Im Fazit lässt sich ja erken­nen das NuSoap am bes­ten geeig­net ist.

Einen Kommentar schreiben