WP Development

The purpose of this article is to describe the WordPress XML-RPC API, explain how to use it, and demonstrate how to extend it to create custom API methods for interacting programmatically with WordPress. I assume a relative unfamiliarity with XML-RPC in general, WordPress XML-RPC, and spend time first covering the basics, if you want to skip straight to the section on extending the WordPress XML-RPC API, feel free to.

What is XML-RPC?

As defined by the wikipedia article:

XML-RPC is a remote procedure call (RPC) protocol which uses XML to encode its calls and HTTP as a transport mechanism.

XML-RPC provides a means of remotely calling methods on a target system, and is simpler to understand and implement in general than SOAP. It’s an ideal way to programmatically interact with WordPress as XML-RPC is supported in the WordPress core. An XML-RPC client can be written in virtually any programming language, making it a very flexible choice for an API implementation. Lets take a look at a sample request/response:

Request:

<?xml version="1.0" encoding="iso-8859-1"?>
<methodCall>
  <methodName>system.listMethods</methodName>
  <params/>
</methodCall>

Response:

<?xml version="1.0"?>
<methodResponse>
  <params>
    <param>
      <value>
        <array>
          <data>
            <value><string>system.multicall</string></value>
            <value><string>system.listMethods</string></value>
          </data>
        </array>
      </value>
    </param>
  </params>
</methodResponse>

As shown by the above examples, constructing an XML-RPC request is quite simple, and the protocol defines a number of standard datatypes which can be returned. This example demonstrates an array containing string values. All the standard datatypes can be represented, including boolean, integer, double and struct (associative-array), among others.

XML-RPC is well supported by PHP, which provides the “experimental” xmlrpc_encode_request() and xmlrpc_decode() methods which will generate the request XML, and parse the response XML into PHP datatypes, respectively. This means that you can write a client without worrying about composing and decoding the XML transport strings, you can pass plain old PHP datatypes to the remote procedure, and receive plain old PHP datatypes in response.

XML-RPC in WordPress

Enable XML-RPC

XML-RPC is disabled in WordPress by default. To enable it: go to Settings > Writing > Remote Publishing, and check the box next to XML-RPC:

Add XML-RPC User

Next, although optional, it’s a good practice to add an XML-RPC specific user, with a Role of “Contributor”:

Test Your Connection

If you’ve been following along you should now be able to perform a test XML-RPC call against your target WordPress installation. You can perform a test call from the command line using cURL:

$ curl -d '<?xml version="1.0" encoding="iso-8859-1"?><methodCall><methodName>system.listMethods</methodName><params/></methodCall>' http://127.0.0.1/xmlrpc.php

or with wget:

$ wget --post-data='<?xml version="1.0" encoding="iso-8859-1"?><methodCall><methodName>system.listMethods</methodName><params/></methodCall>' http://127.0.0.1/xmlrpc.php

Substituting http://127.0.0.1/xmlrpc.php with your target hostname. If everything is working correctly you should get a response similar to the example one from above in the What is XML-RPC? section.

An Example PHP Client

At this point you should have XML-RPC enabled on your WordPress installed, an xmlrpc-client user created, and hopefully have tested your connection from the command line. If you’re on a primitive operating system that lacks a functional command line, or simply wish to see how to perform an remote call from PHP, here is a sample PHP XML-RPC client which you can use and modify for your own purposes:

class XMLRPC_Client {

  private $url;

  function __construct( $url ) {
    $this->url = $url;
  }

  /**
   * Call the XML-RPC method named $method and return the results, or die trying!
   *
   * @param string $method XML-RPC method name
   * @param mixed ... optional variable list of parameters to pass to XML-RPC call
   *
   * @return array result of XML-RPC call
   */
  public function call() {

    // get arguments
    $params = func_get_args();
    $method = array_shift( $params );

    $post = xmlrpc_encode_request( $method, $params );

    $ch = curl_init();

    // set URL and other appropriate options
    curl_setopt( $ch, CURLOPT_URL,            $this->url );
    curl_setopt( $ch, CURLOPT_POST,           true );
    curl_setopt( $ch, CURLOPT_POSTFIELDS,     $post );
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );

    // issue the request
    $response = curl_exec( $ch );
    $response_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
    $curl_errorno = curl_errno( $ch );
    $curl_error   = curl_error( $ch );
    curl_close( $ch );

    // check for curl errors
    if ( $curl_errorno != 0 ) {
      die( "Curl ERROR: {$curl_errorno} - {$curl_error}n" );
    }

    // check for server errors
    if ( $response_code != 200 ) {
      die( "ERROR: non-200 response from server: {$response_code} - {$response}n" );
    }

    return xmlrpc_decode( $response );
  }
}

We can perform a call equivalent to our command-line tests above with the following:

$client = new XMLRPC_Client( "http://127.0.0.1/xmlrpc.php" );
$available_methods = $client->call( 'system.listMethods' );
print_r( $available_methods );

If everything is working properly, you should again see a listing of all available WordPress XML-RPC methods, as in our previous examples.

The call() method of our example client accepts one or more parameters, where the first is required and is the remote method name to invoke, and all following parameters are optional and are passed to the remote method as arguments. We can demonstrate this with a call to the core wp.getCommentCount method, which requires a WordPress username and password to execute:

$client = new XMLRPC_Client( "http://127.0.0.1/xmlrpc.php" );
$comment_count = $client->call( 'wp.getCommentCount', 1, 'xmlrpc-user', 'xmlrpc-pass' );
print_r( $comment_count );

Where wp.getCommentCount is the remote method name, 1 is the “blog id” (this does not seem to be currently used by WordPress, so you can just set it to ‘1’), xmlrpc-user is the XML-RPC client user name we configured earlier, and xmlrpc-pass is the password for that user. If everything works properly you should see a response like:

Array
(
  [approved] => 745
  [awaiting_moderation] => 0
  [spam] => 0
  [total_comments] => 745
)

If there is an error, you may see a response like the following:

Array
(
  [faultCode] => 403
  [faultString] => Bad login/pass combination.
)

This demonstrates how an error response is returned from an XML-RPC method. The faultCode 403 indicates a bad login/pass combination, faultCode 405 indicates XML-RPC services are disabled in WordPress, and so on.

Register a Custom XML-RPC Method

With these WordPress XML-RPC basics now under our belt we are ready to create and register a custom remote method, which is actually surprisingly easy. We will make use of the xmlrpc_methods filter to add our custom method. As is tradition we’ll create a “Hello World” method that says hello to a passed name, and demonstrates all of the typical XML-RPC boilerplate, even though it’s not strictly needed for this simple case.

add_filter( 'xmlrpc_methods', 'add_xml_rpc_methods' );

Our add_xml_rpc_methods() function can be defined like the following:

function add_xml_rpc_methods( $methods ) {
  $methods['frs.helloWorld'] = 'hello_world';
  return $methods;
}

Where frs.helloWorld is the XML-RPC method name, and hello_world is the callback, the name of the function to be called when the frs.helloWorld XML-RPC request is received. The method name can technically be anything you like, however the best practice is to prepend your method name with an identifier unique to you/your business. For instance, core WordPress methods begin with a ‘wp’ prefix (ie ‘wp.getCommentCount’), here I’ve scoped ‘helloWorld’ with ‘frs’ for “fox run software”, giving me a complete method name of “frs.helloWorld”. A simple implementation of the callback method follows:

function hello_world( $params ) {

  global $wp_xmlrpc_server;

  $blog_id  = (int) $params[0]; // not used, but follow in the form of the wordpress built in XML-RPC actions
  $username = $params[1];
  $password = $params[2];
  $args     = $params[3];

  // verify credentials
  if ( ! $wp_xmlrpc_server->login( $username, $password ) ) {
    return $wp_xmlrpc_server->error;
  }

  // check for edit_posts capability (requires contributor role)
  // (obviously not required for this simple example, but just for demonstration purposes)
  if ( ! current_user_can( 'edit_posts' ) )
    return new IXR_Error( 403, __( 'You are not allowed access to details about orders.' ) );

  do_action( 'xmlrpc_call', 'frs.helloWorld' ); // patterned on the core XML-RPC actions

  // required parameter
  if ( ! isset( $args['name'] ) ) return new IXR_Error( 500, __( "Missing parameter 'name'" ) );

  // return success
  return "Hello " . $args['name'] . "!";
}

Breaking it down, the global $wp_xmlrpc_server is used to log in the passed XML-RPC user/pass, otherwise an error is returned. Although not necessary for this simple example, the logged-in XML-RPC user is checked for the edit_posts capability. Errors are returned by constructing an IXR_Error object, which accepts a fault code and fault string (remember our faultCode/faultString example responses from before?) The xmlrpc_call action is invoked, as in the WordPress core methods. Our XML-RPC name parameter is available from the $args variable, and finally we return our “Hello!” string. Note that you could return any of the standard XML-RPC datatypes here, for instance return an array and WordPress will take care of encoding it into the XML representation and returning it to your client.

Test this custom method in the same manner as our calls to the core methods previously:

$client = new XMLRPC_Client( "http://127.0.0.1/xmlrpc.php" );
$hello = $client->call( 'frs.helloWorld', 1, 'xmlrpc-user', 'xmlrpc-pass', array( 'name' => 'Justin' ) );
print_r( $hello );

If all is well you should see something like the following displayed:

string(13) "Hello Justin!"

Working Demo Plugin

I’ve packaged all the previous code including the custom frs.helloWorld XML-RPC method implementation, and sample XML-RPC PHP client into a plugin which you can feel free to download, use, extend, build on or learn from:

Download the WP XML-RPC Demo Plugin

If you end up using this article to extend the WordPress XML-RPC API for your own purposes, let us know what you did in the comments section below. I’d love to hear about it!

Published by Nik McLaughlin

You can find Nik around the WP space, on LinkedIn, or on his personal blog.

6 Comments

  1. Brilliant Article Justin!

    This help me lot.

    Thanks for this useful post.

    • You’re very welcome Sanjay, glad to hear you found it helpful. Did you already, or are you planning on doing anything interesting with the RPC API? Exposing any cool methods or functionality?

  2. Hello,

    thanks for this nice tuts !

    Once you have created your plugin which extends the core methods, where did you write your client ?

    Do you know a way to package your client script as a plugin ? and then call it with a nice URL ?

    This would be very nice !

    Thanks

    ben

    • Hey Ben, glad you enjoyed! The client for the particular project that spawned this article was written to be executed periodically as a script on a server. There’s probably better ways of exposing WordPress functionality over URLs (REST-like) than by wrapping the XML-RPC protocol. For instance, you could just write a plugin that responds to certain URLs and invokes whatever methods you like, rather than going through the extra XML-RPC hoop. Hope this helps

  3. Dear Justin,
    I am a newbie in WP . I download your plugin and install it in my wp 3.6. Now I need to know ,
    how to run this to see a “Hello world” stuff you mention. Please reply me with details.

    Thanks,
    Anes

  4. Thanks Justin!

    You saved my day.

    Regards,
    Olivier

Hmm, looks like this article is quite old! Its content may be outdated, so comments are now closed.