Skip to content

Commit 77465a7

Browse files
author
Frederic Guillot
committed
first commit
0 parents  commit 77465a7

File tree

3 files changed

+299
-0
lines changed

3 files changed

+299
-0
lines changed

README.markdown

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
JsonRPC - PHP Client and Server
2+
===============================
3+
4+
A simple Json-RPC client/server that just works.
5+
There is only 2 files.
6+
7+
Features
8+
--------
9+
10+
- JSON-RPC 2.0 protocol only
11+
- Very simple and easy to use
12+
- License: Unlicense http://unlicense.org/
13+
14+
Requirements
15+
------------
16+
17+
- The only dependency is curl and Reflection classes
18+
- Works only with PHP >= 5.3
19+
20+
Example
21+
-------
22+
23+
### Server
24+
25+
<?php
26+
27+
require 'JsonRPC/Server.php';
28+
29+
use JsonRPC\Server;
30+
31+
$server = new Server;
32+
33+
// Procedures registration
34+
35+
$server->register('addition', function ($a, $b) {
36+
37+
return $a + $b;
38+
});
39+
40+
$server->register('random', function ($start, $end) {
41+
42+
return mt_rand($start, $end);
43+
});
44+
45+
// Return the response to the client
46+
echo $server->execute();
47+
48+
### Client
49+
50+
Example with positional parameters:
51+
52+
<?php
53+
54+
require 'JsonRPC/Client.php';
55+
56+
use JsonRPC\Client;
57+
58+
$client = new Client('http://localhost/server.php');
59+
$result = $client->execute('addition', array(3, 5));
60+
61+
var_dump($result);
62+
63+
Example with named arguments:
64+
65+
<?php
66+
67+
require 'JsonRPC/Client.php';
68+
69+
use JsonRPC\Client;
70+
71+
$client = new Client('http://localhost/server.php');
72+
$result = $client->execute('random', array('end' => 10, 'start' => 1));
73+
74+
var_dump($result);
75+
76+
Arguments are called in the right order.
77+
If there is an error, the `execute()` method return `NULL`.

src/JsonRPC/Client.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
namespace JsonRPC;
4+
5+
class Client
6+
{
7+
private $url;
8+
private $timeout;
9+
private $debug;
10+
11+
12+
public function __construct($url, $timeout = 5, $debug = false)
13+
{
14+
$this->url = $url;
15+
$this->timeout = $timeout;
16+
$this->debug = $debug;
17+
}
18+
19+
20+
public function execute($procedure, array $params = array())
21+
{
22+
$id = mt_rand();
23+
24+
$payload = array(
25+
'jsonrpc' => '2.0',
26+
'method' => $procedure,
27+
'id' => $id
28+
);
29+
30+
if (! empty($params)) {
31+
32+
$payload['params'] = $params;
33+
}
34+
35+
$result = $this->doRequest($payload);
36+
37+
if (isset($result['id']) && $result['id'] == $id && array_key_exists('result', $result)) {
38+
39+
return $result['result'];
40+
}
41+
else if ($this->debug && isset($result['error'])) {
42+
43+
print_r($result['error']);
44+
}
45+
46+
return null;
47+
}
48+
49+
50+
public function doRequest($payload)
51+
{
52+
$headers = array(
53+
'Connection: close',
54+
'Content-Type: application/json'
55+
);
56+
57+
$ch = curl_init();
58+
59+
curl_setopt($ch, CURLOPT_URL, $this->url);
60+
curl_setopt($ch, CURLOPT_HEADER, false);
61+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
62+
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout);
63+
curl_setopt($ch, CURLOPT_USERAGENT, 'JSON-RPC PHP Client');
64+
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
65+
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
66+
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
67+
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
68+
69+
$response = json_decode(curl_exec($ch), true);
70+
71+
curl_close($ch);
72+
73+
return is_array($response) ? $response : array();
74+
}
75+
}

src/JsonRPC/Server.php

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?php
2+
3+
namespace JsonRPC;
4+
5+
class Server
6+
{
7+
private $payload = array();
8+
private $procedures = array();
9+
10+
11+
public function __construct(array $payload = array())
12+
{
13+
$this->payload = $payload;
14+
}
15+
16+
17+
public function getPayload()
18+
{
19+
$result = json_decode(file_get_contents('php://input'), true);
20+
if ($result) return $result;
21+
22+
return array();
23+
}
24+
25+
26+
public function register($name, \Closure $callback)
27+
{
28+
$this->procedures[$name] = $callback;
29+
}
30+
31+
32+
public function getResponse(array $data)
33+
{
34+
$response = array(
35+
'jsonrpc' => '2.0'
36+
);
37+
38+
if (isset($this->payload['id'])) {
39+
40+
$response['id'] = $this->payload['id'];
41+
}
42+
43+
$response = array_merge($response, $data);
44+
45+
return json_encode($response);
46+
}
47+
48+
49+
public function mapParameters(array $request_params, array $method_params, array &$params)
50+
{
51+
// Positional parameters
52+
if (array_keys($request_params) === range(0, count($request_params) - 1)) {
53+
54+
if (count($request_params) !== count($method_params)) return false;
55+
$params = $request_params;
56+
57+
return true;
58+
}
59+
60+
// Named parameters
61+
foreach ($method_params as $p) {
62+
63+
$name = $p->getName();
64+
65+
if (isset($request_params[$name])) {
66+
67+
$params[$name] = $request_params[$name];
68+
}
69+
else {
70+
71+
return false;
72+
}
73+
}
74+
75+
return true;
76+
}
77+
78+
79+
public function execute()
80+
{
81+
// Check JSON format
82+
if (empty($this->payload)) {
83+
84+
$this->payload = json_decode(file_get_contents('php://input'), true);
85+
86+
if (! $this->payload) {
87+
88+
return $this->getResponse(array(
89+
'error' => array(
90+
'code' => -32700,
91+
'message' => 'Parse error.'
92+
)
93+
));
94+
}
95+
}
96+
97+
// Check JSON-RPC format
98+
if (! isset($this->payload['jsonrpc']) ||
99+
! isset($this->payload['method']) ||
100+
! is_string($this->payload['method']) ||
101+
$this->payload['jsonrpc'] != '2.0' ||
102+
(isset($this->payload['params']) && ! is_array($this->payload['params']))) {
103+
104+
return $this->getResponse(array(
105+
'error' => array(
106+
'code' => -32600,
107+
'message' => 'Invalid Request.'
108+
)
109+
));
110+
}
111+
112+
// Procedure not found
113+
if (! isset($this->procedures[$this->payload['method']])) {
114+
115+
return $this->getResponse(array(
116+
'error' => array(
117+
'code' => -32601,
118+
'message' => 'Procedure not found.'
119+
)
120+
));
121+
}
122+
123+
$callback = $this->procedures[$this->payload['method']];
124+
$params = array();
125+
126+
$reflection = new \ReflectionFunction($callback);
127+
128+
if (isset($this->payload['params'])) {
129+
130+
$parameters = $reflection->getParameters();
131+
132+
if (! $this->mapParameters($this->payload['params'], $parameters, $params)) {
133+
134+
return $this->getResponse(array(
135+
'error' => array(
136+
'code' => -32602,
137+
'message' => 'Invalid params.'
138+
)
139+
));
140+
}
141+
}
142+
143+
$result = $reflection->invokeArgs($params);
144+
145+
return $this->getResponse(array('result' => $result));
146+
}
147+
}

0 commit comments

Comments
 (0)