Keith Devens .com |
Friday, July 4, 2008 | ![]() |
| You can have premature generalization as well as premature optimization. – Bjarne Stroustrup | ||
|
| ← feedParser | Birthday present → |

Lachlan Donald wrote:
Keith (http://www.keithdevens.com/) wrote:
Hmm, interesting. So it should handle redirects. Good point. Does the XML-RPC spec have anything to say about this? If it's open to the implementation, then I'll implement it, along with support for other error codes like 404. Thanks for the input.
Lachlan Donald wrote:
ftp://ftp.rfc-editor.org/in-notes/rfc2616.txt
Thats the relevant RFC. Even if you don't handle redirects, it would be nice to return false for success if there is anything except for a Successful 2xx status code.
As for the XMLRPC spec, all thats mentioned is this:
"Unless there's a lower-level error, always return 200 OK."
I would take this as, if you get anything other than a 200 OK its an error.
Robert Harder wrote:
Just a thanks for making simple function calls instead of too-complex OO objects.
Lachlan Donald wrote:
Basic HTTP Authentication
What I have done is basically add a user name and password to the XMLRPC_request method and then add in a Authentication header.
I will send u a patch if you are interested.
Mark Weston wrote:
Tabular/Dataset data.
I'm interested in using PHP as a front-end Web Services written on the .Net platform. Microsoft turn a .Net Dataset object to a complex XML (DiffGram) structure for transmission over the internet.
Is it feasible to create a PHP object that takes this XML and exposes methods & properties that allow me to navigate through the Dataset, get the original and current value for a particular column etc...
Ideally this object would have the same methods and properties as the Microsoft .Net Dataset object.
Cheers!
Keith (http://www.keithdevens.com/) wrote:
Hi Mark, I'm not that familiar with the dataset stuff you're talking about, but while it seems like there's no reason it couldn't be written in PHP, it sounds like it's outside the scope of my XML-RPC library.
Patrick Boucher wrote:
How about utf8 support? I've got a hard time sending special characters from one end to the other of my distributed system because of encoding questions.
Keith (http://www.keithdevens.com/) wrote:
utf8 support: PHP doesn't support Unicode. Furthermore, I'm just using PHP's built in XML parser, so if PHP did support Unicode I wouldn't have to make any changes to my library to have it work.
However, what are the "special characters" you're trying to send back and forth? Typically, when I have any +127 characters in text (like special single and double quotes, em and en dashes, etc.), PHP has no trouble dealing with it. I've never tried working with Korean, Japanese, Chinese, Thai, Arabic, etc. etc. though. 
Eliot (http://www.slower.net/) wrote:
When I include your file in my ISP's fairly standard PHP 4.2.2, I get a lot of "Call-time pass-by-reference has been deprecated" warnings. It's easy to suppress these with @, but perhaps you could strip out the pass by reference stuff.
Thanks for the software!
Keith (http://www.keithdevens.com/) wrote:
Eliot - known problem. If I could only get myself to release version 3.0 none of that would happen
At the very least, maybe I should put an "error_reporting(E_ALL & ~E_NOTICE)" at the beginning of the script (and restore it at the end) to patch it up for now.
Chris Adams (http://improbable.org/chris/) wrote:
http://improbable.org/chris/xmlrpc.patch has the patch output of the changes I made to work around those call-time pass by reference warnings. The only tricky thing is accounting for our inability to change the prototype for array_unshift - I work around that by pushing a temporary value first and replacing it.
Hendrik wrote:
Kudos to Chris, your patch seems to do the trick! Thanks very much!
lordwo (http://www.bblog.com) wrote:
I have a lot of concern about the compatibility of the XML-RPC library and PHP5.
Warning: Call-time pass-by-reference has been deprecated - argument passed by value; If you would like to pass it by reference, modify the declaration of XML_unserialize(). If you would like to enable call-time pass-by-reference, you can set allow_call_time_pass_reference to true in your INI file. However, future versions may not support this any longer.
That's popping everywhere. Since most of the users have no right to change php.ini directives on their host, it should be corrected for the future.
Thanks for you concern.
Keith (http://keithdevens.com/) wrote:
I know about it but there's nothing I can do under PHP 4. Note, however, that that feature can be changed in an .htaccess file, so pretty much anyone should be able to change it. I'm also almost completely sure the feature hasn't been removed in PHP 5, so there's nothing to worry about. Once PHP 5 comes out I'll be able to fix the code so it doesn't have that problem.
I've ranted about this particular deprecation before. I'm pretty furious about it because the PHP developers deprecated a feature -- and not only did they deprecate it and give NOTICE-level errors... they give WARNINGs -- while there are so many places in PHP that it impacts on, and they provided NO alternative to it. PHP, in version 4, is fundamentally broken in this way.
Rest assured, once PHP 5 comes out and fixes this problem by finally having a sensible object model I'll be able to fix the code. I plan on getting 3.0 done once that happens.
Pallieter Koopmans (http://www.atQuest.nl/) wrote:
Additional sample code (implementations for RSS 0.92, Atom, Echo, etc) and API's (TypePad, Blogger, Nucleus, MovableType, etc) would make this packedge complete. Success with the next version!
Keith (http://keithdevens.com/) wrote:
implementations for RSS 0.92, Atom, Echo, etc
What do those have to do with XML-RPC?
and API's
Bindings for specific APIs don't belong in a library like this. Furthermore, they're specific to a particular CMS.
Robert Castley wrote:
I would like to see support for multiple/unlimited method requests in a single xmlrpc session.
Keith (http://keithdevens.com/) wrote:
In a single session? What do you mean? In one HTTP connection? I don't think that's in the spec.
ash wrote:
Does it make sense to also support HTTP authentication?
Sure it does.
Ryan Johnson wrote:
Just like you're planning on including convience functions, I'd like to see built in (as in, packaged with the library), object wrappers for methods and requests. Unlike most OO freaks, I've actually thought about why this is a good idea, should I send my code along? I've found wrapping the various methods in objects (that are extended from a base XMLRPC_method) a much more reliable and extensible way to handle things.
Keith (http://keithdevens.com/) wrote:
object wrappers for methods and requests
You mean to automatically expose all the methods of an object as an XML-RPC service?
If not that, I'm not quite sure what you're talking about, but I'd be happy to listen to your idea and read any code you send along. I'll e-mail you a copy of this comment and you can reply by e-mail. Or -- and this would be easier for me since it would ensure that your idea is kept along with this entry -- you could elaborate right here and paste whatever code you'd like to. Your choice.
Ryan Johnson wrote:
What I developed is a bit different than the way you interpeted my vauge statement above, so I'll describe it a bit better =) Basically, I wanted a simple way for people to be able to add methods to an RPC server, without having tons of variables floating around. On top of that, the methods themselves don't even call any of the XMLRPC functions, so if you upgrade or change systems, it's a trivial change to your code in one place. So here goes:
<?php
/* Assume these are somewhere in the XMLRPC library file, and are loaded with the rest of it. There are some constants that I've defined (all in caps), but otherwise it should fit in anywhere. */
class XMLRPC_request{
var $request;
var $method;
var $params;
function XMLRPC_Request(){
$this->request = XMLRPC_parse(file_get_contents("php://input"));
$this->method = @XMLRPC_getMethodName($this->request);
$this->params = @XMLRPC_getParams($this->request);
}
function invokeMethod(){
@require_once(PATH_TO_EXTENSIONS.'xmlrpc.'.$this->method.'.php');
$classname = 'XMLRPC_'.str_replace('.','_',$this->method);
$function_name = str_replace('.','_',$this->method);
if(class_exists($classname)){
$response = new $classname();
$response->setup($this->params,$this->request,$function_name);
$response->run();
if(!$response->error&&$response->response)
XMLRPC_response(XMLRPC_prepare($response->response),XMLPRPC_AGENT);
elseif($response->error&&is_null($response->response))
XMLRPC_error(2,'Method returned a null response'.':'.$method,XMLPRPC_AGENT);
else
XMLRPC_error(2,$repsonse->error.':'.$method,XMLPRPC_AGENT);
} else {
XMLRPC_error(2,L_XML_UNDEFINED_METHOD.':'.$method,XMLPRPC_AGENT);
}
}
}
class XMLRPC_method {
var $params;
var $error;
var $request;
var $classname;
var $response;
function XMLRPC_method(){}
function setup(&$params,&$request,$function_name){
$this->params =& $params;
$this->request =& $request;
$this->error = FALSE;
$this->function_name = $function_name;
$this->response = NULL;
}
function run(){
$this->{$this->function_name}();
}
}
class XMLRPC_call {
var $success;
var $response;
function XMLRPC_call($url,$dir,$method,$params,$debug=FALSE){
if($debug)
define('XMLRPC_DEBUG', 1);
if(!is_array($params)){
$this->response = '$params must be passed as an array.';
$this->succes = FALSE;
return;
}
$req = array();
foreach($params as $p)
$req[] = XMLRPC_prepare($p);
list($success,$response) = XMLRPC_request($url,$dir,$method,$req);
$this->success = $success;
$this->response = $response;
if($debug)
XMLRPC_debug_print();
}
}
?>
Now, in a file where the call will point to:
<?php
if(isset($_GET['rpc'])) {
require_once(PATH_TO_CORE.'driver.xmlrpc.php');
$request = new XMLRPC_Request();
$params = $request->params;
$request->invokeMethod();
}
?>
So here is a sample method. It's located in lib/extensions/kiwi.numToString.php (PATH_TO_EXTENSIONS was defined as 'lib/extensions/' in my script.
Notice that the method is called as kiwi.NumToString, but that the class and function are named with underscores because a period would be an invalid name. Note that because of the base class above, the params and response and error variable are already set. $this->error is set to false with begin with, and you just have to set it to anything but false to trigger an error. The key to the design of this, is that the user (implimentor) doesn't have to worry about anything XMLRPC related, taking your abstraction of the datatypes between PHP and XMLRPC slightly further. If this doesn't make sense by reading it, I can further document it.
<?php
class XMLRPC_kiwi_NumToString extends XMLRPC_method{
function kiwi_NumToString(){
switch($this->params[0]){
case '1':
$this->response = 'One';
break;
case '2':
$this->response = 'Two';
break;
case '3':
$this->response = 'Three';
break;
default:
$this->error = 'Please choose a number, 1-3. You chose: '.$this->params[0];
break;
}
}
}
?>
And now here's code that actually works (as in, you can query the server right now using the code in this comment).
<?php
require_once('lib/core/driver.xmlrpc.php');
$params[] = 2;
$request = new XMLRPC_call('kiwi3.com','/kiwi/?rpc','kiwi.numToString',$params,FALSE);
echo $request->response;
/* Response is "Two" */
?>
A lot of code to get the string Two, but hey, it works really well with complex applications too =)
If any of that doesn't make sense I can explain my rationale further. I think with PHP5 being so OO, something like this should come as an option. Even if this looks like unnessecary and useless OO, it really does compartmentalize things much more nicely, and make the implimentation code MUCH more consistent, at least for large applications. It's complete overkill for anything simple, I would agree to anyone who is critizing at this moment. - Ryan
Ryan Johnson wrote:
Something I'm not sure I made clear (that I just remembered), is that all you need to do to make a new method available on your RPC server is just drop a file called
your.MethodName.php
with the appropriate class name ( XMLRPC_your_MethodName in this case), in the folder which the script is looking in (in this case PATH_TO_EXTENSIONS), and that method automagically becomes available.
Ryan Johnson wrote:
One more thing I also forgot (total scatterbrain, sorry), was that the reason the call is split up in the server script above (the one that calls invokeMethod() ), is because usually I run $params through my authentication function to see wether to continue or not. I yanked that out for simplicity of the example.
Hartmut Seichter (http://www.technotecture.com) wrote:
Thanks for the script, it helped me to implement a lot of features for my little project. Take-and-give, I've cleaned up the call-by-reference things:
Best Regards,
Hartmut
Gwyneth Llewelyn (http://gwynethllewelyn.net/) wrote:
Hello,
I'm no professional programmer, so I've used your library for some two years now for the very very simple cases where I need to do some XML-RPC requests, since it's a tiny library, non-OO, uses native PHP data types, and well, it's universally installable — no tricky bits or messing with php.ini. It's also incredibly stable, under all versions of PHP I tried.
The only thing that seems to be missing is the ability to communicate using HTTPS
Several XML servers now require the ability to do either TLS encoding or sometimes even more sophisticated mechanisms... which are handled easily by libCURL (natively), fortunately, allowing me to play around with them.
I've thus added the following function (XMLRPC_request_CURL) to use CURL to do the request:
<?php
// Added by Gwyneth Llewelyn 20060730
// Uses libCURL to handle special cases
function XMLRPC_request_CURL($site, $location, $methodName, $params = NULL, $user_agent = NULL){
$data["methodCall"]["methodName"] = $methodName;
$param_count = count($params);
if(!$param_count){
$data["methodCall"]["params"] = NULL;
}else{
for($n = 0; $n<$param_count; $n++){
$data["methodCall"]["params"]["param"][$n]["value"] = $params[$n];
}
}
$data = XML_serialize($data);
if(defined('XMLRPC_DEBUG') and XMLRPC_DEBUG){
XMLRPC_debug('XMLRPC_request', "<p>Received the following parameter list to send:</p>" . XMLRPC_show($params, 'print_r', true));
}
$headers = array("Content-Type: text/xml", "Content-Length: " . strlen($data));
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $site . $location);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_POSTFIELDSIZE, strlen($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 9);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
if (curl_errno($ch))
XMLRPC_debug('XMLRPC_request', '<p>Connection failed: ' . curl_error($ch) . '</p>');
else
curl_close($ch);
if(defined('XMLRPC_DEBUG') and XMLRPC_DEBUG){
XMLRPC_debug('XMLRPC_request', "<p>Sent the following request:</p>\n\n" . XMLRPC_show($headers . $data, 'print_r', true));
}
$data = XML_unserialize($response);
if(defined('XMLRPC_DEBUG') and XMLRPC_DEBUG){
XMLRPC_debug('XMLRPC_request', "<p>Received the following response:</p>\n\n" . XMLRPC_show($response, 'print_r', true) . "<p>Which was serialized into the following data:</p>\n\n" . XMLRPC_show($data, 'print_r', true));
}
if(isset($data['methodResponse']['fault'])){
$return = array(false, XMLRPC_adjustValue(&$data['methodResponse']['fault']['value']));
if(defined('XMLRPC_DEBUG') and XMLRPC_DEBUG){
XMLRPC_debug('XMLRPC_request', "<p>Returning:</p>\n\n" . XMLRPC_show($return, 'var_dump', true));
}
return $return;
}else{
$return = array(true, XMLRPC_adjustValue(&$data['methodResponse']['params']['param']['value']));
if(defined('XMLRPC_DEBUG') and XMLRPC_DEBUG){
XMLRPC_debug('XMLRPC_request', "<p>Returning:</p>\n\n" . XMLRPC_show($return, 'var_dump', true));
}
return $return;
}
}
?>
It works for me
Just use $site with things like https://mysite.com or tls://mysite.com or even ftp://mysite.com 
Of course, I cannot tell how much of a performance hit this is, compared to other libraries doing XML-RPC (PEAR, XML-RPC EPI, etc.), although CURL is at least implemented in C... The beauty of this is having everything working flawlessly, and since CURL supports so many different protocols, at least one can use your library with all of them 
Thanks for your wonderful library!
- Gwyn
Feel free to post a comment below. Please see my comment policy.
Formatting Rules (No HTML):
Generated in about 0.161s.
(Used 8 db queries)

Support for HTTP success codes. The current version of the library seems to not care if the xmlrpc server returned "302 Moved Temporarily" or "200 Success", which is irritating because it goes ahead and tries to serialize the web server error message into a response.