| JsonRPC PHP Client and Server |
| ============================= |
|
|
| A simple Json-RPC client/server that just works. |
|
|
| Features |
| -------- |
|
|
| - JSON-RPC 2.0 only |
| - The server support batch requests and notifications |
| - Authentication and IP based client restrictions |
| - Custom Middleware |
| - Fully unit tested |
| - Requirements: PHP >= 5.3.4 |
| - License: MIT |
|
|
| Author |
| ------ |
|
|
| Frédéric Guillot |
|
|
| Installation with Composer |
| -------------------------- |
|
|
| ```bash |
| composer require fguillot/json-rpc @stable |
| ``` |
|
|
| Examples |
| -------- |
|
|
| ### Server |
|
|
| Callback binding: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Server; |
| |
| $server = new Server(); |
| $server->getProcedureHandler() |
| ->withCallback('addition', function ($a, $b) { |
| return $a + $b; |
| }) |
| ->withCallback('random', function ($start, $end) { |
| return mt_rand($start, $end); |
| }) |
| ; |
| |
| echo $server->execute(); |
| ``` |
|
|
| Callback binding from array: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Server; |
| |
| $callbacks = array( |
| 'getA' => function() { return 'A'; }, |
| 'getB' => function() { return 'B'; }, |
| 'getC' => function() { return 'C'; } |
| ); |
| |
| $server = new Server(); |
| $server->getProcedureHandler()->withCallbackArray($callbacks); |
| |
| echo $server->execute(); |
| ``` |
|
|
| Class/Method binding: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Server; |
| |
| class Api |
| { |
| public function doSomething($arg1, $arg2 = 3) |
| { |
| return $arg1 + $arg2; |
| } |
| } |
| |
| $server = new Server(); |
| $procedureHandler = $server->getProcedureHandler(); |
| |
| // Bind the method Api::doSomething() to the procedure myProcedure |
| $procedureHandler->withClassAndMethod('myProcedure', 'Api', 'doSomething'); |
| |
| // Use a class instance instead of the class name |
| $procedureHandler->withClassAndMethod('mySecondProcedure', new Api, 'doSomething'); |
| |
| // The procedure and the method are the same |
| $procedureHandler->withClassAndMethod('doSomething', 'Api'); |
| |
| // Attach the class, the client will be able to call directly Api::doSomething() |
| $procedureHandler->withObject(new Api()); |
| |
| echo $server->execute(); |
| ``` |
|
|
| Class/Method binding from array: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Server; |
| |
| class MathApi |
| { |
| public function addition($arg1, $arg2) |
| { |
| return $arg1 + $arg2; |
| } |
| |
| public function subtraction($arg1, $arg2) |
| { |
| return $arg1 - $arg2; |
| } |
| |
| public function multiplication($arg1, $arg2) |
| { |
| return $arg1 * $arg2; |
| } |
| |
| public function division($arg1, $arg2) |
| { |
| return $arg1 / $arg2; |
| } |
| } |
| |
| $callbacks = array( |
| 'addition' => array( 'MathApi', addition ), |
| 'subtraction' => array( 'MathApi', subtraction ), |
| 'multiplication' => array( 'MathApi', multiplication ), |
| 'division' => array( 'MathApi', division ) |
| ); |
| |
| $server = new Server(); |
| $server->getProcedureHandler()->withClassAndMethodArray($callbacks); |
| |
| echo $server->execute(); |
| ``` |
|
|
| Server Middleware: |
|
|
| Middleware might be used to authenticate and authorize the client. |
| They are executed before each procedure. |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Server; |
| use JsonRPC\MiddlewareInterface; |
| use JsonRPC\Exception\AuthenticationFailureException; |
| |
| class Api |
| { |
| public function doSomething($arg1, $arg2 = 3) |
| { |
| return $arg1 + $arg2; |
| } |
| } |
| |
| class MyMiddleware implements MiddlewareInterface |
| { |
| public function execute($username, $password, $procedureName) |
| { |
| if ($username !== 'foobar') { |
| throw new AuthenticationFailureException('Wrong credentials!'); |
| } |
| } |
| } |
| |
| $server = new Server(); |
| $server->getMiddlewareHandler()->withMiddleware(new MyMiddleware()); |
| $server->getProcedureHandler()->withObject(new Api()); |
| echo $server->execute(); |
| ``` |
|
|
| You can raise a `AuthenticationFailureException` when the API credentials are wrong or a `AccessDeniedException` when the user is not allowed to access to the procedure. |
|
|
| ### Client |
|
|
| Example with positional parameters: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Client; |
| |
| $client = new Client('http://localhost/server.php'); |
| $result = $client->execute('addition', [3, 5]); |
| ``` |
|
|
| Example with named arguments: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Client; |
| |
| $client = new Client('http://localhost/server.php'); |
| $result = $client->execute('random', ['end' => 10, 'start' => 1]); |
| ``` |
|
|
| Arguments are called in the right order. |
|
|
| Examples with the magic method `__call()`: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Client; |
| |
| $client = new Client('http://localhost/server.php'); |
| $result = $client->random(50, 100); |
| ``` |
|
|
| The example above use positional arguments for the request and this one use named arguments: |
|
|
| ```php |
| $result = $client->random(['end' => 10, 'start' => 1]); |
| ``` |
|
|
| ### Client batch requests |
|
|
| Call several procedures in a single HTTP request: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Client; |
| |
| $client = new Client('http://localhost/server.php'); |
| |
| $results = $client->batch() |
| ->foo(['arg1' => 'bar']) |
| ->random(1, 100) |
| ->add(4, 3) |
| ->execute('add', [2, 5]) |
| ->send(); |
| |
| print_r($results); |
| ``` |
|
|
| All results are stored at the same position of the call. |
|
|
| ### Client exceptions |
|
|
| Client exceptions are normally thrown when an error is returned by the server. You can change this behaviour by |
| using the `$returnException` argument which causes exceptions to be returned. This can be extremely useful when |
| executing the batch request. |
|
|
| - `BadFunctionCallException`: Procedure not found on the server |
| - `InvalidArgumentException`: Wrong procedure arguments |
| - `JsonRPC\Exception\AccessDeniedException`: Access denied |
| - `JsonRPC\Exception\ConnectionFailureException`: Connection failure |
| - `JsonRPC\Exception\ServerErrorException`: Internal server error |
|
|
| ### Enable client debugging |
|
|
| You can enable the debug mode to see the JSON request and response: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Client; |
| |
| $client = new Client('http://localhost/server.php'); |
| $client->getHttpClient()->withDebug(); |
| ``` |
|
|
| The debug output is sent to the PHP system logger. |
| You can configure the log destination in your `php.ini`. |
|
|
| Output example: |
|
|
| ```json |
| ==> Request: |
| { |
| "jsonrpc": "2.0", |
| "method": "removeCategory", |
| "id": 486782327, |
| "params": [ |
| 1 |
| ] |
| } |
| ==> Response: |
| { |
| "jsonrpc": "2.0", |
| "id": 486782327, |
| "result": true |
| } |
| ``` |
|
|
| ### IP based client restrictions |
|
|
| The server can allow only some IP addresses: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Server; |
| |
| $server = new Server; |
| |
| // IP client restrictions |
| $server->allowHosts(['192.168.0.1', '127.0.0.1']); |
| |
| [...] |
| |
| // Return the response to the client |
| echo $server->execute(); |
| ``` |
|
|
| If the client is blocked, you got a 403 Forbidden HTTP response. |
|
|
| ### HTTP Basic Authentication |
|
|
| If you use HTTPS, you can allow client by using a username/password. |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Server; |
| |
| $server = new Server; |
| |
| // List of users to allow |
| $server->authentication(['user1' => 'password1', 'user2' => 'password2']); |
| |
| [...] |
| |
| // Return the response to the client |
| echo $server->execute(); |
| ``` |
|
|
| On the client, set credentials like that: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Client; |
| |
| $client = new Client('http://localhost/server.php'); |
| $client->getHttpClient() |
| ->withUsername('Foo') |
| ->withPassword('Bar'); |
| ``` |
|
|
| If the authentication failed, the client throw a RuntimeException. |
|
|
| Using an alternative authentication header: |
|
|
| ```php |
| |
| use JsonRPC\Server; |
| |
| $server = new Server(); |
| $server->setAuthenticationHeader('X-Authentication'); |
| $server->authentication(['myusername' => 'mypassword']); |
| ``` |
|
|
| The example above will use the HTTP header `X-Authentication` instead of the standard `Authorization: Basic [BASE64_CREDENTIALS]`. |
| The username/password values need be encoded in base64: `base64_encode('username:password')`. |
|
|
| ### Local Exceptions |
|
|
| By default, the server will relay all exceptions to the client. |
| If you would like to relay only some of them, use the method `Server::withLocalException($exception)`: |
|
|
| ```php |
| <?php |
| |
| use JsonRPC\Server; |
| class MyException1 extends Exception {}; |
| class MyException2 extends Exception {}; |
| |
| $server = new Server(); |
| |
| // Exceptions that should NOT be relayed to the client, if they occurs |
| $server |
| ->withLocalException('MyException1') |
| ->withLocalException('MyException2') |
| ; |
| |
| [...] |
| |
| echo $server->execute(); |
| ``` |
|
|
| ### Callback before client request |
|
|
| You can use a callback to change the HTTP headers or the URL before to make the request to the server. |
|
|
| Example: |
|
|
| ```php |
| <?php |
| |
| $client = new Client(); |
| $client->getHttpClient()->withBeforeRequestCallback(function(HttpClient $client, $payload) { |
| $client->withHeaders(array('Content-Length: '.strlen($payload))); |
| }); |
| |
| $client->myProcedure(123); |
| ``` |
|
|