Building Rock Solid Websites

.

Drupal XMLRPC from Remote PHP site

In the Drupal universe there are oodles of examples for connection of Drupal <-> Drupal sites using XMLRPC and the Services Module. (Although often those examples are out of date.)

But what if you are wanting PHP <->Drupal? Where you can interrogate the Drupal site and update it's data from a PHP script? Very handy...but very hard to find information or examples on...so without boring you further, here is a functioning example that can be used to pull data from your Drupal site.

It is broken into two files; CONNECT.PHP and DRUXML.CLASS.PHP. The connect.php is an example for your scripting, how you throw the function together, with what variables, etc. You can make this into a sub-function of one of your scripts, and pass it numerous different Drupal methods and parameters for those methods (see the Services module for the list of methods). It is set for both Session and Api Key authentication, so your Drupal site is still secure.

Much of the code is adapted from here :http://drupal.org/node/339845

ZIP file attached. Have fun.

Code here:

connect.php

<?php
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/*
*
* name: PHP interface to Drupal via XMLRPC
* by onyx@chalcedony.co.nz
* http://chalcedony.co.nz
*/
require('druxml.class.php');

//Set initial connection parameters
$localDomain = 'example.com'; //the domain that this php code is on. NEEDS to be input for the api key in the Services module api key setup
$apiKey = 'e3880ad3e757b3f93daff0ffc68a15d4'; // the api key copied from the drupal Services module
$endPoint = 'http://example.com/dru/services/xmlrpc'; //the drupal site's xmlrpc url
$credentials = array(); // the user credentials for attaching to the drupal site. The first connection is anonymous.
$auth_user = array(0 => 'xmluser',1 => 'password');// add the authorised user name and password for connection 0=username,1=password
$func = 'node.view'; //the default function
$param = array(0=>'15'); // the default node, sent as array

$talkative = TRUE; // spit out debugging information to screen?

// create connection
$drupalSession = new Drupal_connect( $localDomain, $apiKey, $endPoint, $credentials, $func, $param, $talkative );
$retVal = $drupalSession->dru_connect($endPoint);
$drupalSession->anon_session_id = $retVal['sessid'];
if ($drupalSession->anon_session_id) {
//$drupalSession comes back as an array. It looks like this:
//Array ( [sessid] => 3eaec0962d42c7a7bb42b642551e8e0a [user] => Array ( [uid] => 0 [hostname] => 123.123.2.3 [roles] => Array ( [1] => anonymous user ) [session] => [cache] => 0 ) )

if ($talkative) print 'Anon session id: '.$drupalSession->anon_session_id.'';

$drupalSession->credentials = $auth_user;
// connect for the first time as an authenticated user BEFORE we go getting node data
$data = $drupalSession->auth_connect($localDomain, $endPoint, $drupalSession->anon_session_id, $apiKey, $drupalSession->credentials, 'user.login', $param, $talkative);
if ($talkative) print_r($data);

// now we go get node data
$data = $drupalSession->auth_connect($localDomain, $endPoint, $drupalSession->anon_session_id, $apiKey, $drupalSession->credentials, $func, $param, $talkative);
print_r($data);
}
/**
* 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;
}
?>

druxml.class.php

<?php
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/*
*
* name: PHP interface to Drupal via XMLRPC
* by onyx@chalcedony.co.nz
* http://chalcedony.co.nz
*/

/**
* REQUIRES Service module and XMLRPC server set in the drupal site, with API Key authentication
* *********************************************************************************************/
class Drupal_connect {
function __construct( $domain = '', $apiKey = '', $endPoint = '', $credentials = '', $func ='', $param='', $verbose = TRUE )
{
// set local domain or IP address
// this needs to match the domain set when you created the API key
$this->domain = $domain;

// set API key
$this->kid = $apiKey;

// set target web service endpoint
$this->endpoint = $endPoint;

// extended debugging
$this->verbose = $verbose;

// user credentials for login
$this->credentials = $credentials;

// function within drupal services to run; REMEMBER PERMISSIONS!
$this->func = $func;

// parameters to pass to drupal function
$this->param = $param;

}
public function dru_connect ($endPoint){
// needs only $endPoint passed to it.
/*
* anonymous connection function
* 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);
$output ='';
// display the result on screen
if (xmlrpc_is_fault($response)) {
print 'Error from '.$endPoint.':';
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
} else {
// let's look at what came back
$output.= 'Received from '.$endPoint.': ';
$output.= ''. htmlspecialchars(print_r($response, true)) .'';
$output.='';
}
if ($this->verbose) {
// print_r ($response);
print $output;
}

return $response;
}

public function auth_connect( $domain, $endPoint, $sessid, $kid, $user_credentials, $method_name='user.login', $param, $verbose) {
/*
* 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");
$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,
$sessid,
);

// any user-defined arguments for this service
// here we use the login credentials if it is a user.login method, else pass the method parameters
if ($method_name =='user.login') { $user_args = $user_credentials;
} else {
$user_args = $param;
}
//print_r($user_args);
// 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,
)
)
);

$output ='';
// connect
$connect = file_get_contents($endPoint, false, $context);
//print $connect;
// retrieve the result
$response = xmlrpc_decode($connect);

// display the result on screen
if (xmlrpc_is_fault($response)) {
print 'Error';
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
} else {
$output .='Received';
$output .= ''. htmlspecialchars(print_r($response, true)) .'';
if ($method_name =='user.login'){
// 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 = $sessid;
if ($this->verbose) {
// print_r ($response);
print $output;
}

return $response;

}
}

}
?>

Comments

d'oh

(SIgh. Nevermind. I see both files there. Just assumed it was a partial example. THANKS :)

Attached?!

Hey, I stumbled across this, but can't seem to find the attached files...!! :) Could you attach them? Or email me: zallen at gmail.