📜 ⬆️ ⬇️

JSON-RPC 2.0 and PHP

If you are a developer, and you have a project in PHP, and he finally needed to implement his own API - this article is definitely for you;).

JSON-RPC v1.0 appeared in 2005, 5 years later the second version appeared . In the age of javascript and mobile applications, many developers still use their own bikes instead of a ready-made standard.

Why JSON-RPC, and yes even 2.0?


I will try to highlight key features:

For those familiar with version 1.0, significant innovations in 2.0 were named parameters and a call queue.
A simple request / response is as follows:
--> {"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3} <-- {"jsonrpc": "2.0", "result": 19, "id": 3} --> {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1} <-- {"jsonrpc": "2.0", "result": 19, "id": 1} --> [ {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, {"jsonrpc": "2.0", "method": "foobar", "id": "2"} ] <-- [ {"jsonrpc": "2.0", "result": 7, "id": "1"}, {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found."}, "id": "2"} ] 


In this complicated “creative” world of programming, the question always arises: to take something ready or write something of your own?
')

Perfect world. What do we need?


As usual, we define the criteria for the tool that I want to find / get:

Enough for a start.

A bit of theory


Because JSON-RPC is a fairly young protocol, there are still moments in it that are not fully approved. One of them is the Service Mapping Description proposed by Dojo. SMD can fully describe a web service, starting from its methods, ending with expanded return types. Unfortunately, very few solutions support its implementation, for example, the Zend_Json_Server , the inputEx framework ( generating the test forms ) and the Dojo framework itself .

Let us turn to the search for existing solutions.

Existing PHP implementations


I took the list of clients from the tablet in Wikipedia .
php-json-rpcjsonrpc2phptivokajuniorjson-rpc-phpJSONRpc2Zend
Json
Server
zoServices
Server
Compliance specification-Proper support for Notification in Batch mode.
+No optional named params+++No optional named params
Number of files-2> 736one> 56
SMD diagram------+-
Tests-+-+-++-
Internal implementation (1..5)-four
Manual mapping of exported functions
3. Too difficult implementation for such a simple task.four4. Difficult4. Magic Inside!4+. Zend3
Customer
Compliance specificationNo Batch and Notification-+++No batch-Notificaiton missing
Number of filesone-> 7four2one-6
Tests---+-+--
Internal implementation (1..5)four-3. Extra steps to call methodsfourfour4+. :)-3
Automatic generation--------

A clever reader can argue, what a difference, how many files are used for implementation, you can still glue everything into one. Yes, it is possible, but this is an additional unnecessary action.

As we see, there is no “ideal” solution that would suit us and which can be used calmly without a file. I hope that on other platforms the situation is much better :)
There is a general feeling of the projects being left undone; no solution can offer a full cycle of using the exported API (server, smd-schema, client-generation).
I also do not really understand those developers who are trying to make PHP, say Java or C #. For the most part, PHP is used in the request / response scheme, and not in the application server scheme with its own states. The script is the same not compiled closed library.

The answer to the question “use something ready” or “write your own” is obvious.

Eazy JSON-RPC 2.0


Project on github . All requirements, indicated earlier, are implemented :)

Server

There are two use options: either to inherit from the BaseJsonRpcServer class, or to create an instance of it and pass the exported object to the constructor:
 <?php include 'BaseJsonRpcServer.php'; include 'tests/lib/DateTimeRpcService.php'; //   $server = new DateTimeRpcService(); //  new BaseJsonRpcServer( new DateTimeService() ); $server->Execute(); 

Thus, we have opened out all public methods of the DateTimeRpcService class. The SMD scheme can be obtained through the smd GET parameter (for example, eazyjsonrpc / example-server.php? Smd ). At construction of the scheme phpDoc-blocks are considered.
What is there for such a scheme ...
 {"transport":"POST","envelope":"JSON-RPC-2.0","SMDVersion":"2.0","contentType":"application\/json","target":"\/example-server.php","services":{"GetTime":{"parameters":[{"name":"timezone","optional":true,"type":"string","default":"UTC"},{"name":"format","optional":true,"type":"string","default":"c"}],"description":"Get Current Time","returns":{"type":"string"}},"GetTimeZones":{"parameters":[],"description":"Returns associative array containing dst, offset and the timezone name","returns":{"type":"array"}},"GetRelativeTime":{"parameters":[{"name":"text","optional":false,"type":"string","description":"a date\/time string\r"},{"name":"timezone","optional":true,"type":"string","default":"UTC"},{"name":"format","optional":true,"type":"string","default":"c"}],"description":"Get Relative time","returns":{"type":"string"}},"Implode":{"parameters":[{"name":"glue","optional":false,"type":"string"},{"name":"pieces","optional":true,"type":"array","default":["1","2","3"]}],"description":"Implode Function","returns":{"type":"string","description":"string"}}},"description":"Simple Date Time Service"} 



Client

Again, there are two use cases: either create an instance of the BaseJsonRpcClient class and pass it the web service link in the constructor, or use the generator:
 <?php include 'BaseJsonRpcClient.php'; $client = new BaseJsonRpcClient( 'http://eazyjsonrpc/example-server.php' ); $result = $client->GetRelativeTime( 'yesterday' ); 


Generator

Based on the SMD scheme, we can generate a class to work with the server (see the example of DateTimeServiceClient.php ). To do this, call the generator:
 php JsonRpcClientGenerator.php http://eazyjsonrpc/example-server.php?smd DateTimeServiceClient 

The result of the command will be the DateTimeServiceClient.php file with the methods we need.

A spoon of tar

The unspoken rule for calling class-> method () in JSON-RPC is using class.method as the method name (through a dot).
In the current implementation of this functionality is not provided. It is assumed that the url is an exported class, then the option with dots disappears :). Regarding the client side, you can always add something here, it's just PHP.

It is also possible in SMD to describe the returned types as objects with their properties, but due to the complexity of the implementation, we will omit this moment.

For those who want to find detailed documentation, I can suggest reading the method names again, phpDoc comments for them, and the source code of the Server or Client.

Lifehacks


What we have with authentication?

There are several implementation options:
  1. Use HTTP Basic Auth. In the client, it is enough to add a login and password to the $ CurlOptions array :)
  2. Use tokens via HTTP headers. To obtain tokens, you can write the necessary method.
  3. Use tokens as method parameters.

How to deal with file uploads?

Some people offer a strange variant with encoding a file in base64 and sending it in some field.
A more or less normal solution is to implement a method that tells you where to start downloading a file.
Example
 --> {"jsonrpc": "2.0", "method": "send_image", "params": ..., "id": 1} <-- {"jsonrpc": "2.0", "result": {"URL": "/exampleurl?id=12345", "maxsize": 10000000, "accepted-format":["jpg", "png"]}, "id": 1} -->      .      ,    --> {"jsonrpc": "2.0", "method": "send_done", "params": {"checksum": "1a5e8f13"}, "id": 2} <-- {"jsonrpc": "2.0", "result": "ok"} 

Error processing

The protocol itself already provides for the presence of an error object with the code , message, and data fields. When using BaseJsonRpcServer inside the called method, you can throw an Exception in which to pass code and data . You can add your message to the $ errorMessages array at a specific code .

Exporting objects with specific fields only.

It is all up to you, how you implement it - so be it. I can only advise to create some class ObjectToJsonConverter , in which to implement the transformation of the object into the desired array.
Example
 <?php class ObjectToJsonConverter { /** * @param City $city * @return array */ public static function GetCity( City $city ) { return array( 'id' => $city->cityId , 'name' => $city->title , 'region' => $city->region->title ); } } // -     return array_map( 'ObjectToJsonConverter::GetCity', $cities ); 


Conversion to client-side objects

Here again, it all depends on you. For example, you can create the required classes and write some simple converter back (ideas on conversion can be taken in MyShowsClient.php )

Conclusion


Hopefully, after reading the article, JSON-RPC will not be deprived of attention when choosing an interaction protocol.
Even after covering the tests> 89% of the code, I can only say: "It seems to work" :)

Source: https://habr.com/ru/post/150803/


All Articles