Skip to content

Commit 593555e

Browse files
committed
Implement version 1.0
1 parent 13885b5 commit 593555e

File tree

7 files changed

+245
-2
lines changed

7 files changed

+245
-2
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
],
1515
"license": "GPL-3.0-or-later",
1616
"require": {
17-
"php": "^7||^8"
17+
"php": "^7.4||^8",
18+
"ext-json": "*"
1819
},
1920
"require-dev": {
2021
"phpunit/phpunit": "^7||^8||^9"

src/JSONRPCException.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
namespace YOCLIB\JSONRPC;
3+
4+
use Exception;
5+
6+
class JSONRPCException extends Exception{
7+
8+
}

src/Message.php

Lines changed: 203 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,208 @@
11
<?php
22
namespace YOCLIB\JSONRPC;
33

4-
class Message{
4+
use JsonException;
5+
6+
abstract class Message{
7+
8+
private object $value;
9+
10+
/**
11+
* @param object $value
12+
*/
13+
private function __construct(object $value){
14+
$this->value = $value;
15+
}
16+
17+
/**
18+
* @return string
19+
*/
20+
public function toJSON(): string{
21+
return json_encode($this->value);
22+
}
23+
24+
/**
25+
* @param $id
26+
* @param string $method
27+
* @param array $params
28+
* @return RequestMessage
29+
*/
30+
public static function createRequestMessageV1($id,string $method,array $params=[]): RequestMessage{
31+
return new RequestMessage((object) [
32+
'id' => $id,
33+
'method' => $method,
34+
'params' => $params,
35+
]);
36+
}
37+
38+
/**
39+
* @param $id
40+
* @param string $method
41+
* @param array $params
42+
* @return NotificationMessage
43+
*/
44+
public static function createNotificationMessageV1(string $method,array $params=[]): NotificationMessage{
45+
return new NotificationMessage((object) [
46+
'id' => null,
47+
'method' => $method,
48+
'params' => $params,
49+
]);
50+
}
51+
52+
/**
53+
* @param $id
54+
* @param $result
55+
* @param object|string|null $error
56+
* @return RequestMessage
57+
* @throws JSONRPCException
58+
*/
59+
public static function createResponseMessageV1($id,$result=null,$error=null): RequestMessage{
60+
if(!is_null($result) && !is_null($error)){
61+
throw new JSONRPCException('[V1] Only one property "result" or "error" can be non null.');
62+
}
63+
if(!is_object($error) && !is_string($error) && !is_null($error)){
64+
throw new JSONRPCException('[V1] The "error" property in request MUST be an string, object or null.');
65+
}
66+
return new RequestMessage((object) [
67+
'id' => $id,
68+
'result' => $result,
69+
'error' => $error,
70+
]);
71+
}
72+
73+
/**
74+
* @param string $json
75+
* @param bool $strictId
76+
* @return Message[]|array|Message
77+
* @throws JSONRPCException
78+
*/
79+
public static function parse(string $json,bool $strictId=true){
80+
try{
81+
$message = json_decode($json,false,512,JSON_THROW_ON_ERROR);
82+
}catch(JsonException $e){
83+
throw new JSONRPCException('[V1] Failed to decode JSON.');
84+
}
85+
if(is_array($message)){
86+
$messages = [];
87+
foreach($message AS $msg){
88+
$messages[] = self::handleMessage($msg,$strictId);
89+
}
90+
return $messages;
91+
}
92+
return self::handleMessage($message,$strictId);
93+
}
94+
95+
/**
96+
* @param $message
97+
* @param bool $strictId
98+
* @return Message
99+
* @throws JSONRPCException
100+
*/
101+
private static function handleMessage($message,bool $strictId=true){
102+
if(isset($message['jsonrpc']) && $message['jsonrpc']==='2.0'){
103+
return self::handleMessageV2($message,$strictId);
104+
}else{
105+
return self::handleMessageV1($message,$strictId);
106+
}
107+
}
108+
109+
private static function handleMessageV2($message,bool $strictId=true){
110+
return null;
111+
}
112+
113+
114+
private static function isRequestV1($message): bool{
115+
return property_exists($message,'method') || property_exists($message,'params');
116+
}
117+
118+
private static function isResponseV1($message): bool{
119+
return property_exists($message,'result') || property_exists($message,'error');
120+
}
121+
122+
/**
123+
* @param object $message
124+
* @return void
125+
* @throws JSONRPCException
126+
*/
127+
private static function validateMethodPropertyV1(object $message){
128+
if(!property_exists($message,'method')){
129+
throw new JSONRPCException('[V1] Missing "method" property in request.');
130+
}
131+
if(!is_string($message->method)){
132+
throw new JSONRPCException('[V1] The "method" property in request MUST be a string.');
133+
}
134+
}
135+
136+
/**
137+
* @param $message
138+
* @return void
139+
* @throws JSONRPCException
140+
*/
141+
private static function validateParamsPropertyV1($message){
142+
if(!property_exists($message,'params')){
143+
throw new JSONRPCException('[V1] Missing "params" property in request.');
144+
}
145+
if(!is_array($message['params'])){
146+
throw new JSONRPCException('[V1] The "params" property in request MUST be an array.');
147+
}
148+
}
149+
150+
/**
151+
* @param $message
152+
* @return void
153+
* @throws JSONRPCException
154+
*/
155+
private static function validateResultPropertyV1($message){
156+
if(!property_exists($message,'result')){
157+
throw new JSONRPCException('[V1] Missing "result" property in request.');
158+
}
159+
}
160+
161+
/**
162+
* @param $message
163+
* @return void
164+
* @throws JSONRPCException
165+
*/
166+
private static function validateErrorPropertyV1($message){
167+
if(!property_exists($message,'error')){
168+
throw new JSONRPCException('[V1] Missing "error" property in request.');
169+
}
170+
if(!is_object($message->error) && !is_string($message->error) && !is_null($message->error)){
171+
throw new JSONRPCException('[V1] The "error" property in request MUST be an string, object or null.');
172+
}
173+
}
174+
175+
/**
176+
* @param $message
177+
* @param bool $strictId
178+
* @return Message
179+
* @throws JSONRPCException
180+
*/
181+
private static function handleMessageV1($message,bool $strictId=true){
182+
if(self::isRequestV1($message)){
183+
self::validateMethodPropertyV1($message);
184+
self::validateParamsPropertyV1($message);
185+
186+
if(property_exists($message,'id') && $strictId?($message->id!==null):($message->id)){
187+
return new RequestMessage($message);
188+
}else{
189+
return new NotificationMessage($message);
190+
}
191+
}elseif(self::isResponseV1($message)){
192+
self::validateResultPropertyV1($message);
193+
self::validateErrorPropertyV1($message);
194+
if(!is_null($message->result) && !is_null($message->error)){
195+
throw new JSONRPCException('[V1] Only one property "result" or "error" can be non null.');
196+
}
197+
198+
if(property_exists($message,'id')){
199+
return new ResponseMessage($message);
200+
}else{
201+
throw new JSONRPCException('[V1] Missing "id" property in response.');
202+
}
203+
}else{
204+
throw new JSONRPCException('[V1] Unknown message type.');
205+
}
206+
}
5207

6208
}

src/NotificationMessage.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
namespace YOCLIB\JSONRPC;
3+
4+
class NotificationMessage extends RequestMessage{
5+
6+
}

src/RequestMessage.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
namespace YOCLIB\JSONRPC;
3+
4+
class RequestMessage extends Message{
5+
6+
}

src/ResponseMessage.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
namespace YOCLIB\JSONRPC;
3+
4+
class ResponseMessage extends Message{
5+
6+
}

tests/MessageTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@
33

44
use PHPUnit\Framework\TestCase;
55

6+
use YOCLIB\JSONRPC\JSONRPCException;
7+
use YOCLIB\JSONRPC\Message;
8+
69
class MessageTest extends TestCase{
710

11+
/**
12+
* @return void
13+
* @throws JSONRPCException
14+
*/
15+
public function testMessages(){
16+
$this->assertEquals('{"id":123,"method":"myMethod","params":[]}',Message::createRequestMessageV1(123,'myMethod')->toJSON());
17+
$this->assertEquals('{"id":null,"method":"myMethod","params":[]}',Message::createNotificationMessageV1('myMethod')->toJSON());
18+
$this->assertEquals('{"id":123,"result":"myResult","error":null}',Message::createResponseMessageV1(123,'myResult')->toJSON());
19+
$this->assertEquals('{"id":123,"result":null,"error":"myError"}',Message::createResponseMessageV1(123,null,'myError')->toJSON());
20+
}
21+
822
}

0 commit comments

Comments
 (0)