Posted by gateway69 on March 22, 2010 at 7:27pm
I have been trying to use the 6.x-2.0 to try to pull information via XMLRPC but im running into some roadblocks, I found a page that shows how to connect via php/services/api key etc it seems to be outdated or some errors in it..
I was curious if anyone using 6.x-2.0 would share a small snippet of php code that I could test to pull some node data...
Any tutorials on this?
sorry it seems like a lot of people are using this with flex, but im working on non flash services system.
Comments
Try this
I'm currently working on a PHP implementation based on some examples I've found kicking around. I'll post something more complete with proper references once I've finished my project, but here's a bit of code that should get you started:
This script will connect to drupal, login as a user, and get node id 1:
<?php
include('drupalxmlrpc.class.php');
// Get Drupal session
$DX = new DrupalXmlrpc();
$user = $DX->userLogin();
$result = $DX->nodeGet(1);
// close drupal session
if(isset($DX)) $DX->userLogout();
?>
drupalxmlrpc.class.php:
<?php
class DrupalXmlrpc {
function __construct() {
// 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
$this->domain = 'mydrupal.com';
// set API key
$this->kid = '5bdd389a3cd78ca9bf342198e7ba3b69';
// set target web service endpoint
$this->endpoint = 'http://localhost/drupal/services/xmlrpc';
// get initial session
$this->systemConnect();
// set the user credentials our script should login with
$this->user_credentials = array(
0 => 'myuser',
1 => 'mypass',
);
}
/**
* Function for generating a random string, used for
* generating a token for the XML-RPC session
*/
private function getUniqueCode($length = "") {
$code = md5(uniqid(rand(), true));
if ($length != "") return substr($code, 0, $length);
else return $code;
}
private function send_xmlrpc($method_name, $user_args = array()) {
// set vars for this connection
$nonce = $this->getUniqueCode("10");
$timestamp = (string) strtotime("now");
$required_args = array();
// now prepare a hash
$hash_parameters = array(
$timestamp,
$this->domain,
$nonce,
$method_name,
);
$hash = hash_hmac("sha256", implode(';', $hash_parameters), $this->kid);
// prepared the arguments for this service
$required_args = array(
$hash,
$this->domain,
$timestamp,
$nonce,
// note, this is now the logged in sessid returned by user.login
$this->session_id,
);
// 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($this->endpoint, false, $context);
// retrieve the result
$response = xmlrpc_decode($connect);
return $response;
}
/*
* Firstly we touch the system.connect service
* to open a PHP/Drupal session
*
* Note, this method does not require the API key
*/
private function systemConnect() {
// 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($this->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 'Anonymous session obtained'."\n";
$this->session_id = $response['sessid'];
return $response['sessid'];
}
}
// login and return user object
public function userLogin() {
$method_name = 'user.login';
$user_args = $this->user_credentials;
$response = $this->send_xmlrpc($method_name, $user_args);
if (xmlrpc_is_fault($response)) {
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
return FALSE;
} else {
print 'Logged in user: '.$response['user']['name']."\n";
// SAVE OUR LOGGED IN SESSID
$this->session_id = $response['sessid'];
// SAVE OUR USER OBJECT - we'll need it later
$user = new stdClass();
$user = (object) $response['user'];
$this->authenticated_user = $user;
return $user;
}
}
public function userLogout() {
$method_name = 'user.logout';
$response = $this->send_xmlrpc($method_name);
// 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 (is_array($response) && xmlrpc_is_fault($response)) {
print '<h1>Error</h1>';
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
} else {
print 'Logged out user'."\n";
}
}
public function nodeGet($nid, $fields = array()) {
$method_name = 'node.get';
$user_args = array(
$nid,
$fields
);
$response = $this->send_xmlrpc($method_name, $user_args);
// display the result on screen
if (is_array($response) && xmlrpc_is_fault($response)) {
print '<h1>Error</h1>';
print '<pre>'. htmlspecialchars(print_r($response, true)) .'</pre>';
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
return FALSE;
} else {
print 'Got node: '. print_r($response,1)."\n";
return $response;
}
}
}
?>
thanks ill give that a shot..
thanks ill give that a shot.. anything right now that works would be grateful :)
looks like missing or an
looks like missing or an additional ; somewhere.. quick cut and paste as I run out the door
Parse error: syntax error, unexpected ';', expecting T_FUNCTION in D:\xampp\htdocs\test2\drupalxmlrpc.class.php on line 191
You probably figured this
You probably figured this out, but the code was missing a "}" at the end.
sweet got it working,
sweet got it working, thanks...
!! you adding to this or?
thanks for your help and time on the code
hey, thanks for the code.
hey, thanks for the code. Exactly what I needed.
-Blake
I had a question, I haven't
I had a question, I haven't used xml rpc before so bit new to this service, right now the example node spits out an array to the page by calling
$result = $DX->nodeGet(4);which is fine, how ever I need to get it formatted into a xml file so I can read it in from my client, looking at http://php.net/manual/en/book.xmlrpc.php it seems that xmlrpc_encode_request() should return the array into xml format.. any ideas.. looking at your code I see that you call this a few times, but in function nodeGet it gets all put and returned as an array.
I guess I could take the node array and grab the elements i need into xml, but wouldn't that be an extra step?
thoughts?
if all you need is that
if all you need is that result in xml, take a look at xmlrpc_encode() http://www.php.net/manual/en/function.xmlrpc-encode.php
That should return xml you can write directly to a file.
Zend Framework alternative
I don' t have xmlrpc enabled in php so I took advantage of a Zend Framework class: here is the results:
I've revised the code - better for a general audience
The previous entry had a few problems. For one it didn't save your SID after login, which is necessary. Secondly it depended on my personal configuration library in the constructor. Here's the new and improved version, tested for general use.
Some configuration notes:
First, the usage test case:
<?php
$config = array();
$config['domain'] = "mailosauras:8888";
$config['kid'] = "drupal web key";
$config['drupal_site_root'] = "http://localhost:8888";
$config['credentials'] = array('username', 'password');
$DX = new Namespace_Web_DrupalService($config);
$user = $DX->userLogin();
echo '<h2>User of Login</h2>';
echo '<pre>', print_r($user, 1), '</pre>';
echo '<h2>Get User 10 (change to match an existing user)</h2>';
echo '<pre>', print_r($DX->userGet(10), 1), '</pre>';
echo '<h2>Node Data(Change to match an existing node)</h2>';
$result = $DX->nodeGet(10);
echo '<pre>', print_r($result, 1), '</pre>';
?>
and the class.
<?php
/
* This class adds drupal centric funvtionality to the XMLRPC process.
* Its designed to handle custom features of the drupal site including
* the security key.
*
*/
class Namespace_Web_DrupalService {
/
* the CLIENT site
* @var string
*/
public $domain = '';
/
* the site security key - specific to Drupal services
* @var string
*/
public $kid = '';
/
* the SERVICE domain. Just the host, no trailing slash
* as in 'http://www.site.com'
* @var string
*/
public $drupal_site_root = '';
public $user_credentials = array('user', 'pass');
public $authenticated_user = NULL;
function __construct(array $pParams = array()) {
foreach($pParams as $field => $value) {
$this->$field = $value;
}
// get initial session
$this->systemConnect();
}
/**
* gets a Zend Client
*/
private $_client;
private function _client() {
if (!$this->_client) {
$this->_client = new Zend_XmlRpc_Client($this->drupal_site_root . '/services/xmlrpc');
}
return $this->_client;
}
/**
* Function for generating a random string, used for
* generating a token for the XML-RPC session
*/
private function getUniqueCode($length = "") {
$code = md5(uniqid(rand(), true));
if ($length != "") return substr($code, 0, $length);
else return $code;
}
private function send_xmlrpc($method_name, $user_args = array()) {
// set vars for this connection
$nonce = $this->getUniqueCode("10");
$timestamp = (string) strtotime("now");
$required_args = array();
// now prepare a hash
$hash_parameters = array(
$timestamp,
$this->domain,
$nonce,
$method_name,
);
$hash = hash_hmac("sha256", implode(';', $hash_parameters), $this->kid);
// prepared the arguments for this service
$required_args = array(
$hash,
$this->domain,
$timestamp,
$nonce,
// note, this is now the logged in sessid returned by user.login
$this->session_id,
);
// add the arguments to the request
foreach ($user_args as $arg) {
array_push($required_args, $arg);
}
$response = $this->_client()->call($method_name, $required_args);
return $response;
}
/*
* Firstly we touch the system.connect service
* to open a PHP/Drupal session
*
* Note, this method does not require the API key
*/
private function systemConnect() {
// set vars for this connection
$method_name = 'system.connect';
$required_args = array();
$response = $this->_client()->call($method_name, $required_args);
return $this->session_id = $response['sessid'];
}
// login and return user object
public function userLogin() {
$method_name = 'user.login';
$user_args = $this->user_credentials;
$response = $this->send_xmlrpc($method_name, $user_args);
// SAVE OUR LOGGED IN SESSID
/**
* logged in session requires new sessid
*/
$this->session_id = $response['sessid'];
// SAVE OUR USER OBJECT - we'll need it later
$user = new stdClass();
$user = (object) $response['user'];
$this->authenticated_user = $user;
return $user;
//}
}
public function userLogout() {
$method_name = 'user.logout';
$response = $this->send_xmlrpc($method_name);
// 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 (is_array($response) && xmlrpc_is_fault($response)) {
// print '<h1>Error</h1>';
throw new Exception("xmlrpc: $response[faultString] ($response[faultCode])");
} else {
// print 'Logged out user'."\n";
}
}
public function nodeGet($nid, $fields = array()) {
$method_name = 'node.get';
$user_args = array(
$nid,
$fields
);
$response = $this->send_xmlrpc($method_name, $user_args);
// display the result on screen
// if (is_array($response) && xmlrpc_is_fault($response)) {
// print '<h1>Error</h1>';
// print '<pre>'. htmlspecialchars(print_r($response, true)) .'</pre>';
// trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
// return FALSE;
// } else {
// print 'Got node: '. print_r($response,1)."\n";
return $response;
// }
}
public function userGet($uid){
return $this->send_xmlrpc('user.get', array($uid));
}
}
more about the why
The original class, while quite nice, depended on an optional PHP library. I find this a little inconvenient in that it adds to the deployment footprint. It also hard-coded a lot of configurations in the constructor which isn't the best way to generalize a solution. (If you want a hard-coded configuration in your class system, best to do it in a child class of an abstract parent.)
I just added a documentation
I just added a documentation page giving an example of Services 6.x-2.0 PHP xmlrpc with api key authentication, without using any of the 'experimental' php functions (like xmlrpc_encode_request()) - this code only uses drupal API functions:
Services 6.x-2.0 PHP xmlrpc example with api key
http://drupal.org/node/774298
Include some more information on your link.
Hello,
Your link (http://drupal.org/node/774298) needs a bit of updating. I know above you said this uses Drupal API functions but on the actual page you do not say this. It took a bit of time to figure this out.
I am still a bit lost in this area but I am not giving up.
Thanks again for the code.
chris
I added a bit more
I added a bit more information to the link... tell me what you're stuck on. Perhaps I can help...
Just a note: In my
Just a note: In my implementation (original post) I chose to use php's "experimental" xmlrpc function because bootstrapping Drupal is often unnecessary and sometimes not possible.
Thanks guys..
Hello, Thanks again for the code and and your time.
Instead of bootstrapping drupal for the xmlrpc function and using the expermental php xmlrpc, why not take the functions needed out of the common.inc and the xmlrpc.inc and have a custom include.
We have an internal ubuntu sandbox here. Very easy to add the php xmlrpc. For a live website.. not so sure on that.
So I included the common.inc.
I am still playing with the code a bit. I will dive more into this next week.
I will keep you both up to date as to what we did.
Thanks for your time and effort.
chris