PHP-RPC update 3

I figured, the best way to come up with a usable PHP-RPC spec, is to put it in practice, and see where the problems appear. This is to prevent putting 'academical correctness' before usefulness.

So, I created a server implementation, and so far it works well. It's under a bsd license, so feel free to give it a shot. Right now I put everything in the 'Sabre' package, hopefully at one point this will be Zend, PEAR2 or Solar, but I'll look at that when I can put the 1.0 stamp on it. This implementation is only tested with PHP 5.2 and 5.1.

download link

The server class:

<?php

  // creating the server object
  require_once 'Sabre/PHPRPC/Server.php';
  $server = new Sabre_PHPRPC_Server();

  // handling method calls
  // Method contains a method name.. this could for example be 'blog.getPosts'
  // argumentNotation is 1 if its a simple array, it is 2 if the parameters are specified as a struct
  function invokeMethod($method,array $arguments,$argumentNotation) {

     return 'Hello World! You called the following method: ' . $method;

  }

  $server->setInvokeCallback('invokeMethod');

  // after this point, everything goes automatic.
  $server->exec();

?>

A sample client:

<?php

  $url = 'http://www.example.org/phprpc.php';

  $data = file_get_contents($url . '?method=system.testingMethod');
  $data = unserialize($data);

  echo $data['result']; // will output "Hello World! You called the following method: system.testingMethod

?>

Here's the updated proposal. Changes have been highlighted.

The proposal (0.2)

Goals

  • Client should be very easy to implement. Server is allowed to be a bit more complex.
  • No duplication of the HTTP protocol. For example, HTTP already provides encryption, redirecting and authentication.
  • PHP 4/5/6 compatiblity.
  • Client and server implementations should be built from the idea 'be strict in what you produce, be liberal in what you accept'

The request

Requests are made using either GET or POST. Both should be accepted. GET is more appropriate for fetching information, whereas POST is used for posting new data. POST has the advantage that it doesn't have any limits in the size of the request and an encoding can be supplied. GET has the advantage that information can be fetched using a one-liner.

When there is no encoding specified, UTF-8 is assumed. Data supplied using POST should be encoded as application/x-www-form-urlencoded (this is how a browser submits data by default).

The method thats called should always be supplied as the 'method' variable. The method can contain periods (.) to seperate namespaces like XML-RPC. Arguments can be specified in two ways, and the API documentation should specify what the appropriate way is. The first way is using named arguments, a GET example would be:

http://www.example.org/services/phprpc?method=getUsers&maxItems=20

The method here is getUsers, the named argument is maxItems and its value is 20.

The second way is using a list of arguments, which might be more appropriate in some cases where you want to directly map services and methods from a class on the server to the api. This is also how XML-RPC works.

http://www.example.org/services/phprpc?method=getUsers&arguments[0]=20&arguments[1]=1

The first argument is 20, the second is 1.

Smart clients should autodetect if the user is trying to use named arguments or a sequence by checking out the type of the keys in the array.

Smart servers should use reflection to automatically map named arguments to the actual arguments in a list.

Clients SHOULD supply the version of PHP they are running. This can be either a complete version number, or just the major version (e.g.: 4, 5, 6). Clients should supply this as the phpVersion parameter. If the versionnumber is not supplied, the current stable PHP version is assumed, which is at the time of writing 5.

Clients MAY also supply the version of the PHP-RPC protocol as the 'version' parameter. Currently this is 0.2.

Clients MAY supply a returnClasses parameter. The value for returnClasses is either 0 or 1 and this can tell the server if the client is aware of typed objects that might be sent from the server.

The server

The server MUST allow requests both GET and POST requests. The server MUST treat any incoming text without encoding as UTF-8.

The server SHOULD allow both named arguments and indexed arguments for methods where this is possible.

If the client sent phpVersion the server MUST convert the returned serialized string so it can be read by the server. If the phpVersion is 4 or 5 the server MUST convert all unicode-strings (type U) to binary strings (type s). If the phpVersion is 4 the server MUST convert all private and protected properties to public properties.

Servers SHOULD also convert all typed objects to either STDClass'es or arrays when the client supplied returnClasses is set to 0, if this is appropriate.

I'm fairly sure I will remove the following paragraph. If PHP gets an HTTP 500 on a file_get_contents, it will throw an error, which removes the possibility to easily grab the error message.

When the method-call was successful the server should send HTTP code 200. When an error occurred the server should send an appropriate HTTP error code. (for example 400 for missing arguments, 500 for unexpected exceptions, 401 if the user should authenticate itself first and 403 is the method was not allowed to be called).

The return data is always in PHP's serialize data format. The Content-Type header should always be 'application/x-php-serialized'

The server will always return an error with the following properties:

result
The actual return data.. (or an array with information about an exception, in which case it should have at least the 'message' property.)
status
HTTP status code for the method call. (200 = success, 500 = internal server error, 400 = bad request, etc etc.) Custom error codes have to start at 600.
version
optional: PHP-RPC protocol version. Currently this is 0.2
server
optional: Name of the server. Can be any string.

Web mentions

Comments

  • Stefan

    Stefan

    I will be following how this works out. On one hand, I like this idea, but on the other, it's yet another way to do remote calls. With the abundance of support for XML-RPC and SOAP outthere, why the hell use this one? I'm missing the added value for using this one. Maybe you can explain this a bit?
  • Evert

    Evert

    Hi Stefan, Glad to hear you're interested! These are the main advantages for using PHP-RPC instead of XML-RPC or SOAP.. 1. It allows you to send object + classnames over the wire without complex class mapping. 2. You only need 2 functioncalls to decode it (file_get_contents and unserialize) 3. Its the fastest way to serialize data for php.
  • Evert

    Evert

    4. People are already using this today (like yahoo) but there's no proper standard for it, so every implementation works different. Using a standard people could start using generic clients.
  • stefan

    stefan

    OK, thanks for your answers. I understand very much reason #1, and I guess #4 also makes sense. #3 ... well, fast is not always good, but I can go with that as well. It's just #2 that sticks me a bit. OK, it will work like this, but any remote requests should, imho, use the CURL options instead of this one. You have much more control, for instance over your timeouts. You don't want deadlocks or any such, you want things to time out gracefully and handle this case in your code. I do like the approach of being able to send object and class data over the line, even though this is also of course a bit questionable: The idea to have seperated entities doing certain functions implies certain abstraction, and in this case it would be the most "clean" if these entities don't know anything about eachother except where they are and what they do. Yet, I can imagine the usefulness in some cases I guess :) I may just check this out a bit more and play with it. I have one situation that asked for a similar approach.
  • Evert

    Evert

    Hi Stefan, That is good feedback.. About the file_get_contents thing, I would also recommend using CURL if you want to make things reliable, however.. file_get_contents is such an easy way to do prototyping and 'meshing up', its just an added benefit.. As for classmapping.. the spec recommends server implementations should implement 'returnClasses'. If this is set to 0, it should return STDClasses instead of typed objects.. I'd also really recommend only sending VO's over the wire (http://en.wikipedia.org/wiki/Value_Objects)
  • luchyx

    luchyx

    HI Evert. I want to know you opinion about this issue related to AS3-amf3. http://www.riaevolution.com/blog/?p=57 Grettings!
  • Stefan

    Stefan

    Yes, I do like prototyping, but if you have your own standard CURL class to do your connections, it's just as easy to prototype with CURL ;) Thanks about the extended info on how the communication should take place.
  • Evert

    Evert

    Definitely .. thats also the beauty of open standards such as HTTP..
  • afshin

    afshin

    hello , i have 2 error with this package , therefor i cant see the data parse from the site help please tnx errors -> Warning: file_get_contents() [function.file-get-contents]: URL file-access is disabled in the server configuration in /home/fstpict/public_html/test/client.php on line 5 Warning: file_get_contents(http://fsriau.ac.ir/service_1/phprpc.php?method=system.testingMethod) [function.file-get-contents]: failed to open stream: no suitable wrapper could be found in /home/fstpict/public_html/test/client.php on line 5
  • Evert

    Evert

    Hi afshin, The problem here is that a security setting is enabled, which disallows direct opening of http:// urls. The setting you need to change is: allow_url_fopen = on This is in your php.ini. If you are on a shared host, you should contact your admin to enable this setting.
  • Anonymous

    super