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, after 5 years the second version appeared . In the age of javascript and mobile applications, many developers still use their own bikes instead of a ready-made simple standard.

    Why JSON-RPC, and even 2.0?


    I will try to highlight the key features:
    • It is at least some standard. A wide selection of libraries for various platforms.
    • Fast easy parsing. JSON-RPC is not as monstrous as XML-RPC or SOAP.
    • Built-in error handling. With REST bikes, you have to figure out how to build the answer and what to do next.
    • Call Queue Support. Now 5 HTTP requests can be wrapped in one :).
    • A single point for the API. For example, /rpc-server.php will be able to handle various methods.
    • Support for named and optional parameters when calling methods.

    For those familiar with version 1.0, the significant innovations in 2.0 were named parameters and call queues.
    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 complex "creative" world of programming, the question always arises: take something ready or write something of your own?

    Perfect world. What we need?


    As usual, we define the criteria for the tool that you want to find / get:
    • Full compliance with specifications. Given this factor, most likely there will be no problems with other client / server solutions
    • Single-file independent implementation. I don’t want to drag any framework a la Zend or PEAR
    • Availability of tests. At least some guarantee that the open-source solution will work correctly;)
    • Beautiful implementation inside. Everything should be as simple, clear and logical as possible.
    • The presence of auto-discover mechanism. JSON-RPC Server must be able to provide meta information about its methods
    • Simple connection. I connected the file, called a couple of methods, everything works.
    • Generation of client proxy classes. Why write a client when we already have a ready-made server with metadata? ;)

    Enough for a start.

    Bit of theory


    Because JSON-RPC is a fairly young protocol, then 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 deployed return types. Unfortunately, very few solutions support its implementation, for example, Zend_Json_Server , inputEx framework ( generation of test forms ) and Dojo framework itself .

    Let's move on to the search for existing solutions.

    Existing implementations for PHP


    I took the list of clients from a tablet on Wikipedia .
     php-json-rpcjsonrpc2phptivokajuniorjson-rpc-phpJSONRpc2Zend
    json
    server
    zoServices
    Server        
    Compliance-Correct support for Notification in Batch mode.
    +No optional named params+++No optional named params
    Number of files-2> 7361> 56
    SMD circuit------+-
    Tests-+-+-++-
    Internal implementation (1..5)-4
    Manual mapping of exported functions
    3. Too complicated implementation for such a simple task44. Difficult4. Magic Inside!4+. Zend3.
    Client        
    ComplianceNo Batch and Notification -+++No batch-Notificaiton missing
    Number of files1-> 7421-6
    Tests---+-+--
    Internal implementation (1..5)4-3. Extra steps for calling methods444+. :)-3
    Automatic generation--------

    A smart reader may argue, what difference does it make, how many files are used for implementation, you can still glue everything into one. Yes, you can, but this is an additional extra action.

    As we see, there is no “ideal” solution that would suit us and which can be safely used without a file. I hope that on other platforms things are much better :)
    There is a general feeling of unfinished projects, 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 states. A script is not a compiled private 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 cases: either inherit from the BaseJsonRpcServer class, or create an instance of it and pass the exported object to the constructor:
    Execute();
    

    Thus, we exposed all the public methods of the DateTimeRpcService class. The SMD scheme can be obtained through the smd GET parameter (for example, eazyjsonrpc / example-server.php? Smd ). When building a scheme, phpDoc blocks are taken into account.
    What kind of circuit is there ...
    {"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

    There are two possible uses again: either create an instance of the BaseJsonRpcClient class and pass the web service link to it in the constructor, or use the generator:
    GetRelativeTime( 'yesterday' );
    


    Generator

    Based on the SMD scheme, we can generate a class for working with the server (see the example 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 a DateTimeServiceClient.php file with the methods we need.

    A spoon of tar

    An unspoken rule for calling class-> method () in JSON-RPC is using class.method as the name of the method (through the dot).
    In the current implementation, such functionality is not provided. It is assumed that url is an exported class, then the dotted option disappears :). Regarding the client part - you can always add here, this is just PHP.

    Also in SMD there is an opportunity to describe return types in the form of objects with their properties, but in view of implementation complexity, we will omit this moment.

    For those who want to find detailed documentation, I can offer to read the names of methods, phpDoc comments to them and the source code of Server or Client again.

    Life hacks


    What do we have with authentication?

    There are several implementation options:
    1. Use HTTP Basic Auth. In the client, just add the username and password to the $ CurlOptions array :)
    2. Use tokens through HTTP headers. To get tokens, you can write the necessary method.
    3. Use tokens as method parameters.

    What about uploading files?

    Some people offer a strange option 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 at what address you can 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 existence of an error object with the code , message, and data fields . When using BaseJsonRpcServer inside the called method, you can throw an Exception into which to pass code and data . You can add your message to the $ errorMessages array using a specific code .

    Export objects with specific fields only

    It all depends entirely on you, how you implement it will be so. I can only advise creating some ObjectToJsonConverter class in which to implement the conversion of the object into the required array.
    Example
     $city->cityId
                    , 'name'   => $city->title
                    , 'region' => $city->region->title 
                );
            }
        }
           // где-то в конце экспортируемого метода
        return array_map( 'ObjectToJsonConverter::GetCity', $cities );
    


    Convert to Client Side Objects

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

    Conclusion


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

    Also popular now: