Skip to content

Server usage

johnstevenson edited this page Nov 30, 2012 · 35 revisions

Contents

The Basics

The server provides a robust mechanism for handling all JSON-RPC requests, including notification and batch requests. It also provides custom error-logging and advanced functionality.

Setting it up, however, is simple. All you need is a class containing the methods that you expose to the client, which you pass to the constructor of JsonRpc\Server. For an instantiated class:

<?php
$methods = new MethodsClass();
$server = new JsonRpc\Server($methods);

For a static class:

<?php
$server = new JsonRpc\Server('\\\\Namespace\\\\MethodsStatic');

Then you call the $server->receive method, which will do all the work for you:

<?php
$server->receive();

And that is it. The library will handle all requests, invoke your methods and return the response. It also handles any errors it may encounter. The requirements of the methods class are described below:

Methods Class

It must be a class, because the server looks-up the method to check and order its parameters. If you used an ordinary function or closure this would not be possible.

It should contain a public property error which is used by your methods to signify an error and is returned by the server as an appropriate error response. Take a look at the example method class below.

<?php
class MyMethodsClass
{

  public $error = null;

  public function divide($dividend, $divisor, $int = false)
  {

    if (!$divisor)
    {
      $this->error = 'Cannot divide by zero';
    }
    else
    {
      $quotient = $dividend / $divisor;
      return $int ? (int) $quotient : $quotient;
    }

  }

}

If the divide method is passed 0 as its divisor, it sets the error property to a string message. The server checks for this and returns an appropriate JSON-RPC response:

{"jsonrpc": "2.0", "error": {"code": -32000, "message": "Server error", "data": "Cannot divide by zero"}, "id": 1}

The format is explained in the official specification. Note, however, that the error code used is reserved for "implementation-defined server-errors" and that the error property is used as the data member of the error structure. You can set your own error codes and messages by using an array or object. For example the above error could be set as:

<?php
$this->error = array(
  'code' => -32000,
  'message' => 'Server error',
  'data' => 'Cannot divide by zero'
);

The server uses the following rules for interpreting the error property of the method class, in this order:

  • if it is an integer it is set as the code: {"code": <error>, "message": "Server error"}
  • if it is a scalar it is set as the data: {"code": -32000, "message": "Server error", "data": <error>}
  • if it is an array or object it uses any values it finds, defaulting to: {"code": -32000, "message": "Server error"}

* Note that an Exception will be thrown if you set the error code outside of the accepted range (-32000 to -32099).

Tip: The server will always set the error property to null before calling a method

See Standard Error Codes for further information.

Errors and Exceptions

The server switches display_errors off so that no error output is sent to the client and it also handles all errors and exceptions that might occur. For example:

  • if the method does not exist (or is not callable)
  • if the method call does not have the correct number of required parameters
  • if an Exception is thrown in your methods class

These conditions are returned with the appropriate error response and in the case of exceptions are logged using PHP's error_log function. Of course this requires that log_errors is enabled.

Custom error-logging

If you want to use more advanced logging, like monolog for example, then you can pass an instance of your logger to the $server->setLogger function:

<?php
$server = new JsonRpc\Server($methods);

$log = new Logger('name');
$server->setLogger($log);

Internally, the server calls the addRecord function of the logger instance with a level of CRITICAL (see RFC 5424) and a message obtained from Exception->getMessage(). If you provide you own logging class it should look something like this:

<?php
class MyLoggingClass
{

  public function addRecord($level, $message, array $context = array())
  {
    // do logging stuff
  }

}

Advanced

This section covers the following topics:

Associative Arrays

If your methods require key-value structures in their arguments, these will be decoded by the server and passed as stdClass objects by default. If you prefer to receive these arguments as associative arrays, you can use the $server->setObjectsAsArrays() function.

<?php
...
$server->setObjectsAsArrays();
$server->receive();

...

public function addUser($user)
{
  $name = $user['name'];
  $id = $user['id'];
  ...
}

Note that you must call this before you call $server->receive() and that it will affect all objects passed to all methods.

Method Parameters

These can be sent by a client as either positional or named parameters and the server will check that the correct number of required parameters is present before calling your method. For positional parameters, each element of an array corresponds to the same position of each argument, while for named parameters an object is used, with the key name corresponding to the argument name. For example:

<?php
function divide($dividend, $divisor, $int = false)

JSON ... "params": [50, 10]                         # positional
JSON ... "params": {"dividend": 10, "divisor": 5}   # named
JSON ... "params": {"divisor": 5, "dividend": 10}   # named, out of order but still works

Magic Methods

You can route all your methods through a single magic method like __call or __callStatic. This has the advantage of grouping common routines in a single place, rather than including them within every method. However, you have to do more work and it is your responsibility to check that the correct number of parameters have been passed and that if they are named they are in the correct order.

<?php
class MyMagicMethodClass
{

  public $error = null;

  public function __call($method, $params)
  {

    # method_exists(), function_exists(), is_callable() - use whatever you need
    if (method_exists($this, $method))
    {
      # do common routines ...

      try
      {
        $result = call_user_func_array(array($this, $method), $params);
        return $result;
      }
      catch (Exception $e)
      {
        $this->error = -32603;
      }

    }
    else
    {
      $this->error = -32601;
    }

  }
  
  private function divide ...

}

Standard Error Codes

The following standard error codes are supported by default. If you set the error property of your methods class to any of these numeric values, an error object will be returned with the corresponding message:

-32000 Server error
-32600 Invalid Request
-32601 Method not found
-32602 Invalid params
-32603 Internal error
-32700 Parse error

Transport

The built-in transport mechanism reads php://input to obtain the data sent by the client. If this is restrictive you can override it by passing the input data to the $server->receive function.

<?php
// get the input sent from the client and do something with it

$server = new JsonRpc\Server($methods);
$server->receive($input);

Note that this does not give you any control over the data being returned to the client. If you need access to this then you must supply your own transport mechanism. See the Advanced functionality topic for more details.