--- /dev/null
+<?php
+
+class IPF_XmlRpc_Fault
+{
+ protected $_code;
+ protected $_encoding = 'UTF-8';
+ protected $_message;
+
+ protected $_internal = array(
+ 404 => 'Unknown Error',
+
+ // 610 - 619 reflection errors
+ 610 => 'Invalid method class',
+ 611 => 'Unable to attach function or callback; not callable',
+ 612 => 'Unable to load array; not an array',
+ 613 => 'One or more method records are corrupt or otherwise unusable',
+
+ // 620 - 629 dispatch errors
+ 620 => 'Method does not exist',
+ 621 => 'Error instantiating class to invoke method',
+ 622 => 'Method missing implementation',
+ 623 => 'Calling parameters do not match signature',
+
+ // 630 - 639 request errors
+ 630 => 'Unable to read request',
+ 631 => 'Failed to parse request',
+ 632 => 'Invalid request, no method passed; request must contain a \'methodName\' tag',
+ 633 => 'Param must contain a value',
+ 634 => 'Invalid method name',
+ 635 => 'Invalid XML provided to request',
+ 636 => 'Error creating xmlrpc value',
+
+ // 640 - 649 system.* errors
+ 640 => 'Method does not exist',
+
+ // 650 - 659 response errors
+ 650 => 'Invalid XML provided for response',
+ 651 => 'Failed to parse response',
+ 652 => 'Invalid response',
+ 653 => 'Invalid XMLRPC value in response',
+ );
+
+ public function __construct($code = 404, $message = '')
+ {
+ $this->setCode($code);
+ $code = $this->getCode();
+
+ if (empty($message) && isset($this->_internal[$code])) {
+ $message = $this->_internal[$code];
+ } elseif (empty($message)) {
+ $message = 'Unknown error';
+ }
+ $this->setMessage($message);
+ }
+
+ public function setCode($code)
+ {
+ $this->_code = (int) $code;
+ return $this;
+ }
+
+ public function getCode()
+ {
+ return $this->_code;
+ }
+
+ public function setMessage($message)
+ {
+ $this->_message = (string) $message;
+ return $this;
+ }
+
+ public function getMessage()
+ {
+ return $this->_message;
+ }
+
+ public function setEncoding($encoding)
+ {
+ $this->_encoding = $encoding;
+ return $this;
+ }
+
+ public function getEncoding()
+ {
+ return $this->_encoding;
+ }
+
+ public function loadXml($fault)
+ {
+ if (!is_string($fault)) {
+ throw new IPF_Exception('Invalid XML provided to fault');
+ }
+
+ try {
+ $xml = @new SimpleXMLElement($fault);
+ } catch (Exception $e) {
+ // Not valid XML
+ throw new IPF_Exception('Failed to parse XML fault: ' . $e->getMessage(), 500);
+ }
+
+ // Check for fault
+ if (!$xml->fault) {
+ // Not a fault
+ return false;
+ }
+
+ if (!$xml->fault->value->struct) {
+ // not a proper fault
+ throw new IPF_Exception('Invalid fault structure', 500);
+ }
+
+ $structXml = $xml->fault->value->asXML();
+ $structXml = preg_replace('/<\?xml version=.*?\?>/i', '', $structXml);
+ $struct = IPF_XmlRpc_Value::getXmlRpcValue(trim($structXml), IPF_XmlRpc_Value::XML_STRING);
+ $struct = $struct->getValue();
+
+ if (isset($struct['faultCode'])) {
+ $code = $struct['faultCode'];
+ }
+ if (isset($struct['faultString'])) {
+ $message = $struct['faultString'];
+ }
+
+ if (empty($code) && empty($message)) {
+ throw new IPF_Exception('Fault code and string required');
+ }
+
+ if (empty($code)) {
+ $code = '404';
+ }
+
+ if (empty($message)) {
+ if (isset($this->_internal[$code])) {
+ $message = $this->_internal[$code];
+ } else {
+ $message = 'Unknown Error';
+ }
+ }
+
+ $this->setCode($code);
+ $this->setMessage($message);
+
+ return true;
+ }
+
+ public static function isFault($xml)
+ {
+ $fault = new self();
+ try {
+ $isFault = $fault->loadXml($xml);
+ } catch (IPF_Exception $e) {
+ $isFault = false;
+ }
+
+ return $isFault;
+ }
+
+ public function saveXML()
+ {
+ // Create fault value
+ $faultStruct = array(
+ 'faultCode' => $this->getCode(),
+ 'faultString' => $this->getMessage()
+ );
+ $value = IPF_XmlRpc_Value::getXmlRpcValue($faultStruct);
+ $valueDOM = new DOMDocument('1.0', $this->getEncoding());
+ $valueDOM->loadXML($value->saveXML());
+
+ // Build response XML
+ $dom = new DOMDocument('1.0', 'ISO-8859-1');
+ $r = $dom->appendChild($dom->createElement('methodResponse'));
+ $f = $r->appendChild($dom->createElement('fault'));
+ $f->appendChild($dom->importNode($valueDOM->documentElement, 1));
+
+ return $dom->saveXML();
+ }
+
+ public function __toString()
+ {
+ return $this->saveXML();
+ }
+}
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Request
+{
+ protected $_encoding = 'UTF-8';
+ protected $_method;
+ protected $_xml;
+ protected $_params = array();
+ protected $_fault = null;
+ protected $_types = array();
+ protected $_xmlRpcParams = array();
+
+ public function __construct($method = null, $params = null)
+ {
+ if ($method !== null) {
+ $this->setMethod($method);
+ }
+
+ if ($params !== null) {
+ $this->setParams($params);
+ }
+ }
+
+ public function setEncoding($encoding)
+ {
+ $this->_encoding = $encoding;
+ return $this;
+ }
+
+ public function getEncoding()
+ {
+ return $this->_encoding;
+ }
+
+ public function setMethod($method)
+ {
+ if (!is_string($method) || !preg_match('/^[a-z0-9_.:\/]+$/i', $method)) {
+ $this->_fault = new IPF_XmlRpc_Fault(634, 'Invalid method name ("' . $method . '")');
+ $this->_fault->setEncoding($this->getEncoding());
+ return false;
+ }
+
+ $this->_method = $method;
+ return true;
+ }
+
+ public function getMethod()
+ {
+ return $this->_method;
+ }
+
+ public function addParam($value, $type = null)
+ {
+ $this->_params[] = $value;
+ if (null === $type) {
+ // Detect type if not provided explicitly
+ if ($value instanceof IPF_XmlRpc_Value) {
+ $type = $value->getType();
+ } else {
+ $xmlRpcValue = IPF_XmlRpc_Value::getXmlRpcValue($value);
+ $type = $xmlRpcValue->getType();
+ }
+ }
+ $this->_types[] = $type;
+ $this->_xmlRpcParams[] = array('value' => $value, 'type' => $type);
+ }
+
+ public function setParams()
+ {
+ $argc = func_num_args();
+ $argv = func_get_args();
+ if (0 == $argc) {
+ return;
+ }
+
+ if ((1 == $argc) && is_array($argv[0])) {
+ $params = array();
+ $types = array();
+ $wellFormed = true;
+ foreach ($argv[0] as $arg) {
+ if (!is_array($arg) || !isset($arg['value'])) {
+ $wellFormed = false;
+ break;
+ }
+ $params[] = $arg['value'];
+
+ if (!isset($arg['type'])) {
+ $xmlRpcValue = IPF_XmlRpc_Value::getXmlRpcValue($arg['value']);
+ $arg['type'] = $xmlRpcValue->getType();
+ }
+ $types[] = $arg['type'];
+ }
+ if ($wellFormed) {
+ $this->_xmlRpcParams = $argv[0];
+ $this->_params = $params;
+ $this->_types = $types;
+ } else {
+ $this->_params = $argv[0];
+ $this->_types = array();
+ $xmlRpcParams = array();
+ foreach ($argv[0] as $arg) {
+ if ($arg instanceof IPF_XmlRpc_Value) {
+ $type = $arg->getType();
+ } else {
+ $xmlRpcValue = IPF_XmlRpc_Value::getXmlRpcValue($arg);
+ $type = $xmlRpcValue->getType();
+ }
+ $xmlRpcParams[] = array('value' => $arg, 'type' => $type);
+ $this->_types[] = $type;
+ }
+ $this->_xmlRpcParams = $xmlRpcParams;
+ }
+ return;
+ }
+
+ $this->_params = $argv;
+ $this->_types = array();
+ $xmlRpcParams = array();
+ foreach ($argv as $arg) {
+ if ($arg instanceof IPF_XmlRpc_Value) {
+ $type = $arg->getType();
+ } else {
+ $xmlRpcValue = IPF_XmlRpc_Value::getXmlRpcValue($arg);
+ $type = $xmlRpcValue->getType();
+ }
+ $xmlRpcParams[] = array('value' => $arg, 'type' => $type);
+ $this->_types[] = $type;
+ }
+ $this->_xmlRpcParams = $xmlRpcParams;
+ }
+
+ public function getParams()
+ {
+ return $this->_params;
+ }
+
+ public function getTypes()
+ {
+ return $this->_types;
+ }
+
+ public function loadXml($request)
+ {
+ if (!is_string($request)) {
+ $this->_fault = new IPF_XmlRpc_Fault(635);
+ $this->_fault->setEncoding($this->getEncoding());
+ return false;
+ }
+
+ try {
+ $xml = @new SimpleXMLElement($request);
+ } catch (Exception $e) {
+ // Not valid XML
+ $this->_fault = new IPF_XmlRpc_Fault(631);
+ $this->_fault->setEncoding($this->getEncoding());
+ return false;
+ }
+
+ // Check for method name
+ if (empty($xml->methodName)) {
+ // Missing method name
+ $this->_fault = new IPF_XmlRpc_Fault(632);
+ $this->_fault->setEncoding($this->getEncoding());
+ return false;
+ }
+
+ $this->_method = (string) $xml->methodName;
+
+ // Check for parameters
+ if (!empty($xml->params)) {
+ $types = array();
+ $argv = array();
+ foreach ($xml->params->children() as $param) {
+ if (! $param->value instanceof SimpleXMLElement) {
+ $this->_fault = new IPF_XmlRpc_Fault(633);
+ $this->_fault->setEncoding($this->getEncoding());
+ return false;
+ }
+
+ try {
+ $param = IPF_XmlRpc_Value::getXmlRpcValue($param->value, IPF_XmlRpc_Value::XML_STRING);
+ $types[] = $param->getType();
+ $argv[] = $param->getValue();
+ } catch (Exception $e) {
+ $this->_fault = new IPF_XmlRpc_Fault(636);
+ $this->_fault->setEncoding($this->getEncoding());
+ return false;
+ }
+ }
+
+ $this->_types = $types;
+ $this->_params = $argv;
+ }
+
+ $this->_xml = $request;
+
+ return true;
+ }
+
+ public function isFault()
+ {
+ return $this->_fault instanceof IPF_XmlRpc_Fault;
+ }
+
+ public function getFault()
+ {
+ return $this->_fault;
+ }
+
+ protected function _getXmlRpcParams()
+ {
+ $params = array();
+ if (is_array($this->_xmlRpcParams)) {
+ foreach ($this->_xmlRpcParams as $param) {
+ $value = $param['value'];
+ $type = isset($param['type']) ? $param['type'] : IPF_XmlRpc_Value::AUTO_DETECT_TYPE;
+
+ if (!$value instanceof IPF_XmlRpc_Value) {
+ $value = IPF_XmlRpc_Value::getXmlRpcValue($value, $type);
+ }
+ $params[] = $value;
+ }
+ }
+
+ return $params;
+ }
+
+ public function saveXML()
+ {
+ $args = $this->_getXmlRpcParams();
+ $method = $this->getMethod();
+
+ $dom = new DOMDocument('1.0', $this->getEncoding());
+ $mCall = $dom->appendChild($dom->createElement('methodCall'));
+ $mName = $mCall->appendChild($dom->createElement('methodName', $method));
+
+ if (is_array($args) && count($args)) {
+ $params = $mCall->appendChild($dom->createElement('params'));
+
+ foreach ($args as $arg) {
+ /* @var $arg IPF_XmlRpc_Value */
+ $argDOM = new DOMDocument('1.0', $this->getEncoding());
+ $argDOM->loadXML($arg->saveXML());
+
+ $param = $params->appendChild($dom->createElement('param'));
+ $param->appendChild($dom->importNode($argDOM->documentElement, 1));
+ }
+ }
+
+ return $dom->saveXML();
+ }
+
+ public function __toString()
+ {
+ return $this->saveXML();
+ }
+}
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Request_Http extends IPF_XmlRpc_Request
+{
+ protected $_headers;
+ protected $_xml;
+ public function __construct()
+ {
+ $fh = fopen('php://input', 'r');
+ if (!$fh) {
+ $this->_fault = new IPF_Exception(630);
+ return;
+ }
+
+ $xml = '';
+ while (!feof($fh)) {
+ $xml .= fgets($fh);
+ }
+ fclose($fh);
+
+ $this->_xml = $xml;
+
+ $this->loadXml($xml);
+ }
+
+ public function getRawRequest()
+ {
+ return $this->_xml;
+ }
+
+ public function getHeaders()
+ {
+ if (null === $this->_headers) {
+ $this->_headers = array();
+ foreach ($_SERVER as $key => $value) {
+ if ('HTTP_' == substr($key, 0, 5)) {
+ $header = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
+ $this->_headers[$header] = $value;
+ }
+ }
+ }
+
+ return $this->_headers;
+ }
+
+ public function getFullRequest()
+ {
+ $request = '';
+ foreach ($this->getHeaders() as $key => $value) {
+ $request .= $key . ': ' . $value . "\n";
+ }
+
+ $request .= $this->_xml;
+
+ return $request;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Request_Stdin extends IPF_XmlRpc_Request
+{
+ protected $_xml;
+
+ public function __construct()
+ {
+ $fh = fopen('php://stdin', 'r');
+ if (!$fh) {
+ $this->_fault = new IPF_XmlRpc_Fault(630);
+ return;
+ }
+
+ $xml = '';
+ while (!feof($fh)) {
+ $xml .= fgets($fh);
+ }
+ fclose($fh);
+
+ $this->_xml = $xml;
+
+ $this->loadXml($xml);
+ }
+
+ public function getRawRequest()
+ {
+ return $this->_xml;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Response
+{
+ protected $_return;
+ protected $_type;
+ protected $_encoding = 'UTF-8';
+ protected $_fault = null;
+
+ public function __construct($return = null, $type = null)
+ {
+ $this->setReturnValue($return, $type);
+ }
+
+ public function setEncoding($encoding)
+ {
+ $this->_encoding = $encoding;
+ return $this;
+ }
+
+ public function getEncoding()
+ {
+ return $this->_encoding;
+ }
+
+ public function setReturnValue($value, $type = null)
+ {
+ $this->_return = $value;
+ $this->_type = (string) $type;
+ }
+
+ public function getReturnValue()
+ {
+ return $this->_return;
+ }
+
+ protected function _getXmlRpcReturn()
+ {
+ return IPF_XmlRpc_Value::getXmlRpcValue($this->_return);
+ }
+
+ public function isFault()
+ {
+ return $this->_fault instanceof IPF_XmlRpc_Fault;
+ }
+
+ public function getFault()
+ {
+ return $this->_fault;
+ }
+
+ public function loadXml($response)
+ {
+ if (!is_string($response)) {
+ $this->_fault = new IPF_XmlRpc_Fault(650);
+ $this->_fault->setEncoding($this->getEncoding());
+ return false;
+ }
+
+ try {
+ $xml = @new SimpleXMLElement($response);
+ } catch (Exception $e) {
+ // Not valid XML
+ $this->_fault = new IPF_XmlRpc_Fault(651);
+ $this->_fault->setEncoding($this->getEncoding());
+ return false;
+ }
+
+ if (!empty($xml->fault)) {
+ // fault response
+ $this->_fault = new IPF_XmlRpc_Fault();
+ $this->_fault->setEncoding($this->getEncoding());
+ $this->_fault->loadXml($response);
+ return false;
+ }
+
+ if (empty($xml->params)) {
+ // Invalid response
+ $this->_fault = new IPF_XmlRpc_Fault(652);
+ $this->_fault->setEncoding($this->getEncoding());
+ return false;
+ }
+
+ try {
+ if (!isset($xml->params) || !isset($xml->params->param) || !isset($xml->params->param->value)) {
+ throw new IPF_Exception('Missing XML-RPC value in XML');
+ }
+ $valueXml = $xml->params->param->value->asXML();
+ $valueXml = preg_replace('/<\?xml version=.*?\?>/i', '', $valueXml);
+ $value = IPF_XmlRpc_Value::getXmlRpcValue(trim($valueXml), IPF_XmlRpc_Value::XML_STRING);
+ } catch (IPF_Exception $e) {
+ $this->_fault = new IPF_XmlRpc_Fault(653);
+ $this->_fault->setEncoding($this->getEncoding());
+ return false;
+ }
+
+ $this->setReturnValue($value->getValue());
+ return true;
+ }
+
+ public function saveXML()
+ {
+ $value = $this->_getXmlRpcReturn();
+ $valueDOM = new DOMDocument('1.0', $this->getEncoding());
+ $valueDOM->loadXML($value->saveXML());
+
+ $dom = new DOMDocument('1.0', $this->getEncoding());
+ $response = $dom->appendChild($dom->createElement('methodResponse'));
+ $params = $response->appendChild($dom->createElement('params'));
+ $param = $params->appendChild($dom->createElement('param'));
+
+ $param->appendChild($dom->importNode($valueDOM->documentElement, true));
+
+ return $dom->saveXML();
+ }
+
+ public function __toString()
+ {
+ return $this->saveXML();
+ }
+}
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Response_Http extends IPF_XmlRpc_Response
+{
+ public function __toString()
+ {
+ if (!headers_sent()) {
+ header('Content-Type: text/xml; charset=' . strtolower($this->getEncoding()));
+ }
+ return parent::__toString();
+ }
+}
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Server
+{
+ protected $_encoding = 'UTF-8';
+ protected $_methods = array();
+ protected $_request = null;
+ protected $_responseClass = 'IPF_XmlRpc_Response_Http';
+
+ protected $_table = array();
+
+ protected $_typeMap = array(
+ 'i4' => 'i4',
+ 'int' => 'int',
+ 'integer' => 'int',
+ 'double' => 'double',
+ 'float' => 'double',
+ 'real' => 'double',
+ 'boolean' => 'boolean',
+ 'bool' => 'boolean',
+ 'true' => 'boolean',
+ 'false' => 'boolean',
+ 'string' => 'string',
+ 'str' => 'string',
+ 'base64' => 'base64',
+ 'dateTime.iso8601' => 'dateTime.iso8601',
+ 'date' => 'dateTime.iso8601',
+ 'time' => 'dateTime.iso8601',
+ 'time' => 'dateTime.iso8601',
+ 'array' => 'array',
+ 'struct' => 'struct',
+ 'null' => 'nil',
+ 'nil' => 'nil',
+ 'void' => 'void',
+ 'mixed' => 'struct'
+ );
+
+ public function __construct()
+ {
+ // Setup system.* methods
+ $system = array(
+ 'listMethods',
+ 'methodHelp',
+ 'methodSignature',
+ 'multicall'
+ );
+
+ $class = IPF_Server_Reflection::reflectClass($this);
+ foreach ($system as $method) {
+ $reflection = new IPF_Server_Reflection_Method($class, new ReflectionMethod($this, $method), 'system');
+ $reflection->system = true;
+ $this->_methods[] = $reflection;
+ }
+
+ $this->_buildDispatchTable();
+ }
+
+ protected function _fixTypes(IPF_Server_Reflection_Function_Abstract $method)
+ {
+ foreach ($method->getPrototypes() as $prototype) {
+ foreach ($prototype->getParameters() as $param) {
+ $pType = $param->getType();
+ if (isset($this->_typeMap[$pType])) {
+ $param->setType($this->_typeMap[$pType]);
+ } else {
+ $param->setType('void');
+ }
+ }
+ }
+ }
+
+ protected function _buildDispatchTable()
+ {
+ $table = array();
+ foreach ($this->_methods as $dispatchable) {
+ if ($dispatchable instanceof IPF_Server_Reflection_Function_Abstract) {
+ // function/method call
+ $ns = $dispatchable->getNamespace();
+ $name = $dispatchable->getName();
+ $name = empty($ns) ? $name : $ns . '.' . $name;
+
+ if (isset($table[$name])) {
+ throw new IPF_Exception('Duplicate method registered: ' . $name);
+ }
+ $table[$name] = $dispatchable;
+ $this->_fixTypes($dispatchable);
+
+ continue;
+ }
+
+ if ($dispatchable instanceof IPF_Server_Reflection_Class) {
+ foreach ($dispatchable->getMethods() as $method) {
+ $ns = $method->getNamespace();
+ $name = $method->getName();
+ $name = empty($ns) ? $name : $ns . '.' . $name;
+
+ if (isset($table[$name])) {
+ throw new IPF_Exception('Duplicate method registered: ' . $name);
+ }
+ $table[$name] = $method;
+ $this->_fixTypes($method);
+ continue;
+ }
+ }
+ }
+
+ $this->_table = $table;
+ }
+
+ public function setEncoding($encoding)
+ {
+ $this->_encoding = $encoding;
+ return $this;
+ }
+
+ public function getEncoding()
+ {
+ return $this->_encoding;
+ }
+
+ public function addFunction($function, $namespace = '')
+ {
+ if (!is_string($function) && !is_array($function)) {
+ throw new IPF_Exception('Unable to attach function; invalid', 611);
+ }
+
+ $argv = null;
+ if (2 < func_num_args()) {
+ $argv = func_get_args();
+ $argv = array_slice($argv, 2);
+ }
+
+ $function = (array) $function;
+ foreach ($function as $func) {
+ if (!is_string($func) || !function_exists($func)) {
+ throw new IPF_Exception('Unable to attach function; invalid', 611);
+ }
+ $this->_methods[] = IPF_Server_Reflection::reflectFunction($func, $argv, $namespace);
+ }
+
+ $this->_buildDispatchTable();
+ }
+
+ public function loadFunctions($array)
+ {
+ if (!is_array($array)) {
+ throw new IPF_Exception('Unable to load array; not an array', 612);
+ }
+
+ foreach ($array as $key => $value) {
+ if (!$value instanceof IPF_Server_Reflection_Function_Abstract
+ && !$value instanceof IPF_Server_Reflection_Class)
+ {
+ throw new IPF_Exception('One or more method records are corrupt or otherwise unusable', 613);
+ }
+
+ if ($value->system) {
+ unset($array[$key]);
+ }
+ }
+
+ foreach ($array as $dispatchable) {
+ $this->_methods[] = $dispatchable;
+ }
+
+ $this->_buildDispatchTable();
+ }
+
+ public function setPersistence($class = null)
+ {
+ }
+
+ public function setClass($class, $namespace = '', $argv = null)
+ {
+ if (is_string($class) && !class_exists($class)) {
+ if (!class_exists($class)) {
+ throw new IPF_Exception('Invalid method class', 610);
+ }
+ }
+
+ $argv = null;
+ if (3 < func_num_args()) {
+ $argv = func_get_args();
+ $argv = array_slice($argv, 3);
+ }
+
+ $this->_methods[] = IPF_Reflection::reflectClass($class, $argv, $namespace);
+ $this->_buildDispatchTable();
+ }
+
+ public function setRequest($request)
+ {
+ if (is_string($request) && class_exists($request)) {
+ $request = new $request();
+ if (!$request instanceof IPF_XmlRpc_Request) {
+ throw new IPF_Exception('Invalid request class');
+ }
+ $request->setEncoding($this->getEncoding());
+ } elseif (!$request instanceof IPF_XmlRpc_Request) {
+ throw new IPF_Exception('Invalid request object');
+ }
+
+ $this->_request = $request;
+ return $this;
+ }
+
+ public function getRequest()
+ {
+ return $this->_request;
+ }
+
+ public function fault($fault, $code = 404)
+ {
+ if (!$fault instanceof Exception) {
+ $fault = (string) $fault;
+ $fault = new IPF_Exception($fault, $code);
+ }
+ return IPF_XmlRpc_Server_Fault::getInstance($fault);
+ }
+
+ protected function _handle(IPF_XmlRpc_Request $request)
+ {
+ $method = $request->getMethod();
+
+ // Check for valid method
+ if (!isset($this->_table[$method])) {
+ throw new IPF_Exception('Method "' . $method . '" does not exist', 620);
+ }
+
+ $info = $this->_table[$method];
+ $params = $request->getParams();
+ $argv = $info->getInvokeArguments();
+ if (0 < count($argv)) {
+ $params = array_merge($params, $argv);
+ }
+
+ // Check calling parameters against signatures
+ $matched = false;
+ $sigCalled = $request->getTypes();
+
+ $sigLength = count($sigCalled);
+ $paramsLen = count($params);
+ if ($sigLength < $paramsLen) {
+ for ($i = $sigLength; $i < $paramsLen; ++$i) {
+ $xmlRpcValue = IPF_XmlRpc_Value::getXmlRpcValue($params[$i]);
+ $sigCalled[] = $xmlRpcValue->getType();
+ }
+ }
+
+ $signatures = $info->getPrototypes();
+ foreach ($signatures as $signature) {
+ $sigParams = $signature->getParameters();
+ $tmpParams = array();
+ foreach ($sigParams as $param) {
+ $tmpParams[] = $param->getType();
+ }
+ if ($sigCalled === $tmpParams) {
+ $matched = true;
+ break;
+ }
+ }
+ if (!$matched) {
+ throw new IPF_Exception('Calling parameters do not match signature', 623);
+ }
+
+ if ($info instanceof IPF_Server_Reflection_Function) {
+ $func = $info->getName();
+ $return = call_user_func_array($func, $params);
+ } elseif (($info instanceof IPF_Server_Reflection_Method) && $info->system) {
+ // System methods
+ $return = $info->invokeArgs($this, $params);
+ } elseif ($info instanceof IPF_Server_Reflection_Method) {
+ // Get class
+ $class = $info->getDeclaringClass()->getName();
+
+ if ('static' == $info->isStatic()) {
+ // for some reason, invokeArgs() does not work the same as
+ // invoke(), and expects the first argument to be an object.
+ // So, using a callback if the method is static.
+ $return = call_user_func_array(array($class, $info->getName()), $params);
+ } else {
+ // Object methods
+ try {
+ $object = $info->getDeclaringClass()->newInstance();
+ } catch (Exception $e) {
+ throw new IPF_Exception('Error instantiating class ' . $class . ' to invoke method ' . $info->getName(), 621);
+ }
+
+ $return = $info->invokeArgs($object, $params);
+ }
+ } else {
+ throw new IPF_Exception('Method missing implementation ' . get_class($info), 622);
+ }
+
+ $response = new ReflectionClass($this->_responseClass);
+ return $response->newInstance($return);
+ }
+
+ public function handle(IPF_XmlRpc_Request $request = null)
+ {
+ // Get request
+ if ((null === $request) && (null === ($request = $this->getRequest()))) {
+ $request = new IPF_XmlRpc_Request_Http();
+ $request->setEncoding($this->getEncoding());
+ }
+
+ $this->setRequest($request);
+
+ if ($request->isFault()) {
+ $response = $request->getFault();
+ } else {
+ try {
+ $response = $this->_handle($request);
+ } catch (Exception $e) {
+ $response = $this->fault($e);
+ }
+ }
+
+ // Set output encoding
+ $response->setEncoding($this->getEncoding());
+
+ return $response;
+ }
+
+ public function setResponseClass($class)
+ {
+ if (class_exists($class)) {
+ $reflection = new ReflectionClass($class);
+ if ($reflection->isSubclassOf(new ReflectionClass('IPF_XmlRpc_Response'))) {
+ $this->_responseClass = $class;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function getFunctions()
+ {
+ $return = array();
+ foreach ($this->_methods as $method) {
+ if ($method instanceof IPF_Server_Reflection_Class
+ && ($method->system))
+ {
+ continue;
+ }
+
+ $return[] = $method;
+ }
+
+ return $return;
+ }
+
+ public function listMethods()
+ {
+ return array_keys($this->_table);
+ }
+
+ public function methodHelp($method)
+ {
+ if (!isset($this->_table[$method])) {
+ throw new IPF_Exception('Method "' . $method . '"does not exist', 640);
+ }
+
+ return $this->_table[$method]->getDescription();
+ }
+
+ public function methodSignature($method)
+ {
+ if (!isset($this->_table[$method])) {
+ throw new IPF_Exception('Method "' . $method . '"does not exist', 640);
+ }
+ $prototypes = $this->_table[$method]->getPrototypes();
+
+ $signatures = array();
+ foreach ($prototypes as $prototype) {
+ $signature = array($prototype->getReturnType());
+ foreach ($prototype->getParameters() as $parameter) {
+ $signature[] = $parameter->getType();
+ }
+ $signatures[] = $signature;
+ }
+
+ return $signatures;
+ }
+
+ public function multicall($methods)
+ {
+ $responses = array();
+ foreach ($methods as $method) {
+ $fault = false;
+ if (!is_array($method)) {
+ $fault = $this->fault('system.multicall expects each method to be a struct', 601);
+ } elseif (!isset($method['methodName'])) {
+ $fault = $this->fault('Missing methodName', 602);
+ } elseif (!isset($method['params'])) {
+ $fault = $this->fault('Missing params', 603);
+ } elseif (!is_array($method['params'])) {
+ $fault = $this->fault('Params must be an array', 604);
+ } else {
+ if ('system.multicall' == $method['methodName']) {
+ // don't allow recursive calls to multicall
+ $fault = $this->fault('Recursive system.multicall forbidden', 605);
+ }
+ }
+
+ if (!$fault) {
+ try {
+ $request = new IPF_XmlRpc_Request();
+ $request->setMethod($method['methodName']);
+ $request->setParams($method['params']);
+ $response = $this->_handle($request);
+ $responses[] = $response->getReturnValue();
+ } catch (Exception $e) {
+ $fault = $this->fault($e);
+ }
+ }
+
+ if ($fault) {
+ $responses[] = array(
+ 'faultCode' => $fault->getCode(),
+ 'faultString' => $fault->getMessage()
+ );
+ }
+ }
+ return $responses;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Server_Cache
+{
+ public static function save($filename, IPF_XmlRpc_Server $server)
+ {
+ if (!is_string($filename)
+ || (!file_exists($filename) && !is_writable(dirname($filename))))
+ {
+ return false;
+ }
+
+ // Remove system.* methods
+ $methods = $server->getFunctions();
+ foreach ($methods as $name => $method) {
+ if ($method->system) {
+ unset($methods[$name]);
+ }
+ }
+
+ // Store
+ if (0 === @file_put_contents($filename, serialize($methods))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public static function get($filename, IPF_XmlRpc_Server $server)
+ {
+ if (!is_string($filename)
+ || !file_exists($filename)
+ || !is_readable($filename))
+ {
+ return false;
+ }
+
+ if (false === ($dispatch = @file_get_contents($filename))) {
+ return false;
+ }
+
+ if (false === ($dispatchArray = @unserialize($dispatch))) {
+ return false;
+ }
+
+ $server->loadFunctions($dispatchArray);
+
+ return true;
+ }
+
+ public static function delete($filename)
+ {
+ if (is_string($filename) && file_exists($filename)) {
+ unlink($filename);
+ return true;
+ }
+
+ return false;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Server_Fault extends IPF_XmlRpc_Fault
+{
+ protected $_exception;
+ protected static $_faultExceptionClasses = array('IPF_Exception' => true);
+ protected static $_observers = array();
+
+ public function __construct(Exception $e)
+ {
+ $this->_exception = $e;
+ $code = 404;
+ $message = 'Unknown error';
+ $exceptionClass = get_class($e);
+
+ foreach (array_keys(self::$_faultExceptionClasses) as $class) {
+ if ($e instanceof $class) {
+ $code = $e->getCode();
+ $message = $e->getMessage();
+ break;
+ }
+ }
+
+ parent::__construct($code, $message);
+
+ // Notify exception observers, if present
+ if (!empty(self::$_observers)) {
+ foreach (array_keys(self::$_observers) as $observer) {
+ call_user_func(array($observer, 'observe'), $this);
+ }
+ }
+ }
+
+ public static function getInstance(Exception $e)
+ {
+ return new self($e);
+ }
+
+ public static function attachFaultException($classes)
+ {
+ if (!is_array($classes)) {
+ $classes = (array) $classes;
+ }
+
+ foreach ($classes as $class) {
+ if (is_string($class) && class_exists($class)) {
+ self::$_faultExceptionClasses[$class] = true;
+ }
+ }
+ }
+
+ public static function detachFaultException($classes)
+ {
+ if (!is_array($classes)) {
+ $classes = (array) $classes;
+ }
+
+ foreach ($classes as $class) {
+ if (is_string($class) && isset(self::$_faultExceptionClasses[$class])) {
+ unset(self::$_faultExceptionClasses[$class]);
+ }
+ }
+ }
+
+ public static function attachObserver($class)
+ {
+ if (!is_string($class)
+ || !class_exists($class)
+ || !is_callable(array($class, 'observe')))
+ {
+ return false;
+ }
+
+ if (!isset(self::$_observers[$class])) {
+ self::$_observers[$class] = true;
+ }
+
+ return true;
+ }
+
+ public static function detachObserver($class)
+ {
+ if (!isset(self::$_observers[$class])) {
+ return false;
+ }
+
+ unset(self::$_observers[$class]);
+ return true;
+ }
+
+ public function getException()
+ {
+ return $this->_exception;
+ }
+}
--- /dev/null
+<?php
+
+abstract class IPF_XmlRpc_Value
+{
+ protected $_value;
+ protected $_type;
+ protected $_as_xml;
+ protected $_as_dom;
+
+ const AUTO_DETECT_TYPE = 'auto_detect';
+ const XML_STRING = 'xml';
+
+ const XMLRPC_TYPE_I4 = 'i4';
+ const XMLRPC_TYPE_INTEGER = 'int';
+ const XMLRPC_TYPE_DOUBLE = 'double';
+ const XMLRPC_TYPE_BOOLEAN = 'boolean';
+ const XMLRPC_TYPE_STRING = 'string';
+ const XMLRPC_TYPE_DATETIME = 'dateTime.iso8601';
+ const XMLRPC_TYPE_BASE64 = 'base64';
+ const XMLRPC_TYPE_ARRAY = 'array';
+ const XMLRPC_TYPE_STRUCT = 'struct';
+ const XMLRPC_TYPE_NIL = 'nil';
+
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ abstract public function getValue();
+ abstract public function saveXML();
+
+ public function getAsDOM()
+ {
+ if (!$this->_as_dom) {
+ $doc = new DOMDocument('1.0');
+ $doc->loadXML($this->saveXML());
+ $this->_as_dom = $doc->documentElement;
+ }
+
+ return $this->_as_dom;
+ }
+
+ protected function _stripXmlDeclaration(DOMDocument $dom)
+ {
+ return preg_replace('/<\?xml version="1.0"( encoding="[^\"]*")?\?>\n/u', '', $dom->saveXML());
+ }
+
+ public static function getXmlRpcValue($value, $type = self::AUTO_DETECT_TYPE)
+ {
+ switch ($type) {
+ case self::AUTO_DETECT_TYPE:
+ // Auto detect the XML-RPC native type from the PHP type of $value
+ return self::_phpVarToNativeXmlRpc($value);
+
+ case self::XML_STRING:
+ // Parse the XML string given in $value and get the XML-RPC value in it
+ return self::_xmlStringToNativeXmlRpc($value);
+
+ case self::XMLRPC_TYPE_I4:
+ // fall through to the next case
+ case self::XMLRPC_TYPE_INTEGER:
+ return new IPF_XmlRpc_Value_Integer($value);
+
+ case self::XMLRPC_TYPE_DOUBLE:
+ return new IPF_XmlRpc_Value_Double($value);
+
+ case self::XMLRPC_TYPE_BOOLEAN:
+ return new IPF_XmlRpc_Value_Boolean($value);
+
+ case self::XMLRPC_TYPE_STRING:
+ return new IPF_XmlRpc_Value_String($value);
+
+ case self::XMLRPC_TYPE_BASE64:
+ return new IPF_XmlRpc_Value_Base64($value);
+
+ case self::XMLRPC_TYPE_NIL:
+ return new IPF_XmlRpc_Value_Nil();
+
+ case self::XMLRPC_TYPE_DATETIME:
+ return new IPF_XmlRpc_Value_DateTime($value);
+
+ case self::XMLRPC_TYPE_ARRAY:
+ return new IPF_XmlRpc_Value_Array($value);
+
+ case self::XMLRPC_TYPE_STRUCT:
+ return new IPF_XmlRpc_Value_Struct($value);
+
+ default:
+ throw new IPF_Exception('Given type is not a '. __CLASS__ .' constant');
+ }
+ }
+
+ private static function _phpVarToNativeXmlRpc($value)
+ {
+ switch (gettype($value)) {
+ case 'object':
+ // Check to see if it's an XmlRpc value
+ if ($value instanceof IPF_XmlRpc_Value) {
+ return $value;
+ }
+
+ // Otherwise, we convert the object into a struct
+ $value = get_object_vars($value);
+ // Break intentionally omitted
+ case 'array':
+ // Default native type for a PHP array (a simple numeric array) is 'array'
+ $obj = 'IPF_XmlRpc_Value_Array';
+
+ // Determine if this is an associative array
+ if (!empty($value) && is_array($value) && (array_keys($value) !== range(0, count($value) - 1))) {
+ $obj = 'IPF_XmlRpc_Value_Struct';
+ }
+ return new $obj($value);
+
+ case 'integer':
+ return new IPF_XmlRpc_Value_Integer($value);
+
+ case 'double':
+ return new IPF_XmlRpc_Value_Double($value);
+
+ case 'boolean':
+ return new IPF_XmlRpc_Value_Boolean($value);
+
+ case 'NULL':
+ case 'null':
+ return new IPF_XmlRpc_Value_Nil();
+
+ case 'string':
+ // Fall through to the next case
+ default:
+ // If type isn't identified (or identified as string), it treated as string
+ return new IPF_XmlRpc_Value_String($value);
+ }
+ }
+
+ private static function _xmlStringToNativeXmlRpc($simple_xml)
+ {
+ if (!$simple_xml instanceof SimpleXMLElement) {
+ try {
+ $simple_xml = @new SimpleXMLElement($simple_xml);
+ } catch (Exception $e) {
+ // The given string is not a valid XML
+ throw new IPF_Exception('Failed to create XML-RPC value from XML string: '.$e->getMessage(),$e->getCode());
+ }
+ }
+
+ // Get the key (tag name) and value from the simple xml object and convert the value to an XML-RPC native value
+ list($type, $value) = each($simple_xml);
+ if (!$type) { // If no type was specified, the default is string
+ $type = self::XMLRPC_TYPE_STRING;
+ }
+
+ switch ($type) {
+ // All valid and known XML-RPC native values
+ case self::XMLRPC_TYPE_I4:
+ // Fall through to the next case
+ case self::XMLRPC_TYPE_INTEGER:
+ $xmlrpc_val = new IPF_XmlRpc_Value_Integer($value);
+ break;
+ case self::XMLRPC_TYPE_DOUBLE:
+ $xmlrpc_val = new IPF_XmlRpc_Value_Double($value);
+ break;
+ case self::XMLRPC_TYPE_BOOLEAN:
+ $xmlrpc_val = new IPF_XmlRpc_Value_Boolean($value);
+ break;
+ case self::XMLRPC_TYPE_STRING:
+ $xmlrpc_val = new IPF_XmlRpc_Value_String($value);
+ break;
+ case self::XMLRPC_TYPE_DATETIME: // The value should already be in a iso8601 format
+ $xmlrpc_val = new IPF_XmlRpc_Value_DateTime($value);
+ break;
+ case self::XMLRPC_TYPE_BASE64: // The value should already be base64 encoded
+ $xmlrpc_val = new IPF_XmlRpc_Value_Base64($value ,true);
+ break;
+ case self::XMLRPC_TYPE_NIL: // The value should always be NULL
+ $xmlrpc_val = new IPF_XmlRpc_Value_Nil();
+ break;
+ case self::XMLRPC_TYPE_ARRAY:
+ // If the XML is valid, $value must be an SimpleXML element and contain the <data> tag
+ if (!$value instanceof SimpleXMLElement) {
+ throw new IPF_Exception('XML string is invalid for XML-RPC native '. self::XMLRPC_TYPE_ARRAY .' type');
+ }
+
+ // PHP 5.2.4 introduced a regression in how empty($xml->value)
+ // returns; need to look for the item specifically
+ $data = null;
+ foreach ($value->children() as $key => $value) {
+ if ('data' == $key) {
+ $data = $value;
+ break;
+ }
+ }
+
+ if (null === $data) {
+ throw new IPF_Exception('Invalid XML for XML-RPC native '. self::XMLRPC_TYPE_ARRAY .' type: ARRAY tag must contain DATA tag');
+ }
+ $values = array();
+ // Parse all the elements of the array from the XML string
+ // (simple xml element) to IPF_XmlRpc_Value objects
+ foreach ($data->value as $element) {
+ $values[] = self::_xmlStringToNativeXmlRpc($element);
+ }
+ $xmlrpc_val = new IPF_XmlRpc_Value_Array($values);
+ break;
+ case self::XMLRPC_TYPE_STRUCT:
+ // If the XML is valid, $value must be an SimpleXML
+ if ((!$value instanceof SimpleXMLElement)) {
+ throw new IPF_Exception('XML string is invalid for XML-RPC native '. self::XMLRPC_TYPE_STRUCT .' type');
+ }
+ $values = array();
+ // Parse all the memebers of the struct from the XML string
+ // (simple xml element) to IPF_XmlRpc_Value objects
+ foreach ($value->member as $member) {
+ // @todo? If a member doesn't have a <value> tag, we don't add it to the struct
+ // Maybe we want to throw an exception here ?
+ if ((!$member->value instanceof SimpleXMLElement) || empty($member->value)) {
+ continue;
+ //throw new IPF_XmlRpc_Value_Exception('Member of the '. self::XMLRPC_TYPE_STRUCT .' XML-RPC native type must contain a VALUE tag');
+ }
+ $values[(string)$member->name] = self::_xmlStringToNativeXmlRpc($member->value);
+ }
+ $xmlrpc_val = new IPF_XmlRpc_Value_Struct($values);
+ break;
+ default:
+ throw new IPF_Exception('Value type \''. $type .'\' parsed from the XML string is not a known XML-RPC native type');
+ break;
+ }
+ $xmlrpc_val->_setXML($simple_xml->asXML());
+
+ return $xmlrpc_val;
+ }
+
+ private function _setXML($xml)
+ {
+ $this->_as_xml = $xml;
+ }
+
+}
+
+
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Value_Array extends IPF_XmlRpc_Value_Collection
+{
+ public function __construct($value)
+ {
+ $this->_type = self::XMLRPC_TYPE_ARRAY;
+ parent::__construct($value);
+ }
+
+ public function saveXML()
+ {
+ if (!$this->_as_xml) { // The XML code was not calculated yet
+ $dom = new DOMDocument('1.0');
+ $value = $dom->appendChild($dom->createElement('value'));
+ $array = $value->appendChild($dom->createElement('array'));
+ $data = $array->appendChild($dom->createElement('data'));
+
+ if (is_array($this->_value)) {
+ foreach ($this->_value as $val) {
+ /* @var $val IPF_XmlRpc_Value */
+ $data->appendChild($dom->importNode($val->getAsDOM(), true));
+ }
+ }
+
+ $this->_as_dom = $value;
+ $this->_as_xml = $this->_stripXmlDeclaration($dom);
+ }
+
+ return $this->_as_xml;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Value_Base64 extends IPF_XmlRpc_Value_Scalar
+{
+ public function __construct($value, $already_encoded=false)
+ {
+ $this->_type = self::XMLRPC_TYPE_BASE64;
+
+ $value = (string)$value; // Make sure this value is string
+ if (!$already_encoded) {
+ $value = base64_encode($value); // We encode it in base64
+ }
+ $this->_value = $value;
+ }
+
+ public function getValue()
+ {
+ return base64_decode($this->_value);
+ }
+
+ public function saveXML()
+ {
+ if (! $this->_as_xml) { // The XML was not generated yet
+ $dom = new DOMDocument('1.0', 'UTF-8');
+ $value = $dom->appendChild($dom->createElement('value'));
+ $type = $value->appendChild($dom->createElement($this->_type));
+ $type->appendChild($dom->createTextNode($this->_value));
+
+ $this->_as_dom = $value;
+ $this->_as_xml = $this->_stripXmlDeclaration($dom);
+ }
+
+ return $this->_as_xml;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Value_Boolean extends IPF_XmlRpc_Value_Scalar
+{
+ public function __construct($value)
+ {
+ $this->_type = self::XMLRPC_TYPE_BOOLEAN;
+ $this->_value = (int)(bool)$value;
+ }
+
+ public function getValue()
+ {
+ return (bool)$this->_value;
+ }
+
+ public function saveXML()
+ {
+ if (! $this->_as_xml) { // The XML was not generated yet
+ $dom = new DOMDocument('1.0', 'UTF-8');
+ $value = $dom->appendChild($dom->createElement('value'));
+ $type = $value->appendChild($dom->createElement($this->_type));
+ $type->appendChild($dom->createTextNode($this->_value));
+
+ $this->_as_dom = $value;
+ $this->_as_xml = $this->_stripXmlDeclaration($dom);
+ }
+
+ return $this->_as_xml;
+ }
+}
+
--- /dev/null
+<?php
+
+abstract class IPF_XmlRpc_Value_Collection extends IPF_XmlRpc_Value
+{
+ public function __construct($value)
+ {
+ $values = (array)$value; // Make sure that the value is an array
+ foreach ($values as $key => $value) {
+ // If the elements of the given array are not IPF_XmlRpc_Value objects,
+ // we need to convert them as such (using auto-detection from PHP value)
+ if (!$value instanceof parent) {
+ $value = self::getXmlRpcValue($value, self::AUTO_DETECT_TYPE);
+ }
+ $this->_value[$key] = $value;
+ }
+ }
+
+ public function getValue()
+ {
+ $values = (array)$this->_value;
+ foreach ($values as $key => $value) {
+ /* @var $value IPF_XmlRpc_Value */
+ if (!$value instanceof parent) {
+ throw new IPF_Exception('Values of '. get_class($this) .' type must be IPF_XmlRpc_Value objects');
+ }
+ $values[$key] = $value->getValue();
+ }
+ return $values;
+ }
+
+}
+
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Value_DateTime extends IPF_XmlRpc_Value_Scalar
+{
+ public function __construct($value)
+ {
+ $this->_type = self::XMLRPC_TYPE_DATETIME;
+
+ // If the value is not numeric, we try to convert it to a timestamp (using the strtotime function)
+ if (is_numeric($value)) { // The value is numeric, we make sure it is an integer
+ $value = (int)$value;
+ } else {
+ $value = strtotime($value);
+ if ($value === false || $value == -1) { // cannot convert the value to a timestamp
+ throw new IPF_Exception('Cannot convert given value \''. $value .'\' to a timestamp');
+ }
+ }
+ $value = date('c', $value); // Convert the timestamp to iso8601 format
+
+ // Strip out TZ information and dashes
+ $value = preg_replace('/(\+|-)\d{2}:\d{2}$/', '', $value);
+ $value = str_replace('-', '', $value);
+
+ $this->_value = $value;
+ }
+
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+}
+
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Value_Double extends IPF_XmlRpc_Value_Scalar
+{
+ public function __construct($value)
+ {
+ $this->_type = self::XMLRPC_TYPE_DOUBLE;
+ $this->_value = sprintf('%f',(float)$value); // Make sure this value is float (double) and without the scientific notation
+ }
+
+ public function getValue()
+ {
+ return (float)$this->_value;
+ }
+
+}
+
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Value_Integer extends IPF_XmlRpc_Value_Scalar
+{
+ public function __construct($value)
+ {
+ $this->_type = self::XMLRPC_TYPE_INTEGER;
+ $this->_value = (int)$value; // Make sure this value is integer
+ }
+
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+}
+
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Value_Nil extends IPF_XmlRpc_Value_Scalar
+{
+ public function __construct()
+ {
+ $this->_type = self::XMLRPC_TYPE_NIL;
+ $this->_value = null;
+ }
+
+ public function getValue()
+ {
+ return null;
+ }
+
+ public function saveXML()
+ {
+ if (! $this->_as_xml) { // The XML was not generated yet
+ $dom = new DOMDocument('1.0', 'UTF-8');
+ $value = $dom->appendChild($dom->createElement('value'));
+ $type = $value->appendChild($dom->createElement($this->_type));
+
+ $this->_as_dom = $value;
+ $this->_as_xml = $this->_stripXmlDeclaration($dom);
+ }
+
+ return $this->_as_xml;
+ }
+}
+
--- /dev/null
+<?php
+
+abstract class IPF_XmlRpc_Value_Scalar extends IPF_XmlRpc_Value
+{
+ public function saveXML()
+ {
+ if (!$this->_as_xml) { // The XML code was not calculated yet
+ $dom = new DOMDocument('1.0');
+ $value = $dom->appendChild($dom->createElement('value'));
+ $type = $value->appendChild($dom->createElement($this->_type));
+ $type->appendChild($dom->createTextNode($this->getValue()));
+
+ $this->_as_dom = $value;
+ $this->_as_xml = $this->_stripXmlDeclaration($dom);
+ }
+
+ return $this->_as_xml;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Value_String extends IPF_XmlRpc_Value_Scalar
+{
+ public function __construct($value)
+ {
+ $this->_type = self::XMLRPC_TYPE_STRING;
+
+ // Make sure this value is string and all XML characters are encoded
+ $this->_value = $this->_xml_entities($value);
+ }
+
+ public function getValue()
+ {
+ return html_entity_decode($this->_value, ENT_QUOTES, 'UTF-8');
+ }
+
+ private function _xml_entities($str)
+ {
+ return htmlentities($str, ENT_QUOTES, 'UTF-8');
+ }
+
+}
+
--- /dev/null
+<?php
+
+class IPF_XmlRpc_Value_Struct extends IPF_XmlRpc_Value_Collection
+{
+ public function __construct($value)
+ {
+ $this->_type = self::XMLRPC_TYPE_STRUCT;
+ parent::__construct($value);
+ }
+
+ public function saveXML()
+ {
+ if (!$this->_as_xml) { // The XML code was not calculated yet
+ $dom = new DOMDocument('1.0');
+ $value = $dom->appendChild($dom->createElement('value'));
+ $struct = $value->appendChild($dom->createElement('struct'));
+
+ if (is_array($this->_value)) {
+ foreach ($this->_value as $name => $val) {
+ /* @var $val IPF_XmlRpc_Value */
+ $member = $struct->appendChild($dom->createElement('member'));
+ $member->appendChild($dom->createElement('name', $name));
+ $member->appendChild($dom->importNode($val->getAsDOM(), 1));
+ }
+ }
+
+ $this->_as_dom = $value;
+ $this->_as_xml = $this->_stripXmlDeclaration($dom);
+ }
+
+ return $this->_as_xml;
+ }
+}
+