Search by

bednic / json-rpc

allone

JSON RPC 2.0 implementation

Package info

gitlab.com/bednic/json-rpc

Issues

pkg:composer/bednic/json-rpc

Statistics

Installs: 5 076

Dependents: 1

Suggesters: 0

Stars: 0

4.0.0 2026-10-14 23:01 UTC

README

PHP implementation of the JSON-RPC 2.0 specification.

composer require bednic/json-rpc

Server

  1. Create a controller implementing \JSONRPC\ProcedureControllerInterface. Its public methods are the procedures.
  2. Pass the controller to \JSONRPC\Dispatcher.
  3. Call Dispatcher::dispatch(string $json) with the raw request body.
  4. Send the returned value encoded by json_encode to the client. It is a Response for a single request, an array of Response for a batch, or null when there is nothing to answer (only notifications).
class MathController implements \JSONRPC\ProcedureControllerInterface
{
    public function subtract(int $minuend, int $subtrahend): int
    {
        return $minuend - $subtrahend;
    }
}

$dispatcher = new \JSONRPC\Dispatcher(new MathController());
$response   = $dispatcher->dispatch(file_get_contents('php://input'));

if ($response === null) {
    http_response_code(204);
} else {
    header('Content-Type: ' . \JSONRPC\Message::CONTENT_TYPE);
    echo json_encode($response);
}

Params can be passed by position ("params": [42, 23]) or by name ("params": {"subtrahend": 23, "minuend": 42}). JSON objects are passed to procedures as stdClass.

Multiple controllers

Register controllers under a prefix and call them as [prefix].[method], e.g. math.subtract. Prefix rpc is reserved by the specification.

$controllers = new \JSONRPC\SimpleControllerCollector();
$controllers->addController('math', new MathController());
$controllers->addController('user', new UserController());
$dispatcher = new \JSONRPC\Dispatcher($controllers);

Errors

Throw an exception implementing \JSONRPC\ProcedureException to send its code, message and data to the client. Codes from -32768 to -32000 are reserved by the specification, using them is logged by error_log.

class NotFound extends \Exception implements \JSONRPC\ProcedureException
{
    public function __construct(private int $userId)
    {
        parent::__construct('User not found', 404);
    }

    public function getData(): mixed
    {
        return ['userId' => $this->userId];
    }
}

Any other exception is answered with -32603 Internal error without details, so nothing leaks to the client. Pass a callback to log it:

$dispatcher = new \JSONRPC\Dispatcher($controller, null, fn(\Throwable $e) => $logger->error($e));

Notifications (requests without id) are never answered, not even with an error.

Client

$request      = new \JSONRPC\Request('math.subtract', 1, [42, 23]);
$notification = new \JSONRPC\Request('log', null, ['hello']); // no id, no response expected

$body     = json_encode($request);                  // or json_encode([$request, $notification]) for a batch
$response = \JSONRPC\Response::jsonDeserialize($reply); // Response, or Response[] for a batch

if ($response->getError() !== null) {
    echo $response->getError()->getMessage();
} else {
    echo $response->getResult()->getData();          // 19
}

Response::jsonDeserialize throws UnexpectedValueException when the reply is not a valid JSON-RPC 2.0 response.

Example

See tests in this repo.