From 414fcd7bcc6d66735985bebde89a3eae89c21c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Wed, 12 Sep 2018 16:13:56 +0200 Subject: [PATCH] Respect WSDL type definitions when decoding and support classmap option --- README.md | 11 +++++++++ src/Client.php | 37 ++++++++++++++++------------ src/Protocol/ClientDecoder.php | 20 ++++++--------- tests/FunctionalTest.php | 35 ++++++++++++++++++++++++-- tests/Protocol/ClientDecoderTest.php | 23 +++++++++++++++-- 5 files changed, 94 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 8720f83..86bbbde 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,17 @@ $client = new Client($browser, $wsdl, array( )); ``` +You can use the `classmap` option to map certain WSDL types to PHP classes +like this: + +```php +$client = new Client($browser, $wsdl, array( + 'classmap' => array( + 'getBankResponseType' => BankResponse::class + ) +)); +``` + If you find an option is missing or not supported here, PRs are much appreciated! diff --git a/src/Client.php b/src/Client.php index da53727..e085ee3 100644 --- a/src/Client.php +++ b/src/Client.php @@ -120,6 +120,17 @@ * )); * ``` * + * You can use the `classmap` option to map certain WSDL types to PHP classes + * like this: + * + * ```php + * $client = new Client($browser, $wsdl, array( + * 'classmap' => array( + * 'getBankResponseType' => BankResponse::class + * ) + * )); + * ``` + * * If you find an option is missing or not supported here, PRs are much * appreciated! * @@ -143,12 +154,11 @@ final class Client */ public function __construct(Browser $browser, $wsdlContents, array $options = array()) { + $wsdl = $wsdlContents !== null ? 'data://text/plain;base64,' . base64_encode($wsdlContents) : null; + $this->browser = $browser; - $this->encoder = new ClientEncoder( - $wsdlContents !== null ? 'data://text/plain;base64,' . base64_encode($wsdlContents) : null, - $options - ); - $this->decoder = new ClientDecoder(); + $this->encoder = new ClientEncoder($wsdl, $options); + $this->decoder = new ClientDecoder($wsdl, $options); } /** @@ -180,8 +190,13 @@ public function soapCall($name, $args) return $deferred->promise(); } + $decoder = $this->decoder; + return $this->browser->send($request)->then( - array($this, 'handleResponse') + function (ResponseInterface $response) use ($decoder, $name) { + // HTTP response received => decode results for this function call + return $decoder->decode($name, (string)$response->getBody()); + } ); } @@ -262,14 +277,4 @@ public function getLocation($function) // encode request for given $function return (string)$this->encoder->encode($function, array())->getUri(); } - - /** - * @param ResponseInterface $response - * @return mixed - * @internal - */ - public function handleResponse(ResponseInterface $response) - { - return $this->decoder->decode((string)$response->getBody()); - } } diff --git a/src/Protocol/ClientDecoder.php b/src/Protocol/ClientDecoder.php index 7d3e61f..39cb745 100644 --- a/src/Protocol/ClientDecoder.php +++ b/src/Protocol/ClientDecoder.php @@ -11,28 +11,24 @@ final class ClientDecoder extends SoapClient { private $response = null; - public function __construct() - { - // do not pass actual WSDL to parent constructor - // use faked non-wsdl-mode to let every method call pass through (pseudoCall) - parent::__construct(null, array('location' => '1', 'uri' => '2')); - } - /** * Decodes the SOAP response / return value from the given SOAP envelope (HTTP response body) * + * @param string $function * @param string $response * @return mixed * @throws \SoapFault if response indicates a fault (error condition) or is invalid */ - public function decode($response) + public function decode($function, $response) { - // temporarily save response internally for further processing + // Temporarily save response internally for further processing $this->response = $response; - // pretend we just invoked a SOAP function. - // internally, use the injected response to parse its results - $ret = $this->pseudoCall(); + // Let's pretend we just invoked the given SOAP function. + // This won't actually invoke anything (see `__doRequest()`), but this + // requires a valid function name to match its definition in the WSDL. + // Internally, simply use the injected response to parse its results. + $ret = $this->__soapCall($function, array()); $this->response = null; return $ret; diff --git a/tests/FunctionalTest.php b/tests/FunctionalTest.php index c43b8e3..8190a2f 100644 --- a/tests/FunctionalTest.php +++ b/tests/FunctionalTest.php @@ -6,6 +6,10 @@ use Clue\React\Soap\Proxy; use PHPUnit\Framework\TestCase; +class BankResponse +{ +} + /** * @group internet */ @@ -46,6 +50,30 @@ public function testBlzService() $result = Block\await($promise, $this->loop); $this->assertInternalType('object', $result); + $this->assertTrue(isset($result->details)); + $this->assertTrue(isset($result->details->bic)); + } + + public function testBlzServiceWithClassmapReturnsExpectedType() + { + $this->client = new Client(new Browser($this->loop), self::$wsdl, array( + 'classmap' => array( + 'getBankResponseType' => 'BankResponse' + ) + )); + + $this->assertCount(2, $this->client->getFunctions()); + $this->assertCount(3, $this->client->getTypes()); + + $api = new Proxy($this->client); + + $promise = $api->getBank(array('blz' => '12070000')); + + $result = Block\await($promise, $this->loop); + + $this->assertInstanceOf('BankResponse', $result); + $this->assertTrue(isset($result->details)); + $this->assertTrue(isset($result->details->bic)); } public function testBlzServiceWithSoapV12() @@ -64,14 +92,15 @@ public function testBlzServiceWithSoapV12() $result = Block\await($promise, $this->loop); $this->assertInternalType('object', $result); + $this->assertTrue(isset($result->details)); + $this->assertTrue(isset($result->details->bic)); } - public function testBlzServiceNonWsdlMode() + public function testBlzServiceNonWsdlModeReturnedWithoutOuterResultStructure() { $this->client = new Client(new Browser($this->loop), null, array( 'location' => 'http://www.thomas-bayer.com/axis2/services/BLZService', 'uri' => 'http://thomas-bayer.com/blz/', - 'use' => SOAP_LITERAL )); $api = new Proxy($this->client); @@ -83,6 +112,8 @@ public function testBlzServiceNonWsdlMode() $result = Block\await($promise, $this->loop); $this->assertInternalType('object', $result); + $this->assertFalse(isset($result->details)); + $this->assertTrue(isset($result->bic)); } /** diff --git a/tests/Protocol/ClientDecoderTest.php b/tests/Protocol/ClientDecoderTest.php index 4b1e9a6..e4ae4f2 100644 --- a/tests/Protocol/ClientDecoderTest.php +++ b/tests/Protocol/ClientDecoderTest.php @@ -10,7 +10,26 @@ class ClientDecoderTest extends TestCase */ public function testDecodeThrowsSoapFaultForInvalidResponse() { - $decoder = new ClientDecoder(); - $decoder->decode('invalid'); + $decoder = new ClientDecoder(null, array('location' => '1', 'uri' => '2')); + $decoder->decode('anything', 'invalid'); + } + + public function testDecodeMessageToObjectNonWsdl() + { + $decoder = new ClientDecoder(null, array('location' => '1', 'uri' => '2')); + + $res = $decoder->decode('anything', << +Deutsche Bank Ld BrandenburgDEUTDEBB160Potsdam14405 +SOAP + ); + + $expected = new stdClass(); + $expected->bezeichnung = 'Deutsche Bank Ld Brandenburg'; + $expected->bic = 'DEUTDEBB160'; + $expected->ort = 'Potsdam'; + $expected->plz = '14405'; + + $this->assertEquals($expected, $res); } }