lstrojny / fxmlrpc

A modern, super fast XML/RPC client for PHP >=5.6
142 stars 41 forks source link

fxmlrpc: really fast XML/RPC for PHP

Gitter Build Status Average time to resolve an issue Percentage of issues still open

Upgrading to 0.23.x

Instead of php-http/message-factory, we now use the PSR-7 compatible RequestFactoryInterface. You will have to change your custom HTTP client implementation and pass a Psr\Http\Message\RequestFactoryInterface implementation, a Psr\Http\Message\StreamFactoryInterface and a Http\Client\HttpClient to the HttpAdapterTransport. See below for details.

Installation

To install fxmlrpc run this command:

composer require lstrojny/fxmlrpc

Install dependencies

You must choose three packages for for the business of HTTP:

Example:

composer require php-http/message php-http/guzzle7-adapter

Instantiating HttpAdapterTransport

An example instantiation using Guzzle6:

$httpClient = new GuzzleHttp\Client();
$httpClient->...();
$client = new fXmlRpc\Client(
    'http://endpoint.com',
    new fXmlRpc\Transport\HttpAdapterTransport(
        new \Http\Message\MessageFactory\DiactorosMessageFactory(),
        new \Http\Message\StreamFactory\DiactorosStreamFactory(),
        new \Http\Adapter\Guzzle7\Client($httpClient)
    )
);

Upgrading to 0.12.x

Instead of egeloen/http-adapter, we now use the PSR-7 compatible php-http/httplug. You will have to change your custom HTTP client implementation and pass a Http\Message\MessageFactory implementation and a Http\Client\HttpClient to the HttpAdapterTransport. See below for details.

Installation

To install fxmlrpc run this command:

composer require lstrojny/fxmlrpc

Install dependencies

You must choose three packages for for the business of HTTP:

Example:

composer require zendframework/zend-diactoros php-http/message php-http/guzzle6-adapter

Instantiating HttpAdapterTransport

An example instantiation using Guzzle6:

$httpClient = new GuzzleHttp\Client();
$httpClient->...();
$client = new fXmlRpc\Client(
    'http://endpoint.com',
    new fXmlRpc\Transport\HttpAdapterTransport(
        new \Http\Message\MessageFactory\DiactorosMessageFactory(),
        new \Http\Adapter\Guzzle6\Client($httpClient)
    )
);

Upgrading to 0.11.x

We change ParserInterface::parse() method interface, now isn't required to pass second parameter ($isFault), parser should throw an exception FaultException when fault message is encountered in server response.

Upgrading to 0.10.x

0.10.x comes with a couple of breaking changes: We used to ship our own bridges for interoperability with various HTTP clients but moved that responsibility to a 3rd party library called Ivory HTTP Adapter. IMPORTANT NOTE: the library is not installed by default as you could choose to use fxmlrpc with just your own implementation of the fXmlRpc\Transport\TransportInterface. To install the library – and that’s what you most likely want – add this line to your composer.json

"egeloen/http-adapter": "~0.6"

… and run composer update

Instantiating an HTTP transport

In order to use the new adapters, you need to change how you instantiate fXmlRpc and its transport. This is how instantiating a custom transport looked before:

$httpClient = new GuzzleHttp\Client();
$client = new fXmlRpc\Client(
    'http://endpoint.com',
    new fXmlRpc\Transport\Guzzle4Bridge($httpClient)
);

This is how you do it now:

$httpClient = new GuzzleHttp\Client();
$httpClient->...();
$client = new fXmlRpc\Client(
    'http://endpoint.com',
    new fXmlRpc\Transport\HttpAdapterTransport(new Ivory\HttpAdapter\GuzzleHttpHttpAdapter($httpClient))
);

Latest improvements

How fast is it really?

IO performance is out of reach from a userspace perspective, but parsing and serialization speed is what matters. How fast can we generate the XML payload from PHP data structures and how fast can we parse the servers response? fXmlRpc uses stream based XML writers/readers to achieve it’s performance and heavily optimizes (read uglifies) for it. As as result the userland version is only around 2x slower than the native C implementation (ext/xmlrpc).

Parser

Zend\XmlRpc\Value (ZF2): 249.02972793579 sec
Zend_XmlRpc_Value (ZF1): 253.88145494461 sec
fXmlRpc\Parser\XmlReaderParser: 36.274516105652 sec
fXmlRpc\Parser\NativeParser: 18.652323007584 sec

Serializer

Zend\XmlRpc\Request (ZF2): 52.004573106766 sec
Zend_XmlRpc_Request (ZF1): 65.042532920837 sec
fXmlRpc\Serializer\XmlWriterSerializer: 23.652673006058 sec
fXmlRpc\Serializer\NativeSerializer: 9.0790779590607 sec

Usage

Basic Usage

<?php
$client = new fXmlRpc\Client('http://endpoint.com');
$client->call('remoteMethod', array('arg1', true));

Using native (ext/xmlrpc based) serializer/parser (for even better performance)

<?php
$client = new fXmlRpc\Client(
    'http://endpoint.com',
    null,
    new fXmlRpc\Parser\NativeParser(),
    new fXmlRpc\Serializer\NativeSerializer()
);
$client->call('remoteMethod', array('arg1', true));

Prepending and appending arguments

<?php
$client = new fXmlRpc\Client('http://endpoint.com');
$client->prependParams(array('username', 'password'));
$client->appendParams(array('appended'));
...

Using a convenient Proxy object

<?php
$proxy = new fXmlRpc\Proxy(new fXmlRpc\Client('http://endpoint.com'));
// Call system.echo
$proxy->system->echo('Hello World!');

Tracking XML of the request and response

<?php
$transport = new fXmlRpc\Transport\HttpAdapterTransport(...);
$recorder = new Recorder($transport);
$client = new Client('http://foo.com', $recorder);
$client->call('TestMethod', ['param1', 2, ['param3' => true]]);

$lastRequest = $recorder->getLastRequest();
$lastResponse = $recorder->getLastResponse();

If exception occur in the transport layer you can get it using getLastException().

Helpful abstraction for multicall requests

<?php
$result = $client->multicall()
    ->addCall('system.add', array(1, 2))
    ->addCall(
        'system.add',
        array(2, 3),
        function ($result) {
            echo "Result was: " . $result;
        },
        function($result) {
            echo "An error occured: " . var_export($result, true);
        }
    )
    ->onSuccess(function ($result) {echo "Success";}) // Success handler for each call
    ->onError(function ($result) {echo "Error";}) // Error handler for each call
    ->execute();

Integration for various HTTP clients using Ivory

<?php
/** Buzz (https://github.com/kriswallsmith/Buzz) */
$browser = new Buzz\Browser();
$browser->...();
$client = new fXmlRpc\Client(
    'http://endpoint.com',
    new fXmlRpc\Transport\HttpAdapterTransport(new \Ivory\HttpAdapter\BuzzHttpAdapter($browser))
);

/** Zend Framework 1 (http://framework.zend.com/) */
$httpClient = new Zend_Http_Client();
$httpClient->...();
$client = new fXmlRpc\Client(
    'http://endpoint.com',
    new fXmlRpc\Transport\HttpAdapterTransport(new \Ivory\HttpAdapter\Zend1HttpAdapter($httpClient))
);

/** Zend Framework 2 (http://framework.zend.com/zf2) */
$httpClient = new Zend\Http\Client();
$httpClient->...();
$client = new fXmlRpc\Client(
    'http://endpoint.com',
    new fXmlRpc\Transport\HttpAdapterTransport(new \Ivory\HttpAdapter\Zend2HttpAdapter($httpClient))
);

/** Guzzle (http://guzzlephp.org/) */
$httpClient = new Guzzle\Http\Client();
$httpClient->...();
$client = new fXmlRpc\Client(
    'http://endpoint.com',
    new fXmlRpc\Transport\HttpAdapterTransport(new \Ivory\HttpAdapter\GuzzleAdapter($httpClient))
);

/** Guzzle 4+ (http://guzzlephp.org/) */
$httpClient = new GuzzleHttp\Client();
$httpClient->...();
$client = new fXmlRpc\Client(
    'http://endpoint.com',
    new fXmlRpc\Transport\HttpAdapterTransport(new \Ivory\HttpAdapter\GuzzleHttpHttpAdapter($httpClient))
);

Timing XML/RPC requests to find problematic calls

fXmlRpc allows you to time your XML/RPC request, to find out which took how long. It provides a fXmlRpc\Timing\TimingDecorator which can be used with various timers implementing fXmlRpc\Timing\TimerInterface. Currently implemented are bridges for Monolog, Zend Framework 1 Zend_Log and Zend Framework 2 Zend\Log.

Usage:

<?php
$client = new fXmlRpc\Timing\TimingDecorator(
    new fXmlRpc\Client(...),
    new fXmlRpc\Timing\MonologTimerBridge(
        $monolog,
        Monolog\Logger::ALERT,
        'My custom log message template %F'
    )
);