-
-
Save FoxBuru/6fb2d0655778fe820dd0d1c9d6c890ee to your computer and use it in GitHub Desktop.
PHP simple XML-RPC Client
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @author Henrik Hofmeister | |
* @author Ivan de Gyves | |
* @license MIT | |
* @version 1.1 | |
* | |
* XmlRPC using SimpleXML and CURL | |
* XML-RPC server can be a HTTP or HTTPS one | |
* | |
* Usage: | |
* | |
* $xmlRpc = new XmlRPC('localhost','/service/xmlrpc'); | |
* $xmlRpc->setCredentials('username','password'); | |
* $xmlRpc->setDebug(true); | |
* $output = $xmlRpc->call('method', [arg1, arg2, ..., argn]); | |
* | |
* If using SSL: | |
* | |
* $xmlRpc = new XmlRPC('localhost','/service/xmlrpc', TRUE); | |
* | |
* If we need to use a custom certificate/CA: | |
* | |
* $xmlc->setCert($xcert, FALSE, $xkey, $xcpwd); | |
* | |
*/ | |
class XmlRPC { | |
private $secure; | |
private $certdata; | |
private $host; | |
private $path; | |
private $connection; | |
private $debug = false; | |
private $user; | |
private $pass; | |
public function __construct($host,$path, $secure = FALSE) { | |
$this->host = $host; | |
$this->path = $path; | |
$this->secure = $secure; | |
} | |
public function setHost($host, $secure = FALSE) { | |
$this->disconnect(); | |
$this->host = $host; | |
$this->secure = $secure; | |
} | |
public function setCredentials($user,$pass) { | |
$this->user = $user; | |
$this->pass = $pass; | |
} | |
/* | |
* Set a custom certificate TLS context | |
* Useful when we're using custom CA's or self-signed Certs (pwd & pwdless) | |
*/ | |
public function setCert($path, $isCA = FALSE, $key = NULL, $pwd = NULL){ | |
$this->certdata = array( | |
"CA" => $isCA, | |
"path" => $path | |
); | |
if(!empty($key)) | |
$this->certdata["key"] = $key; | |
if(!empty($pwd)) | |
$this->certdata["pwd"] = $pwd; | |
} | |
public function setDebug($debug) { | |
$this->debug = $debug; | |
} | |
public function call($methodName) { | |
$args = func_get_args(); | |
array_shift($args); | |
$request = $this->encodeRequest($methodName,$args); | |
if ($this->debug) { | |
echo "REQUEST:\n$request\n\n"; | |
} | |
$conn = $this->getConnection(); | |
$this->getSSLContext($conn); | |
curl_setopt($conn, CURLOPT_RETURNTRANSFER, 1); | |
if($this->debug) | |
{ | |
curl_setopt($conn, CURLOPT_VERBOSE, 1); | |
} | |
curl_setopt($conn, CURLOPT_USERAGENT,'PHP XML-RPC Client'); | |
curl_setopt($conn, CURLOPT_POST, 1); | |
curl_setopt($conn, CURLOPT_POSTFIELDS, $request); | |
//curl_setopt($conn, CURLOPT_HEADER, 1); | |
if ($this->user) | |
curl_setopt($conn, CURLOPT_USERPWD,$this->user.':'.$this->pass); | |
$response = curl_exec($conn); | |
if ($this->debug) { | |
echo "CURL INFO:\n"; | |
foreach(curl_getinfo($conn) as $name => $val) { | |
if (is_array($val)) | |
$val = implode(', ',$val); | |
echo $name . ': ' . htmlentities($val). "\n"; | |
} | |
echo "\n\nRESPONSE:\n$response"; | |
} | |
$httpCode = curl_getinfo($conn,CURLINFO_HTTP_CODE); | |
if ($httpCode != 200) { | |
if ($httpCode == 401) | |
throw new XmlRPC_Exception("Invalid user name or password - authentication failed",9999); | |
else | |
throw new XmlRPC_Exception("Server replied with an error code: $httpCode",9999); | |
} | |
if (empty($response)) { | |
throw new XmlRPC_Exception('Empty response from server at: '.$this->getUrl(),9999); | |
} | |
$result = $this->parseResponse($response); | |
return $result; | |
} | |
public function __call($name, $arguments) { | |
array_unshift($arguments,$name); | |
return call_user_method_array('call',$this,$arguments); | |
} | |
private function encodeRequest($methodName,$args) { | |
$req = '<?xml version="1.0" encoding="UTF-8" ?>'; | |
$params = ''; | |
foreach($args as $arg) { | |
$parm = XmlRPC_Parm::encode($arg); | |
$params .= sprintf('<param><value>%s</value></param>',$parm); | |
} | |
$req .= "\n<methodCall><methodName>$methodName</methodName><params>$params</params></methodCall>"; | |
return $req; | |
} | |
private function parseResponse($xmlStr) { | |
$xml = simplexml_load_string(trim($xmlStr)); | |
$response = array(); | |
if (count($xml->fault) > 0) { | |
//An error was returned | |
$fault = $this->parseValue($xml->fault->value); | |
throw new XmlRPC_Exception($fault->faultString,$fault->faultCode); | |
} | |
if (count($xml->params->param) == 1) | |
$scalar = true; | |
foreach($xml->params->param as $param) { | |
$valueStruct = $param->value; | |
$value = $this->parseValue($valueStruct); | |
if ($scalar) | |
return $value; | |
else | |
$response[] = $value; | |
} | |
return $response; | |
} | |
private function parseValue($valueStruct) { | |
switch(true) { | |
case count($valueStruct->struct) > 0: | |
$value = new stdClass(); | |
foreach($valueStruct->struct->member as $member) { | |
$name = (string)$member->name; | |
$memberValue = $this->parseValue($member->value); | |
$value->$name = $memberValue; | |
} | |
return $value; | |
break; | |
case count($valueStruct->array) > 0: | |
$value = array(); | |
foreach($valueStruct->array->data->value as $arrayValue) { | |
$value[] = $this->parseValue($arrayValue); | |
} | |
return $value; | |
break; | |
case count($valueStruct->i4) > 0: | |
return (int)$valueStruct->i4; | |
case count($valueStruct->int) > 0: | |
return (int)$valueStruct->int; | |
case count($valueStruct->boolean) > 0: | |
return (boolean) $valueStruct->boolean; | |
case count($valueStruct->string) > 0: | |
return (string)$valueStruct->string; | |
case count($valueStruct->double) > 0: | |
return (double)$valueStruct->double; | |
case count($valueStruct->dateTime) > 0: | |
return (string)$valueStruct->dateTime; | |
case count($valueStruct->base64) > 0: | |
return (string)$valueStruct->base64; | |
} | |
} | |
private function disconnect() { | |
if ($this->connection) | |
@curl_close($this->connection); | |
$this->connection = null; | |
} | |
protected function getUrl() { | |
if ($this->secure) | |
return sprintf('https://%s%s',$this->host,$this->path); | |
else | |
return sprintf('http://%s%s',$this->host,$this->path); | |
} | |
private function getConnection() { | |
if (!$this->connection) | |
$this->connection = curl_init($this->getUrl()); | |
return $this->connection; | |
} | |
private function getSSLContext($conn) { | |
$cd = $this->certdata; | |
if( !empty($conn) && !empty($cd) && | |
array_key_exists('CA', $cd) && | |
array_key_exists('path', $cd) ){ | |
curl_setopt($conn, CURLOPT_SSL_VERIFYPEER, true); | |
curl_setopt($conn, CURLOPT_SSL_VERIFYHOST, 2); | |
if($cd["CA"] === TRUE){ | |
if (file_exists($cd["path"])) { | |
if (is_dir($cd["path"])) | |
curl_setopt($conn, CURLOPT_CAPATH, $cd["path"]); | |
else | |
curl_setopt($conn, CURLOPT_CAINFO, $cd["path"]); | |
} | |
else | |
throw new XmlRPC_Exception("SSL CA specified, but path " . $cd["path"] . " doesn't exists.", 9999); | |
} else { | |
if (file_exists($cd["path"]) && !is_dir($cd["path"])) { | |
curl_setopt($conn, CURLOPT_SSLCERT, $cd["path"]); | |
# Add key and except if not exists | |
if (!empty($cd["key"]) && file_exists($cd["key"]) && !is_dir($cd["key"]) ) | |
curl_setopt($conn, CURLOPT_SSLKEY, $cd["key"]); | |
else | |
throw new XmlRPC_Exception("SSL key specified, but path " . $cd["key"] . " doesn't exists.", 9999); | |
if (!empty($cd["pwd"])) | |
curl_setopt($conn, CURLOPT_SSLCERTPASSWD, $cd["pwd"]); | |
} | |
else | |
throw new XmlRPC_Exception("SSL cert specified, but path " . $cd["path"] . " doesn't exists.", 9999); | |
} | |
} | |
} | |
public function __destruct() { | |
$this->disconnect(); | |
} | |
} | |
class XmlRPC_Exception extends Exception { | |
public function __construct($message, $code) { | |
parent::__construct($message, $code); | |
} | |
} | |
/* PARMS */ | |
class XmlRPC_Parm { | |
private $value; | |
public function __construct($value) { | |
$this->value = $value; | |
} | |
public function getType() { | |
switch(true) { | |
case is_bool($arg): | |
return 'boolean'; | |
case is_int($arg): | |
return 'int'; | |
case is_float($arg): | |
case is_double($arg): | |
return 'double'; | |
break; | |
case is_object($arg): | |
return 'struct'; | |
case is_bool($arg): | |
return 'boolean'; | |
case is_array($arg): | |
return 'array'; | |
default: | |
case is_string($arg): | |
return 'string'; | |
break; | |
} | |
} | |
public function getValue() { | |
return $this->value; | |
} | |
protected function getFormattedValue() { | |
return $this->value; | |
} | |
public function __toString() { | |
return sprintf('<%1$s>%2$s</%1$s>',$this->getType(),$this->getFormattedValue()); | |
} | |
/** | |
* | |
* @param mixed $arg | |
* @return XmlRPC_Parm | |
*/ | |
public static function encode($arg) { | |
switch(true) { | |
case $arg instanceof XmlRPC_Parm: | |
return $arg; | |
case is_object($arg): | |
return new XmlRPC_Struct($arg); | |
case is_array($arg): | |
return new XmlRPC_Array($arg); | |
default: | |
case is_bool($arg): | |
case is_int($arg): | |
case is_float($arg): | |
case is_double($arg): | |
case is_string($arg): | |
return new XmlRPC_Parm($arg); | |
} | |
} | |
public static function decode($param) { | |
} | |
} | |
class XmlRPC_Struct extends XmlRPC_Parm{ | |
protected function getFormattedValue() { | |
$result = ''; | |
foreach($this->getValue() as $name=>$value) { | |
$parm = XmlRPC_Parm::encode($value); | |
$result .= sprintf('<member><name>%s</name><value>%s</value></member>',$name,$parm); | |
} | |
return $result; | |
} | |
public function getType() { | |
return 'struct'; | |
} | |
} | |
class XmlRPC_Array extends XmlRPC_Parm{ | |
protected function getFormattedValue() { | |
$result = '<data>'; | |
foreach($this->getValue() as $value) { | |
$parm = XmlRPC_Parm::encode($value); | |
$result .= sprintf('<value>%s</value>',$parm); | |
} | |
return $result.'</data>'; | |
} | |
public function getType() { | |
return 'array'; | |
} | |
} | |
class XmlRPC_Date extends XmlRPC_Parm{ | |
protected function getFormattedValue() { | |
return date('Ymd\TH:i:s',parent::getFormattedValue()); | |
} | |
public function getType() { | |
return 'dateTime.iso8601'; | |
} | |
} | |
class XmlRPC_Binary extends XmlRPC_Parm{ | |
protected function getFormattedValue() { | |
return base64_encode(parent::getFormattedValue()); | |
} | |
public function getType() { | |
return 'base64'; | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment