msenkpiel
11/7/2011 - 11:46 AM

simple jsonrpc server howto php

simple jsonrpc server howto php

the request ...
================
{

"method":"foobar",
"params":["foo", "bar", "baz"]

}



the service ...
===============

class MyService
{


    public function foobar($foo, $bar, $baz)
    {

        return "HelloWord"

    }



}


the dispatcher ....
===================


class Dispatcher
{

    /**
     * @throws Exception
     * @return void
     */
    public function run()
    {

        $this->_init();


        $responseData = array(
            "result" => null,
            "error" => null,
        );

        try {

            $requestText = "" . $this->_fetchRequestText();
            $rpc = json_decode($requestText, true);
            /*
            $rpc = array(
            "method"=>"foobar",
            "params"=>array("foo","bar","baz"),
            );
            */

            if (!is_array($rpc)) {
                throw new Exception("Invalid rpc request");
            }
            $rpcMethod = null;
            if (array_key_exists("method", $rpc)) {
                $rpcMethod = $rpc["method"];
            }
            $rpcParams = $rpc["params"];
            if (array_key_exists("params", $rpc)) {
                $rpcParams = $rpc["params"];
                if ($rpcParams === null) {
                    $rpcParams = array();
                }
            }

            if (!is_string($rpcMethod)) {
                throw new Exception("Invalid rpc method");
            }
            if (!is_array($rpcParams)) {
                throw new Exception("Invalid rpc params");
            }

            $service = new MyService();

            $reflectionClass = new ReflectionClass($service);

            if (!$reflectionClass->hasMethod($rpcMethod)) {
                throw new Exception("rpc method does not exist!");
            }
            $reflectionMethod = $reflectionClass->getMethod($rpcMethod);
            $this->_validateReflectionMethod($reflectionMethod);

            $serviceResult = $reflectionMethod->invokeArgs(
                $service, (array)$rpcParams
            );

            $responseData["result"] = $serviceResult;

        } catch (Exception $e) {
            $responseData["error"] = array(
                "message" => $e->getMessage(),
            );
        }


        $responseText = null;
        try {
            $responseText = json_encode($responseData);
        } catch (Exception $e) {
        }

        if (!is_string($responseText)) {

            $responseData = array(
                "error" => array(
                    "message" => "error encoding rpc response",
                ),
                "result" => null
            );

            $responseText = "".json_encode($responseData);
        }


        echo $responseText;


    }

    /**
     * @throws ErrorException
     * @return void
     */
    protected function _init()
    {
        /*
            RECOMMENDED SETUP
            =================
        */
        ini_set("display_errors", true);
        error_reporting(E_ALL|E_STRICT & ~E_NOTICE);
        // php 5.3+
        set_error_handler(function($errno, $errstr, $errfile, $errline){
            throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
        });
        // php < 5.3
        /*
        set_error_handler(array($this, "phpErrorHandler"));
        */
    }

    /**
     * for php<5.3
     * @throws ErrorException
     * @param  $errno
     * @param  $errstr
     * @param  $errfile
     * @param  $errline
     * @return void
     */
    public function phpErrorHandler($errno, $errstr, $errfile, $errline)
    {
        throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
    }


    /**
     * @return string
     */
    protected function _fetchRequestText()
    {
        $requestText = file_get_contents('php://input');
        return $requestText;
    }



       /**
     * @throws Exception
     * @param ReflectionMethod $reflectionMethod
     * @return void
     */
    protected function _validateReflectionMethod(
                ReflectionMethod $reflectionMethod
            )
    {
        $reflectionMethodName = $reflectionMethod->getName();
        if ($reflectionMethodName[0] === "_") {
            throw new Exception("rpc method is not invokable!");
        }
        if (!$reflectionMethod->isPublic()) {
            throw new Exception("rpc method is not invokable!");
        }
        if ($reflectionMethod->isStatic()) {
            throw new Exception("rpc method is not invokable!");
        }
        if ($reflectionMethod->isAbstract()) {
            throw new Exception("rpc method is not invokable!");
        }
        if ($reflectionMethod->isInternal()) {
            throw new Exception("rpc method is not invokable!");
        }

        if(method_exists($reflectionMethod, "isConstructor")) {
            if ($reflectionMethod->isConstructor()) {
                throw new Exception("rpc method is not invokable!");
            }
        }
        if(method_exists($reflectionMethod, "isDestructor")) {
            if ($reflectionMethod->isDestructor()) {
                throw new Exception("rpc method is not invokable!");
            }
        }
        
        if(method_exists($reflectionMethod, "isClosure")) {
            if ($reflectionMethod->isClosure()) {
                throw new Exception("rpc method is not invokable!");
            }
        }
        if(method_exists($reflectionMethod, "isDeprecated")) {
            if ($reflectionMethod->isDeprecated()) {
                throw new Exception("rpc method is not invokable!");
            }
        }
    }

}



the application:
================

$dispatcher = new Dispatcher();
$dispatcher->run();




the response:
=============

{
"result":"HelloWorld",
"error":null
}