How to create an XML-RPC server with CakePHP

This article is also available in the following languages:
By filippo.toso
This tutorial describes a method to create an XML-RPC server that goes a bit against Cake’s logic and conventions.
I like the way the framework “distributes” the logic of the web application between different controllers (i.e. Posts, Comments, and so on). But when I want to develop an XML-RPC server, I prefer to place all the code into a single controller so I can maintain consistency and have a single place to look for errors. The other advantage of this solution is that you don’t need to add logic in the single controllers to handle XML-RPC calls.

For the implementation of the XML-RPC protocol I decided to go with the Inutio XML-RPC Library (http://scripts.incutio.com/xmlrpc/). This library is completely object oriented, easy to use and works with both PHP 4 and PHP 5.

You can download a little modified version from the following URL:

http://www.creativepark.it/downloads/xmlrpc.zip

The 2 changes I made are described in the header of the file.

Here are the steps to build your XML-RPC server with CakePHP:

  1. Download the xmlrpc.zip archive and extract its content into your app/vendors folder.
  2. Create the app/controllers/xml_rpc_controller.php file as follows:

File: app/controllers/xml_rpc_controller.php

Controller Class:

<?php 

// Import the app/vendor/xmlrpc.php library
App::import('Vendor''xmlrpc');

class 
XmlRpcController extends AppController {

    
// This demo doesn't need models
    
var $uses = array();
    
    
// The XML-RPC server object
    
var $server null;

    function 
index() {

        
// Disable debug information
        // Required to generate valid XML output
        
Configure::write('debug'0); 

        
// Avoids render() call
        
$this->autoRender false;

        
// XML-RPC callbacks settings
        // Use this parameter to map XML-RPC methods to your protected or private controller methods
        
$callbacks = array();
        
$callbacks['demo.sayHello'] = array(&$this'_sayHello');

        
// Handle XML-RPC request
        
$this->server = new IXR_Server($callbacks);
    }
    
    
// Protected Method
    
function _sayHello($name$country) {
        return 
"Hi {$name}! You come from {$country}!";
    }
    
}

?>

That’s all. You have created your first, simple XML-RPC server.
To test its behavior, you can use the following client script:

<?php

// Include the XML-RPC class
require_once(dirname(__FILE__) . '/xmlrpc.php');

// Set here your XML-RPF control url
$url 'http://cake.local/xml_rpc';

// Create the client object
$client = new IXR_Client($url);

// Set the debug property to true during development to see both request and response data
// $client->debug = true;

// Call the demo.sayHello method passing 'Filippo' and 'Italy' as first and second parameter
if (!$client->query('demo.sayHello''Filippo''Italy')) {
    die(
'Something went wrong - '.$client->getErrorCode().' : '.$client->getErrorMessage());
}

// Display the result
echo '<pre>';
print_r($client->getResponse());
echo 
'</pre>';

?>

Of course this isn’t very useful, but it’s a starting point.

Here is the another controller that exposes some functions of the Blog sample application (requires the Post table and model).

File: app/controllers/xml_rpc_controller.php

Controller Class:

<?php 

// Import the app/vendor/xmlrpc.php library
App::import('Vendor''xmlrpc');

class 
XmlRpcController extends AppController {

    
// This time we need the Post model
    
var $uses = array('Post');
    
    
// The XML-RPC server object
    
var $server null;
    
    
// XML-RPC access point
    
function index() {

        
// Disable debug information
        // Required to generate valid XML output
        
Configure::write('debug'0); 

        
// Avoids render() call
        
$this->autoRender false;

        
// XML-RPC callbacks settings
        // Use this parameter to map XML-RPC methods to your protected or private controller methods
        
$callbacks = array();
        
$callbacks['post.view']   = array(&$this'_postView');
        
$callbacks['post.add']    = array(&$this'_postAdd');
        
$callbacks['post.edit']   = array(&$this'_postEdit');
        
$callbacks['post.delete'] = array(&$this'_postDelete');

        
// Handle XML-RPC request
        
$this->server = new IXR_Server($callbacks);
    }

    function 
_postView($id null) {
        if (!
$id) {
            return new 
IXR_Error(2'Invalid Post');
        }
        return 
$this->Post->read(null$id);
    }

    function 
_postAdd($data = array()) {
        if (!empty(
$data)) {
            
$this->Post->create();
            if (
$this->Post->save($data)) {
                return (int)
$this->Post->id;
            } else {
                return new 
IXR_Error(1'Post not saved');
            }
        }
        return 
false;
    }
    
    function 
_postEdit($data = array()) {
        if (empty(
$data)) {
            return new 
IXR_Error(2'Invalid Post');
        } elseif (!
$this->Post->save($data)) {
            return new 
IXR_Error(1'Post not saved');
        }
        return 
true;
    }    
    
    function 
_postDelete($id null) {
        if (!
$id) {
            return new 
IXR_Error(2'Invalid Post');
        } elseif (!
$this->Post->del($id)) {
            return new 
IXR_Error(3'Post not deleted');
        }        
        return 
true;
    }    
    
}

?>

And this is a simple client that uses all the methods exposed by the controller:

<?php

// Include the XML-RPC class
require_once(dirname(__FILE__) . '/xmlrpc.php');

// Set here your XML-RPF control url
$url 'http://cake.local/xml_rpc';

// Create the client object
$client = new IXR_Client($url);

// Set the debug property to true during development to see both request and response data
// $client->debug = true;

echo('<pre>');

// ####################### Let's create a new Post ####################### //

// Set Post data
$post = array ('Post' => 
  array (
    
'title' => 'My First XML-RPC Post',
    
'body' => 'This is the post body.',
  ),
);

// Call post.add
if (!$client->query('post.add'$post)) {
    die(
'Something went wrong - ' $client->getErrorCode() . ' : ' $client->getErrorMessage());
}

// Get new Post ID
$post_id = (int)$client->getResponse();

echo(
"New Post ID: {$post_id}\r\n");

// ####################### Let's display the new Post ####################### //

// Call post.view
if (!$client->query('post.view'$post_id)) {
    die(
'Something went wrong - ' $client->getErrorCode() . ' : ' $client->getErrorMessage());
}

// Get the Post data
$data $client->getResponse();

// Display the Post data
print('Post data: ');
print_r($data);

// ####################### Let's edit the Post ####################### //

// Change Post data
$data['Post']['title']    = 'My First Edited XML-RPC Post';
$data['Post']['modified'] =  date('Y-m-d H:i:s');

// Call post.edit
if (!$client->query('post.edit'$data)) {
    die(
'Something went wrong - ' $client->getErrorCode() . ' : ' $client->getErrorMessage());
}

echo(
"Post Modified\r\n");

// ####################### Let's delete the Post ####################### //

// Call post.delete
if (!$client->query('post.delete'$data['Post']['id'])) {
    die(
'Something went wrong - ' $client->getErrorCode() . ' : ' $client->getErrorMessage());
}

echo(
"Post Deleted\r\n");

echo(
'</pre>');

?>

As you can seen, there’s a bit of redundance of code between the XmlRpcController and the PostsController of the Blog sample application.

Creating an XML-RPC server with CakePHP is straight forward. You can easily build an API for external developers using a common protocol and the power of CakePHP.

Comments

  • Posted 11/19/09 09:09:36 PM
    I'm wondering if this still works, and if so can anybody help me figure out why it won't for me.
    I have followed the instructions and all i get it
    POST /xml_rpc HTTP/1.0
    Host: localhost
    Content-Type: text/xml
    User-Agent: The Incutio XML-RPC PHP Library
    Content-length: 217

    <?xml version="1.0"?>
    <methodCall>
    <methodName>demo.sayHello</methodName>
    <params>
    <param><value><string>Filippo</string></value></param>
    <param><value><string>Italy</string></value></param>
    </params></methodCall>
    Something went wrong - -32300 : transport error - HTTP status code was not 200

    I have tried taking the same server bits and putting them in a file without any cake bits and it works fine, so it leads me to believe cake is doing something that this doesn't like, however I can't find it.
  • Posted 09/08/09 02:43:18 AM
    Very well done! Good tutorial.

    Just a question: how do you authenticate on the WebService?
    Since my application has auth enabled (ACL), I've added a method at the beginning of the XML_RPC_Controller that says

        // Aggiunto per poter fare accedere gli utenti non autenticati
        function beforeFilter() 
        {
             parent::beforeFilter(); 
             $this->Auth->allowedActions = array('index');
        }

    Anyway I'd like to have some kind of http authentication for my methods. Any idea how to get that?

    Thanks,
    Massimo
  • Posted 11/29/08 07:26:13 AM
    I think that more organic way of achieving XML-RPC in cake would be a dispatching of xml-rpc request into ordinary controllers methods

    don't you think?

    (however I admit that after one day looking into the core code I've decided that I will better go the REST way with ATOM pub protocol)
    • Posted 11/29/08 08:22:29 AM
      I think that more organic way of achieving XML-RPC in cake would be a dispatching of xml-rpc request into ordinary controllers methods
      When I develop an XML-RPC server, I prefer to place all the code into a single controller so I can maintain consistency and have a single place to look for errors.

      I don't like very much to have a lot of "if" in my controllers actions that checks for this or that kind of requests. Most of the time the controller actions are far more complex than the Blog example and trying to integrate the XML-RPC request code will only make it more messy. Moving the XML-RPC code in a specific controller, make the code cleaner and target oriented.

      The use of a signle XmlRpcController makes also possible to have a single XML-RPC entry point for any XML-RPC request for the whole web application. This means that third party developers don't need to use a different URLs for different requests (i.e. one URL for Posts, one URL for Comments, and so on). This behavior is much more consistent with the target of XML-RPC, even if it goes a bit against Cake's logic and conventions.

Comments are closed for articles over a year old