├── .github └── workflows │ └── test.yaml ├── README.md ├── composer.json ├── src └── fXmlRpc │ ├── AbstractDecorator.php │ ├── CallClientInterface.php │ ├── Client.php │ ├── ClientInterface.php │ ├── CodeGenerator │ └── XmlReaderParserBitmaskGenerator.php │ ├── Exception │ ├── AbstractTransportException.php │ ├── ExceptionInterface.php │ ├── FaultException.php │ ├── HttpException.php │ ├── InvalidArgumentException.php │ ├── MissingExtensionException.php │ ├── ParserException.php │ ├── RuntimeException.php │ ├── SerializationException.php │ └── TransportException.php │ ├── ExtensionSupportInterface.php │ ├── MulticallBuilder.php │ ├── MulticallBuilderInterface.php │ ├── MulticallClientInterface.php │ ├── Parser │ ├── BestParserDelegate.php │ ├── NativeParser.php │ ├── ParserInterface.php │ ├── XmlChecker.php │ └── XmlReaderParser.php │ ├── Proxy.php │ ├── Serializer │ ├── NativeSerializer.php │ ├── SerializerInterface.php │ └── XmlWriterSerializer.php │ ├── Timing │ ├── AbstractTimerBridge.php │ ├── MonologTimerBridge.php │ ├── Psr3TimerBridge.php │ ├── TimerInterface.php │ ├── TimingDecorator.php │ ├── ZendFrameworkOneTimerBridge.php │ └── ZendFrameworkTwoTimerBridge.php │ ├── Transport │ ├── HttpAdapterTransport.php │ ├── PsrTransport.php │ ├── Recorder.php │ └── TransportInterface.php │ └── Value │ ├── Base64.php │ └── Base64Interface.php └── vendor └── .gitadd /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: PHP ${{ matrix.php-version }} 8 | 9 | runs-on: ubuntu-18.04 10 | 11 | strategy: 12 | matrix: 13 | php-version: 14 | - 7.2 15 | - 7.3 16 | - 7.4 17 | - 8.0 18 | 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v2 22 | 23 | - name: Install PHP with extensions 24 | uses: shivammathur/setup-php@v2 25 | with: 26 | php-version: ${{ matrix.php-version }} 27 | coverage: pcov 28 | tools: composer:v2 29 | extensions: xmlrpc, xmlreader 30 | 31 | - name: Install Node 32 | uses: actions/setup-node@v1 33 | with: 34 | node-version: 12 35 | 36 | - name: Install Composer dependencies 37 | uses: ramsey/composer-install@v1 38 | with: 39 | composer-options: --prefer-dist 40 | 41 | - name: Install NPM dependencies 42 | run: npm install 43 | 44 | - name: Run Tests 45 | run: vendor/bin/phpunit 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fxmlrpc: really fast XML/RPC for PHP 2 | 3 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/lstrojny/fxmlrpc?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | [![Build Status](https://secure.travis-ci.org/lstrojny/fxmlrpc.svg)](http://travis-ci.org/lstrojny/fxmlrpc) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/lstrojny/fxmlrpc.svg)](http://isitmaintained.com/project/lstrojny/fxmlrpc "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/lstrojny/fxmlrpc.svg)](http://isitmaintained.com/project/lstrojny/fxmlrpc "Percentage of issues still open") 5 | 6 | - A convenient, object oriented API (similar to the XML/RPC client in Zend Framework) 7 | - Very fast serializing and parsing of the XML payloads involved 8 | - Stick to the HTTP client you already use provided by [HTTPlug](http://httplug.io/) 9 | - Licensed under the terms of the liberal MIT license 10 | - Supports modern standards: easy installation via composer, fully PSR-0, PSR-1 and PSR-2 compatible 11 | - Relentlessly unit- and integration tested 12 | - Implements all known XML/RPC extensions 13 | 14 | ## Upgrading to 0.23.x 15 | 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`. 16 | See below for details. 17 | 18 | ## Installation 19 | To install fxmlrpc run this command: 20 | 21 | ``` 22 | composer require lstrojny/fxmlrpc 23 | ``` 24 | 25 | ### Install dependencies 26 | 27 | You must choose three packages for for the business of HTTP: 28 | - A PSR-7 compatible HTTP message (request / response) implementation 29 | - A compatible HTTP RequestFactoryInterface and StreamFactoryInterface implementations to create HTTP messages 30 | - A PSR-7 compatible HTTP client 31 | Two widespread message implementations are [laminas/laminas-diactoros](https://github.com/laminas/laminas-diactoros) and [guzzle/psr7](https://github.com/guzzle/psr7). Message factories for both implementations are available in [php-http/message](https://github.com/php-http/message). 32 | For HTTP clients you can pick e.g.[php-http/guzzle7-adapter](https://github.com/php-http/guzzle7-adapter), [php-http/guzzle6-adapter](https://github.com/php-http/guzzle6-adapter), [php-http/guzzle5-adapter](https://github.com/php-http/guzzle5-adapter), [php-http/curl-client](https://github.com/php-http/curl-client) or [php-http/buzz-adapter](https://github.com/php-http/buzz-adapter). 33 | 34 | Example: 35 | 36 | ``` 37 | composer require php-http/message php-http/guzzle7-adapter 38 | ``` 39 | 40 | ### Instantiating `HttpAdapterTransport` 41 | An example instantiation using Guzzle6: 42 | ```php 43 | $httpClient = new GuzzleHttp\Client(); 44 | $httpClient->...(); 45 | $client = new fXmlRpc\Client( 46 | 'http://endpoint.com', 47 | new fXmlRpc\Transport\HttpAdapterTransport( 48 | new \Http\Message\MessageFactory\DiactorosMessageFactory(), 49 | new \Http\Message\StreamFactory\DiactorosStreamFactory(), 50 | new \Http\Adapter\Guzzle7\Client($httpClient) 51 | ) 52 | ); 53 | ``` 54 | 55 | ## Upgrading to 0.12.x 56 | 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`. 57 | See below for details. 58 | 59 | ## Installation 60 | To install fxmlrpc run this command: 61 | 62 | ``` 63 | composer require lstrojny/fxmlrpc 64 | ``` 65 | 66 | ### Install dependencies 67 | 68 | You must choose three packages for for the business of HTTP: 69 | - A PSR-7 compatible HTTP message (request / response) implementation 70 | - A compatible HTTP message factory implementation to create HTTP messages 71 | - A PSR-7 compatible HTTP client 72 | Two widespread message implementations are [zend-diactoros](https://github.com/zendframework/zend-diactoros) and [guzzle/psr7](https://github.com/guzzle/psr7). Message factories for both implementations are available in [php-http/message](https://github.com/php-http/message). 73 | For HTTP clients you can pick e.g. [php-http/guzzle6-adapter](https://github.com/php-http/guzzle6-adapter), [php-http/guzzle5-adapter](https://github.com/php-http/guzzle5-adapter), [php-http/curl-client](https://github.com/php-http/curl-client) or [php-http/buzz-adapter](https://github.com/php-http/buzz-adapter). 74 | 75 | Example: 76 | 77 | ``` 78 | composer require zendframework/zend-diactoros php-http/message php-http/guzzle6-adapter 79 | ``` 80 | 81 | ### Instantiating `HttpAdapterTransport` 82 | An example instantiation using Guzzle6: 83 | ```php 84 | $httpClient = new GuzzleHttp\Client(); 85 | $httpClient->...(); 86 | $client = new fXmlRpc\Client( 87 | 'http://endpoint.com', 88 | new fXmlRpc\Transport\HttpAdapterTransport( 89 | new \Http\Message\MessageFactory\DiactorosMessageFactory(), 90 | new \Http\Adapter\Guzzle6\Client($httpClient) 91 | ) 92 | ); 93 | ``` 94 | 95 | ## Upgrading to 0.11.x 96 | 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. 97 | 98 | ## Upgrading to 0.10.x 99 | 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](https://github.com/egeloen/ivory-http-adapter). 100 | *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` 101 | 102 | ``` 103 | "egeloen/http-adapter": "~0.6" 104 | ``` 105 | 106 | … and run `composer update` 107 | 108 | ### Instantiating an HTTP transport 109 | 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: 110 | 111 | ```php 112 | $httpClient = new GuzzleHttp\Client(); 113 | $client = new fXmlRpc\Client( 114 | 'http://endpoint.com', 115 | new fXmlRpc\Transport\Guzzle4Bridge($httpClient) 116 | ); 117 | ``` 118 | 119 | This is how you do it now: 120 | ```php 121 | $httpClient = new GuzzleHttp\Client(); 122 | $httpClient->...(); 123 | $client = new fXmlRpc\Client( 124 | 'http://endpoint.com', 125 | new fXmlRpc\Transport\HttpAdapterTransport(new Ivory\HttpAdapter\GuzzleHttpHttpAdapter($httpClient)) 126 | ); 127 | ``` 128 | 129 | ## Latest improvements 130 | - `[BC]` PSR-7 support 131 | - `[IMPROVEMENT]` PHP7 compatibility 132 | - `[IMPROVEMENT]` Refactor parsers throw fault exception instead of client (see #53, contribution by [Piotr Olaszewski](https://github.com/piotrooo)) 133 | - `[FEATURE]` Add XML validation on the client side. Configurable but enabled per default 134 | - `[FEATURE]` Transport decorator which contains XML of the last request, response and exception (see #47, contribution by [Piotr Olaszewski](https://github.com/piotrooo)) 135 | - `[BC]` PSR-4 for autoloading (see #29) 136 | - `[BC]` Rename `fXmlRpc\Multicall` to `fXmlRpc\MulticallBuilder` 137 | - `[BC]` Make the surface of the `ClientInterface` signifcantly smaller (see #24 for details) 138 | - `[BC]` Replaces built-in transports with [Ivory HTTP Adapter](https://github.com/egeloen/ivory-http-adapter). PECL HTTP is no longer supported. Contribution by [Márk Sági-Kazár](https://github.com/sagikazarmark) 139 | - `[BUG]` Fix serialization issue with XmlWriterSerializer (see #19 for details) 140 | - `[FEATURE]` New bridge for [artax](https://github.com/amphp/artax) (with contributions by [Markus Staab](https://github.com/staabm)) 141 | - `[FEATURE]` New bridge for Guzzle 4 (contribution by [Robin van der Vleuten](https://github.com/RobinvdVleuten)) 142 | - `[FEATURE]` Allow HTTP transport headers to be controlled 143 | - `[FEATURE]` Allow transport content type and charset to be controlled (see #9) 144 | - `[BC]` Removing outdated PeclHttpBridge 145 | - `[BC]` Requiring PHP 5.4 146 | - `[BUG]` Fixing huge issue in `XmlWriterSerializer` (see #4 for details) 147 | - `[FEATURE]` Special API for multicall 148 | - `[FEATURE]` Supports all Java XML/RPC extensions 149 | - `[BC]` `fXmlRpc\AbstractDecorator` and `fXmlRpc\ClientInterface` now includes methods to prepend and append parameters 150 | - `[BC]` `fXmlRpc\Client` is marked as final. Properties marked as private. Extend via decorator. 151 | - `[BC]` Marked deprecated constructor of `fXmlRpc\Value\Base64` as private. Additionally, the value object is final now 152 | - `[TESTING]` Integration test suite against Java XML/RPC and Python XML/RPC 153 | - `[BUG]` Fixing implicit string type handling (where string is no child of value) 154 | - `[IMPROVEMENT]` Improved exception handling 155 | - `[BC]` Changing naming scheme to studly caps 156 | - `[BUG]` Fixing various array/struct edge cases 157 | - `[IMPROVEMENT]` Small memory and performance improvements for serializers and parsers 158 | - `[BC]` Deprecated constructor of `fXmlRpc\Value\Base64` and introduced `::serialize()` an `::deserialize()` instead. 159 | - `[FEATURE]` Adding `fXmlRpc\Client::prependParams()` and `fXmlRpc\Client::appendParams()` to set default params. This helps e.g. when you need to add authorization information for every call 160 | - `[FEATURE]` Timing Loggers now support threshold based logging to ease controlling your servers responds in a certain time 161 | - `[TESTING]` Travis now runs the test suite against various versions of supported HTTP clients and logging components. 162 | 163 | ### How fast is it really? 164 | 165 | IO performance is out of reach from a userspace perspective, but parsing and 166 | serialization speed is what matters. How fast can we generate the XML payload 167 | from PHP data structures and how fast can we parse the servers response? fXmlRpc 168 | uses stream based XML writers/readers to achieve it’s performance and heavily 169 | optimizes (read uglifies) for it. As as result the userland version is only 170 | around 2x slower than the native C implementation (ext/xmlrpc). 171 | 172 | 173 | #### Parser 174 | ``` 175 | Zend\XmlRpc\Value (ZF2): 249.02972793579 sec 176 | Zend_XmlRpc_Value (ZF1): 253.88145494461 sec 177 | fXmlRpc\Parser\XmlReaderParser: 36.274516105652 sec 178 | fXmlRpc\Parser\NativeParser: 18.652323007584 sec 179 | ``` 180 | 181 | #### Serializer 182 | ``` 183 | Zend\XmlRpc\Request (ZF2): 52.004573106766 sec 184 | Zend_XmlRpc_Request (ZF1): 65.042532920837 sec 185 | fXmlRpc\Serializer\XmlWriterSerializer: 23.652673006058 sec 186 | fXmlRpc\Serializer\NativeSerializer: 9.0790779590607 sec 187 | ``` 188 | 189 | 190 | ### Usage 191 | 192 | #### Basic Usage 193 | ```php 194 | call('remoteMethod', array('arg1', true)); 197 | ``` 198 | 199 | #### Using native (ext/xmlrpc based) serializer/parser (for even better performance) 200 | ```php 201 | call('remoteMethod', array('arg1', true)); 209 | ``` 210 | 211 | #### Prepending and appending arguments 212 | ```php 213 | prependParams(array('username', 'password')); 216 | $client->appendParams(array('appended')); 217 | ... 218 | ``` 219 | 220 | #### Using a convenient Proxy object 221 | ```php 222 | system->echo('Hello World!'); 226 | ``` 227 | 228 | #### Tracking XML of the request and response 229 | ```php 230 | call('TestMethod', ['param1', 2, ['param3' => true]]); 235 | 236 | $lastRequest = $recorder->getLastRequest(); 237 | $lastResponse = $recorder->getLastResponse(); 238 | ``` 239 | 240 | If exception occur in the transport layer you can get it using `getLastException()`. 241 | 242 | ### Helpful abstraction for multicall requests 243 | ```php 244 | multicall() 246 | ->addCall('system.add', array(1, 2)) 247 | ->addCall( 248 | 'system.add', 249 | array(2, 3), 250 | function ($result) { 251 | echo "Result was: " . $result; 252 | }, 253 | function($result) { 254 | echo "An error occured: " . var_export($result, true); 255 | } 256 | ) 257 | ->onSuccess(function ($result) {echo "Success";}) // Success handler for each call 258 | ->onError(function ($result) {echo "Error";}) // Error handler for each call 259 | ->execute(); 260 | ``` 261 | 262 | #### Integration for various HTTP clients using [Ivory](https://github.com/egeloen/ivory-http-adapter) 263 | ```php 264 | ...(); 268 | $client = new fXmlRpc\Client( 269 | 'http://endpoint.com', 270 | new fXmlRpc\Transport\HttpAdapterTransport(new \Ivory\HttpAdapter\BuzzHttpAdapter($browser)) 271 | ); 272 | 273 | /** Zend Framework 1 (http://framework.zend.com/) */ 274 | $httpClient = new Zend_Http_Client(); 275 | $httpClient->...(); 276 | $client = new fXmlRpc\Client( 277 | 'http://endpoint.com', 278 | new fXmlRpc\Transport\HttpAdapterTransport(new \Ivory\HttpAdapter\Zend1HttpAdapter($httpClient)) 279 | ); 280 | 281 | /** Zend Framework 2 (http://framework.zend.com/zf2) */ 282 | $httpClient = new Zend\Http\Client(); 283 | $httpClient->...(); 284 | $client = new fXmlRpc\Client( 285 | 'http://endpoint.com', 286 | new fXmlRpc\Transport\HttpAdapterTransport(new \Ivory\HttpAdapter\Zend2HttpAdapter($httpClient)) 287 | ); 288 | 289 | /** Guzzle (http://guzzlephp.org/) */ 290 | $httpClient = new Guzzle\Http\Client(); 291 | $httpClient->...(); 292 | $client = new fXmlRpc\Client( 293 | 'http://endpoint.com', 294 | new fXmlRpc\Transport\HttpAdapterTransport(new \Ivory\HttpAdapter\GuzzleAdapter($httpClient)) 295 | ); 296 | 297 | /** Guzzle 4+ (http://guzzlephp.org/) */ 298 | $httpClient = new GuzzleHttp\Client(); 299 | $httpClient->...(); 300 | $client = new fXmlRpc\Client( 301 | 'http://endpoint.com', 302 | new fXmlRpc\Transport\HttpAdapterTransport(new \Ivory\HttpAdapter\GuzzleHttpHttpAdapter($httpClient)) 303 | ); 304 | ``` 305 | 306 | #### Timing XML/RPC requests to find problematic calls 307 | fXmlRpc allows you to time your XML/RPC request, to find out which took how long. It provides a 308 | `fXmlRpc\Timing\TimingDecorator` which can be used with various timers implementing 309 | `fXmlRpc\Timing\TimerInterface`. Currently implemented are bridges for Monolog, Zend Framework 1 310 | `Zend_Log` and Zend Framework 2 `Zend\Log`. 311 | 312 | Usage: 313 | ```php 314 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc; 25 | 26 | /** 27 | * Abstract base class for client decorators 28 | * 29 | * Extend this base class if you want to decorate functionality of the client 30 | */ 31 | abstract class AbstractDecorator implements ClientInterface 32 | { 33 | /** @var ClientInterface */ 34 | protected $wrapped; 35 | 36 | /** {@inheritdoc} */ 37 | public function __construct(ClientInterface $wrapped) 38 | { 39 | $this->wrapped = $wrapped; 40 | } 41 | 42 | /** {@inheritdoc} */ 43 | public function call($methodName, array $arguments = []) 44 | { 45 | return $this->wrapped->call($methodName, $arguments); 46 | } 47 | 48 | /** {@inheritdoc} */ 49 | public function multicall() 50 | { 51 | return $this->wrapped->multicall(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/fXmlRpc/CallClientInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc; 25 | 26 | use fXmlRpc\Exception\ExceptionInterface; 27 | 28 | interface CallClientInterface 29 | { 30 | /** 31 | * Execute remote call 32 | * 33 | * @param string $method 34 | * @param array $arguments 35 | * @throws ExceptionInterface 36 | * @return mixed 37 | */ 38 | public function call($method, array $arguments = []); 39 | } 40 | -------------------------------------------------------------------------------- /src/fXmlRpc/Client.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc; 25 | 26 | use fXmlRpc\Exception\InvalidArgumentException; 27 | use fXmlRpc\Parser\ParserInterface; 28 | use fXmlRpc\Parser\XmlReaderParser; 29 | use fXmlRpc\Serializer\SerializerInterface; 30 | use fXmlRpc\Serializer\XmlWriterSerializer; 31 | use fXmlRpc\Transport\PsrTransport; 32 | use fXmlRpc\Transport\TransportInterface; 33 | use Http\Discovery\Psr17FactoryDiscovery; 34 | use Http\Discovery\Psr18ClientDiscovery; 35 | 36 | final class Client implements ClientInterface 37 | { 38 | /** @var string */ 39 | private $uri; 40 | 41 | /** @var TransportInterface */ 42 | private $transport; 43 | 44 | /** @var Parser\ParserInterface */ 45 | private $parser; 46 | 47 | /** @var Serializer\SerializerInterface */ 48 | private $serializer; 49 | 50 | /** @var array */ 51 | private $prependParams = []; 52 | 53 | /** @var array */ 54 | private $appendParams = []; 55 | 56 | /** 57 | * Create new client instance 58 | * 59 | * If no specific transport, parser or serializer is passed, default implementations 60 | * are used. 61 | * 62 | * @param string $uri 63 | * @param TransportInterface $transport 64 | * @param Parser\ParserInterface $parser 65 | * @param Serializer\SerializerInterface $serializer 66 | */ 67 | public function __construct( 68 | $uri = null, 69 | TransportInterface $transport = null, 70 | ParserInterface $parser = null, 71 | SerializerInterface $serializer = null 72 | ) 73 | { 74 | $this->uri = $uri; 75 | $this->transport = $transport 76 | ?: new PsrTransport(Psr17FactoryDiscovery::findRequestFactory(), Psr18ClientDiscovery::find()); 77 | $this->parser = $parser ?: new XmlReaderParser(); 78 | $this->serializer = $serializer ?: new XmlWriterSerializer(); 79 | } 80 | 81 | /** 82 | * Set the endpoint URI 83 | * 84 | * @param string $uri 85 | */ 86 | public function setUri($uri) 87 | { 88 | if (!is_string($uri)) { 89 | throw InvalidArgumentException::expectedParameter(0, 'string', $uri); 90 | } 91 | 92 | $this->uri = $uri; 93 | } 94 | 95 | /** 96 | * Return endpoint URI 97 | * 98 | * @return string 99 | */ 100 | public function getUri() 101 | { 102 | return $this->uri; 103 | } 104 | 105 | /** 106 | * Prepend default parameters that should always be prepended 107 | * 108 | * @param array $params 109 | */ 110 | public function prependParams(array $params) 111 | { 112 | $this->prependParams = $params; 113 | } 114 | 115 | /** 116 | * Get default parameters that are always prepended 117 | * 118 | * @return array 119 | */ 120 | public function getPrependParams() 121 | { 122 | return $this->prependParams; 123 | } 124 | 125 | /** 126 | * Append default parameters that should always be prepended 127 | * 128 | * @param array $params 129 | */ 130 | public function appendParams(array $params) 131 | { 132 | $this->appendParams = $params; 133 | } 134 | 135 | /** 136 | * Get default parameters that are always appended 137 | * 138 | * @return array 139 | */ 140 | public function getAppendParams() 141 | { 142 | return $this->appendParams; 143 | } 144 | 145 | /** {@inheritdoc} */ 146 | public function call($methodName, array $params = []) 147 | { 148 | if (!is_string($methodName)) { 149 | throw InvalidArgumentException::expectedParameter(0, 'string', $methodName); 150 | } 151 | 152 | $params = array_merge($this->prependParams, $params, $this->appendParams); 153 | $payload = $this->serializer->serialize($methodName, $params); 154 | $response = $this->transport->send($this->uri, $payload); 155 | 156 | return $this->parser->parse($response); 157 | } 158 | 159 | /** {@inheritdoc} */ 160 | public function multicall() 161 | { 162 | return new MulticallBuilder($this); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/fXmlRpc/ClientInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc; 25 | 26 | interface ClientInterface extends CallClientInterface, MulticallClientInterface 27 | { 28 | } 29 | -------------------------------------------------------------------------------- /src/fXmlRpc/CodeGenerator/XmlReaderParserBitmaskGenerator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | namespace fXmlRpc\CodeGenerator; 26 | 27 | final class XmlReaderParserBitmaskGenerator 28 | { 29 | private $basicTypes = [ 30 | 'methodResponse', 31 | 'params', 32 | 'fault', 33 | 'param', 34 | 'value', 35 | 'array', 36 | 'member', 37 | 'name', 38 | '#text', 39 | 'string', 40 | 'struct', 41 | 'int', 42 | 'biginteger', 43 | 'i8', 44 | 'i4', 45 | 'i2', 46 | 'i1', 47 | 'boolean', 48 | 'double', 49 | 'float', 50 | 'bigdecimal', 51 | 'dateTime.iso8601', 52 | 'dateTime', 53 | 'base64', 54 | 'nil', 55 | 'dom', 56 | 'data', 57 | ]; 58 | 59 | private $combinedTypes = []; 60 | 61 | private $typeCount = 0; 62 | 63 | private $values = []; 64 | 65 | public function __construct() 66 | { 67 | $this->combinedTypes = [ 68 | 'expectedForMethodResponse' => ['params', 'fault'], 69 | 'expectedForMember' => ['name', 'value'], 70 | 'expectedForSimpleType' => ['#text', 'value'], 71 | 'expectedForNil' => ['nil', 'value'], 72 | 'expectedForValue' => [ 73 | 'string', 74 | 'array', 75 | 'struct', 76 | 'int', 77 | 'biginteger', 78 | 'i8', 79 | 'i4', 80 | 'i2', 81 | 'i1', 82 | 'boolean', 83 | 'double', 84 | 'float', 85 | 'bigdecimal', 86 | 'dateTime.iso8601', 87 | 'dateTime', 88 | 'base64', 89 | 'nil', 90 | 'dom', 91 | '#text', 92 | 'value', 93 | ], 94 | 'expectedForStruct' => ['member', 'struct', 'value'], 95 | 'expectedForData' => ['data', 'value', 'array'], 96 | 'expectedAfterValue' => [ 97 | 'param', 98 | 'value', 99 | 'data', 100 | 'member', 101 | 'name', 102 | 'int', 103 | 'i4', 104 | 'i2', 105 | 'i1', 106 | 'base64', 107 | 'fault' 108 | ], 109 | 'expectedAfterParam' => ['param', 'params'], 110 | 'expectedAfterName' => ['value', 'member'], 111 | 'expectedAfterMember' => ['struct', 'member'], 112 | 'allFlags' => $this->basicTypes, 113 | ]; 114 | 115 | $this->typeCount = count($this->basicTypes); 116 | } 117 | 118 | private function createBitmaskVariable($type, $bitmask, $prefix = '') 119 | { 120 | $variableName = preg_match('/^\w+[\d\w_]*$/', $type) 121 | ? 'static $' . $prefix . $type 122 | : '${\'' . $prefix . $type . '\'}'; 123 | $this->values[$type] = $bitmask; 124 | 125 | return $variableName . ' = 0b' . sprintf('%0' . $this->typeCount . 'b', $this->values[$type]) . ';'; 126 | } 127 | 128 | public function generate() 129 | { 130 | $code = []; 131 | $bitmask = 1; 132 | foreach ($this->basicTypes as $type) { 133 | $code[] = $this->createBitmaskVariable($type, $bitmask, 'flag'); 134 | $bitmask = $bitmask << 1; 135 | } 136 | 137 | foreach ($this->combinedTypes as $type => $combination) { 138 | $value = 0; 139 | foreach ($combination as $subType) { 140 | $value |= $this->values[$subType]; 141 | } 142 | $code[] = $this->createBitmaskVariable($type, $value); 143 | } 144 | 145 | $commentStart = <<<'EOS' 146 | // This following assignments are auto-generated using %s 147 | // Don’t edit manually 148 | EOS; 149 | 150 | $commentStart = sprintf($commentStart, __CLASS__); 151 | 152 | $commentEnd = '// End of auto-generated code'; 153 | 154 | return $commentStart . "\n" . implode("\n", $code) . "\n" . $commentEnd; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/fXmlRpc/Exception/AbstractTransportException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Exception; 25 | 26 | abstract class AbstractTransportException extends RuntimeException 27 | { 28 | } 29 | -------------------------------------------------------------------------------- /src/fXmlRpc/Exception/ExceptionInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Exception; 25 | 26 | use Throwable; 27 | 28 | interface ExceptionInterface extends Throwable 29 | { 30 | } 31 | -------------------------------------------------------------------------------- /src/fXmlRpc/Exception/FaultException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Exception; 25 | 26 | final class FaultException extends RuntimeException 27 | { 28 | private $faultCode; 29 | 30 | public static function fault($response) 31 | { 32 | $exception = new static(isset($response['faultString']) ? $response['faultString'] : 'Unknown'); 33 | $exception->faultCode = isset($response['faultCode']) ? $response['faultCode'] : 0; 34 | 35 | return $exception; 36 | } 37 | 38 | public function getFaultString() 39 | { 40 | return $this->getMessage(); 41 | } 42 | 43 | public function getFaultCode() 44 | { 45 | return $this->faultCode; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/fXmlRpc/Exception/HttpException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Exception; 25 | 26 | use Exception; 27 | 28 | final class HttpException extends AbstractTransportException 29 | { 30 | public static function httpError($message, $statusCode, Exception $previous = null) 31 | { 32 | return new static('An HTTP error occurred: ' . $message, $statusCode, $previous); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/fXmlRpc/Exception/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Exception; 25 | 26 | use InvalidArgumentException as BaseInvalidArgumentException; 27 | 28 | final class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface 29 | { 30 | public static function expectedParameter($position, $expected, $actualValue) 31 | { 32 | return new static( 33 | sprintf( 34 | 'Expected parameter %d to be of type "%s", "%s" given', 35 | $position, 36 | $expected, 37 | is_object($actualValue) 38 | ? sprintf('%s" of type "%s', gettype($actualValue), get_class($actualValue)) 39 | : gettype($actualValue) 40 | ) 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/fXmlRpc/Exception/MissingExtensionException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Exception; 25 | 26 | final class MissingExtensionException extends RuntimeException 27 | { 28 | public static function extensionMissing($extension) 29 | { 30 | return new static(sprintf('PHP extension "ext/%s" is required but not installed', $extension)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/fXmlRpc/Exception/ParserException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Exception; 25 | 26 | final class ParserException extends RuntimeException 27 | { 28 | public static function unexpectedTag($tagName, $elements, array $definedVariables, $depth, $xml) 29 | { 30 | $expectedElements = []; 31 | foreach ($definedVariables as $variableName => $variable) { 32 | if (substr($variableName, 0, 4) !== 'flag') { 33 | continue; 34 | } 35 | 36 | if (($elements & $variable) === $variable) { 37 | $expectedElements[] = substr($variableName, 4); 38 | } 39 | } 40 | 41 | return new static( 42 | sprintf( 43 | 'Invalid XML. Expected one of "%s", got "%s" on depth %d (context: "%s")', 44 | implode('", "', $expectedElements), 45 | $tagName, 46 | $depth, 47 | $xml 48 | ) 49 | ); 50 | } 51 | 52 | public static function notXml($string) 53 | { 54 | return new static(sprintf('Invalid XML. Expected XML, string given: "%s"', $string)); 55 | } 56 | 57 | public static function xmlrpcExtensionLibxmlParsehugeNotSupported() 58 | { 59 | return new static( 60 | 'Parsing huge XML responses using libxml’s LIBXML_PARSEHUGE flag is not supported in ext/xmlrpc' 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/fXmlRpc/Exception/RuntimeException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Exception; 25 | 26 | use RuntimeException as BaseRuntimeException; 27 | 28 | abstract class RuntimeException extends BaseRuntimeException implements ExceptionInterface 29 | { 30 | } 31 | -------------------------------------------------------------------------------- /src/fXmlRpc/Exception/SerializationException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Exception; 25 | 26 | final class SerializationException extends RuntimeException 27 | { 28 | public static function invalidType($value) 29 | { 30 | return new static(sprintf('Could not serialize %s of type "%s"', gettype($value), get_resource_type($value))); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/fXmlRpc/Exception/TransportException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Exception; 25 | 26 | use Exception; 27 | 28 | final class TransportException extends RuntimeException 29 | { 30 | public static function transportError($error) 31 | { 32 | $message = $error instanceof Exception ? $error->getMessage() : $error; 33 | $previous = $error instanceof Exception ? $error : null; 34 | 35 | return new static('Transport error occurred: ' . $message, null, $previous); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/fXmlRpc/ExtensionSupportInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc; 25 | 26 | interface ExtensionSupportInterface 27 | { 28 | const EXTENSION_NIL = 'nil'; 29 | 30 | /** 31 | * Enables extension 32 | * 33 | * @param string $extension 34 | * @return null 35 | */ 36 | public function enableExtension($extension); 37 | 38 | /** 39 | * Disables extension 40 | * 41 | * @param string $extension 42 | * @return null 43 | */ 44 | public function disableExtension($extension); 45 | 46 | /** 47 | * Returns true if an extension is enabled 48 | * 49 | * @param string $extension 50 | * @return boolean 51 | */ 52 | public function isExtensionEnabled($extension); 53 | } 54 | -------------------------------------------------------------------------------- /src/fXmlRpc/MulticallBuilder.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc; 25 | 26 | use fXmlRpc\Exception\InvalidArgumentException; 27 | 28 | final class MulticallBuilder implements MulticallBuilderInterface 29 | { 30 | /** @var ClientInterface */ 31 | private $client; 32 | 33 | /** @var integer */ 34 | private $index = 0; 35 | 36 | /** @var array */ 37 | private $calls = []; 38 | 39 | /** @var array */ 40 | private $handlers = []; 41 | 42 | /** @var callable */ 43 | private $onSuccess; 44 | 45 | /** @var callable */ 46 | private $onError; 47 | 48 | /** @param ClientInterface $client */ 49 | public function __construct(ClientInterface $client) 50 | { 51 | $this->client = $client; 52 | } 53 | 54 | /** {@inheritdoc} */ 55 | public function addCall($methodName, array $params = [], callable $onSuccess = null, callable $onError = null) 56 | { 57 | if (!is_string($methodName)) { 58 | throw InvalidArgumentException::expectedParameter(1, 'string', $methodName); 59 | } 60 | 61 | $this->calls[$this->index] = compact('methodName', 'params'); 62 | $this->handlers[$this->index] = compact('onSuccess', 'onError'); 63 | ++$this->index; 64 | 65 | return $this; 66 | } 67 | 68 | /** {@inheritdoc} */ 69 | public function onSuccess(callable $onSuccess) 70 | { 71 | $this->onSuccess = $onSuccess; 72 | 73 | return $this; 74 | } 75 | 76 | /** {@inheritdoc} */ 77 | public function onError(callable $onError) 78 | { 79 | $this->onError = $onError; 80 | 81 | return $this; 82 | } 83 | 84 | /** {@inheritdoc} */ 85 | public function execute() 86 | { 87 | $results = $this->client->call('system.multicall', [$this->calls]); 88 | 89 | foreach ($results as $index => $result) { 90 | $this->processResult($this->handlers[$index], $result); 91 | } 92 | 93 | return $results; 94 | } 95 | 96 | /** 97 | * @param array $handler 98 | * @param mixed $result 99 | */ 100 | protected function processResult(array $handler, $result) 101 | { 102 | $isError = is_array($result) && isset($result['faultCode']); 103 | 104 | $this->invokeHandler($handler['onSuccess'], $handler['onError'], $isError, $result); 105 | $this->invokeHandler($this->onSuccess, $this->onError, $isError, $result); 106 | } 107 | 108 | /** 109 | * @param callable|null $onSuccess 110 | * @param callable|null $onError 111 | * @param boolean $isError 112 | * @param mixed $result 113 | */ 114 | protected function invokeHandler($onSuccess, $onError, $isError, $result) 115 | { 116 | if ($isError && $onError !== null) { 117 | call_user_func($onError, $result); 118 | } elseif ($onSuccess !== null) { 119 | call_user_func($onSuccess, $result); 120 | } 121 | } 122 | 123 | /** @return ClientInterface */ 124 | public function getClient() 125 | { 126 | return $this->client; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/fXmlRpc/MulticallBuilderInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc; 25 | 26 | use fXmlRpc\Exception\InvalidArgumentException; 27 | 28 | interface MulticallBuilderInterface 29 | { 30 | /** 31 | * Register a success handler applicable to all multicall responses 32 | * 33 | * @param callable $handler 34 | * @throws InvalidArgumentException 35 | * @return MulticallBuilderInterface 36 | */ 37 | public function onSuccess(callable $handler); 38 | 39 | /** 40 | * Register a error handler applicable to all multicall responses 41 | * 42 | * @param callable $handler 43 | * @throws InvalidArgumentException 44 | * @return MulticallBuilderInterface 45 | */ 46 | public function onError(callable $handler); 47 | 48 | /** 49 | * Add a call to the end of the multicall stack 50 | * 51 | * @param string $methodName 52 | * @param array $params 53 | * @param callable $onSuccess 54 | * @param callable $onError 55 | * @return MulticallBuilderInterface 56 | */ 57 | public function addCall($methodName, array $params = [], callable $onSuccess = null, callable $onError = null); 58 | 59 | /** 60 | * Send the multicall request to the server 61 | * 62 | * @return array 63 | */ 64 | public function execute(); 65 | } 66 | -------------------------------------------------------------------------------- /src/fXmlRpc/MulticallClientInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc; 25 | 26 | interface MulticallClientInterface 27 | { 28 | /** 29 | * Start sequence of multicall 30 | * 31 | * @return MulticallBuilderInterface 32 | */ 33 | public function multicall(); 34 | } 35 | -------------------------------------------------------------------------------- /src/fXmlRpc/Parser/BestParserDelegate.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Parser; 25 | 26 | final class BestParserDelegate implements ParserInterface 27 | { 28 | /** @var NativeParser */ 29 | private $nativeParser; 30 | 31 | /** @var XmlReaderParser */ 32 | private $xmlReaderParser; 33 | 34 | public function __construct($validateResponse = true) 35 | { 36 | $this->nativeParser = new NativeParser($validateResponse); 37 | $this->xmlReaderParser = new XmlReaderParser($validateResponse); 38 | } 39 | 40 | public function parse($xmlString) 41 | { 42 | return !NativeParser::isBiggerThanParseLimit($xmlString) 43 | ? $this->nativeParser->parse($xmlString) 44 | : $this->xmlReaderParser->parse($xmlString); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/fXmlRpc/Parser/NativeParser.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Parser; 25 | 26 | use DateTime; 27 | use DateTimeZone; 28 | use fXmlRpc\Exception\FaultException; 29 | use fXmlRpc\Exception\MissingExtensionException; 30 | use fXmlRpc\Exception\ParserException; 31 | use fXmlRpc\Value\Base64; 32 | 33 | final class NativeParser implements ParserInterface 34 | { 35 | const LIBXML_PARSEHUGE_THRESHOLD = 1024 * 1024 * 10; 36 | 37 | /** 38 | * @var bool 39 | */ 40 | private $validateResponse; 41 | 42 | public function __construct($validateResponse = true) 43 | { 44 | if (!extension_loaded('xmlrpc')) { 45 | throw MissingExtensionException::extensionMissing('xmlrpc'); 46 | } 47 | $this->validateResponse = $validateResponse; 48 | } 49 | 50 | /** 51 | * @param string $xmlString 52 | * @return bool 53 | */ 54 | public static function isBiggerThanParseLimit($xmlString) 55 | { 56 | return strlen($xmlString) > static::LIBXML_PARSEHUGE_THRESHOLD; 57 | } 58 | 59 | /** {@inheritdoc} */ 60 | public function parse($xmlString) 61 | { 62 | if ($this->validateResponse) { 63 | XmlChecker::validXml($xmlString); 64 | } 65 | 66 | $result = xmlrpc_decode($xmlString, 'UTF-8'); 67 | 68 | if ($result === null && self::isBiggerThanParseLimit($xmlString)) { 69 | throw ParserException::xmlrpcExtensionLibxmlParsehugeNotSupported(); 70 | } 71 | 72 | $toBeVisited = [&$result]; 73 | while (isset($toBeVisited[0]) && $value = &$toBeVisited[0]) { 74 | 75 | $type = gettype($value); 76 | if ($type === 'object') { 77 | $xmlRpcType = $value->{'xmlrpc_type'}; 78 | if ($xmlRpcType === 'datetime') { 79 | $value = DateTime::createFromFormat( 80 | 'Ymd\TH:i:s', 81 | $value->scalar, 82 | isset($timezone) ? $timezone : $timezone = new DateTimeZone('UTC') 83 | ); 84 | 85 | } elseif ($xmlRpcType === 'base64') { 86 | if ($value->scalar !== '') { 87 | $value = Base64::serialize($value->scalar); 88 | } else { 89 | $value = null; 90 | } 91 | } 92 | 93 | } elseif ($type === 'array') { 94 | foreach ($value as &$element) { 95 | $toBeVisited[] = &$element; 96 | } 97 | } 98 | 99 | array_shift($toBeVisited); 100 | } 101 | 102 | if (is_array($result)) { 103 | reset($result); 104 | if (xmlrpc_is_fault($result)) { 105 | throw FaultException::fault($result); 106 | } 107 | } 108 | 109 | return $result; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/fXmlRpc/Parser/ParserInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Parser; 25 | 26 | use fXmlRpc\Exception\FaultException; 27 | 28 | /** 29 | * Parser to parse XML responses into its PHP representation 30 | */ 31 | interface ParserInterface 32 | { 33 | /** 34 | * Parse XML string into PHP representation 35 | * 36 | * @param string $xmlString 37 | * @throws FaultException 38 | * @return mixed 39 | */ 40 | public function parse($xmlString); 41 | } 42 | -------------------------------------------------------------------------------- /src/fXmlRpc/Parser/XmlChecker.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Parser; 25 | 26 | use fXmlRpc\Exception\ParserException; 27 | 28 | /** 29 | * Class XmlChecker to check is correct XML 30 | * @author Piotr Olaszewski 31 | */ 32 | final class XmlChecker 33 | { 34 | /** 35 | * @param string $xml 36 | * @throws ParserException 37 | */ 38 | public static function validXml($xml) 39 | { 40 | $useErrors = libxml_use_internal_errors(true); 41 | 42 | $isCorrect = simplexml_load_string($xml); 43 | if ($isCorrect === false) { 44 | libxml_use_internal_errors($useErrors); 45 | throw ParserException::notXml($xml); 46 | } 47 | 48 | libxml_use_internal_errors($useErrors); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/fXmlRpc/Parser/XmlReaderParser.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Parser; 25 | 26 | use DateTime; 27 | use DateTimeZone; 28 | use DOMDocument; 29 | use fXmlRpc\Exception\FaultException; 30 | use fXmlRpc\Exception\MissingExtensionException; 31 | use fXmlRpc\Exception\ParserException; 32 | use fXmlRpc\Value\Base64; 33 | use XMLReader; 34 | 35 | final class XmlReaderParser implements ParserInterface 36 | { 37 | /** 38 | * @var bool 39 | */ 40 | private $validateResponse; 41 | 42 | public function __construct($validateResponse = true) 43 | { 44 | if (!extension_loaded('xmlreader')) { 45 | throw MissingExtensionException::extensionMissing('xmlreader'); 46 | } 47 | $this->validateResponse = $validateResponse; 48 | } 49 | 50 | /** {@inheritdoc} */ 51 | public function parse($xmlString) 52 | { 53 | if ($this->validateResponse) { 54 | XmlChecker::validXml($xmlString); 55 | } 56 | 57 | $useErrors = libxml_use_internal_errors(true); 58 | 59 | $xml = new XMLReader(); 60 | $xml->xml($xmlString, 'UTF-8', LIBXML_COMPACT | LIBXML_NOCDATA | LIBXML_NOBLANKS | LIBXML_PARSEHUGE); 61 | $xml->setParserProperty(XMLReader::VALIDATE, false); 62 | $xml->setParserProperty(XMLReader::LOADDTD, false); 63 | 64 | // This following assignments are auto-generated using fXmlRpc\CodeGenerator\XmlReaderParserBitmaskGenerator 65 | // Don’t edit manually 66 | static $flagmethodResponse = 0b000000000000000000000000001; 67 | static $flagparams = 0b000000000000000000000000010; 68 | static $flagfault = 0b000000000000000000000000100; 69 | static $flagparam = 0b000000000000000000000001000; 70 | static $flagvalue = 0b000000000000000000000010000; 71 | static $flagarray = 0b000000000000000000000100000; 72 | static $flagmember = 0b000000000000000000001000000; 73 | static $flagname = 0b000000000000000000010000000; 74 | ${'flag#text'} = 0b000000000000000000100000000; 75 | static $flagstring = 0b000000000000000001000000000; 76 | static $flagstruct = 0b000000000000000010000000000; 77 | static $flagint = 0b000000000000000100000000000; 78 | static $flagbiginteger = 0b000000000000001000000000000; 79 | static $flagi8 = 0b000000000000010000000000000; 80 | static $flagi4 = 0b000000000000100000000000000; 81 | static $flagi2 = 0b000000000001000000000000000; 82 | static $flagi1 = 0b000000000010000000000000000; 83 | static $flagboolean = 0b000000000100000000000000000; 84 | static $flagdouble = 0b000000001000000000000000000; 85 | static $flagfloat = 0b000000010000000000000000000; 86 | static $flagbigdecimal = 0b000000100000000000000000000; 87 | ${'flagdateTime.iso8601'} = 0b000001000000000000000000000; 88 | static $flagdateTime = 0b000010000000000000000000000; 89 | static $flagbase64 = 0b000100000000000000000000000; 90 | static $flagnil = 0b001000000000000000000000000; 91 | static $flagdom = 0b010000000000000000000000000; 92 | static $flagdata = 0b100000000000000000000000000; 93 | // End of auto-generated code 94 | 95 | $aggregates = []; 96 | $depth = 0; 97 | $nextExpectedElements = 0b000000000000000000000000001; 98 | $i = 0; 99 | $isFault = false; 100 | while ($xml->read()) { 101 | $i++; 102 | $nodeType = $xml->nodeType; 103 | 104 | if (($nodeType === XMLReader::COMMENT || $nodeType === XMLReader::DOC_TYPE) || 105 | ( 106 | $nodeType === XMLReader::SIGNIFICANT_WHITESPACE && 107 | ($nextExpectedElements & 0b000000000000000000100000000) !== 0b000000000000000000100000000) 108 | ) { 109 | continue; 110 | } 111 | 112 | if ($nodeType === XMLReader::ENTITY_REF) { 113 | libxml_use_internal_errors($useErrors); 114 | return ''; 115 | } 116 | 117 | $tagName = $xml->localName; 118 | if ($nextExpectedElements !== null && 119 | ($flag = isset(${'flag' . $tagName}) ? ${'flag' . $tagName} : -1) && 120 | ($nextExpectedElements & $flag) !== $flag 121 | ) { 122 | libxml_use_internal_errors($useErrors); 123 | throw ParserException::unexpectedTag( 124 | $tagName, 125 | $nextExpectedElements, 126 | get_defined_vars(), 127 | $xml->depth, 128 | $xml->readOuterXml() 129 | ); 130 | } 131 | 132 | processing: 133 | switch ($nodeType) { 134 | case XMLReader::ELEMENT: 135 | switch ($tagName) { 136 | case 'methodResponse': 137 | // Next: params, fault 138 | $nextExpectedElements = 0b000000000000000000000000110; 139 | break; 140 | 141 | case 'params': 142 | // Next: param 143 | $nextExpectedElements = 0b000000000000000000000001000; 144 | $aggregates[$depth] = []; 145 | $isFault = false; 146 | break; 147 | 148 | case 'fault': 149 | $isFault = true; 150 | // Break intentionally omitted 151 | case 'param': 152 | // Next: value 153 | $nextExpectedElements = 0b000000000000000000000010000; 154 | break; 155 | 156 | case 'array': 157 | $aggregates[++$depth] = []; 158 | // Break intentionally omitted 159 | case 'data': 160 | // Next: array, data, value 161 | $nextExpectedElements = 0b100000000000000000000110000; 162 | break; 163 | 164 | case 'struct': 165 | // Next: struct, member, value 166 | $nextExpectedElements = 0b000000000000000010001010000; 167 | $aggregates[++$depth] = []; 168 | break; 169 | 170 | case 'member': 171 | // Next: name, value 172 | $nextExpectedElements = 0b000000000000000000010010000; 173 | $aggregates[++$depth] = []; 174 | break; 175 | 176 | case 'name': 177 | // Next: #text 178 | $nextExpectedElements = 0b000000000000000000100000000; 179 | $type = 'name'; 180 | break; 181 | 182 | case 'value': 183 | $nextExpectedElements = 0b011111111111111111100110000; 184 | $type = 'value'; 185 | $aggregates[$depth + 1] = ''; 186 | break; 187 | 188 | case 'base64': 189 | case 'string': 190 | case 'biginteger': 191 | case 'i8': 192 | case 'dateTime.iso8601': 193 | case 'dateTime': 194 | // Next: value, $tagName, #text 195 | $nextExpectedElements = 0b000000000000000000100010000 | ${'flag' . $tagName}; 196 | $type = $tagName; 197 | $aggregates[$depth + 1] = ''; 198 | break; 199 | 200 | case 'nil': 201 | // Next: value, $tagName 202 | $nextExpectedElements = 0b001000000000000000000010000 | ${'flag' . $tagName}; 203 | $type = $tagName; 204 | $aggregates[$depth + 1] = null; 205 | break; 206 | 207 | case 'int': 208 | case 'i4': 209 | case 'i2': 210 | case 'i1': 211 | // Next: value, #text, $tagName 212 | $nextExpectedElements = 0b000000000000000000100010000 | ${'flag' . $tagName}; 213 | $type = $tagName; 214 | $aggregates[$depth + 1] = 0; 215 | break; 216 | 217 | case 'boolean': 218 | // Next: value, #text, $tagName 219 | $nextExpectedElements = 0b000000000000000000100010000 | ${'flag' . $tagName}; 220 | $type = 'boolean'; 221 | $aggregates[$depth + 1] = false; 222 | break; 223 | 224 | case 'double': 225 | case 'float': 226 | case 'bigdecimal': 227 | // Next: value, #text, $tagName 228 | $nextExpectedElements = 0b000000000000000000100010000 | ${'flag' . $tagName}; 229 | $type = $tagName; 230 | $aggregates[$depth + 1] = 0.0; 231 | break; 232 | 233 | case 'dom': 234 | $type = 'dom'; 235 | // Disable type checking 236 | $nextExpectedElements = null; 237 | $aggregates[$depth + 1] = $xml->readInnerXml(); 238 | break; 239 | } 240 | break; 241 | 242 | case XMLReader::END_ELEMENT: 243 | switch ($tagName) { 244 | case 'params': 245 | case 'fault': 246 | break 3; 247 | 248 | case 'param': 249 | // Next: params, param 250 | $nextExpectedElements = 0b000000000000000000000001010; 251 | break; 252 | 253 | case 'value': 254 | $nextExpectedElements = 0b100100000011100100011011100; 255 | $aggregates[$depth][] = $aggregates[$depth + 1]; 256 | break; 257 | 258 | case 'array': 259 | case 'struct': 260 | --$depth; 261 | // Break intentionally omitted 262 | case 'string': 263 | case 'int': 264 | case 'biginteger': 265 | case 'i8': 266 | case 'i4': 267 | case 'i2': 268 | case 'i1': 269 | case 'boolean': 270 | case 'double': 271 | case 'float': 272 | case 'bigdecimal': 273 | case 'dateTime.iso8601': 274 | case 'dateTime': 275 | case 'base64': 276 | case 'nil': 277 | // Next: value 278 | $nextExpectedElements = 0b000000000000000000000010000; 279 | break; 280 | 281 | case 'data': 282 | // Next: array 283 | $nextExpectedElements = 0b000000000000000000000100000; 284 | break; 285 | 286 | case 'name': 287 | // Next: value, member 288 | $nextExpectedElements = 0b000000000000000000001010000; 289 | $aggregates[$depth]['name'] = $aggregates[$depth + 1]; 290 | break; 291 | 292 | case 'member': 293 | // Next: struct, member 294 | $nextExpectedElements = 0b000000000000000010001000000; 295 | $aggregates[$depth - 1][$aggregates[$depth]['name']] = $aggregates[$depth][0]; 296 | unset($aggregates[$depth], $aggregates[$depth + 1]); 297 | --$depth; 298 | break; 299 | } 300 | break; 301 | 302 | case XMLReader::TEXT: 303 | case XMLReader::SIGNIFICANT_WHITESPACE: 304 | switch ($type) { 305 | case 'int': 306 | case 'i4': 307 | case 'i2': 308 | case 'i1': 309 | $value = (int)$xml->value; 310 | break; 311 | 312 | case 'boolean': 313 | $value = $xml->value === '1'; 314 | break; 315 | 316 | case 'double': 317 | case 'float': 318 | case 'bigdecimal': 319 | $value = (float)$xml->value; 320 | break; 321 | 322 | case 'dateTime.iso8601': 323 | $value = DateTime::createFromFormat( 324 | 'Ymd\TH:i:s', 325 | $xml->value, 326 | isset($timezone) ? $timezone : $timezone = new DateTimeZone('UTC') 327 | ); 328 | break; 329 | 330 | case 'dateTime': 331 | $value = DateTime::createFromFormat( 332 | 'Y-m-d\TH:i:s.uP', 333 | $xml->value, 334 | isset($timezone) ? $timezone : $timezone = new DateTimeZone('UTC') 335 | ); 336 | break; 337 | 338 | case 'base64': 339 | $value = Base64::deserialize($xml->value); 340 | break; 341 | 342 | case 'dom': 343 | $doc = new DOMDocument('1.0', 'UTF-8'); 344 | $doc->loadXML($aggregates[$depth + 1]); 345 | $value = $doc; 346 | break; 347 | 348 | default: 349 | $value = &$xml->value; 350 | break; 351 | } 352 | 353 | $aggregates[$depth + 1] = $value; 354 | if ($nextExpectedElements === null) { 355 | break; 356 | } 357 | // Next: any 358 | $nextExpectedElements = 0b111111111111111111111111111; 359 | break; 360 | } 361 | 362 | if ($xml->isEmptyElement && $nodeType !== XMLReader::END_ELEMENT) { 363 | $nodeType = XMLReader::END_ELEMENT; 364 | goto processing; 365 | } 366 | } 367 | 368 | libxml_use_internal_errors($useErrors); 369 | 370 | $result = $aggregates ? array_pop($aggregates[0]) : null; 371 | 372 | if ($isFault) { 373 | throw FaultException::fault($result); 374 | } 375 | 376 | return $result; 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /src/fXmlRpc/Proxy.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc; 25 | 26 | class Proxy 27 | { 28 | /** @var ClientInterface */ 29 | private $client; 30 | 31 | /** @var string */ 32 | private $namespace; 33 | 34 | /** @var string */ 35 | private $namespaceSeparator = '.'; 36 | 37 | /** @var Proxy[string] */ 38 | private $proxies = []; 39 | 40 | /** 41 | * @param ClientInterface $client 42 | * @param string $namespaceSeparator 43 | * @param string $namespace 44 | */ 45 | public function __construct(ClientInterface $client, $namespaceSeparator = '.', $namespace = null) 46 | { 47 | $this->client = $client; 48 | $this->namespaceSeparator = $namespaceSeparator; 49 | $this->namespace = $namespace; 50 | } 51 | 52 | /** 53 | * Invokes remote command 54 | * 55 | * @param string $method 56 | * @param array $parameters 57 | * @return mixed 58 | */ 59 | public function __call($method, array $parameters) 60 | { 61 | return $this->client->call($this->prependNamespace($method), $parameters); 62 | } 63 | 64 | /** 65 | * Returns namespace specific Proxy instance 66 | * 67 | * @param string $namespace 68 | * @return Proxy 69 | */ 70 | public function __get($namespace) 71 | { 72 | $namespace = $this->prependNamespace($namespace); 73 | if (!isset($this->proxies[$namespace])) { 74 | $this->proxies[$namespace] = new static($this->client, $this->namespaceSeparator, $namespace); 75 | } 76 | 77 | return $this->proxies[$namespace]; 78 | } 79 | 80 | /** 81 | * Prepend namespace if set 82 | * 83 | * @param string $string 84 | * @return string 85 | */ 86 | protected function prependNamespace($string) 87 | { 88 | if ($this->namespace === null) { 89 | return $string; 90 | } 91 | 92 | return $this->namespace . $this->namespaceSeparator . $string; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/fXmlRpc/Serializer/NativeSerializer.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Serializer; 25 | 26 | use DateTime; 27 | use fXmlRpc\Exception\MissingExtensionException; 28 | use fXmlRpc\Exception\SerializationException; 29 | use fXmlRpc\Value\Base64Interface; 30 | use function get_object_vars; 31 | use function random_bytes; 32 | 33 | final class NativeSerializer implements SerializerInterface 34 | { 35 | private static $replacementTokens = []; 36 | 37 | public function __construct() 38 | { 39 | if (!extension_loaded('xmlrpc')) { 40 | throw MissingExtensionException::extensionMissing('xmlrpc'); 41 | } 42 | } 43 | 44 | /** {@inheritdoc} */ 45 | public function serialize($method, array $params = []) 46 | { 47 | $request = xmlrpc_encode_request( 48 | $method, 49 | self::convert($params), 50 | ['encoding' => 'UTF-8', 'escaping' => 'markup', 'verbosity' => 'no_white_space'] 51 | ); 52 | 53 | return str_replace('' . self::getReplacementToken('struct') . '', '', $request); 54 | } 55 | 56 | private static function convert(array $params): array 57 | { 58 | foreach ($params as $key => $value) { 59 | $type = gettype($value); 60 | 61 | if ($type === 'array') { 62 | 63 | /** Find out if it is a struct or an array */ 64 | $expectedIndex = 0; 65 | $isStruct = false; 66 | foreach ($value as $actualIndex => &$child) { 67 | if ($expectedIndex !== $actualIndex) { 68 | $isStruct = true; 69 | break; 70 | } 71 | $expectedIndex++; 72 | } 73 | if ($isStruct) { 74 | $value = (object) $value; 75 | goto struct; 76 | } else { 77 | $params[$key] = self::convert($value); 78 | } 79 | } elseif ($type === 'object') { 80 | if ($value instanceof DateTime) { 81 | $params[$key] = (object) [ 82 | 'xmlrpc_type' => 'datetime', 83 | 'scalar' => $value->format('Ymd\TH:i:s'), 84 | 'timestamp' => $value->format('u'), 85 | ]; 86 | 87 | } elseif ($value instanceof Base64Interface) { 88 | $params[$key] = (object) [ 89 | 'xmlrpc_type' => 'base64', 90 | 'scalar' => $value->getDecoded(), 91 | ]; 92 | 93 | } else { 94 | struct: 95 | $struct = []; 96 | $value = self::convert(get_object_vars($value)); 97 | foreach ($value as $structKey => $structValue) { 98 | // Tricks ext/xmlrpc into always handling this as a struct while still discarding the null-byte 99 | $struct[$structKey . "\0"] = $structValue; 100 | } 101 | $params[$key] = empty($struct) ? self::getReplacementToken('struct') : $struct; 102 | } 103 | } elseif ($type === 'resource') { 104 | throw SerializationException::invalidType($value); 105 | } 106 | } 107 | 108 | return $params; 109 | } 110 | 111 | private static function getReplacementToken(string $scope): string 112 | { 113 | return self::$replacementTokens[$scope] ?? (self::$replacementTokens[$scope] = bin2hex(random_bytes(12))); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/fXmlRpc/Serializer/SerializerInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Serializer; 25 | 26 | /** 27 | * Serializer creates XML from native PHP types 28 | */ 29 | interface SerializerInterface 30 | { 31 | /** 32 | * Serialize XML/RPC method name and params into XML representation 33 | * 34 | * @param string $method 35 | * @param array $params 36 | * @return string 37 | */ 38 | public function serialize($method, array $params = []); 39 | } 40 | -------------------------------------------------------------------------------- /src/fXmlRpc/Serializer/XmlWriterSerializer.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Serializer; 25 | 26 | use XMLWriter; 27 | use Closure; 28 | use DateTime; 29 | use fXmlRpc\Value\Base64Interface; 30 | use fXmlRpc\ExtensionSupportInterface; 31 | use fXmlRpc\Exception\SerializationException; 32 | use fXmlRpc\Exception\MissingExtensionException; 33 | 34 | final class XmlWriterSerializer implements SerializerInterface, ExtensionSupportInterface 35 | { 36 | /** @var XMLWriter */ 37 | private $writer; 38 | 39 | /** @var array */ 40 | private $extensions = []; 41 | 42 | public function __construct() 43 | { 44 | if (!extension_loaded('xmlwriter')) { 45 | throw MissingExtensionException::extensionMissing('xmlwriter'); 46 | } 47 | 48 | $this->writer = new XMLWriter(); 49 | $this->writer->openMemory(); 50 | } 51 | 52 | /** {@inheritdoc} */ 53 | public function enableExtension($extension) 54 | { 55 | $this->extensions[$extension] = true; 56 | } 57 | 58 | /** {@inheritdoc} */ 59 | public function disableExtension($extension) 60 | { 61 | $this->extensions[$extension] = false; 62 | } 63 | 64 | /** {@inheritdoc} */ 65 | public function isExtensionEnabled($extension) 66 | { 67 | return isset($this->extensions[$extension]) ? $this->extensions[$extension] : true; 68 | } 69 | 70 | /** {@inheritdoc} */ 71 | public function serialize($methodName, array $params = []) 72 | { 73 | $writer = $this->writer; 74 | 75 | $writer->startDocument('1.0', 'UTF-8'); 76 | $writer->startElement('methodCall'); 77 | $writer->writeElement('methodName', $methodName); 78 | $writer->startElement('params'); 79 | 80 | $endNode = static function () use ($writer) { 81 | $writer->endElement(); 82 | }; 83 | $valueNode = static function () use ($writer) { 84 | $writer->startElement('value'); 85 | }; 86 | $paramNode = static function () use ($writer) { 87 | $writer->startElement('param'); 88 | }; 89 | 90 | $toBeVisited = []; 91 | foreach (array_reverse($params) as $param) { 92 | $toBeVisited[] = $endNode; 93 | $toBeVisited[] = $param; 94 | $toBeVisited[] = $paramNode; 95 | } 96 | 97 | $nilTagName = $this->isExtensionEnabled(ExtensionSupportInterface::EXTENSION_NIL) ? 'nil' : 'string'; 98 | 99 | while ($toBeVisited) { 100 | $node = array_pop($toBeVisited); 101 | $type = gettype($node); 102 | 103 | if ($type === 'string') { 104 | $writer->startElement('value'); 105 | $writer->writeElement('string', $node); 106 | $writer->endElement(); 107 | 108 | } elseif ($type === 'integer') { 109 | $writer->startElement('value'); 110 | $writer->writeElement('int', $node); 111 | $writer->endElement(); 112 | 113 | } elseif ($type === 'double') { 114 | if (!isset($precision)) { 115 | $precision = ini_get('precision'); 116 | } 117 | 118 | $writer->startElement('value'); 119 | $writer->writeElement('double', $node); 120 | $writer->endElement(); 121 | 122 | } elseif ($type === 'boolean') { 123 | $writer->startElement('value'); 124 | $writer->writeElement('boolean', $node ? '1' : '0'); 125 | $writer->endElement(); 126 | 127 | } elseif ($type === 'NULL') { 128 | $writer->startElement('value'); 129 | $writer->writeElement($nilTagName); 130 | $writer->endElement(); 131 | 132 | } elseif ($type === 'array') { 133 | /** Find out if it is a struct or an array */ 134 | $isStruct = false; 135 | $expectedIndex = 0; 136 | foreach ($node as $actualIndex => &$child) { 137 | if ($expectedIndex !== $actualIndex) { 138 | $isStruct = true; 139 | break; 140 | } 141 | $expectedIndex++; 142 | } 143 | 144 | if (!$isStruct) { 145 | $toBeVisited[] = $endNode; 146 | $toBeVisited[] = $endNode; 147 | $toBeVisited[] = $endNode; 148 | foreach (array_reverse($node) as $value) { 149 | $toBeVisited[] = $value; 150 | } 151 | $toBeVisited[] = static function () use ($writer) { 152 | $writer->startElement('array'); 153 | $writer->startElement('data'); 154 | }; 155 | $toBeVisited[] = $valueNode; 156 | 157 | } else { 158 | struct: 159 | $toBeVisited[] = $endNode; 160 | $toBeVisited[] = $endNode; 161 | foreach (array_reverse($node, true) as $key => $value) { 162 | $toBeVisited[] = $endNode; 163 | $toBeVisited[] = $value; 164 | $toBeVisited[] = static function () use ($writer, $key) { 165 | $writer->writeElement('name', $key); 166 | }; 167 | $toBeVisited[] = static function () use ($writer) { 168 | $writer->startElement('member'); 169 | }; 170 | } 171 | $toBeVisited[] = static function () use ($writer) { 172 | $writer->startElement('struct'); 173 | }; 174 | $toBeVisited[] = $valueNode; 175 | } 176 | 177 | } elseif ($type === 'object') { 178 | 179 | if ($node instanceof Closure) { 180 | $node(); 181 | 182 | } elseif ($node instanceof DateTime) { 183 | $writer->startElement('value'); 184 | $writer->writeElement('dateTime.iso8601', $node->format('Ymd\TH:i:s')); 185 | $writer->endElement(); 186 | 187 | } elseif ($node instanceof Base64Interface) { 188 | $writer->startElement('value'); 189 | $writer->writeElement('base64', $node->getEncoded() . "\n"); 190 | $writer->endElement(); 191 | 192 | } else { 193 | $node = get_object_vars($node); 194 | goto struct; 195 | } 196 | } elseif ($type === 'resource') { 197 | throw SerializationException::invalidType($node); 198 | } 199 | } 200 | 201 | $writer->endElement(); 202 | $writer->endElement(); 203 | 204 | $xml = $writer->flush(true); 205 | 206 | // NativeSerializer does not inject a newline after the declaration 207 | if ($xml[38] === "\n") { 208 | $xml = substr($xml, 0, 38) . substr($xml, 39); 209 | } 210 | 211 | return $xml; 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/fXmlRpc/Timing/AbstractTimerBridge.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Timing; 25 | 26 | /** 27 | * Base class for timing bridges 28 | * 29 | * Base class for bridging between timing information and various logger 30 | * implementations. 31 | */ 32 | abstract class AbstractTimerBridge implements TimerInterface 33 | { 34 | /** @var object */ 35 | protected $logger; 36 | 37 | /** @var array|integer */ 38 | protected $level; 39 | 40 | /** @var string */ 41 | protected $messageTemplate = 'fXmlRpc call took %01.10Fs'; 42 | 43 | /** 44 | * Set log level 45 | * 46 | * @param mixed $level 47 | * @param mixed $default 48 | */ 49 | protected function setLevel($level, $default) 50 | { 51 | if (is_array($level)) { 52 | krsort($level); 53 | } 54 | 55 | $this->level = $level ?: $default; 56 | } 57 | 58 | /** 59 | * Get log level by callTime 60 | * 61 | * @param float $callTime 62 | * @return integer 63 | */ 64 | protected function getLevel($callTime) 65 | { 66 | if (!is_array($this->level)) { 67 | return $this->level; 68 | } 69 | 70 | $level = null; 71 | foreach ($this->level as $threshold => $level) { 72 | if ($callTime >= $threshold) { 73 | return $level; 74 | } 75 | } 76 | 77 | return $level; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/fXmlRpc/Timing/MonologTimerBridge.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Timing; 25 | 26 | use Monolog\Logger; 27 | 28 | final class MonologTimerBridge extends AbstractTimerBridge 29 | { 30 | /** 31 | * Create new monolog bridge 32 | * 33 | * Allows passing custom log level and message template (with sprintf() control characters) for log message 34 | * customization 35 | * 36 | * @param Logger $logger 37 | * @param null|array $level 38 | * @param null $messageTemplate 39 | */ 40 | public function __construct(Logger $logger, $level = null, $messageTemplate = null) 41 | { 42 | $this->logger = $logger; 43 | $this->setLevel($level, Logger::DEBUG); 44 | $this->messageTemplate = $messageTemplate ?: $this->messageTemplate; 45 | } 46 | 47 | /** {@inheritdoc} */ 48 | public function recordTiming($callTime, $method, array $arguments) 49 | { 50 | $level = $this->getLevel($callTime); 51 | 52 | $this->logger->addRecord( 53 | $level, 54 | sprintf($this->messageTemplate, $callTime), 55 | ['xmlrpcMethod' => $method, 'xmlrpcArguments' => $arguments] 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/fXmlRpc/Timing/Psr3TimerBridge.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Timing; 25 | 26 | use Psr\Log\LoggerInterface; 27 | use Psr\Log\LogLevel; 28 | 29 | final class Psr3TimerBridge extends AbstractTimerBridge 30 | { 31 | /** 32 | * Create new bridge or a PSR-3 compatible logger 33 | * 34 | * Allows passing custom log level and message template (with sprintf() control characters) for log message 35 | * customization 36 | * 37 | * @param LoggerInterface $logger 38 | * @param integer $level 39 | * @param string $messageTemplate 40 | */ 41 | public function __construct(LoggerInterface $logger, $level = null, $messageTemplate = null) 42 | { 43 | $this->logger = $logger; 44 | $this->setLevel($level, LogLevel::DEBUG); 45 | $this->messageTemplate = $messageTemplate ?: $this->messageTemplate; 46 | } 47 | 48 | /** {@inheritdoc} */ 49 | public function recordTiming($callTime, $method, array $arguments) 50 | { 51 | $this->logger->log( 52 | $this->getLevel($callTime), 53 | sprintf($this->messageTemplate, $callTime), 54 | ['xmlrpcMethod' => $method, 'xmlrpcArguments' => $arguments] 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/fXmlRpc/Timing/TimerInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Timing; 25 | 26 | interface TimerInterface 27 | { 28 | /** 29 | * Records a timing information 30 | * 31 | * @param float $callTime 32 | * @param string $method 33 | * @param array $arguments 34 | * @return null 35 | */ 36 | public function recordTiming($callTime, $method, array $arguments); 37 | } 38 | -------------------------------------------------------------------------------- /src/fXmlRpc/Timing/TimingDecorator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Timing; 25 | 26 | use fXmlRpc\ClientInterface; 27 | use fXmlRpc\AbstractDecorator; 28 | 29 | final class TimingDecorator extends AbstractDecorator 30 | { 31 | /** 32 | * @var TimerInterface 33 | */ 34 | private $timer; 35 | 36 | /** 37 | * Create new client decorator to record timing information 38 | * 39 | * @param ClientInterface $wrapped 40 | * @param TimerInterface $timer 41 | */ 42 | public function __construct(ClientInterface $wrapped, TimerInterface $timer) 43 | { 44 | parent::__construct($wrapped); 45 | $this->timer = $timer; 46 | } 47 | 48 | /** {@inheritdoc} */ 49 | public function call($methodName, array $arguments = []) 50 | { 51 | $startTime = microtime(true); 52 | $result = parent::call($methodName, $arguments); 53 | $this->timer->recordTiming(microtime(true) - $startTime, $methodName, $arguments); 54 | 55 | return $result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/fXmlRpc/Timing/ZendFrameworkOneTimerBridge.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | namespace fXmlRpc\Timing; 26 | 27 | use Zend_Log as Log; 28 | 29 | final class ZendFrameworkOneTimerBridge extends AbstractTimerBridge 30 | { 31 | /** 32 | * Create new Zend_Log bridge 33 | * 34 | * Allows passing custom log level and message template (with sprintf() control characters) for log message 35 | * customization 36 | * 37 | * @param Log $logger 38 | * @param integer $level 39 | * @param string $messageTemplate 40 | */ 41 | public function __construct(Log $logger, $level = null, $messageTemplate = null) 42 | { 43 | $this->logger = $logger; 44 | $this->setLevel($level, Log::DEBUG); 45 | $this->messageTemplate = $messageTemplate ?: $this->messageTemplate; 46 | } 47 | 48 | /** {@inheritdoc} */ 49 | public function recordTiming($callTime, $method, array $arguments) 50 | { 51 | $this->logger->log( 52 | sprintf($this->messageTemplate, $callTime), 53 | $this->getLevel($callTime), 54 | ['xmlrpcMethod' => $method, 'xmlrpcArguments' => $arguments] 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/fXmlRpc/Timing/ZendFrameworkTwoTimerBridge.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | namespace fXmlRpc\Timing; 26 | 27 | use Zend\Log\LoggerInterface; 28 | 29 | final class ZendFrameworkTwoTimerBridge extends AbstractTimerBridge 30 | { 31 | /** 32 | * Create new Zend\Log\LoggerInterface bridge 33 | * 34 | * Allows passing custom log level and message template (with sprintf() control characters) for log message 35 | * customization 36 | * 37 | * @param LoggerInterface $logger 38 | * @param string $method 39 | * @param string $messageTemplate 40 | */ 41 | public function __construct(LoggerInterface $logger, $method = null, $messageTemplate = null) 42 | { 43 | $this->logger = $logger; 44 | $this->setLevel($method, 'debug'); 45 | $this->messageTemplate = $messageTemplate ?: $this->messageTemplate; 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function recordTiming($callTime, $method, array $arguments) 52 | { 53 | $level = $this->getLevel($callTime); 54 | $this->logger->{$level}( 55 | sprintf($this->messageTemplate, $callTime), 56 | ['xmlrpcMethod' => $method, 'xmlrpcArguments' => $arguments] 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/fXmlRpc/Transport/HttpAdapterTransport.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Transport; 25 | 26 | use fXmlRpc\Exception\HttpException; 27 | use fXmlRpc\Exception\TransportException; 28 | use Psr\Http\Client\ClientExceptionInterface; 29 | use Psr\Http\Client\ClientInterface; 30 | use Psr\Http\Message\RequestFactoryInterface; 31 | use Psr\Http\Message\StreamFactoryInterface; 32 | 33 | final class HttpAdapterTransport implements TransportInterface 34 | { 35 | private $messageFactory; 36 | 37 | private $streamFactory; 38 | 39 | private $client; 40 | 41 | public function __construct( 42 | RequestFactoryInterface $messageFactory, 43 | StreamFactoryInterface $streamFactory, 44 | ClientInterface $client 45 | ) { 46 | $this->client = $client; 47 | $this->messageFactory = $messageFactory; 48 | $this->streamFactory = $streamFactory; 49 | } 50 | 51 | /** {@inheritdoc} */ 52 | public function send($endpoint, $payload) 53 | { 54 | try { 55 | $request = $this->messageFactory->createRequest('POST', $endpoint) 56 | ->withHeader('Content-Type', 'text/xml; charset=UTF-8') 57 | ->withBody($this->streamFactory->createStream($payload)); 58 | 59 | $response = $this->client->sendRequest($request); 60 | if ($response->getStatusCode() !== 200) { 61 | throw HttpException::httpError($response->getReasonPhrase(), $response->getStatusCode()); 62 | } 63 | 64 | return (string)$response->getBody(); 65 | } catch (ClientExceptionInterface $e) { 66 | throw TransportException::transportError($e); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/fXmlRpc/Transport/PsrTransport.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Transport; 25 | 26 | use fXmlRpc\Exception\HttpException; 27 | use fXmlRpc\Exception\TransportException; 28 | use Psr\Http\Message\RequestFactoryInterface; 29 | use Psr\Http\Client\ClientInterface; 30 | use Psr\Http\Client\ClientExceptionInterface; 31 | 32 | /** 33 | * Supports transport via any classes that implement the PSR-17 (Request Factory) and PSR-18 (HTTP Client) standards. 34 | */ 35 | final class PsrTransport implements TransportInterface 36 | { 37 | private $requestFactory; 38 | 39 | private $client; 40 | 41 | public function __construct(RequestFactoryInterface $requestFactory, ClientInterface $client) 42 | { 43 | $this->requestFactory = $requestFactory; 44 | $this->client = $client; 45 | } 46 | 47 | /** {@inheritdoc} */ 48 | public function send($endpoint, $payload) 49 | { 50 | try { 51 | $request = $this->requestFactory->createRequest('POST', $endpoint) 52 | ->withHeader('Content-Type', 'text/xml; charset=UTF-8'); 53 | 54 | $request->getBody()->write($payload); 55 | 56 | $response = $this->client->sendRequest($request); 57 | 58 | if ($response->getStatusCode() !== 200) { 59 | throw HttpException::httpError($response->getReasonPhrase(), $response->getStatusCode()); 60 | } 61 | 62 | return (string) $response->getBody(); 63 | 64 | } catch (ClientExceptionInterface $e) { 65 | throw TransportException::transportError($e); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/fXmlRpc/Transport/Recorder.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Transport; 25 | use Exception; 26 | 27 | /** 28 | * Transport decorator which contains XML of the last request and response. 29 | * 30 | * @author Piotr Olaszewski 31 | */ 32 | class Recorder implements TransportInterface 33 | { 34 | /** @var TransportInterface */ 35 | private $transport; 36 | 37 | /** @var string|null */ 38 | private $lastRequest = null; 39 | 40 | /** @var string|null */ 41 | private $lastResponse = null; 42 | 43 | /** @var Exception|null */ 44 | private $lastException = null; 45 | 46 | public function __construct(TransportInterface $transport) 47 | { 48 | $this->transport = $transport; 49 | } 50 | 51 | /** {@inheritdoc} */ 52 | public function send($endpoint, $payload) 53 | { 54 | $this->lastResponse = $this->lastException = null; 55 | $this->lastRequest = $payload; 56 | 57 | try { 58 | $this->lastResponse = $this->transport->send($endpoint, $payload); 59 | 60 | return $this->lastResponse; 61 | } catch (Exception $e) { 62 | $this->lastException = $e; 63 | 64 | throw $e; 65 | } 66 | } 67 | 68 | /** 69 | * Returns the XML sent in the last request. 70 | * 71 | * @return string|null 72 | */ 73 | public function getLastRequest() 74 | { 75 | return $this->lastRequest; 76 | } 77 | 78 | /** 79 | * Returns the XML received in the last response. 80 | * 81 | * @return string|null 82 | */ 83 | public function getLastResponse() 84 | { 85 | return $this->lastResponse; 86 | } 87 | 88 | /** 89 | * Returns exception when last request fail. 90 | * 91 | * @return Exception|null 92 | */ 93 | public function getLastException() 94 | { 95 | return $this->lastException; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/fXmlRpc/Transport/TransportInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Transport; 25 | 26 | use fXmlRpc\Exception\AbstractTransportException; 27 | 28 | /** 29 | * Transport interface 30 | * 31 | * This interface is implemented by transports to provide means to send 32 | * requests over the wire. 33 | */ 34 | interface TransportInterface 35 | { 36 | /** 37 | * Send XML/RPC request over the wire and return the payload 38 | * 39 | * @param string $endpoint 40 | * @param string $payload 41 | * @throws AbstractTransportException If a transport error occurred 42 | * @return string 43 | */ 44 | public function send($endpoint, $payload); 45 | } 46 | -------------------------------------------------------------------------------- /src/fXmlRpc/Value/Base64.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Value; 25 | 26 | final class Base64 implements Base64Interface 27 | { 28 | /** @var string */ 29 | private $encoded; 30 | 31 | /** @var string */ 32 | private $decoded; 33 | 34 | /** 35 | * @param string $encoded 36 | * @param string $decoded 37 | */ 38 | private function __construct($encoded, $decoded) 39 | { 40 | $this->encoded = $encoded; 41 | $this->decoded = $decoded; 42 | } 43 | 44 | /** 45 | * Return new base64 value object by encoded value 46 | * 47 | * @param string $string 48 | * @return Base64 49 | */ 50 | public static function serialize($string) 51 | { 52 | return new static(null, $string); 53 | } 54 | 55 | /** 56 | * Return new base64 value by string 57 | * 58 | * @param string $value 59 | * @return Base64 60 | */ 61 | public static function deserialize($value) 62 | { 63 | return new static(trim($value), null); 64 | } 65 | 66 | /** {@inheritdoc} */ 67 | public function getEncoded() 68 | { 69 | if ($this->encoded === null) { 70 | $this->encoded = base64_encode($this->decoded); 71 | } 72 | 73 | return $this->encoded; 74 | } 75 | 76 | /** {@inheritdoc} */ 77 | public function getDecoded() 78 | { 79 | if ($this->decoded === null) { 80 | $this->decoded = base64_decode($this->encoded); 81 | } 82 | 83 | return $this->decoded; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/fXmlRpc/Value/Base64Interface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | namespace fXmlRpc\Value; 25 | 26 | interface Base64Interface 27 | { 28 | /** 29 | * Get base64 value as base64 encoded string 30 | * 31 | * @return string 32 | */ 33 | public function getEncoded(); 34 | 35 | /** 36 | * Get base64 value as binary string 37 | * 38 | * @return string 39 | */ 40 | public function getDecoded(); 41 | } 42 | -------------------------------------------------------------------------------- /vendor/.gitadd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lstrojny/fxmlrpc/b5f6c22ea0791715538c51053abfd20469e9f990/vendor/.gitadd --------------------------------------------------------------------------------