on Expert Tutorials

How to build PHP XML-RPC Server, Client and Android application

27 comments
android1
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedInShare on RedditShare on StumbleUpon

In this article I will show how to build PHP XML RPC server and how to consume provided services with PHP and an Android application as a client. PHP side of things will be built on top of Zend Framework and for Android will be using very thin XML RPC library android-xmlrpc ( http://code.google.com/p/android-xmlrpc/ ).

Assumption is that you already have downloaded Zend Framework and have Android SDK set up (as well as Android emulator). I’ll be using Eclipse both for PHP and Android development. Also, I’ll be using ZF tool for creating project, controllers and models.
We’ll start with building XMLRPC server component.

XMLRPC server component

In your web server’s document root run zf tool with this command: zf create project xmlrpc-test. Afterwards step into this newly created directory xmlrpc-test.

Now we’ll create needed controllers and models.

First run zf create controller Server (which creates our ServerController), then we’ll create our model zf create model Data. Because we’ll also create client side of XMLRPC lets create this controller now to finish out work with zf tool: zf create controller Client.

We should test if our website is running properly, go to http://localhost/xmlrpc-test/public with your browser and you should see image similar to the one below.

zend_framework

Now we’re ready to create our project in Eclipse, open PHP workspace, and go to File->New->PHP project, enter xmlrpc-test as a project name and click Finish. Our project is ready and all files created earlier are in it.

Eclipse - create new project

Lets open our Server controller (application/controllers/Server.php) and start building XMLRPC server.

class ServerController extends Zend_Controller_Action
{
    public function indexAction()
    {
    	$this->_helper->viewRenderer->setNoRender();
    	
        $server = new Zend_XmlRpc_Server();
	  $server->setClass('Application_Model_Data', 'cf');
		
	  echo $server->handle();
    }
}

First, we disable our view. We instantiate Zend_XmlRpc_Server and add class which will respond to calls from client, this will be our Application_Model_Data class and we will set its namespace as cf.

We echo the servers handle method, and voila, our very simple and basic XMLRPC server is done.

Now let’s open our model (application/models/Data.php) and create some test method for returning dummy data.

class Application_Model_Data
{
	/**
	 * Test method
	 * 
	 * @return string
	 */
	public function test()
	{
		return 'Hello XMLRPC!';
	}
}

This class, Application_Model_Data is classic PHP class, the most important thing here is to notice the comments. They are mandatory, because when call occurs, Zend_XmlRpc_Server will be doing reflection of this class before running it, checking if called method exists and if its arguments are matching defined ones and are correct type. So, we have to define types of all input parameters (none in this test method) as well as method return type.

Let’s write XML RPC client to check if everything is OK so far.

XMLRPC PHP client

Earlier we created ClientController class (located in application/controller/Client.php) with default indexAction method and now we’ll add our code in it.

class ClientController extends Zend_Controller_Action
{
    public function indexAction()
    {
        $client = new Zend_XmlRpc_Client('http://localhost/xmlrpc-test/public/server/');
        try {
        	$data = $client->call('cf.test');
        	$this->view->data = $data;
        } catch (Zend_XmlRpc_Client_HttpException $e) {
        	require_once 'Zend/Exception.php';
        	throw new Zend_Exception($e);
		} catch (Zend_XmlRpc_Client_FaultException $e) {
 			require_once 'Zend/Exception.php';
        		throw new Zend_Exception($e);
		}
    }
}

First we instantiate Zend_XmlRpc_Client and provide its constructor URI of our XML RPC server. Then we try to make a call to a test method (‘cf’ is a namespace defined for our Application_Model_Data class). We’ll forward returned data to our view.

Our view is located in application/views/scripts/client/index.phtml. In it we are only echoing data returned from our test method.

<h1>Via XMLRPC</h1>
<?php echo $this->escape($this->data); ?>

If we run http://localhost/xmlrpc-test/public/client in our browser we should get something like this:

XML-RPC client result in browser

Let’s add another method to our Application_Model_Data class which will be expecting single Integer parameter and it will be returning an associative array.

/**
 * Fetches data 
 * 
 * @param integer $num
 * @return array
 */
public function getData($num) 
{
	$data = array();
	
	for ($a = 0; $a < $num; $a++) {
		$data[] = array(
			'title' => 'Codeforest.net',
			'number' => $a + 1,
			'datetime' => date('Y-m-d H:i:s')
		);
	}
	
	return $data;
}

Method is called getData (yes, very creative) and it’s pretty simple. Again, notice the comments with defined @param and @return lines.

We also have to upgrade our ClientController class and instead of calling cf.test method will be calling cf.getData method. We only have to supstitute one line (7th):

$data = $client->call('cf.test');

with this one:

$data = $client->call('cf.getData’, 15);

Everything else in the ClientController class stays the same.

We have to update our view script with something like this:

<h1>Via XMLRPC</h1>
<?php if (count($this->data) > 0):?>
<ul>
	<?php foreach ($this->data as $row):?>
	<li><?php echo $this->escape($row['number'])?> - <?php echo $this->escape($row['title'])?>, <?php echo $this->escape($row['datetime'])?></li>
	<?php endforeach;?>
</ul>
<?php endif; ?>

Again, we go to our browser to test http://localhost/xmlrpc-test/public/client. Result is similar to:

xml-rpc-result

This concludes our PHP and Zend Framework part of the article.

XMLRPC Android client

If using Eclipse switch to Android workspace (File->Switch Workspace, pick your Android workspace).

After you have switched workspace go to File->New->Android Project, enter project name, select build target and fill the rest of the properties.

Download Android XML-RPC client library from http://code.google.com/p/android-xmlrpc/.

Create new package in our project (right mouse click on project name in Package explorer and select New->Package). Name this package org.xmlrpc.android and copy files form archive directory android-xmlrpc/src/org/xmlrpc/android into this newly created package.

Eclipse-package

Now our directory structure should be similar to picture below:

directory-structure

Because we’ll be needing internet connection (to connect to our XML-RPC server) we need to add internet permission to our android manifest file. Open AndroidManifest.xml and in raw XML view add next line just before closing manifest tag:

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

In our example we’ll be using default layout file res/layout/main.xml but we’ll add an ID to its TextView element so that we can fill it out with received data. So open res/layout/main.xml file and locate TextView element. Add an android.id attribute with @+id/text_view value to it:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView  
	android:id="@+id/text_view"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />
</LinearLayout>

Now we can start building our Android Client. Open Client.java in our com.cf.xmlrpc package and enter:

public class Client extends Activity {
	
	private XMLRPCClient client;
	private URI uri;
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        uri = URI.create("http://10.0.2.2/xmlrpc-test/public/server/");
	  client = new XMLRPCClient(uri);
		
	  TextView textView = (TextView) findViewById(R.id.text_view);
    }
}

First, we create class variables which will hold our XMLRPC Client and URI for our server. We can access this variables throughout our class.

In onCreate method we assign our uri variable URI object. As you can see we are using 10.0.2.2 address to access our own local web server. 127.0.0.1 or localhost is used by the underlying linux OS on emulator.

Then we create our XMLRPCClient and give its constructor our uri variable.

We’ll also get reference to TextView element of our layout where we’ll display our results.

If Eclipse is signaling for some error due to not having imported needed classes press Ctrl+Shift+O, this will import all needed classes automatically.

Next, we’ll create method which will call test method on our XML-RPC server (built earlier with Zend Framework).

private String testMethod() {
	String text = "";
	try {
		text = (String) client.call("cf.test");
	} catch (XMLRPCException e) {
		Log.w("XMLRPC Test", "Error", e);
		text = "XMLRPC error";
	}		
	return text;
}

We’re using try – catch block to catch errors and to deal with them. After creating this method we have to call it from onCreate method and display its return value to TextView element. Our modified Client activity class should look like this:

public class Client extends Activity {
	
	private XMLRPCClient client;
	private URI uri;
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        uri = URI.create("http://10.0.2.2/xmlrpc-test/public/server/");
	  client = new XMLRPCClient(uri);
		
	  TextViewtextView = (TextView) findViewById(R.id.text_view);
	  textView.setText(testMethod());
    }
	
    private String testMethod() {
	  String text = "";
        try {
		text = (String) client.call("cf.test");
	  } catch (XMLRPCException e) {
		Log.w("XMLRPC Test", "Error", e);
		text = "XMLRPC error";
	  }		
	  return text;
    }
}

We are ready to test our Android client. Select Run->Run configurations and create new Android application configuration for our project:

Creating-run-conf

After the emulator boots up we should have something similar to this:

Android emulator

Hoorah! Our Android client is using our XML RPC web service!

Lets now create method which will call our other server function getData.

private String getDataMethod(int num) {
	String text = "";
	try {
		Object[] data = (Object[]) client.call("cf.getData", num);
		for(Object o: data) {
			HashMap map = (HashMap) o;
			text = text + "'datetime' => " + map.get("datetime") + ", 'number' => " + map.get("number") + ", 'title' => " + map.get("title") + "\n\n";
		}
	} catch (XMLRPCException e) {
		Log.w("XMLRPC Test", "Error", e);
		text = "XMLRPC error";
	}		
	return text;
}

Method we are calling (cf.getData) expects one parameter (integer) and returns an associative array (from PHP’s perspective). In Java we received array with map items.

If you are having trouble figuring out what your custom XML RPC service is returning you be sure to check out great post at Inchoo. It describes how to parse the data returned from XMLRPC.

Next step is to switch method calls in onCreate method:

textView.setText(testMethod());

to this one:

textView.setText(getDataMethod(12));

After saving and running (again select Run->Run configurations) our changed Android client we should have on the emulator something like:

Android working XML-RPC


And that’s it. We covered all the elements necessary to build PHP XML-RPC Server, Client and Android application that will harvest it.

Feel free to leave a comment, a question or a critique. Also feel free to request a follow up or an article with stuff that interests you.

Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedInShare on RedditShare on StumbleUpon



  • http://twitter.com/tacodev Taco

    Nice article, but I’d strongly suggest using a JSON-RPC setup. A big disadvantage of XML-RPC is the amount of data it generates. This is especially important when working on mobile systems.

    For both Zend and Android JSON-RPC libraries are available.

    • http://lukapeharda.com Luka Peharda

      I agree, and as you say especially on mobile systems where every byte counts.

      I wrote this article as a spin-off of a project where XML-RPC server was needed (due to legacy reasons) and JSON-RPC wasn’t an option.

      It’s a great idea for an article, with a special accent on difference in amount of data generated.

  • jas

    Any idea why I would be getting 404 for the public/client and public/server folders?

    They didn’t seem to be get created when creating the initial project.

    • http://lukapeharda.com Luka Peharda

      Those are virtual directories.

      Did you use zf tool to create project? Did you created controllers with it?

      Did you entere ‘zf create controller Server’ and ‘zf create controller Client’ in command line?

      • louis

        hi luka, explain to me how to run zf tool with this command: zf create project xmlrpc-test?
        i totally new to new this topic, but i really interested in it. please guide.
        thank

  • jas

    Roger.

    If I try to run it again I receive a ‘This project already has a controller named Client &/or Server’.

    • http://lukapeharda.com Luka Peharda

      Hmm, that means that you have already created them before. Can you access localhost/xmlrpc-test/public without any errors? Did you try to compare you project and its files to the ones provided in source files?

  • jas

    I think it might be the zend installation. I installed from source from http://www.zend.com/core/start.

    Went into the htdocs folder, ran the commands to create a new project, new server controller and new client controller.

    The test page came up fine, I then went though and added the code to the files you indicated however the application/controller/Client.php and application/controller/Server.php were named ClientController.php and ServerController.php (not sure if that makes much of a difference).

    And from what I understand is that the bootstrap.php should then load the newly registered client and server controllers from their ‘virtual folder’s correct?

  • jacinto

    Awesome tutorial…. thank you very much! :D

  • Soho bar

    Well, not that bad… Pretty good job for a rookie ;)

  • Seba

    hi, very good tutorial, i’ve have just one problem, when i am trying to use
    the URL of my local host always get an error from the android application “HTTP status code: 500 != 200″.

    i’ve tried to use :

    10.0.2.2
    127.0.0.1
    192.168.150.121 (my IP)

    but nothing seems to work, same error always, but when i use the url from your
    example “http://www.lukapeharda.com/demos/xmlrpc-test/public/server/” it works
    just fine.

    i hope you can give me some advice.

    • http://lukapeharda.com Luka Peharda

      It seems to me that you are having some problems with your local web server.
      Did you follow the steps in tutorial? Did you try to connect to XMLRPC server with PHP client?

  • Seba

    thanks for your response Luka, the application runs ok, the problem is:

    When I use this URL:

    $client = new Zend_XmlRpc_Client(‘http://www.lukapeharda.com/demos/xmlrpc-
    test/public/server/’);

    works ok

    but, when i put this URL:

    $client = new Zend_XmlRpc_Client(‘http://localhost/xmlrpc-
    test/public/server/’);

    It doesn’t.

    Could be a problem with Zend library? I am using the code downloaded from the
    web just for check and i have the same problem. The only three diferences that I can
    see are:

    Server.
    Zend library (maybe)
    ServerControler (maybe is diferent the one in your web than the one in the example?)

    Thx

  • aivaras

    Nice tutorial, but I have one issue. Please, open this link – http://www.lukapeharda.com/demos/xmlrpc-
    test/public/server/ – it shows:
    ServerController faultCode 630
    faultStringUnable to read request

    I get same thing on my localhost and not sure how to solve it. Any hints would be very appreciated, please.

  • aivaras

    Hi Luka,
    Ignore my previous message-question. I have sorted myself, just wanted to see a result too fast. I can see I have same issue with pointing to localhost – it throws me an exception. I’ll try to figure out myself and let you all guys know what works for me.

    Anyway, thanks for good tutorial. Next stage – android implementation :) Keep in touch

  • zzgl

    Hi;
    I want to write an android program but I’m beginner so I dont understand something.can you help me.?

    • http://lukapeharda.com Luka Peharda

      Sure. Shoot.

      • zzgl

        now. I write the example above on eclipse but it did not work. I tried my uri and method name and did not work.I dont know how can ı solve the problem.

        • http://lukapeharda.com Luka Peharda

          Could you provide more details? Did you get stuck on creating Android client and if so did you check that server is working correctly with PHPclient?

  • zzgl

    falsefalsetruenofalse,

    THAT IS SAP PART AND

    package com.pcis.Planet;

    import org.ksoap2.SoapEnvelope;
    import org.ksoap2.serialization.SoapObject;
    import org.ksoap2.serialization.SoapSerializationEnvelope;
    import org.ksoap2.transport.HttpTransportSE;

    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.Toast;

    public class Proje01Activity extends Activity {
    /** Called when the activity is first created. */
    private static final String SOAP_ACTION = “ZpcisMuratkWs01″;
    private static final String METHOD_NAME=”ZpcisMuratkWs01″;
    private static final String NAMESPACE=”urn:sap-com:document:sap:rfc:functions”;
    private static final String URL=”http://pcissp09.pcis.com.tr:8090/sap/bc/srt/wsdl/sdef_ZPCIS_MURATK_TICKET/wsdl11/ws_policy/document?sap-client=900″;
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    Button btTest = (Button) this.findViewById(R.id.btTest);

    btTest.setOnClickListener(btTestListener);
    }
    public Button.OnClickListener btTestListener = new Button.OnClickListener() {
    public void onClick(View v) {
    try {
    // Create SOAP request
    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
    SoapEnvelope.VER11);
    envelope.setOutputSoapObject(request);
    HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
    androidHttpTransport.call(SOAP_ACTION, envelope);

    // Get response from envelope
    Object result = envelope.getResponse();

    // Display result
    Toast.makeText(Proje01Activity.this, result.toString(),
    50000).show();

    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    };
    }
    THAT İS MY ANDROİD CODE
    CAN YOU TELL ME WHERE AM I WRONG.
    BEST REGARDS

    • http://lukapeharda.com Luka Peharda

      This article is about XML-RPC, not SOAP.

      • zzgl

        So;
        How could I do?Can you help me.

  • zzgl

    THAT SAP PART SORRY

  • zzgl

    I CANT SEND SAP PART.

  • Akhil N K

    Hi,

    Awesome tutorial to start with…
    Can you explain the same a little bit deeper by qouting an example which send login information to the web service and then the webservice checks the DB for the existence of the user and responds as required? That would add more to the value of this article.

    • http://lukapeharda.com Luka Peharda

      Well, this is out of the scope of this tutorial. You should check out Zend Framework documentation for DB authentication (zend.auth.adapter.dbtable.html).

      After you manage to authenticate locally, just move this logic to service method. You should create method that accepts two params, username and password and returns boolean (true or false). Call this method from client and you should be pretty much done. You should use some kind of persistence system on client side to save authentication status.

  • Lee sea wuyhs

    This post is very good.
    Thanks you!