Last updated June 30, 2010. Created on November 27, 2008.
Edited by greg.harvey, heyrocker, fp, tomhung. Log in to edit this page.

Here is an example of a PHP script that calls the Services module's packaged XML-RPC server. This example uses the system.connect, user.login, user.logout and views.get methods. It should provide guidance for how to call web services presented by the Services module using API keys with session expiry enabled.

<?php
/**
 * IMPORTANT
 * Make sure your computer or server clock is set correctly before
 * attempting to access this web service. Time is a part of the
 * authentication process and if your clock does not match the
 * system clock, you will not be able to authenticate.
 */

/**
 * Function for generating a random string, used for
 * generating a token for the XML-RPC session
 */
function getUniqueCode($length = "") {
 
$code = md5(uniqid(rand(), true));
  if (
$length != "") return substr($code, 0, $length);
  else return
$code;
}


// set local domain or IP address
// this needs to match the domain set when you created the API key
// it can be a straight string
$domain = $_SERVER['SERVER_NAME'];

// set API key
$kid = '35328fcd1b8cf9e101fc0e398de0be08';

// set target web service endpoint
$endpoint = 'http://www.mysite.com/services/xmlrpc';

// set the user credentials our script should login with
// we use these later on
$user_credentials = array(
 
0 => 'example.user',
 
1 => 'password',
);


/*
 * Firstly we touch the system.connect service
 * to open a PHP/Drupal session
 *
 * Note, this method does not require the API key
 */

// set vars for this connection
$method_name = 'system.connect';
$required_args = array();

// prepare the request
$request = xmlrpc_encode_request(
 
$method_name, $required_args
);

// prepare the request context
$context = stream_context_create(
  array(
   
'http' => array(
     
'method' => "POST",
     
'header' => "Content-Type: text/xml",
     
'content' => $request,
    )
  )
);

// connect
$connect = file_get_contents($endpoint, false, $context);
// retrieve the result
$response = xmlrpc_decode($connect);

// display the result on screen
if (xmlrpc_is_fault($response)) {
    print
'<h1>Error</h1>';
   
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
} else {
   
// let's look at what came back
   
print '<h1>Received</h1>';
    print
'<pre>'. htmlspecialchars(print_r($response, true)) .'</pre>';
}


/*
 * Now we have a session, we can login to
 * retrieve our article feed
 *
 * This is our first use of the API key
 */

// set vars for this connection
$nonce = getUniqueCode("10");
$method_name = 'user.login';
$timestamp = (string) strtotime("now");
$required_args = array();

// now prepare a hash
$hash_parameters = array(
 
$timestamp,
 
$domain,
 
$nonce,
 
$method_name,
);

// create a hash using our API key
$hash = hash_hmac("sha256", implode(';', $hash_parameters), $kid);

// prepared the arguments for this service
// you can see the required arguments on the method's test page
// http://www.mysite.com/admin/build/services
$required_args = array(
 
$hash,
 
$domain,
 
$timestamp,
 
$nonce,
 
$response['sessid'],
);

// any user-defined arguments for this service
// here we use the login credentials we specified at the top of the script
$user_args = $user_credentials;

// add the arguments to the request
foreach ($user_args as $arg) {
 
array_push($required_args, $arg);
}

// prepare the request
$request = xmlrpc_encode_request(
 
$method_name, $required_args
);

// prepare the request context
$context = stream_context_create(
  array(
   
'http' => array(
     
'method' => "POST",
     
'header' => "Content-Type: text/xml",
     
'content' => $request,
    )
  )
);

// connect
$connect = file_get_contents($endpoint, false, $context);
// retrieve the result
$response = xmlrpc_decode($connect);

// display the result on screen
if (xmlrpc_is_fault($response)) {
  print
'<h1>Error</h1>';
 
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
} else {
  print
'<h1>Received</h1>';
  print
'<pre>'. htmlspecialchars(print_r($response, true)) .'</pre>';

 
// SAVE OUR USER OBJECT - we'll need it later
 
$user = new stdClass();
 
$user = (object) $response['user'];

 
// ALSO SAVE OUR LOGGED IN SESSID - we'll need it to logout
  // sessid changes after we login
 
$loggedinsessid = $response['sessid'];
}


/*
 * Now let's retrieve the results of a view
 */

// set vars for this connection
$nonce = getUniqueCode("10");
$method_name = 'views.get';
$timestamp = (string) strtotime("now");
$required_args = array();

// now prepare a hash
$hash_parameters = array(
 
$timestamp,
 
$domain,
 
$nonce,
 
$method_name,
);

$hash = hash_hmac("sha256", implode(';', $hash_parameters), $kid);

// prepared the arguments for this service
$required_args = array(
 
$hash,
 
$domain,
 
$timestamp,
 
$nonce,
 
// note, this is now the logged in sessid returned by user.login
 
$response['sessid'],
);


// any user-defined arguments for this service
// you can see the required arguments on the method's test page
// http://www.mysite.com/admin/build/services
$user_args = array(
 
// view name
 
0 => 'subscriptions',
 
// display name
 
1 => 'feed_1',
 
2 => array(),
 
// view arguments
 
3 => array((int) $user->uid),
 
4 => 0,
 
5 => 0,
 
6 => 1,
);

// add the arguments to the request
foreach ($user_args as $arg) {
 
array_push($required_args, $arg);
}

// prepare the request
$request = xmlrpc_encode_request(
 
$method_name, $required_args
);

// prepare the request context
$context = stream_context_create(
  array(
   
'http' => array(
     
'method' => "POST",
     
'header' => "Content-Type: text/xml",
     
'content' => $request,
    )
  )
);

// connect
$connect = file_get_contents($endpoint, false, $context);
// retrieve the result
$response = xmlrpc_decode($connect);

// display the result on screen
if (xmlrpc_is_fault($response)) {
  print
'<h1>Error</h1>';
  print
'<pre>'. htmlspecialchars(print_r($response, true)) .'</pre>'
 
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
} else {
  print
'<h1>Received</h1>';
  print
'<pre>'. htmlspecialchars(print_r($response, true)) .'</pre>';
}


/*
 * Finally, logout your logged in user session
 */

// set vars for this connection
$nonce = getUniqueCode("10");
$method_name = 'user.logout';
$timestamp = (string) strtotime("now");
$required_args = array();

// now prepare a hash
$hash_parameters = array(
 
$timestamp,
 
$domain,
 
$nonce,
 
$method_name,
);

$hash = hash_hmac("sha256", implode(';', $hash_parameters), $kid);

// prepared the arguments for this service
$required_args = array(
 
$hash,
 
$domain,
 
$timestamp,
 
$nonce,
 
// note, this is now the sessid we saved after the user.login method above
 
$loggedinsessid,
);

// prepare the request
$request = xmlrpc_encode_request(
 
$method_name, $required_args
);

// prepare the request context
$context = stream_context_create(
  array(
   
'http' => array(
     
'method' => "POST",
     
'header' => "Content-Type: text/xml",
     
'content' => $request,
    )
  )
);

// connect
$connect = file_get_contents($endpoint, false, $context);
// retrieve the result
$response = xmlrpc_decode($connect);

// display the result on screen
// NOTE, in this case you may get a PHP warning, because the response
// from this method is not an array - this is a bug, but it doesn't really affect us
if (xmlrpc_is_fault($response)) {
    print
'<h1>Error</h1>';
   
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
} else {
    print
'<h1>Received</h1>';
    print
'<pre>'. htmlspecialchars(print_r($response, true)) .'</pre>';
}

/*
 * All Done!
 */
?>

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.

Comments

Alexander Matveev’s picture

I'm getting error:

Warning: file_get_contents(http://___/services/xmlrpc) [function.file-get-contents]: failed to open stream: HTTP request failed! HTTP/1.1 403 Forbidden in /___/xmlrpc.php on line 68

Notice: xmlrpc_is_fault() [function.xmlrpc-is-fault]: Array argument expected in /___/xmlrpc.php on line 73
Received

Can't get it work. Sad.

--
Regards,
Alexander Matveev
https://www.facebook.com/matveev.alex

greg.harvey’s picture

The error message tells you everything you need to know. 403, access denied. Your server is set up wrong - permissions are messed up.

gateway69’s picture

Im getting this error

Notice: xmlrpc: 1 1 Invalid API key. (1) in D:\xampp\htdocs\test\test.php on line 153

I have the proper $key set in $kid, based upon what I set up in drupal services / keys...

Im testing on localhost

any thoughts?

adub’s picture

Have you set authentication method in services configuration?

gateway69’s picture

Under admin/build/services/settings

I have key auth selected from the pulldown

use keys checkec, and uses sess id's

?

winnie80’s picture

have you check what is your domain?

cause on localhost $domain = $_SERVER['SERVER_NAME']; will only register as 'localhost'

so what i do if i were you is to change the domain on key settings into 'localhost' and use the newly generated api key

hope this can help you

Onyx’s picture

Attention all noobs like me...this code DOES work for 6.x-2.x to get XMLRPC Server in the Services Module to produce a response FROM A REMOTE site. Other code I have found elsewhere in the Services pages relied upon undocumented, and unavailable xmlrpc libraries and functions to work...hence they didn't, and I wasted days trying to find out where these libraries were, or trying to get REST or JSON to work (no dice there either).

Remember: line 24, $domain="mydrupalsite/dir" IF your site is in a subfolder. And that same string is what gets used in the admin/build/services/keys/list interface in order to create the authentication key $kid.

Also remember you need to have the right permissions set in order to make any of this happen. (May need node_service module/load node data set for Anonymous user for some other data loading, I have not tested further!)

Also check out this video: http://blip.tv/file/1278046 for useful background info.

http://chalcedony.co.nz -- Building rock solid websites --

calebtr’s picture

Since the main difference with the 6.x-2.x branch is the permissions, make sure the user you are logging in with (or anonymous) has permission to use the services here.

Also keep in mind that the request here doesn't send a useragent string. My .htaccess file was set to ignore requests without a useragent string, and I had to comment that out to get it to work.

Onyx’s picture

I have used this code, thanks, to build a helper function for PHP sites. More details here: http://chalcedony.co.nz/gemsofwisdom/drupal-xmlrpc-from-remote-php-site

http://chalcedony.co.nz -- Building rock solid websites --

jan_v’s picture

Thank you. Code worked great !

ran on localhost, called to online service with version 6.x.2.2

hamberger’s picture

EDIT: When I switch from 6.x-2.4 to 6.x-2.2 I only get the access denied for the views.get but the user.logout is working. Any ideas why this is happending in 6.x-2.4 / has anybody got this code working on 6.x-2.4. Thanks!!

Although I get back the user object correctly after user.login I get the following errors for the last 2 operations. Any ideas what I would need to change. (I've set all the permission and the user is the admin user)
Thanks a lot for your help, Stephan

Array
(
[faultCode] => 401
[faultString] => Access denied
)

Notice: xmlrpc: Access denied (401) in C:\xampplite\htdocs\ctact\d6\test_login.php on line 244
Error

Notice: xmlrpc: User is not logged in. (406) in C:\xampplite\htdocs\ctact\d6\test_login.php on line 307

egarias’s picture

I got the same problem.
I had everything working, then after several updates (Services and other modules) I had the same error.
I updated to Services 6.30 Beta2 And It works!!!

Egarias
Turning opportunities into business

sanjith’s picture

i am getting

xmlrpc: Wrong username or password. (401)

pls suggest me some working example !

sanjith’s picture

*Hard coded redirect issue from one module.

sanjith’s picture

* Hard coded redirection is the issue

I disabled that module, now its working fine.