XML-RPC for PHP version 1.1 December 17, 2002 Edd Dumbill Useful Information Company
edd@usefulinc.com
1999,2000,2001 Edd Dumbill, Useful Information Company All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the "XML-RPC for PHP" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Introduction XML-RPC is a format devised by Userland Software for achieving remote procedure call via XML. XML-RPC has its own web site, www.XmlRpc.com The most common implementations of XML-RPC available at the moment use HTTP as the transport. A list of implementations for other languages such as Perl and Python can be found on the www.xmlrpc.com. This collection of PHP classes provides a framework for writing XML-RPC clients and servers in PHP. The server code works only with versions of PHP3 >= 3.0.12. The code is also known to work with PHP4. If you wish to use SSL to communicate with remote servers, you need the "curl" extension compiled into your PHP installation, this is available in PHP 4.0.2 and greater, although 4.0.6 has a bug preventing SSL working. Acknowledgements Jim Winstead jimw@php.net Peter Kocks peter.kocks@baygate.com Nicolay Mausz mausz@flying-dog.com Ben Margolin ben@wendy.auctionwatch.com Dan Libby dan@libby.com Gaetano Giunta g.giunta@libero.it Idan Sofer i_sofer@yahoo.com Giancarlo Pinerolo ping@alt.it Justin Miller justin@voxel.net Files in the distribution xmlrpc.inc the XML-RPC classes. include() this in your PHP files to use the classes. xmlrpcs.inc the XML-RPC server class. include() this in addition to xmlrpc.inc to get server functionality bettydemo.php demo which retrieves a state name from a number using Dave Winer's XML-RPC server at betty.userland.com server.php a sample server hosting three functions: a US State lookup tool, a struct sorter and an echo function. client.php, agesort.php, echotest.php client code to exercise the various functions in server.php base64test.php, stringtest.php Tests to verify that encoding and decoding of base 64 and entities is functioning correctly. vardemo.php examples of how to construct xmlrpcval types demo1.txt, demo2.txt, demo3.txt XML-RPC responses captured in a file for testing purposes (you can use these to test the xmlrpcmsg->parseResponse() method). httptest.php Testing that HTTP response detection works OK. test.pl, test.py Perl and Python programs to exercise server.php to test that some of the methods work. Make sure you point these at your server, not mine! workspace.testPhpServer.fttb Frontier scripts to exercise the demo server. Thanks to Dave Winer for permission to include these. See Dave's announcement of these. phpunit.php Fred Yankowski's unit test framework for PHP. testsuite.php Start of a unit test suite for this software package. If you do development on this software, please consider submitting tests for this suite. which.php A demo of the interopEchoTests.whichToolkit method. discuss.php, comment.php Software used in the PHP chapter of to provide a comment server and allow the attachment of comments to stories from Meerkat's data store. rsakey.pem A test certificate for the SSL support. It has the passphrase "test." Bugs This is a bare framework. The "nice" bits haven't been put in yet. Specifically, no HTTP response checking is performed, and no type validation or coercion has been put in. PHP being a loosely-typed language, this is going to have to be done explicitly. dateTime.iso8601 is supported opaquely. It can't be done natively as the XML-RPC specification explictly forbids passing of timezone specifiers in ISO8601 format dates. You can, however, use the and functions to do the encoding and decoding for you. If alternative character set encoding is sent in HTTP header than it will be ignored for the moment. We speak only UTF-8... If more than 32k of HTTP headers are encountered (like, why?) then the response parsing code will break. Support Online Support XML-RPC for PHP is offered "as-is" without any warranty or commitment to support. However, informal advice and help is available via the XML-RPC for PHP mailing list and XML-RPC.com. The PHP XML-RPC interest mailing list is run by the author. More details can be found here. For more general XML-RPC questions, there is a Yahoo! Groups XML-RPC mailing list. The XML-RPC.com discussion group is a useful place to get help with using XML-RPC. This group is also gatewayed into the Yahoo! Groups mailing list. The Jellyfish Book Together with Simon St.Laurent and Joe Johnston, I wrote a book on XML-RPC for O'Reilly and Associates on XML-RPC. It features a rather fetching jellyfish on the cover. Complete details of the book are available from O'Reilly's web site. I'm responsible for the chapter on PHP, which includes a worked example of creating a forum server, and hooking it up the O'Reilly's Meerkat service in order to allow commenting on news stories from around the Web. If you've benefitted from the effort I've put into writing this software, then please consider buying the book! Class documentation xmlrpc_client This is the basic class used to represent a client of an XML-RPC server. Creation The constructor has the following syntax: $client=new xmlrpc_client $server_path $server_hostname $server_port Here's an example client set up to query Userland's XML-RPC server at betty.userland.com: $client=new xmlrpc_client("/RPC2", "betty.userland.com", 80); The server_port parameter is optional, and if omitted will default to 80 when using HTTP and 443 when using HTTPS (see the "send" method below.) Methods This class supports the following methods. send This method takes the form: $response=$client->send $xmlrpc_message $timeout $server_method Where $xmlrpc_message is an instance of xmlrpcmsg (see ), and $response is an instance of xmlrpcresp (see ). The $timeout is optional, and will be set to 0 (wait forever) if omitted. This timeout value is passed to fsockopen(). The server_method parameter is optional, and if omitted will default to 'http'. The only other valid value is 'https', which will use an SSL HTTP connection to connect to the remote server. Note that your PHP must have the "curl" extensions compiled in in order to use this feature. Note that when using SSL you should normally set your port number to 443, unless the SSL server you are contacting runs at any other port. PHP 4.0.2 or greater is required for SSL functionality. PHP 4.0.6 has a bug which prevents SSL working. If the value of $response is 0 rather than an xmlrpcresp object, then this signifies an I/O error has occured. You can find out what the I/O error was from the values $client->errno and $client->errstring. In addition to low-level errors, the XML-RPC server you were querying may return an error in the xmlrpcresp object. See for details of how to handle these errors. setCredentials $client->setCredentials $username $password This method sets the username and password for authorizing the client to a server. With the default (HTTP) transport, this information is used for HTTP Basic authorization. setCertificate $client->setCertificate $certificate $passphrase This method sets the optional certificate and passphrase used in SSL-enabled communication with a remote server (when the server_method is set to 'https' in the client's construction). The certificate parameter must be the filename of a PEM formatted certificate. The passphrase parameter must contain the password required to use the certificate. This requires the "curl" extensions to be compiled into your installation of PHP. setSSLVerifyPeer $client->setSSLVerifyPeer $i This method defines whether connections made to XMLRPC backends via HTTPS should verify the remote host's SSL certificate, and cause the connection to fail if the cert verification fails. $i should be a boolean value. setSSLVerifyHost $client->setSSLVerifyHost $i This method defines whether connections made to XMLRPC backends via HTTPS should verify the remote host's SSL certificate's common name (CN). By default, only the existence of a CN is checked. $i should be an integer value; 0 to not check the CN at all, 1 to merely check for its existence, and 2 to check that the CN on the certificate matches the hostname that is being connected to. setDebug $client->setDebug $debugOn $debugOn is either 0 or 1 depending on whether you require the client to print debugging information to the browser. The default is not to output this information. The debugging information includes the raw data returned from the XML-RPC server it was querying, and the PHP value the client attempts to create to represent the value returned by the server. This option can be very useful when debugging servers as it allows you to see exactly what the server returns. xmlrpcmsg This class provides a representation for a request to an XML-RPC server. A client sends an xmlrpcmsg to a server, and receives back an xmlrpcresp (see ). Creation The constructor takes the following form: $msg=new xmlrpcmsg $methodName $parameterArray Where $methodName is a string indicating the name of the method you wish to invoke, and $parameterArray is a simple Array of xmlrpcval objects. Here's an example message to the US state name server: $msg=new xmlrpcmsg("examples.getStateName", array(new xmlrpcval(23, "int"))); This example requests the name of state number 23. For more information on xmlrpcval objects, see . Methods serialize $outString=$msg->serialize Returns the an XML string representing the XML-RPC message. addParam $msg->addParam $xmlrpcVal Adds the xmlrpcval $xmlrpcVal to the parameter list for this method call. getParam $xmlrpcVal=$msg->getParam $n Gets the $nth parameter in the message. Use this method in server implementations. Returns the undef value if no such parameter exists. getNumParams $n=$msg->getNumParams Returns the number of parameters attached to this message. method $methName=$msg->method $msg->method $methName Gets or sets the method contained in the XML-RPC message. parseResponse $response=$msg->parseResponse $xmlString Given an incoming XML-RPC server response contained in the string $xmlString, this method constructs an xmlrpcresp response object and returns it, setting error codes as appropriate (see ). This method processes any HTTP/MIME headers it finds. parseResponseFile $response=$msg->parseResponseFile $fileHandle Given an incoming XML-RPC server response on the file handle $fileHandle, this method reads the data and passes it to parseResponse This method is useful to construct responses from pre-prepared files (see files demo1.txt, demo2.txt, demo3.txt in this distribution). It processes any HTTP headers it finds. xmlrpcresp This class is used to contain responses to XML-RPC requests. A server method handler will construct an xmlrpcresp and pass it as a return value. This same value will be returned by the result of an invocation of the send method of the xmlrpc_client class. Creation $resp=new xmlrpcresp $xmlrpcval $resp=new xmlrpcresp 0 $errcode $errstring The first instance is used when execution has happened without difficulty: $xmlrpcval is an xmlrpcval value with the result of the method execution contained in it. The second type of constructor is used in case of failure. $errcode and $errstring are used to provide indication of what has gone wrong. See for more information on passing error codes. Methods faultCode $fn=$resp->faultCode Returns the integer fault code return from the XML-RPC response $resp. A zero value indicates success, any other value indicates a failure response. faultString $fs=$resp->faultString Returns the human readable explanation of the fault indicated by $resp->faultCode. value $xmlrpcVal=$resp->value Returns an xmlrpcval object containing the return value sent by the server. If the response's faultCode is non-zero then the value returned by this method should not be used (it may not even be an object). serialize $outString=$resp->serialize Returns an XML string representation of the response. xmlrpcval This is where a lot of the hard work gets done. This class enables the creation and encapsulation of values for XML-RPC. Ensure you've read the XML-RPC spec at http://www.xmlrpc.com/stories/storyReader$7 before reading on as it will make things clearer. The xmlrpcval class can store arbitrarily complicated values using the following types: i4 int boolean string double dateTime.iso8601 base64 array struct. You should refer to the spec for more information on what each of these types mean. Notes on types int The type i4 is accepted as a synonym for int. The value parsing code will always convert i4 to int: int is regarded by this implementation as the canonical name for this type. base64 Base 64 encoding is performed transparently to the caller when using this type. Therefore you ought to consider it as a "binary" data type, for use when you want to pass none 7-bit clean data. Decoding is also transparent. boolean The values true and 1 map to true. All other values (including the empty string) are converted to false. string The characters < > " and & are converted to their entity equivalents &lt; &gt; &quot; and &amp; for transport through XML-RPC. The current XML-RPC spec recommends only encoding < & but this implementation goes further, for reasons explained by the XML 1.0 recommendation. TODO: &apos; entity not yet supported Creation The constructor is the normal way to create an xmlrpcval. The constructor can take these forms: $myVal=new xmlrpcval $myVal=new xmlrpcval $stringVal $myVal=new xmlrpcval $scalarVal "int" | "boolean" | "string" | "double" | "dateTime.iso8601" | "base64" $myVal=new xmlrpcval $arrayVal "array" | "struct" The first constructor creates an empty value, which must be altered using the methods addScalar, addArray or addStruct before it can be used. The second constructor creates a simple string value. The third constructor is used to create a scalar value. The second parameter must be a name of an XML-RPC type. Examples: $myInt=new xmlrpcvalue(1267, "int"); $myString=new xmlrpcvalue("Hello, World!", "string"); $myBool=new xmlrpcvalue(1, "boolean"); The fourth constructor form can be used to compose complex XML-RPC values. The first argument is either a simple array in the case of an XML-RPC array or an associative array in the case of a struct. The elements of the array must be xmlrpcval objects themselves. Examples: $myArray=new xmlrpcval(array( new xmlrpcval("Tom"), new xmlrpcval("Dick"), new xmlrpcval("Harry")), "array"); $myStruct=new xmlrpcval(array( "name" => new xmlrpcval("Tom"), "age" => new xmlrpcval(34, "int"), "geek" => new xmlrpcval(1, "boolean")), "struct"); See the file vardemo.php in this distribution for more examples. Methods addScalar $ok=$val->addScalar $stringVal $ok=$val->addScalar $scalarVal "int" | "boolean" | "string" | "double" | "dateTime.iso8601" | "base64" If $val is an empty xmlrpcval this method makes it a scalar value, and sets that value. If $val is already a scalar value, then no more scalars can be added and 0 is returned. If all went OK, 1 is returned. There is a special case if $val is an array: the scalar value passed is appended to the array. addArray $ok=$val->addArray $arrayVal Turns an empty xmlrpcval into an array with contents as specified by $arrayVal. See the fourth constructor form for more information. addStruct $ok=$val->addArray $assocArrayVal Turns an empty xmlrpcval into a struct with contents as specified by $assocArrayVal. See the fourth constructor form for more information. kindOf $kind=$val->kindOf Returns a string containing "struct", "array" or "scalar" describing the base type of the value. If it returns "undef" it means that the value hasn't been initialised. serialize $outString=$val->serialize Returns a string containing the XML-RPC representation of this value. scalarval $scalarVal=$val->scalarval If $val->kindOf()=="scalar", this method returns the actual PHP-language value of the scalar (base 64 decoding is automatically handled here). scalartyp $typeName=$val->scalartyp If $val->kindOf()=="scalar", this method returns a string denoting the type of the scalar. As mentioned before, i4 is always coerced to int. arraymem $xmlrpcVal=$val->arraymem $n Returns the $nth element in the array represented by the value $val. The value returned is an xmlrpcval object. arraysize $len=$val->arraysize If $val is an array, returns the number of elements in that array. structmem $xmlrpcVal=$val->structmem $memberName Returns the element called $memberName from the struct represented by the value $val. The value returned is an xmlrpcval object. structeach list($key,$value)=$val->structeach Returns the next (key,value) pair from the struct, when $val is a struct. See also . structreset $val->structreset Resets the internal pointer for structeach() to the beginning of the struct, where $val is a struct. xmlrpc_server The current implementation of this class has been kept as simple as possible. The constructor for the server basically does all the work. Here's a minimal example: function foo ($params) { ... } $s=new xmlrpc_server( array("examples.myFunc" => array("function" => "foo"))); This performs everything you need to do with a server. The single argument is an associative array from method names to function names. The request is parsed and despatched to the relevant function, which is reponsible for returning a xmlrpcresp object, which gets serialized back to the caller. See server.php in this distribution for examples of how to do this. Here is a more detailed look at what the handler function foo may do: function foo ($params) { global $xmlrpcerruser; // import user errcode value // $params is an Array of xmlrpcval objects if ($err) { // this is an error condition return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 "There's a problem, Captain"); } else { // this is a successful value being returned return new xmlrpcresp(new xmlrpcval("All's fine!", "string")); } } The dispatch map The first argument to the xmlrpc_server constructor is an array, called the dispatch map. In this array is the information the server needs to service the XML-RPC methods you define. The dispatch map takes the form of an associative array of associative arrays: the outer array has one entry for each method, the key being the method name. The corresponding value is another associative array, which can have the following members: function - this entry is mandatory. It must be a name of a function in the global scope which services the XML-RPC method. signature - this entry is an array containg the possible signatures (see ) for the method. If this entry is present then the server will check that the correct number and type of parameters have been sent for this method before dispatching it. docstring - this entry is a string containing documentation for the method. The documentation may contain HTML markup. Look at the server.php example in the distribution to see what a dispatch map looks like. Method signatures A signature is a description of a method's return type and its parameter types. A method may have more than one signature. Within a server's dispatch map, each method has an array of possible signatures. Each signature is an array of types. The first entry is the return type. For instance, the method string examples.getStateName(int) has the signature array($xmlrpcString, $xmlrpcInt) and, assuming that it the only possible signature for the method, might be used like this in server creation: $findstate_sig=array(array($xmlrpcString, $xmlrpcInt)); $findstate_doc='When passed an integer between 1 and 51 returns the name of a US state, where the integer is the index of that state name in an alphabetic order.'; $s=new xmlrpc_server( array( "examples.getStateName" => array("function" => "findstate", "signature" => $findstate_sig, "docstring" => $findstate_doc))); For convenience the strings representing the XML-RPC types have been encoded as global variables: $xmlrpcI4="i4"; $xmlrpcInt="int"; $xmlrpcBoolean="boolean"; $xmlrpcDouble="double"; $xmlrpcString="string"; $xmlrpcDateTime="dateTime.iso8601"; $xmlrpcBase64="base64"; $xmlrpcArray="array"; $xmlrpcStruct="struct"; Delaying the server response You may want to construct the server, but for some reason not fulfill the request immediately (security verification, for instance). If you pass the constructor a second argument of 0 this will have the desired effect. You can then use the service() method of the server class to service the request. For example: $s=new xmlrpc_server($myDispMap, 0); // ... some code that does other stuff here $s->service(); Fault reporting Fault codes for your servers should start at the value indicated by the global $xmlrpcerruser + 1. Standard errors returned by the server include: 1 Unknown method Returned if the server was asked to dispatch a method it didn't know about 2 Invalid return payload This error is actually generated by the client, not server, code, but signifies that a server returned something it couldn't understand. 3 Incorrect parameters This error is generated when the server has signature(s) defined for a method, and the parameters passed by the client do not match any of signatures. 4 Can't introspect: method unknown This error is generated by the builtin system.* methods when any kind of introspection is attempted on a method undefined by the server. 5 Didn't receive 200 OK from remote server This error is generated by the client when a remote server doesn't return HTTP/1.1 200 OK in response to a request. A more detailed error report is added onto the end of the phrase above. 100- XML parse errors Returns 100 plus the XML parser error code for the fault that occurred. The faultString returned explains where the parse error was in the incoming XML stream. Helper functions XML-RPC for PHP contains some helper functions which you can use to make processing of XML-RPC requests easier. Date functions The XML-RPC specification has this to say on dates:
Don't assume a timezone. It should be specified by the server in its documentation what assumptions it makes about timezones.
Unfortunately, this means that date processing isn't straightforward. Although XML-RPC uses ISO 8601 format dates, it doesn't use the timezone specifier. We strongly recommend that in every case where you pass dates in XML-RPC calls, you use UTC (GMT) as your timezone. Most computer languages include routines for handling GMT times natively, and you won't have to translate between timezones. For more information about dates, see ISO 8601: The Right Format for Dates, which has a handy link to a PDF of the ISO 8601 specification. Note that XML-RPC uses exactly one of the available representations: CCYYMMDDTHH:MM:SS. iso8601_encode $isoString=iso8601_encode $time_t$utc=0 Returns an ISO 8601 formatted date generated from the UNIX timestamp $time_t, as returned by the PHP function time(). The argument $utc can be omitted, in which case it defaults to 0. If it is set to 1, then the function corrects the time passed in for UTC. Example: if you're in the GMT-6:00 timezone and set $utc, you will receive a date representation six hours ahead of your local time. The included demo program vardemo.php includes a demonstration of this function. iso8601_decode $time_t=iso8601_decode $isoString$utc=0 Returns a UNIX timestamp from an ISO 8601 encoded time and date string passed in. If $utc is 1 then $isoString is assumed to be in the UTC timezone, and thus the $time_t result is also UTC: otherwise, the timezone is assumed to be your local timezone and you receive a local timestamp.
Easy use with PHP arrays Dan Libby was kind enough to contribute two helper functions that make it easier to translate to and from PHP arrays. This makes it easier to deal with complex structures. At the moment support is limited to int, double, string, array and struct datatypes; note also that all PHP arrays are encoded as structs due to PHP not being able to tell the difference between a hash and a linear array. These functions reside in xmlrpc.inc. xmlrpc_decode $arr=xmlrpc_decode $xmlrpc_val Returns a PHP array stuffed with the values found in the xmlrpcval $xmlrpc_val, translated into native PHP types. xmlrpc_encode $xmlrpc_val=xmlrpc_encode $phpval Returns an xmlrpcval populated with the PHP values in $phpval. Works recursively on arrays and structs. Note that there's no support for non-base types like base-64 values or date-times. Debugging aids xmlrpc_debugmsg xmlrpc_debugmsg $debugstring Sends the contents of $debugstring in XML comments in the server return payload. If a PHP client has debugging turned on, the user will be able to see server debug information. Use this function in your methods so you can pass back diagnostic information. It is only available from xmlrpcs.inc.
Reserved methods In order to extend the functionality offered by XML-RPC servers without impacting on the protocol, I've included experimental support in this release for reserved methods. All methods starting with system. are considered reserved by the server. PHP for XML-RPC itself provides three special methods, detailed in this chapter. system.listMethods This method may be used to enumerate the methods implemented by the XML-RPC server. The system.listMethods method requires no parameters. It returns an array of strings, each of which is the name of a method implemented by the server. system.methodSignature This method takes one parameter, the name of a method implemented by the XML-RPC server. It returns an array of possible signatures for this method. A signature is an array of types. The first of these types is the return type of the method, the rest are parameters. Multiple signatures (ie. overloading) are permitted: this is the reason that an array of signatures are returned by this method. Signatures themselves are restricted to the top level parameters expected by a method. For instance if a method expects one array of structs as a parameter, and it returns a string, its signature is simply "string, array". If it expects three integers, its signature is "string, int, int, int". If no signature is defined for the method, a none-array value is returned. Therefore this is the way to test for a non-signature, if $resp below is the response object from a method call to system.methodSignature: $v=$resp->value(); if ($v->kindOf()!="array") { // then the method did not have a signature defined } See the introspect.php demo included in this distribution for an example of using this method. system.methodHelp This method takes one parameter, the name of a method implemented by the XML-RPC server. It returns a documentation string describing the use of that method. If no such string is available, an empty string is returned. The documentation string may contain HTML markup. Examples The best examples are to be found in the sample files included with the distribution. Some are included here. XML-RPC client: state name query Code to get the corresponding state name from a number (1-50) from Dave Winer's server $f=new xmlrpcmsg('examples.getStateName', array(new xmlrpcval($HTTP_POST_VARS["stateno"], "int"))); $c=new xmlrpc_client("/RPC2", "betty.userland.com", 80); $r=$c->send($f); $v=$r->value(); if (!$r->faultCode()) { print "State number ". $HTTP_POST_VARS["stateno"] . " is " . $v->scalarval() . "<BR>"; print "<HR>I got this value back<BR><PRE>" . htmlentities($r->serialize()). "</PRE><HR>\n"; } else { print "Fault: "; print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'<BR>"; }