Skip to content
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
58 changes: 54 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,17 @@ 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:
in order to handle async requests, the WSDL file contents and an optional
array of SOAP options:

```php
$loop = React\EventLoop\Factory::create();
$browser = new Clue\React\Buzz\Browser($loop);

$client = new Client($browser, $wsdl);
$wsdl = '<?xml …';
$options = array();

$client = new Client($browser, $wsdl, $options);
```

If you need custom DNS, TLS or proxy settings, you can explicitly pass a
Expand Down Expand Up @@ -142,6 +146,46 @@ try {
error instead of throwing a `SoapFault`. It is not recommended to use this
extension in production, so this should only ever affect test environments.

The `Client` constructor accepts an array of options. All given options will
be passed through to the underlying `SoapClient`. However, not all options
make sense in this async implementation and as such may not have the desired
effect. See also [`SoapClient`](http://php.net/manual/en/soapclient.soapclient.php)
documentation for more details.

If working in WSDL mode, the `$options` parameter is optional. If working in
non-WSDL mode, the WSDL parameter must be set to `null` and the options
parameter must contain the `location` and `uri` options, where `location` is
the URL of the SOAP server to send the request to, and `uri` is the target
namespace of the SOAP service:

```php
$client = new Client($browser, null, array(
'location' => 'http://example.com',
'uri' => 'http://ping.example.com',
));
```

Similarly, if working in WSDL mode, the `location` option can be used to
explicitly overwrite the URL of the SOAP server to send the request to:

```php
$client = new Client($browser, $wsdl, array(
'location' => 'http://example.com'
));
```

You can use the `soap_version` option to change from the default SOAP 1.1 to
use SOAP 1.2 instead:

```php
$client = new Client($browser, $wsdl, array(
'soap_version' => SOAP_1_2
));
```

If you find an option is missing or not supported here, PRs are much
appreciated!

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.
Expand All @@ -166,19 +210,21 @@ $promise = $proxy->ping('hello', 42);

#### getFunctions()

The `getFunctions(): string[]` method can be used to
The `getFunctions(): string[]|null` method can be used to
return an array of functions defined in the WSDL.

It returns the equivalent of PHP's
[`SoapClient::__getFunctions()`](http://php.net/manual/en/soapclient.getfunctions.php).
In non-WSDL mode, this method returns `null`.

#### getTypes()

The `getTypes(): string[]` method can be used to
The `getTypes(): string[]|null` method can be used to
return an array of types defined in the WSDL.

It returns the equivalent of PHP's
[`SoapClient::__getTypes()`](http://php.net/manual/en/soapclient.gettypes.php).
In non-WSDL mode, this method returns `null`.

#### getLocation()

Expand Down Expand Up @@ -207,6 +253,10 @@ same location and accessing the first location is sufficient.
assert('http://example.com/soap/service' === $client->getLocation(0));
```

When the `location` option has been set in the `Client` constructor
(such as when in non-WSDL mode), this method returns the value of the
given `location` option.

Passing a `$function` not defined in the WSDL file will throw a `SoapFault`.

### Proxy
Expand Down
File renamed without changes.
31 changes: 31 additions & 0 deletions examples/02-client-blz-non-wsdl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

use Clue\React\Buzz\Browser;
use Clue\React\Soap\Client;
use Clue\React\Soap\Proxy;

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

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

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

$client = new Client($browser, null, array(
'location' => 'http://www.thomas-bayer.com/axis2/services/BLZService',
'uri' => 'http://thomas-bayer.com/blz/',
'use' => SOAP_LITERAL
));
$api = new Proxy($client);

$api->getBank(new SoapVar($blz, XSD_STRING, null, null, 'blz', 'http://thomas-bayer.com/blz/'))->then(
function ($result) {
echo 'SUCCESS!' . PHP_EOL;
var_dump($result);
},
function (Exception $e) {
echo 'ERROR: ' . $e->getMessage() . PHP_EOL;
}
);

$loop->run();
File renamed without changes.
68 changes: 60 additions & 8 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@
*
* 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:
* in order to handle async requests, the WSDL file contents and an optional
* array of SOAP options:
*
* ```php
* $loop = React\EventLoop\Factory::create();
* $browser = new Clue\React\Buzz\Browser($loop);
*
* $client = new Client($browser, $wsdl);
* $wsdl = '<?xml …';
* $options = array();
*
* $client = new Client($browser, $wsdl, $options);
* ```
*
* If you need custom DNS, TLS or proxy settings, you can explicitly pass a
Expand Down Expand Up @@ -79,6 +83,46 @@
* error instead of throwing a `SoapFault`. It is not recommended to use this
* extension in production, so this should only ever affect test environments.
*
* The `Client` constructor accepts an array of options. All given options will
* be passed through to the underlying `SoapClient`. However, not all options
* make sense in this async implementation and as such may not have the desired
* effect. See also [`SoapClient`](http://php.net/manual/en/soapclient.soapclient.php)
* documentation for more details.
*
* If working in WSDL mode, the `$options` parameter is optional. If working in
* non-WSDL mode, the WSDL parameter must be set to `null` and the options
* parameter must contain the `location` and `uri` options, where `location` is
* the URL of the SOAP server to send the request to, and `uri` is the target
* namespace of the SOAP service:
*
* ```php
* $client = new Client($browser, null, array(
* 'location' => 'http://example.com',
* 'uri' => 'http://ping.example.com',
* ));
* ```
*
* Similarly, if working in WSDL mode, the `location` option can be used to
* explicitly overwrite the URL of the SOAP server to send the request to:
*
* ```php
* $client = new Client($browser, $wsdl, array(
* 'location' => 'http://example.com'
* ));
* ```
*
* You can use the `soap_version` option to change from the default SOAP 1.1 to
* use SOAP 1.2 instead:
*
* ```php
* $client = new Client($browser, $wsdl, array(
* 'soap_version' => SOAP_1_2
* ));
* ```
*
* If you find an option is missing or not supported here, PRs are much
* appreciated!
*
* 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.
Expand All @@ -93,14 +137,16 @@ final class Client
/**
* Instantiate a new SOAP client for the given WSDL contents.
*
* @param Browser $browser
* @param string $wsdlContents
* @param Browser $browser
* @param string|null $wsdlContents
* @param array $options
*/
public function __construct(Browser $browser, $wsdlContents)
public function __construct(Browser $browser, $wsdlContents, array $options = array())
{
$this->browser = $browser;
$this->encoder = new ClientEncoder(
'data://text/plain;base64,' . base64_encode($wsdlContents)
$wsdlContents !== null ? 'data://text/plain;base64,' . base64_encode($wsdlContents) : null,
$options
);
$this->decoder = new ClientDecoder();
}
Expand Down Expand Up @@ -145,8 +191,9 @@ public function soapCall($name, $args)
*
* It returns the equivalent of PHP's
* [`SoapClient::__getFunctions()`](http://php.net/manual/en/soapclient.getfunctions.php).
* In non-WSDL mode, this method returns `null`.
*
* @return string[]
* @return string[]|null
*/
public function getFunctions()
{
Expand All @@ -158,8 +205,9 @@ public function getFunctions()
*
* It returns the equivalent of PHP's
* [`SoapClient::__getTypes()`](http://php.net/manual/en/soapclient.gettypes.php).
* In non-WSDL mode, this method returns `null`.
*
* @return string[]
* @return string[]|null
*/
public function getTypes()
{
Expand Down Expand Up @@ -191,6 +239,10 @@ public function getTypes()
* assert('http://example.com/soap/service' === $client->getLocation(0));
* ```
*
* When the `location` option has been set in the `Client` constructor
* (such as when in non-WSDL mode), this method returns the value of the
* given `location` option.
*
* Passing a `$function` not defined in the WSDL file will throw a `SoapFault`.
*
* @param string|int $function
Expand Down
2 changes: 1 addition & 1 deletion src/Protocol/ClientDecoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ final class ClientDecoder extends SoapClient

public function __construct()
{
// to not pass actual WSDL to parent constructor
// 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'));
}
Expand Down
18 changes: 13 additions & 5 deletions src/Protocol/ClientEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,22 @@ public function encode($name, $args)
*/
public function __doRequest($request, $location, $action, $version, $one_way = 0)
{
$headers = array();
if ($version === SOAP_1_1) {
$headers = array(
'SOAPAction' => $action,
'Content-Type' => 'text/xml; charset=utf-8'
);
} elseif ($version === SOAP_1_2) {
$headers = array(
'Content-Type' => 'application/soap+xml; charset=utf-8; action=' . $action
);
}

$this->request = new Request(
'POST',
(string)$location,
array(
'SOAPAction' => (string)$action,
'Content-Type' => 'text/xml; charset=utf-8',
'Content-Length' => strlen($request)
),
$headers,
(string)$request
);

Expand Down
35 changes: 35 additions & 0 deletions tests/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

use Clue\React\Soap\Client;
use PHPUnit\Framework\TestCase;
use React\Promise\Promise;
use Psr\Http\Message\RequestInterface;

class ClientTest extends TestCase
{
Expand All @@ -16,4 +18,37 @@ public function testConstructorThrowsWhenUrlIsInvalid()

$client = new Client($browser, $wsdl);
}

public function testNonWsdlClientReturnsSameLocationOptionForAnyFunction()
{
$browser = $this->getMockBuilder('Clue\React\Buzz\Browser')->disableOriginalConstructor()->getMock();

$client = new Client($browser, null, array('location' => 'http://example.com', 'uri' => 'http://example.com/uri'));

$this->assertEquals('http://example.com', $client->getLocation('anything'));
}

public function testNonWsdlClientReturnsNoTypesAndFunctions()
{
$browser = $this->getMockBuilder('Clue\React\Buzz\Browser')->disableOriginalConstructor()->getMock();

$client = new Client($browser, null, array('location' => 'http://example.com', 'uri' => 'http://example.com/uri'));

$this->assertNull($client->getTypes());
$this->assertNull($client->getFunctions());
}

public function testNonWsdlClientSendsPostRequestToGivenLocationForAnySoapCall()
{
$verify = function (RequestInterface $request) {
return ($request->getMethod() === 'POST' && (string)$request->getUri() === 'http://example.com');
};
$promise = new Promise(function () { });
$browser = $this->getMockBuilder('Clue\React\Buzz\Browser')->disableOriginalConstructor()->getMock();
$browser->expects($this->once())->method('send')->with($this->callback($verify))->willReturn($promise);

$client = new Client($browser, null, array('location' => 'http://example.com', 'uri' => 'http://example.com/uri'));

$client->soapCall('ping', array());
}
}
Loading