class-jsonrpc will ease your php/javascript communications.

Jonathan Gotti Jonathan Gotti on May 31, 2011 | General, Javascript, What I'm Doing, Php, Json | 0 comments | Recent Posts

Lately I’ve worked on a new class jsonRPC that you can find and review here: http://retrospectiva.jgotti.net/projects/simplemvc/browse/trunk/includes/class-jsonrpc.php

Using it you will be able to deploy json rpc service in no time here is a quick overview.

jsonRPC hello world

Before entering in details we will review a traditional hello world example:

<?php
require 'class-jsonrpc.php';
/**
* hello world service
* @param string $name
* @return string
*/
function hello($name=null){
  return 'Hello '.($name?$name:'world');
}
jsonRPC::init(array('hello'));

Copy and save the content of this file to a file named hello.php.

Then to see the result just call the url: http://myserver.com/hello.php?method=hello you will see a jsonRPC response with ‘Hello world’ as the result value. {"result":"Hello world"}

As you may have see you also can pass a parameter to the method by calling http://myserver.com/hello.php?method=hello&params=[“john”] this will result in a response like : {"result":"Hello john"}

RUNTIME CONFIGURATION

Now we will start to dive a little deeper in the code and have a look to the runtime configuration options you may use while creating your own jsonrpc service.

Configuration options are declared as static public properties of the jsonRPC class and so you can configure them anywhere in your code at runtime, here’s available options for now: h2. Configuration options:

  • jsonRPC::$falseIsError : this is false by default, but if you want to consider any registered methods returning false as a service error (so it will trigger an error response instead of a result response) you have to turn this to true.
  • jsonRPC::$autoCleanMagicQuotes : setted to true by default, will try to clean up the parameters if magic_quotes_gpc is active, you can disable this feature by setting this to false.
  • jsonRPC::$noCache : false by default, you should set this to true inside methods that mustn’t be cached by the browser (will return appropriate headers) or at the begining of your service if you don’t want any cache at all. Consider to leave this option to false on heavy treatment that don’t change very much as it will save you cpu time and bandwidth.
  • jsonRPC::$allowDiscovery : true by default will automaticly register method discovery and htmlDiscovery, the last one being called if no methods is passed in the query.

DISCOVERY AND HTMLDISCOVERY METHODS

There’s two methods to discover what the service has to offer and what is attended.

  • discovery This will return you a json description of which methods are available and what are the parameters it takes, if they’re optional or not and so on.
  • htmlDiscovery This is the same as discovery method but in a more human readable format as it will display results as a html documentation. This method will be called as default if no method is passed in the jsonRPC query and if jsonRPC::$allowDiscovery is true. It is a documentation for the human while discovery is more for programmatic discovering of the service (kinda of SOAP wsdl equiv).

Both thoose methods will use php relection and a bit of phpDoc parsing to create their response, so correctly document your registered methods. See it in action by calling our previous example without precising a method in the query http://myserver.com/hello.php

WRITING YOUR OWN SERVICE

When writing a json rpc service using jsonRPC class you don’t have to worry about returning a specially formatted response, the class will do it for you and respond what your functions/methods returned in a correct json representation. If you want to return an error response instead you can throw a jsonRpcMethodException like this:

<?php
// replace the jsonRPC::init line in previous example with thoose ones
function giveMeAnError(){
  throw new jsonRpcMethodException('this is an error');
}
jsonRPC::init(array(
  'hello',
  'giveError'=>'giveMeAnError' // the method is exposed as giveError not as giveMeAnError when declared this way
));

should return {"error":{"code":32099,"message":"method result error","data":"this is an error"}}

So we we now have a service that manage two methods, hello and giveError.

REGISTERING CLASS METHODS INSTEAD OF FUNCTIONS

Until now we only have binded methods from php function but we also could have registered some class methods, so let’s do that and bind a new greatings class methods as a service

<?php
require 'class-jsonrpc.php';
class greatings{
 static public function hi($name=null){
  return 'Hi '.($name?$name:'world');
 }
 static public function morning($name=null){
  return 'Good morning '.($name?$name:'world');
 }
 static public function everyone(array $name=null){
  if($name){
   $name=implode(', ',$name);
  }
  return array('hi'=>self::hi($name),'morning'=>self::morning($name));
 }
 static public function all($name){
  return array('hi'=>self::hi($name),'morning'=>self::morning($name));
 }
 function time(){
  return 'it is '.date('H\Hi',time());
 }
}
jsonRPC::init('greatings');

Doing this all our greatings static methods are exposed to the world. Only static public methods are we passed in the name of a class definition not a living instance, if we had passed a living instance instead of a class name would have registered the instance public methods instead of the static ones.

Try it by replacing jsonRPC::init('greatings'); by jsonRPC::init(new greatings());

Additionaly you can pass a second parameter $classMapping as an array like describe above when registering function to register only desired class methods. A third parameter $bindParentMethodsToo will allow you to bind parents class methods as well.

Registering function and class methods

It is totally possible to register multiple class and/or mixing class methods and functions as service methods, but this won’t be possible using the init() method. If you need/want to do so you have to create an instance of jsonRPC server yourself and call bindClass / bindMethod on your own for each of them here an example registering all of the previous example:

<?php
require 'class-jsonrpc.php';

/**
* hello world service
* @param string $name
* @return string
*/
function hello($name=null){
  return 'Hello '.($name?$name:'world');
}

function giveMeAnError(){
  throw new jsonRpcMethodException('this is an error');
}

class greatings{
 static public function hi($name=null){
  return 'Hi '.($name?$name:'world');
 }
 static public function morning($name=null){
  return 'Good morning '.($name?$name:'world');
 }
 static public function everyone(array $name=null){
  if($name){
   $name=implode(', ',$name);
  }
  return array('hi'=>self::hi($name),'morning'=>self::morning($name));
 }
 static public function all($name){
  return array('hi'=>self::hi($name),'morning'=>self::morning($name));
 }
 function time(){
  return 'it is '.date('H\Hi',time());
 }
}

$jsonService = new jsonRPC();

$jsonService->bindMethod(array('hello','giveError'=>'giveMeAnError'));
$jsonService->bindClass('greatings');
$jsonService->bindClass(new greatings());

$jsonService->response($jsonService->processRequest());

FINALLY CALLING THE SERVICE FROM WITHIN YOUR WEB PAGES

All of this is nice but the real purpose of all that mess is to call our service from our webPages and/or from other services the jsonRPC methods allow you to use the most convenient way for you, here’s a list of different way to call a service method in the order the service try to handle them.

  • Using a full jsonrpc request passed in as a single parameter by GET/POST request if a GET/POST parameter named jsonrpc contains a full jsonrpc request (see jsonrpc definition on the net for more info here’s a example: jsonrpc={id:"requestUniqueId",method:"methodname",params:["param1","param2"]}) then it will proceed with that
  • using a partial jsonrpc request passed as multiple GET/POST parameters this is the way we use to try our service in this overview, each part of the request is passed as GET/POST parameters. Here’s a GET example: http://myserver.com/service.php?id=requestUniqueID&method=methodName&params=["param1","param2"]
  • using a true jsonrpc request as raw POST datas this is the way a jsonrpc is intended to work search the web for jsonrpc service definition for some example, it’s the harder and better way to make it

JSONP

If you prefer to get a JSONP response simply add a callback parameter to the queryString of the requested service url. For example with our previously created service: http://myserver.com/hello.php?method=hello&callback=mycallback will return mycallback({"result":"Hello world"});

UTILITIES

At last but not least there’s two jsonRpc methods to assist you dealing with jsonrpc services, the first one is a service method jqueryProxy that should be used as a script HTML tag src attribute to help you call service methods here’s a quick sample

<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js" type="text/javascript"></script>
<script src="hello.php?method=jqueryProxy"></script>
</head>
<body>
<a href="javascript:jsonrpc.request('hello',['John'],function(r){alert('success:'+r)});">call Hello</a>
</body>
</html>

The other methods is jsonRPC::syncRequest intended to be called in your php code to contact other jsonrpc services, it use fsockopen functions to perform a true jsonrpc request, it’s one of the lightweight way of calling a jsonrpc service from within php code, but you’re free to use other tools like curl to do the same. here’s an example of using it: jsonRPC::syncRequest('http://myserver.com/hello.php','hello',array('John')); the service response will be returned as a php object.

Hope this piece of code will help some of you, as always feedback is welcome. Happy coding,

Malko.

EDIT: I’ve almost forgot to tell you that with this we’ve made a jsonRpcController inside the simpleMVC that you can extends to ease a little bit more your service creation when using simpleMVC



Comments