XMLRPC example for Code Igniter

As an exercise, I made an example of a full implementation of a XML-RPC server with Code Igniter 2.

I made a fork of the current CI2 source code on bitbucket and then I wrote two controllers: xmlrpc_server.php and xmlrpc_client.php. In the former might be considered interesting the method __getMyMethods().

xmlrpc_server.php

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
// Created on Aug 11, 2011 by Damiano Venturin http://www.squadrainformatica.com
//
// That's a fully functional XMLRPC server implementation
// It's a CodeIgniter 2.0 controller handling the requests performed by a XMLRPC client (look xmlrpc_client.php)
// I think it's interesting the use of reflection in listing the available XMLRPC methods (__getMyMethods)

class Xmlrpc_server extends CI_Controller {

    public $builtInMethods;

    function __construct()
    {
        parent::__construct();

        $this->builtInMethods = array();

        $this->load->library('xmlrpc');
        $this->load->library('xmlrpcs');

        // A method could be published manually using an array like this one
        // EX.:    $config['functions']['helloWorld'] = array('function' => 'Xmlrpc_server.helloWorld',
        //                                                docstring' => 'Returns an helloWorld array'        
        //                                                );
        // but it's much easier to use the automatic method __getMyMethods

        //Automatically expose xmlrpc methods by providing automatically the configuration
        $this->__getMyMethods();
        $config = array();
        $config['functions'] = $this->builtInMethods['functions'];
        $this->xmlrpcs->initialize($config);
        $this->xmlrpcs->set_debug($this->config->item('xmlrpc_debug'));        

        //Start the server
        $this->xmlrpcs->serve();    
    }    

    /**
     *
     * Analizes self methods using reflection
     * @return Boolean
     */
	private function __getMyMethods()
	{
		$reflection = new ReflectionClass($this);

		//get all methods
		$methods = $reflection->getMethods();
		$this->builtInMethods = array();

		//get properties for each method
		if(!empty($methods))
		{
			foreach ($methods as $method) {
				if(!empty($method->name))
				{
					$methodProp = new ReflectionMethod($this, $method->name);

					//saves all methods names found
					$this->builtInMethods['all'][] = $method->name;

					//saves all private methods names found
					if($methodProp->isPrivate())
					{
						$this->builtInMethods['private'][] = $method->name;
					}

					//saves all private methods names found
					if($methodProp->isPublic())
					{
						$this->builtInMethods['public'][] = $method->name;

						// gets info about the method and saves them. These info will be used for the xmlrpc server configuration.
						// (only for public methods => avoids also all the public methods starting with '__')
						if(!preg_match('/^__/', $method->name, $matches))
						{
							// -method name
							$this->builtInMethods['functions'][$method->name]['function'] = $reflection->getName().'.'.$method->name;

							// -method docstring
							$this->builtInMethods['functions'][$method->name]['docstring'] =  $this->__extractDocString($methodProp->getDocComment());
						}
					}
				}
			}
		} else {
			return false;
		}
		return true;
	}

    /**
     *
     * Manipulates a DocString and returns a readable string
     * @param String $DocComment
     * @return Array $_tmp
     */
    private function __extractDocString($DocComment)
    {
        $split = preg_split("/rn|n|r/", $DocComment);
        $_tmp = array();
        foreach ($split as $id => $row)
        {
            //clean up: removes useless chars like new-lines, tabs and *
            $_tmp[] = trim($row, "* /ntr");
        }            
        return trim(implode("n",$_tmp));
    }

    /**
     *
     * Authentication method for the xml-rpc client
     * @param array $request    Array with the following items: array("username", "password")
     * @return boolean
     */
    private function __auth($request)
    {
        //presets
        $username = trim($this->config->item('xmlrpc_username'));
        $password = trim($this->config->item('xmlrpc_password'));

        //getting sent parameters
        $parameters = $request->output_parameters();
        $givenUsername = trim($this->__clean($parameters['0']));    
        $givenPassword = trim($this->__clean($parameters['1']));

        //if($givenUsername != $username) return false;
        if(md5($givenPassword) != $password) return false;

        return true;
    }

    private function __clean($request)
    {
        return trim(addslashes($request));    
    }

    /**
     * Test method meant to be used by developers to test the XML-RPC client side.
     * Requires no authentication
     * @return array    Array with 4 items: 'Hello', 'World', 'and', 'everybody else'
     */    
    public function helloWorld()
    {                    
        $words = array('Hello', 'World', 'and', 'everybody else');

        $response = array(
                            $words,
                            'struct'
                         );
        return $this->xmlrpc->send_response($response);
    }

    /**
     *
     * Test method meant to be used by developers to test the XML-RPC client side.
     * Returns a multiple array as response. It's interesting to see how the response must be wrapped into several arrays as
     * described in the CI2 documentation http://codeigniter.com/user_guide/libraries/xmlrpc.html.
     * Requires authentication.
     * @return array    Array with 2 items each one composed by 4 items
     */    
    public function getContacts($request)
    {
        //auth check        
        $result = $this->__auth($request);
        if($result == false)
        {
            //$response = array($request->output_parameters(),'struct');
            //return $this->xmlrpc->send_response($response);        
            return $this->xmlrpc->send_error_message('100', 'Wrong credentials');    
        }

        $response = array(
                            array('0' => array(
                                            array(
                                                 'first_name' => array('John', 'string'),
                                                 'last_name' => array('Doe', 'string'),
                                                 'member_id' => array(123435, 'int')),'struct'
                                              ),
                                  '1' => array(
                                            array(
                                                 'first_name' => array('Robert', 'string'),
                                                 'last_name' => array('Brown', 'string'),
                                                 'member_id' => array(123436, 'int')),'struct'
                                              )
                                 ), 'struct'); 

        return $this->xmlrpc->send_response($response);        
    }
}

/* End of xmlrpc_server.php */

xmlrpc_client.php

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
// Created on Aug 11, 2011 2011 by Damiano Venturin http://www.squadrainformatica.com
//
// That's a fully functional XMLRPC client implementation
// It's a CodeIgniter 2.0 controller handling the requests performed by a XMLRPC client (look xmlrpc_client.php)
// I think it's interesting the use of reflection in listing the available XMLRPC methods (displayServerMethods)

class Xmlrpc_client extends CI_Controller {
    function __construct()
    {
        parent::__construct();

        $this->xmlrpc->debug = $this->config->item('xmlrpc_debug');

        $server_url = site_url('xmlrpc_server');
        $this->xmlrpc->server($server_url, 80);    
    }

    public function index()
    {
        $data['methods_list'] = $this->displayServerMethods();
        $this->load->view('xmlrpcDoc',$data);
    }

    /**
     *
     * Produces a human readable list of methods available on the server side
     */
    private function displayServerMethods()
    {
        //get methods list
        $this->xmlrpc->method('system.listMethods');
        $request = array();
        $methods_html = '';
        $methods = $this->makeRequest($request,false);

        //show the docstring for the method
        if(count($methods)>0)
        {
            $this->xmlrpc->method('system.methodHelp');
            $methods_html .= '<dl>';
            foreach ($methods as $method) {        
                $request = array($method,'struct');
                $docstring = $this->makeRequest($request,false);
                if(empty($docstring))
                {
                    $methods_html .= '<dt>'.$method.'</dt><dd>No description available</dd>';
                } else {
                    $methods_html .= '<dt>'.$method.'</dt><dd>'.$docstring.'</dd>';
                }
            }    
            $methods_html .= '</dl>';
        }   
        return $methods_html;
    }

    function makeRequest($request=null,$output=true)
    {
        if(!is_array($request) or empty($request) ) $request = array();    

        //performs the request
        $this->xmlrpc->request($request);

        //handles the request
        if ( ! $this->xmlrpc->send_request())
        {
            echo $this->xmlrpc->display_error();
        }
        else
        {
            $res = $this->xmlrpc->display_response();
            if($output)
            {
                echo '<pre>';
                print_r($res);
                echo '</pre>';
            } else {
                return $res;
            }
        }
    }

    function helloWorld()
    {
        $this->xmlrpc->method('helloWorld');
        $request = array();
        return $this->makeRequest($request);
    }    

    function getContacts()
    {
        $this->xmlrpc->method('getContacts');
        $request = array('user','password');
        return $this->makeRequest($request);
    }        

}

/* End of xmlrpc_client.php */

Here  you can see what’s new compared to a clean CI2 installation.

You can get the code using Mercurial and this command:

hg clone https://damiano@bitbucket.org/damiano/codeigniter-xmlrpc-example

This is the documentation page that you see once you download the code and access the root page index.php

One Response to XMLRPC example for Code Igniter

  1. [...] the same wave of my previous post I’m doing the same example using a REST client/server using Phil Sturgeon‘s REST [...]

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>