Skip to content

Replace Factory with simplified Client constructor #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 52 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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) {
Expand All @@ -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<Client, Exception>` 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.

Expand Down
12 changes: 6 additions & 6 deletions examples/client-blz.php
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<?php

use Clue\React\Soap\Factory;
use Clue\React\Soap\Proxy;
use Clue\React\Buzz\Browser;
use Clue\React\Soap\Client;
use Clue\React\Soap\Proxy;
use Psr\Http\Message\ResponseInterface;

require __DIR__ . '/../vendor/autoload.php';

$loop = React\EventLoop\Factory::create();
$factory = new Factory($loop);
$browser = new Browser($loop);

$blz = isset($argv[1]) ? $argv[1] : '12070000';

$factory->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(
Expand Down
11 changes: 7 additions & 4 deletions examples/client-wsdl.php
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
<?php

use Clue\React\Soap\Factory;
use Clue\React\Buzz\Browser;
use Clue\React\Soap\Client;
use Psr\Http\Message\ResponseInterface;

require __DIR__ . '/../vendor/autoload.php';

$loop = React\EventLoop\Factory::create();
$factory = new Factory($loop);
$browser = new Browser($loop);

$wsdl = isset($argv[1]) ? $argv[1] : 'http://www.thomas-bayer.com/axis2/services/BLZService?wsdl';

$factory->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 .
Expand Down
91 changes: 73 additions & 18 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,96 @@
* 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.
* All public methods of the `Client` are considered *advanced usage*.
*/
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();
}

/**
Expand Down
84 changes: 0 additions & 84 deletions src/Factory.php

This file was deleted.

19 changes: 19 additions & 0 deletions tests/ClientTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

use Clue\React\Soap\Client;
use PHPUnit\Framework\TestCase;

class ClientTest extends TestCase
{
public function testConstructorThrowsWhenUrlIsInvalid()
{
if (extension_loaded('xdebug')) {
$this->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);
}
}
Loading