ExtendedSoapClientService

1. Einleitung

Das APF enthält eine eigene SOAP-Client-Implementierung. Diese baut auf der in PHP enthaltenen SOAP-Extension auf und integriert diese in das APF. Ein weiterer Pluspunkt ist die gegenüber der Standard-Implementierung vereinfachte API.

Die APF-SOAP-Client-Implementierung bietet dabei alle Funktionen des nativen SoapClient, vereinfacht jedoch die Konfiguration des ExtendedSoapClientService über den APF DI-Container.

2. Konfiguration

Die Konfiguration des ExtendedSoapClientService kann auf zwei Arten erfolgen: direkt im Code oder über den DIServiceManager.

2.1. Konfiguration im Code

Die Anwendung des ExtendedSoapClientService im Code erfordert im einfachsten Fall die Angabe einer WSDL oder einer WSDL und einer Service-Url. Um einen Login-Aufruf gegen einen SOAP-Service abzusenden, kann folgendes Beispiel genutzt werden:

PHP-Code
use APF\tools\soap\ExtendedSoapClientService; $service = new ExtendedSoapClientService(); $service->setWsdlUrl('./login-service.wsdl'); $service->setLocation('https://example-com/services/login/soap'); $result = $service->Login(array( 'user' => $user, 'pass' => $pass ));

In den ersten beiden Zeilen wird die initiale Konfiguration des SOAP-Clients erstellt. Das Beispiel geht von einer lokal abgelegten WSDL-Datei aus. Ebenso möglich ist die Angabe einer entfernt abgelegten WSDL.

Sofern der Service-Endpunkt direkt in der WSDL definiert ist, wird die Angabe in Zeile 3 nicht benötigt. Zeile 5 zeigt den Aufruf des in der WSDL definierten Login-Service.

Um die Konstruktionszeit des ExtendedSoapClientService zu optimieren, ist eine lokal abgelegte WSDL empfehlenswert. Sofern dies nicht möglich oder nicht gewünscht ist, kann die Option soap.wsdl_cache_enabled in der php.ini aktiviert werden. Um optimale Ergebnisse zu erzielen sollten die Optionen soap.wsdl_cache_ttl und soap.wsdl_cache_limit mit sinnvollen Werten belegt werden.

2.2. Konfiguration per DIServiceManager

Der Einsatz des DIServiceManager zur Erzeugung des SOAP-Client hat zwei wesentliche Vorteile: Konfiguration und Anwendung können auf einfache Weise getrennt werden und der Einsatz von MOCK-Schichten für Entwicklung und/oder Tests wird deutlich einfacher.

Zur Erzeugung und Konfiguration des ExtendedSoapClientService ist folgende minimale Konfiguration notwendig:

APF-Konfiguration
[LoginService] servicetype = "..." class = "APF\tools\soap\ExtendedSoapClientService" conf.wsdl.method = "setWsdlUrl" conf.wsdl.value = "./login-service.wsdl" conf.service.method = "setLocation" conf.service.value = "https://example-com/services/login/soap"

Sie können den LoginService nun innerhalb eines Controllers oder einer anderen Komponente Ihrer Software wie folgt beziehen und verwenden:

PHP-Code
$service = $this->getDIServiceObject('...', 'LoginService'); $result = $service->Login(array( 'user' => $user, 'pass' => $pass ));

Die Namespace-Angabe im getDIServiceObject()-Aufruf richtet sich nach der Konfiguration Ihrer Applikation. Details zur Konfiguration von Services finden Sie unter Komplexe Services, im Kapitel Konfiguration finden Sie Hinweise zur Definition von Konfigurationen mit dem APF.

Ein ausführliches Beispiel kann der Datei config/tools/soap/EXAMPLE_serviceobjects.ini im apf-configpack-*-Paket entnommen werden.

Um weitere Einstellungen zu treffen, nutzen Sie bitte die im Kapitel 2.3 beschriebenen Methoden im Rahmen einer weiteren

APF-Konfiguration
conf.xyz.method = "..." conf.xyz.value = "..."
Sektion in Ihrer Service-Definition.

2.3. Übersicht der Paramater

In der folgenden Liste finden Sie die Konfigurationsmöglichkeiten des ExtendedSoapClientService. Diese können sowohl zur direkten Konfiguration als auch in Verbindung mit einer Service-Definition verwendet werden:

Parameter Methode Beschreibung
wsdlUrl setWsdlUrl() Mit diesem Parameter wird der Service-Vertrag definiert (WSDL-Datei). Diese kann entweder lokal abgelegt oder vom Service-Endpunkt bezogen werden.
location setLocation() Definiert die Url des Service-Endpunkts. Dieser kann entweder explizit oder in der WSDL definiert werden.
login setHttpAuthUsername() Definiert den Benutzer für HTTP base authentication-geschützte Services.
password setHttpAuthPassword() Definiert das Password für HTTP base authentication-geschützte Services.
compression setCompressionLevel() Aktiviert die komprimierte Übertragung der Nutzlast der SOAP-Anfragen und -Antworten. Als mögliche Werte können die Konstanten SOAP_COMPRESSION_ACCEPT, SOAP_COMPRESSION_GZIP, SOAP_COMPRESSION_DEFLATE oder eine Kombination übergeben werden.
connection_timeout setConnectionTimeout() Definiert den Timeout für die Verbindung zu einem SOAP-Service. Dieser Wert greift nicht für Services mit langsamer Antwortzeit. Um die maximale Zeit für Anfragen zu definieren, nutzen Sie bitte die Direktive default_socket_timeout in der php.ini.
cache_wsdl setCacheWsdl() Aktiviert das Caching einer entfernt angebotenen WSDL-Datei. Bitte beachten Sie die Einstellungsmöglichkeiten dazu in der php.ini!
encoding setEncoding() Legt den Zeichensatz der übergebenen und zurückgelieferten Zeichen fest. Als Standardwert wird UTF-8 gesetzt.
soap_version setSoapVersion() Definiert die verwendete SOAP-Version. Als mögliche Werte können die Konstanten SOAP_1_1 und SOAP_1_2 werden.
classmap registerWsdlObjectMapping() Mit dieser Methode haben Sie die Möglichkeit Objekt-Mapping-Definitionen zu definieren. Als Argument wird eine Instanz der Klasse WsdlObjectMapping erwartet. Details können dem Kapitel 3.3 entnehmen.
proxy_host setProxyHost() Sofern die Kommunikation mit einem SOAP-Service über einen Proxy stattfinden soll, definieren Sie mit dieser Methode bitte den Proxy-Server.
proxy_port setProxyPort() Für die Kommunikation mit einem Proxy-Server können Sie mit dieser Methode den Port des Proxy-Servers festlegen.
proxy_login setProxyUsername() Erfordert der Proxy-Server eine Authentifizierung, übergeben Sie bitte in dieser Methode den Benutzer-Namen.
proxy_password setProxyPassword() Erfordert der Proxy-Server eine Authentifizierung, übergeben Sie bitte in dieser Methode das Passwort.
user_agent setUserAgent() Mit der vorliegenden Option können Sie eine Identifikation ihres Clients definieren. Dieser wird bei der SOAP-Anfrage mit dem UserAgent-Header übertragen.
features enableFeature() Die PHP SOAP-Client-Implementierung unterstützt einige weitere Features. Details können der Dokumentation auf php.net/soap entnommen werden. Mit dieser Methode können sie ein oder mehrere Features aktivieren.

Details zur Signatur der Methoden entnehmen Sie bitte der API-Dokumentation.

Die Konfigurationsmethoden unterstützen das fluent interface. Bei manueller Konfiguration können Parameter daher einfach in der Form
PHP-Code
use APF\tools\soap\ExtendedSoapClientService; $service = new ExtendedSoapClientService(); $service ->setProxyHost('proxy-server') ->setProxyPort(8080) ->setProxyUsername('foo') ->setProxyPassword('bar');
definiert werden.

Details zu den in der Tabelle aufgeführten Features der PHP-SOAP-Extension finden Sie unter php.net/manual/de/soapclient.soapclient.php.

3. Anwendung

Mit dem ExtendendSoapClientService lassen sich zwei Arten der SOAP-Kommunikation abbilden: auf XML- und Objekt-Basis. Die folgenden Kapitel beschreiben die beiden Möglichkeiten in der Anwendung.

Um eine einfache Verwendung der von einer SOAP-Anfrage zurückgegebenen Daten sicherzustellen, empfiehlt sich die Kommunikation basierend auf Objekten (DTOs). Diese können helfen, die Signaturen der Methoden Ihrer Anwendung besser zu typisieren.

3.1. Kommunikation per XML

Nutzen Sie die executeRequest()-Methode des ExtendedSoapClientService, so wird die gewünschte Anfrage im XML-Format erwartet. Hierzu ist es empfehlenswert, SoapUI einzusetzen. Mit Hilfe dieses Tools lassen sich mit der gewünschten WSDL-Datei sehr einfach Beispiel-Anfragen generieren und testen. Diese können Sie dann anschließend direkt in Ihren Quellcode übernehmen und mit den entsprechenden Werten befüllen.

Die Methode executeRequest() nimmt dabei die auszuführende SOAP-Action und ein Anfrage-XML entgegen. Als Ergebnis wird ein XML-Dokument basierend auf einem SimpleXMLElement zurückgeliefert. Beispiel:

PHP-Code
use APF\tools\soap\ExtendedSoapClientService; $request = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header/> <soapenv:Body> <type:AuthenticateRequest> <type:ConsumerIdentification> <type:ConsumerAuthentication> <type:Principal>...</type:Principal> <type:Credential>...</type:Credential> </type:ConsumerAuthentication> </type:ConsumerIdentification> <type:Authentication> <type:Identification> <type:Alias>...</type:Alias> </type:Identification> <type:Security> <type:SecretType>...</type:SecretType> <type:Secret>...</type:Secret> </type:Security> </type:Authentication> </type:AuthenticateRequest> </soapenv:Body> </soapenv:Envelope>'; $client = new ExtendedSoapClientService(); $client->setWsdlUrl('https://example.com/services/v1?wsdl'); $client->setLocation('https://example.com/services/v1'); $responseXml = $client->executeRequest('Authenticate', $request);

Der Inhalt der Variable $responseXml (Instanz eines SimpleXMLElement) kann nun innerhalb Ihrer Applikation direkt weiter verwendet werden.

3.2. Kommunikation über DTOs

Nutzen Sie die __call()-Methode des ExtendedSoapClientService haben Sie die Möglichkeit, das Objekt-Mapping-Feature zu nutzen.

Die Methode __call() - bzw. die entsprechende SOAP-Methode - nimmt ein assoziatives Array der Anfrage-Parameter entgegen. Als Antwort erhalten Sie die Struktur der Antwort in Form der zuvor definierten DTOs zurück (sofern konfiguriert!). Beispiel:

PHP-Code
use APF\tools\soap\ExtendedSoapClientService; $request = array( 'ConsumerIdentification' => array( 'ConsumerAuthentication' => array( 'Principal' => '...', 'Credential' => '...' ) ), 'Authentication' => array( 'Identification' => array( 'Alias' => '...' ), 'Security' => array( 'SecretType' => '...', 'Secret' => '...' ) ) ); $client = new ExtendedSoapClientService(); $client->setWsdlUrl('https://example.com/services/v1?wsdl'); $client->setLocation('https://example.com/services/v1'); $response = $client->Authenticate($request);

Bitte beachten Sie, dass der Aufbau des Anfrage-Arrays hinsichlich der Hierarchie mit der Typ-Definition in der WSDL übereinstimmen muss. Andernfalls kann die PHP SOAP-Extension die Werte nicht korrekt zuordnen und es kommt zu Fehlern.

Die Namen der Array-Offsets müssen mit den XSD-Daten-Typ-Namen übereinstimmen (vergleiche Anfrage-Struktur aus Kapitel 3.1).

In der Variable $response steckt nun die Instanz einer DTO-Klasse. Bitte beachten Sie die Hinweise zur Konfiguration von Objekt-Mappings in Kapitel 3.3.

Die Definition der Anfrage kann statt einem assoziativen Array auch mit Hilfe einer Objekt-Struktur bestehend aus Instanzen der Klasse stdClass formuliert werden. Die zuvor genutzte Anfrage gestaltet sich damit wie folgt:
PHP-Code
$request = new stdClass(); $request->ConsumerIdentification = new stdClass(); $request->ConsumerIdentification->ConsumerAuthentication = new stdClass(); $request->ConsumerIdentification->ConsumerAuthentication->Principal = '...'; $request->ConsumerIdentification->ConsumerAuthentication->Credential = '...'; $request->Authentication = new stdClass(); $request->Authentication->Identification = new stdClass(); $request->Authentication->Identification->Alias = '...'; $request->Authentication->Security = new stdClass(); $request->Authentication->Security->SecretType = '...'; $request->Authentication->Security->Secret = '...';

3.3. Konfiguration Objekt-Mapping

Das Mapping von XSD-Typen auf PHP-Objekte ist ein mächtiges Werkzeug das jedoch gleichzeitig einige Tücken besitzt. Aus diesem Grund stellt der ExtendedSoapClientService ein einfaches Interface für die Konfiguration zur Verfügung. Dieses basiert auf der Klasse WsdlObjectMapping, die folgende Signatur besitzt:

PHP-Code
class WsdlObjectMapping extends APFObject { public function __construct($wsdlType = null, $phpClassName = null) { ... } public function setPhpClassName($phpClassName) { ... } public function getPhpClassName() { ... } public function setWsdlType($wsdlType) { ... } public function getWsdlType() { ... } }

Zur Konstruktion einer Mapping-Definition ist die Typ-Deklaration aus der WSDL bzw. XSD, der Namespace der PHP-Klasse und deren Namen notwendig.

Der Name der Typ-Deklaration muss dem Namen des WSDL- bzw. XSD-Typs entsprechen, nicht dem Tag-Namen bei der Verwendung desselben. Wird im Rahmen einer Anfrage eine Struktur der Form
XML-Code
<type:Identification> <type:Alias>...</type:Alias> </type:Identification>
verwendet und liegt eine Typ-Deklaration von
XML-Code
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="MemberIdentificationType"> <xs:complexType> <xs:sequence> <xs:element type="xs:string" name="Alias"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
vor, so ist $wsdlType mit dem Wert MemberIdentificationType zu belegen. Verwenden Sie statt dessen Identification oder type:Identification schlägt das Mapping fehl.

Die Konfiguration von Mapping-Definitionen kann entweder über die Service-Konfiguration bei Verwendung des DIServiceManager und zusätzlich oder ausschließlich durch Nutzung der registerWsdlObjectMapping()-Methode erledigt werden.

3.3.1. Konfiguration über Service-Konfiguration

Der APF DI-Container bietet die Möglichkeit, Services mit anderen Services zu konfigurieren. Dies lässt sich im Fall des ExtendedSoapClientService dazu nutzen, um Mapping-Definitionen zu injitieren.

Soll der im vorangegangenen Kapitel beschriebene Typ bei der Konstruktion des Services hinzugefügt werden, kann dies mit folgender Service-Definition erfolgen:

APF-Konfiguration
[LoginService] servicetype = "..." class = "APF\tools\soap\ExtendedSoapClientService" conf.wsdl.method = "setWsdlUrl" conf.wsdl.value = "./login-service.wsdl" conf.service.method = "setLocation" conf.service.value = "https://example-com/services/login/soap" init.auth-response.method = "registerWsdlObjectMapping" init.auth-response.name = "VENDOR\..\AuthenticationResponseMapping" [AuthenticationResponseMapping] servicetype = "NORMAL" class = "APF\tools\soap\WsdlObjectMapping" conf.type.method = "setWsdlType" conf.type.value = "AuthenticationResponseType" conf.class.method = "setPhpClassName" conf.class.value = "VENDOR\..\AuthenticationResponse"

Der "Service" AuthenticationResponseMapping definiert dabei über die Klasse WsdlObjectMapping das Objekt-Mapping für den XSD-Typ AuthenticationResponseType. Bei der Konstruktion des Service LoginService wird dieser injiziert und steht bei der Verwendung zur Verfügung. Die Anzahl der Mapping-Definitionen ist nicht begrenzt.

Damit Antworten mit einer Instanz der Klasse AuthenticationResponse repräsentiert werden kann, muss diese erstellt werden. Die Klasse selbst ist dabei eine einfache PHP-Klasse mit entsprechenden Variablen für die Inhalte der Antwort. Das folgende Beispiel geht davon aus, dass bei einem erfolgreichen Login das Datum der letzten Anmeldung und bei einem Fehler eine Exception zurückgegeben wird. Die erfolgreiche Antwort hat folgende XML-Struktur:

XML-Code
<type:AuthenticateResponse> <type:LastLoginDate>...</type:LastLoginDate> </type:AuthenticateResponse>

Die zugehörige PHP-Klasse hat damit folgende Signatur:

PHP-Code
class AuthenticationResponse { private $LastLoginDate; public function getLastLoginDate() { ... } }

Das obenstehende Beispiel geht davon aus, dass die Antwort keine komplexen Typen beinhaltet. Sofern dies der Fall ist, ist es erforderlich, die Typen aller Ergebnis-Typen zu registrieren. Ferner müssen die DTO-Klassen die Struktur der zurückgelieferten Typen beachten. Dies bedeutet beispielsweise, dass für das komplexe Ergebnis innerhalb des AuthenticationResponse eine Variable für einen komplexen Typen vorgesehen werden muss.

Ist das Mapping hinsichtlich der Hierarchie unterbrochen oder wurde kein Mapping definiert, liefert PHP eine Struktur bestehend aus Instanzen von stdClass zurück.

Wird eine Instanz des ExtendedSoapClientService für mehrere Anwendungsfälle genutzt und dies beziehen sich auf gleiche XSD-Typen empfiehlt sich ebenso die Konfiguration der Objekt-Mappings per Service-Konfiguration. Die übrigen Typen können dann pro Anwendungsfall wie im folgenden Kapitel beschrieben nachgereicht werden.
3.3.2. Konfiguration über registerWsdlObjectMapping()

Soll für eine SOAP-Anfrage ein Datentyp oder ein zusätzlicher Datentyp registriert werden, können Sie wie folgt vorgehen:

PHP-Code
use APF\tools\soap\ExtendedSoapClientService; use APF\tools\soap\WsdlObjectMapping; $client->registerWsdlObjectMapping( new WsdlObjectMapping( 'AuthenticationResponseType', 'VENDOR\..\AuthenticationResponse' ) );

$client ist dabei eine Instanz des ExtendedSoapClientService der wie in Kapitel 2 beschrienen erzeugt wurde. Der Namespace des DTOs (zweites Argument der Klasse WsdlObjectMapping) ist dabei abhängig von Ihrer Projekt-Struktur.

Durch mehrfachen Aufruf von registerWsdlObjectMapping() ist es analog zu mehrfach vorhandenen Konfigurationssektionen möglich, mehrere Typen zu registrieren.

4. Beispiele

4.1. Logging

Bei der Nutzung eines SOAP-Interface können unterschiedliche Arten von Störungen auftreten: fachliche und technische. Damit während des Betriebs ausreichend Informationen für eine Fehleranalyse zur Verfügung stehen, ist es empfehlenswert die wesentlichen Informationen in Log-Dateien zu schreiben.

Hierzu können Sie die Möglichkeiten des Logger und die Methoden getLastRequest() und getLastResponse() des ExtendedSoapClientService nutzen. Führen wir das Beispiel aus Kapitel 3 weiter, so können Sie im Fehlerfall die relevanten Funktionen wie folgt in eine Log-Datei schreiben:

PHP-Code
use APF\core\logging\Logger; use APF\core\logging\LogEntry; use APF\core\singleton\Singleton; use APF\tools\soap\ExtendedSoapClientService; /* @var $client ExtendedSoapClientService */ $client = $this->getDIServiceObject('...', 'LoginService'); /* @var $logger Logger */ $logger = Singleton::getInstance(Logger::class); try { $response = $client->Authenticate($request); } catch (Exception $e) { $logger->logEntry( 'service_calls', $e->getMessage() . ' Details: ' . $e, LogEntry::SEVERITY_ERROR); }

Sollen zusätzlich die Anfrage und Antwort der Anfrage aufgezeichnet werden, lässt sich das Schwellwert-Feature des Loggers nutzen:

PHP-Code
use APF\core\registry\Registry; use APF\core\logging\Logger; use APF\core\logging\LogEntry; use APF\core\singleton\Singleton; use APF\tools\soap\ExtendedSoapClientService; /* @var $client ExtendedSoapClientService */ $client = $this->getDIServiceObject('...', 'LoginService'); /* @var $logger Logger */ $logger = Singleton::getInstance(Logger::class); $logTarget = 'service_calls'; $writer = clone $l->getLogWriter( Registry::retrieve('APF\core', 'InternalLogTarget') ); $l->addLogWriter($logTarget, $writer); try { $response = $client->Authenticate($request); $logger->logEntry( $logTarget, $client->getLastRequest(), LogEntry::SEVERITY_TRACE); $logger->logEntry( $logTarget, $client->getLastResponse(), LogEntry::SEVERITY_TRACE); } catch (Exception $e) { $logger->logEntry( $logTarget, $e->getMessage() . ' Details: ' . $e, LogEntry::SEVERITY_ERROR); $logger->logEntry( $logTarget, $client->getLastRequest(), LogEntry::SEVERITY_INFO); $logger->logEntry( $logTarget, $client->getLastResponse(), LogEntry::SEVERITY_INFO); }

Die Inhalte der aktuellen Anfrage und der zugehörigen Antwort werden innerhalb der try-Klammer nur dann in die Log-Datei geschrieben, wenn der Schwellwert entsprechend konfiguriert ist. In jedem Fall wird die Anfrage bei einer fehlerbehafteten Antwort aufgezeichnet.

Um das beschriebene Verhalten zu erreichen, muss die Einstellung

PHP-Code
$logger->setLogThreshold(Logger::$LOGGER_THRESHOLD_ALL);

für den Entwicklungsbetrieb und

PHP-Code
$logger->setLogThreshold(Logger::$LOGGER_THRESHOLD_INFO);

für den Produktionsbetrieb vorgenommen werden.

4.2. Auslesen von registrierten Typen und Funktionen

Die PHP SOAP-Implementierung unterstützt die Ausgabe von für einen Service definierte Daten-Typen und Methoden. Dies ist nicht nur für die Definition von Objekt-Mappings interessant, sondern gibt auch auskunft darüber, ob PHP die geladene WSDL korrekt verarbeitet hat.

Um die für einen Service definierten Typen auszugeben, nutzen Sie bitte

PHP-Code
echo $client->getTypes();

für die registrierten Methoden

PHP-Code
echo $client->getFunctions();

Um die Lesbarkeit zu verbessern können die zurückgegebenen Arrays mit Hilfe von var_dump(), print_r() oder der APF-Funktion printObject() ausgegeben werden.

4.3. Kapselung eines SOAP-Services

Die Kapselung von Funktionen gilt gemeinhin als Vorteil für die Wartbarkeit und Austauschbarkeit von Funktionalitäten innerhalb einer Software. Im Sinne einer mehrschichtigen Architektur ist es daher sinnvoll, eine externe Schnittstelle vom Rest der Anwendung zu kapseln. Damit lässt sich nicht nur die Schnittstelle eigenständig implementieren und testen, sondern bei Bedarf auch gegen eine andere Implementierung oder Technologie austauschen.

Zur Unterstützung bietet das APF die Nutzung des APF DI-Containers in Verbindung mit Interfaces an. Damit lässt sich die genutzte Service-Implementierung per Konfiguration austauschen und gegebenenfalls gegen eine MOCK-Implementierung austauschen. Erstellen Sie einen Service nach den genannten Prinzipien und Techniken, lässt sich der erstellte Code zumeist einfacher mit Unit-Tests validieren.

Ein weiterer Vorteil dieser Vorgehensweise ist ebenso, dass sich die Service-Implementierung nicht mehr um die Erstellung und Konfiguration des SOAP-Client kümmern muss. Dies übernimmt der DI-Container und stellt der Anwendung einen sofort einsetzbaren Service zur Verfügung.

Als Beispiel für dieses Kapitel soll die oben beschrieben Authentifizierungsschnittstelle genutzt werden. Aufgabe ist es nun, einer Anwendung einen Service zur Verfügung zu stellen, der eine Authentifizierung gegegüber einem Dritten - in diesem Fall unserem SOAP-Interface - durchführt.

4.3.1. Definition des Interfaces

Zur internen Repräsentation des Authentifizierungsservice (="Wahrnehmung" des Services innerhalb der Applikation) soll folgendes Interface dienen:

PHP-Code
interface AuthenticationService { /** * @param string $username The user's name. * @param string $password The user's password. * @return bool True in case the authentication succeeded, false if it fails. */ public function authenticate($username, $password); }
4.3.2. Implementierung des Service

Ausgehend von der Interface-Definition aus dem vorangegangenen Kapitel kann nun der Service implementiert werden. Da dieser den ExtendedSoapClientService nutzt und über den DI-Container konfiguriert wird, muss eine entsprechende Methode zur Injektion des SOAP-Client bereitgestellt werden.

PHP-Code
use APF\core\logging\Logger; use APF\core\logging\LogEntry; use APF\core\singleton\Singleton; use APF\tools\soap\ExtendedSoapClientService; use VENDOR\..\AuthenticationService; class AuthenticationServiceImpl implements AuthenticationService { /** * @var ExtendedSoapClientService */ private $client; public function setClient(ExtendedSoapClientService $client) { $this->client = $client; } public function authenticate($username, $password) { /* @var $logger Logger */ $logger = Singleton::getInstance(Logger::class); $request = array( 'ConsumerIdentification' => array( 'ConsumerAuthentication' => array( 'Principal' => '...', 'Credential' => '...' ) ), 'Authentication' => array( 'Identification' => array( 'Alias' => $username ), 'Security' => array( 'SecretType' => 'Password', 'Secret' => $password ) ) ); try { $this->client->Authenticate($request); return true; } catch (\Exception $e) { $logger->logEntry( 'service_calls', 'Authentication failed: ' . $e->getMessage(), LogEntry::SEVERITY_ERROR); return false; } } }
4.3.3. Konfiguration des Service

Um den Login-Service nutzen zu können, ist eine Konfiguration für den DI-Container erforderlich. Diese übernimmt die Konfiguration des AuthenticationService und die Parametrisierung des SOAP-Clients.

Der APF DI-Container bietet Ihnen die Möglichkeit, Services als abhängige Resource von weiteren Services zu verwenden. Der AuthenticationService ist im aktuellen Beispiel vom SOAP-Service abhängig. Damit könnte der SOAP-Service in weiteren Interface-Implementierungen genutzt werden.

Um die Komplexität der Konfiguration zu begrenzen, wird im weiteren davon ausgegangen, dass beide Service-Definitionen in einer Datei abgebildet werden. Dies beschränkt die Wiederverwendung nicht, erhöht jedoch die Übersichtlichkeit des Beispiels.

Die Konfiguration des LoginService gestaltet sich damit wie folgt:

APF-Konfiguration
[LoginService] servicetype = "SINGLETON" class = "VENDOR\..\AuthenticationServiceImpl" init.soap-client.method = "setClient" init.soap-client.namespace = "VENDOR\.." init.soap-client.name = "SoapService" [SoapService] servicetype = "SINGLETON" class = "APF\tools\soap\ExtendedSoapClientService" conf.wsdl.method = "setWsdlUrl" conf.wsdl.value = "http://example.com/.../soap?wsdl" conf.service.method = "setLocation" conf.service.value = "http://example.com/.../soap"
4.3.4. Nutzung des Service

Unter der Annahme, dass die Konfiguration im Namespace VENDOR\.. abgelegt ist, kann der LoginService wie folgt verwendet werden:

PHP-Code
use VENDOR\..\AuthenticationServiceImpl; /* @var $service AuthenticationServiceImpl */ $service = &$this->getDIServiceObject('VENDOR\..', 'LoginService'); if ($service->authenticate($username, $password)) { echo 'Login succeeded!'; } else { echo 'Login failed!'; }

Details zur Definition von Konfigurationen mit dem APF entnehmen Sie bitte dem Kapitel Konfiguration, mehr Informationen zur Service-Konfiguration über den DI-Container finden sich im Kapitel Services.

5. Tipps und Tricks

Dieses Kapitel stellt aus der Entwicklung gewonnene Erfahrungen zusammen. Diese sollen die Arbeit mit der PHP-SOAP-Extension erleichtern und Sie auf bekannten Hürden hinweisen.

  • executeRequest() erwartet eine vollständige XML-Anfrage: Nutzen Sie die Methode executeRequest(), so erwartet die PHP-SOAP-Extension wie im Kapitel 3.1 beschrieben ein vollständiges Anfrage-XML. Wird nur der Inhalt des Body gesendet schlägt die Anfrage fehl.
  • Abbildung Einzelelemente auf Arrays: Beinhaltet das XSD-Schema des konsumierten Services Listen, die auch Einzelelemente beinhalten können, so es empfehlenswert das Feature SOAP_SINGLE_ELEMENT_ARRAYS zu aktivieren. Damit werden auch Einzelelemente als Liste abgebildet und führt innerhalb einer Anwendung (z.B. in einer foreach-Schleife) nicht zu Fehlern.
  • Vererbung nicht implementiert: Die PHP-SOAP-Extension unterstützt keine Vererbung von Typen innerhalb einer Schema-Definition (siehe <xsd:redefine />). Hierzu existiert bereits ein Bug unter bugs.php.net.
  • Objekt-Mapping nur per magic call nutzbar: Die PHP-SOAP-Extension führt das Objekt-Mapping nur in Verbindung mit der Nutzung der __call()-Methode des ExtendedSoapClientService aus. Dieses Verhalten lässt sich auch bei nativer Nutzung des SoapClient feststellen.

Kommentare

Möchten Sie den Artikel eine Anmerkung hinzufügen, oder haben Sie ergänzende Hinweise? Dann können Sie diese hier einfügen. Die bereits verfassten Anmerkungen und Kommentare finden Sie in der untenstehenden Liste.
Für diesen Artikel liegen aktuell keine Kommentare vor.