diff --git a/README.md b/README.md index bc883f2..f68db86 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,6 @@ This project provides a *simple* API for invoking *async* RPCs to remote web ser * [Quickstart example](#quickstart-example) * [Usage](#usage) - * [Factory](#factory) - * [createClient()](#createclient) - * [createClientFromWsdl()](#createclientfromwsdl) * [Client](#client) * [soapCall()](#soapcall) * [getFunctions()](#getfunctions) @@ -55,10 +52,11 @@ web service via SOAP: ```php $loop = React\EventLoop\Factory::create(); -$factory = new Factory($loop); +$browser = new Browser($loop); $wsdl = 'http://example.com/demo.wsdl'; -$factory->createClient($wsdl)->then(function (Client $client) { +$browser->get($wsdl)->then(function (ResponseInterface $response) use ($browser) { + $client = new Client($browser, (string)$response->getBody()); $api = new Proxy($client); $api->getBank(array('blz' => '12070000'))->then(function ($result) { @@ -73,53 +71,76 @@ See also the [examples](examples). ## Usage -### Factory +### Client + +The `Client` class is responsible for communication with the remote SOAP +WebService server. -The `Factory` class is responsible for fetching the WSDL file once and constructing -the [`Client`](#client) instance. -It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage). +It requires a [`Browser`](https://github.com/clue/reactphp-buzz#browser) object +bound to the main [`EventLoop`](https://github.com/reactphp/event-loop#usage) +in order to handle async requests and the WSDL file contents: ```php $loop = React\EventLoop\Factory::create(); -$factory = new Factory($loop); +$browser = new Clue\React\Buzz\Browser($loop); + +$client = new Client($browser, $wsdl); ``` -If you need custom DNS or proxy settings, you can explicitly pass a +If you need custom DNS, TLS or proxy settings, you can explicitly pass a custom [`Browser`](https://github.com/clue/reactphp-buzz#browser) instance: ```php -$browser = new Clue\React\Buzz\Browser($loop); -$factory = new Factory($loop, $browser); +$connector = new \React\Socket\Connector($loop, array( + 'dns' => '127.0.0.1', + 'tcp' => array( + 'bindto' => '192.168.10.1:0' + ), + 'tls' => array( + 'verify_peer' => false, + 'verify_peer_name' => false + ) +)); + +$browser = new Browser($loop, $connector); +$client = new Client($browser, $wsdl); ``` -#### createClient() - -The `createClient(string $wsdl): PromiseInterface` method can be used to -download the WSDL at the given URL into memory and create a new [`Client`](#client). +The `Client` works similar to PHP's `SoapClient` (which it uses under the +hood), but leaves you the responsibility to load the WSDL file. This allows +you to use local WSDL files, WSDL files from a cache or the most common form, +downloading the WSDL file contents from an URL through the `Browser`: ```php -$factory->createClient($url)->then( - function (Client $client) { - // client ready +$browser = new Browser($loop); + +$browser->get($url)->then( + function (ResponseInterface $response) use ($browser) { + // WSDL file is ready, create client + $client = new Client($browser, (string)$response->getBody()); + … }, function (Exception $e) { - // an error occured while trying to download or parse the WSDL + // an error occured while trying to download the WSDL } ); ``` -#### createClientFromWsdl() - -The `createClientFromWsdl(string $wsdlContents): Client` method can be used to -create a new [`Client`](#client) from the given WSDL contents. +The `Client` constructor loads the given WSDL file contents into memory and +parses its definition. If the given WSDL file is invalid and can not be +parsed, this will throw a `SoapFault`: -This works similar to `createClient()`, but leaves you the responsibility to load -the WSDL file. This allows you to use local WSDL files, for instance. - -### Client +```php +try { + $client = new Client($browser, $wsdl); +} catch (SoapFault $e) { + echo 'Error: ' . $e->getMessage() . PHP_EOL; +} +``` -The `Client` class is responsible for communication with the remote SOAP -WebService server. +> Note that if you have `ext-debug` loaded, this may halt with a fatal + error instead of throwing a `SoapFault`. It is not recommended to use this + extension in production, so this should only ever affect test environments. If you want to call RPC functions, see below for the [`Proxy`](#proxy) class. diff --git a/examples/client-blz.php b/examples/client-blz.php index 93a281c..ae05f65 100644 --- a/examples/client-blz.php +++ b/examples/client-blz.php @@ -1,19 +1,19 @@ createClient('http://www.thomas-bayer.com/axis2/services/BLZService?wsdl')->then(function (Client $client) use ($blz) { - //var_dump($client->getFunctions(), $client->getTypes()); - +$browser->get('http://www.thomas-bayer.com/axis2/services/BLZService?wsdl')->done(function (ResponseInterface $response) use ($browser, $blz) { + $client = new Client($browser, (string)$response->getBody()); $api = new Proxy($client); $api->getBank(array('blz' => $blz))->then( diff --git a/examples/client-wsdl.php b/examples/client-wsdl.php index 833c1ff..d4b53c7 100644 --- a/examples/client-wsdl.php +++ b/examples/client-wsdl.php @@ -1,17 +1,20 @@ createClient($wsdl)->then( - function (Client $client) { +$browser->get($wsdl)->done( + function (ResponseInterface $response) use ($browser) { + $client = new Client($browser, (string)$response->getBody()); + echo 'Functions:' . PHP_EOL . implode(PHP_EOL, $client->getFunctions()) . PHP_EOL . PHP_EOL . diff --git a/src/Client.php b/src/Client.php index 2f08424..3a11a16 100644 --- a/src/Client.php +++ b/src/Client.php @@ -13,6 +13,72 @@ * The `Client` class is responsible for communication with the remote SOAP * WebService server. * + * It requires a [`Browser`](https://github.com/clue/reactphp-buzz#browser) object + * bound to the main [`EventLoop`](https://github.com/reactphp/event-loop#usage) + * in order to handle async requests and the WSDL file contents: + * + * ```php + * $loop = React\EventLoop\Factory::create(); + * $browser = new Clue\React\Buzz\Browser($loop); + * + * $client = new Client($browser, $wsdl); + * ``` + * + * If you need custom DNS, TLS or proxy settings, you can explicitly pass a + * custom [`Browser`](https://github.com/clue/reactphp-buzz#browser) instance: + * + * ```php + * $connector = new \React\Socket\Connector($loop, array( + * 'dns' => '127.0.0.1', + * 'tcp' => array( + * 'bindto' => '192.168.10.1:0' + * ), + * 'tls' => array( + * 'verify_peer' => false, + * 'verify_peer_name' => false + * ) + * )); + * + * $browser = new Browser($loop, $connector); + * $client = new Client($browser, $wsdl); + * ``` + * + * The `Client` works similar to PHP's `SoapClient` (which it uses under the + * hood), but leaves you the responsibility to load the WSDL file. This allows + * you to use local WSDL files, WSDL files from a cache or the most common form, + * downloading the WSDL file contents from an URL through the `Browser`: + * + * ```php + * $browser = new Browser($loop); + * + * $browser->get($url)->then( + * function (ResponseInterface $response) use ($browser) { + * // WSDL file is ready, create client + * $client = new Client($browser, (string)$response->getBody()); + * … + * }, + * function (Exception $e) { + * // an error occured while trying to download the WSDL + * } + * ); + * ``` + * + * The `Client` constructor loads the given WSDL file contents into memory and + * parses its definition. If the given WSDL file is invalid and can not be + * parsed, this will throw a `SoapFault`: + * + * ```php + * try { + * $client = new Client($browser, $wsdl); + * } catch (SoapFault $e) { + * echo 'Error: ' . $e->getMessage() . PHP_EOL; + * } + * ``` + * + * > Note that if you have `ext-debug` loaded, this may halt with a fatal + * error instead of throwing a `SoapFault`. It is not recommended to use this + * extension in production, so this should only ever affect test environments. + * * If you want to call RPC functions, see below for the [`Proxy`](#proxy) class. * * Note: It's recommended (and easier) to wrap the `Client` in a [`Proxy`](#proxy) instance. @@ -20,34 +86,23 @@ */ final class Client { - private $wsdl; private $browser; private $encoder; private $decoder; /** - * [internal] Instantiate new SOAP client, see Factory instead + * Instantiate a new SOAP client for the given WSDL contents. * - * @param string $wsdl * @param Browser $browser - * @param ClientEncoder $encoder - * @param ClientDecoder $decoder - * @internal + * @param string $wsdlContents */ - public function __construct($wsdl, Browser $browser, ClientEncoder $encoder = null, ClientDecoder $decoder = null) + public function __construct(Browser $browser, $wsdlContents) { - if ($encoder === null) { - $encoder = new ClientEncoder($wsdl); - } - - if ($decoder === null) { - $decoder = new ClientDecoder($wsdl); - } - - $this->wsdl = $wsdl; $this->browser = $browser; - $this->encoder = $encoder; - $this->decoder = $decoder; + $this->encoder = new ClientEncoder( + 'data://text/plain;base64,' . base64_encode($wsdlContents) + ); + $this->decoder = new ClientDecoder(); } /** diff --git a/src/Factory.php b/src/Factory.php deleted file mode 100644 index f739f0e..0000000 --- a/src/Factory.php +++ /dev/null @@ -1,84 +0,0 @@ -loop = $loop; - $this->browser = $browser; - } - - /** - * Downloads the WSDL at the given URL into memory and create a new [`Client`](#client). - * - * ```php - * $factory->createClient($url)->then( - * function (Client $client) { - * // client ready - * }, - * function (Exception $e) { - * // an error occured while trying to download or parse the WSDL - * } - * ); - * ``` - * - * @param string $wsdl - * @return PromiseInterface Returns a Promise - */ - public function createClient($wsdl) - { - $that = $this; - - return $this->browser->get($wsdl)->then(function (ResponseInterface $response) use ($that) { - return $that->createClientFromWsdl((string)$response->getBody()); - }); - } - - /** - * Creates a new [`Client`](#client) from the given WSDL contents. - * - * This works similar to `createClient()`, but leaves you the responsibility to load - * the WSDL file. This allows you to use local WSDL files, for instance. - * - * @param string $wsdlContents - * @return Client - */ - public function createClientFromWsdl($wsdlContents) - { - $browser = $this->browser; - $url = 'data://text/plain;base64,' . base64_encode($wsdlContents); - - return new Client($url, $browser); - } -} diff --git a/tests/ClientTest.php b/tests/ClientTest.php new file mode 100644 index 0000000..58c78d5 --- /dev/null +++ b/tests/ClientTest.php @@ -0,0 +1,19 @@ +markTestSkipped('Invalid WSDL causes a fatal error when ext-xdebug is loaded'); + } + + $browser = $this->getMockBuilder('Clue\React\Buzz\Browser')->disableOriginalConstructor()->getMock(); + $wsdl = 'invalid'; + + $client = new Client($browser, $wsdl); + } +} diff --git a/tests/FunctionalTest.php b/tests/FunctionalTest.php index 15ef710..2abcd88 100644 --- a/tests/FunctionalTest.php +++ b/tests/FunctionalTest.php @@ -1,7 +1,7 @@ loop = React\EventLoop\Factory::create(); - $factory = new Factory($this->loop); - - $promise = $factory->createClient('http://www.thomas-bayer.com/axis2/services/BLZService?wsdl'); + $wsdl = file_get_contents('http://www.thomas-bayer.com/axis2/services/BLZService?wsdl'); - $this->client = Block\await($promise, $this->loop); - /* @var $client Client */ + $this->loop = React\EventLoop\Factory::create(); + $this->client = new Client(new Browser($this->loop), $wsdl); } public function testBlzService() @@ -70,19 +67,6 @@ public function testBlzServiceWithInvalidMethod() Block\await($promise, $this->loop); } - /** - * @expectedException Exception - */ - public function testCancelCreateClientRejects() - { - $factory = new Factory($this->loop); - - $promise = $factory->createClient('http://www.thomas-bayer.com/axis2/services/BLZService?wsdl'); - $promise->cancel(); - - Block\await($promise, $this->loop); - } - /** * @expectedException Exception */