+++ /dev/null
-<?php
-
-abstract class IPF_Server_Abstract implements IPF_Server_Interface
-{
- protected static $magic_methods = array(
- '__construct',
- '__destruct',
- '__get',
- '__set',
- '__call',
- '__sleep',
- '__wakeup',
- '__isset',
- '__unset',
- '__tostring',
- '__clone',
- '__set_state',
- );
-
- public static function lowerCase(&$value, &$key)
- {
- return $value = strtolower($value);
- }
-}
+++ /dev/null
-<?php
-
-interface IPF_Server_Interface
-{
- public function addFunction($function, $namespace = '');
- public function setClass($class, $namespace = '', $argv = null);
- public function fault($fault = null, $code = 404);
- public function handle($request = false);
- public function getFunctions();
- public function loadFunctions($definition);
- public function setPersistence($mode);
-}
+++ /dev/null
-<?php
-
-class IPF_Server_Reflection
-{
- public static function reflectClass($class, $argv = false, $namespace = '')
- {
- if (is_object($class)) {
- $reflection = new ReflectionObject($class);
- } elseif (class_exists($class)) {
- $reflection = new ReflectionClass($class);
- } else {
- throw new IPF_Exception('Invalid class or object passed to attachClass()');
- }
-
- if ($argv && !is_array($argv)) {
- throw new IPF_Exception('Invalid argv argument passed to reflectClass');
- }
- return new IPF_Server_Reflection_Class($reflection, $namespace, $argv);
- }
-
- public static function reflectFunction($function, $argv = false, $namespace = '')
- {
- if (!is_string($function) || !function_exists($function)) {
- throw new IPF_Exception('Invalid function "' . $function . '" passed to reflectFunction');
- }
- if ($argv && !is_array($argv)) {
- throw new IPF_Exception('Invalid argv argument passed to reflectClass');
- }
- return new IPF_Server_Reflection_Function(new ReflectionFunction($function), $namespace, $argv);
- }
-}
+++ /dev/null
-<?php
-
-class IPF_Server_Reflection_Class
-{
- protected $_config = array();
- protected $_methods = array();
- protected $_namespace = null;
- protected $_reflection;
- public function __construct(ReflectionClass $reflection, $namespace = null, $argv = false)
- {
- $this->_reflection = $reflection;
- $this->setNamespace($namespace);
-
- foreach ($reflection->getMethods() as $method) {
- // Don't aggregate magic methods
- if ('__' == substr($method->getName(), 0, 2)) {
- continue;
- }
-
- if ($method->isPublic()) {
- // Get signatures and description
- $this->_methods[] = new IPF_Server_Reflection_Method($this, $method, $this->getNamespace(), $argv);
- }
- }
- }
-
- public function __call($method, $args)
- {
- if (method_exists($this->_reflection, $method)) {
- return call_user_func_array(array($this->_reflection, $method), $args);
- }
- throw new IPF_Exception('Invalid reflection method');
- }
-
- public function __get($key)
- {
- if (isset($this->_config[$key])) {
- return $this->_config[$key];
- }
-
- return null;
- }
-
- public function __set($key, $value)
- {
- $this->_config[$key] = $value;
- }
-
- public function getMethods()
- {
- return $this->_methods;
- }
-
- public function getNamespace()
- {
- return $this->_namespace;
- }
-
- public function setNamespace($namespace)
- {
- if (empty($namespace)) {
- $this->_namespace = '';
- return;
- }
-
- if (!is_string($namespace) || !preg_match('/[a-z0-9_\.]+/i', $namespace)) {
- throw new IPF_Exception('Invalid namespace');
- }
-
- $this->_namespace = $namespace;
- }
-
- public function __wakeup()
- {
- $this->_reflection = new ReflectionClass($this->getName());
- }
-}
+++ /dev/null
-<?php
-
-class IPF_Server_Reflection_Function extends IPF_Server_Reflection_Function_Abstract{
-}
+++ /dev/null
-<?php
-
-class IPF_Server_Reflection_Function_Abstract
-{
- protected $_reflection;
- protected $_argv = array();
- protected $_config = array();
- protected $_class;
- protected $_description = '';
- protected $_namespace;
-
- protected $_prototypes = array();
-
- private $_return;
- private $_returnDesc;
- private $_paramDesc;
- private $_sigParams;
- private $_sigParamsDepth;
-
- public function __construct(Reflector $r, $namespace = null, $argv = array())
- {
- // In PHP 5.1.x, ReflectionMethod extends ReflectionFunction. In 5.2.x,
- // both extend ReflectionFunctionAbstract. So, we can't do normal type
- // hinting in the prototype, but instead need to do some explicit
- // testing here.
- if ((!$r instanceof ReflectionFunction)
- && (!$r instanceof ReflectionMethod)) {
- throw new IPF_Exception('Invalid reflection class');
- }
- $this->_reflection = $r;
-
- // Determine namespace
- if (null !== $namespace){
- $this->setNamespace($namespace);
- }
-
- // Determine arguments
- if (is_array($argv)) {
- $this->_argv = $argv;
- }
-
- // If method call, need to store some info on the class
- if ($r instanceof ReflectionMethod) {
- $this->_class = $r->getDeclaringClass()->getName();
- }
-
- // Perform some introspection
- $this->_reflect();
- }
-
- protected function _addTree(IPF_Server_Reflection_Node $parent, $level = 0)
- {
- if ($level >= $this->_sigParamsDepth) {
- return;
- }
-
- foreach ($this->_sigParams[$level] as $value) {
- $node = new IPF_Server_Reflection_Node($value, $parent);
- if ((null !== $value) && ($this->_sigParamsDepth > $level + 1)) {
- $this->_addTree($node, $level + 1);
- }
- }
- }
-
- protected function _buildTree()
- {
- $returnTree = array();
- foreach ((array) $this->_return as $value) {
- $node = new IPF_Server_Reflection_Node($value);
- $this->_addTree($node);
- $returnTree[] = $node;
- }
-
- return $returnTree;
- }
-
- protected function _buildSignatures($return, $returnDesc, $paramTypes, $paramDesc)
- {
- $this->_return = $return;
- $this->_returnDesc = $returnDesc;
- $this->_paramDesc = $paramDesc;
- $this->_sigParams = $paramTypes;
- $this->_sigParamsDepth = count($paramTypes);
- $signatureTrees = $this->_buildTree();
- $signatures = array();
-
- $endPoints = array();
- foreach ($signatureTrees as $root) {
- $tmp = $root->getEndPoints();
- if (empty($tmp)) {
- $endPoints = array_merge($endPoints, array($root));
- } else {
- $endPoints = array_merge($endPoints, $tmp);
- }
- }
-
- foreach ($endPoints as $node) {
- if (!$node instanceof IPF_Server_Reflection_Node) {
- continue;
- }
-
- $signature = array();
- do {
- array_unshift($signature, $node->getValue());
- $node = $node->getParent();
- } while ($node instanceof IPF_Server_Reflection_Node);
-
- $signatures[] = $signature;
- }
-
- // Build prototypes
- $params = $this->_reflection->getParameters();
- foreach ($signatures as $signature) {
- $return = new IPF_Server_Reflection_ReturnValue(array_shift($signature), $this->_returnDesc);
- $tmp = array();
- foreach ($signature as $key => $type) {
- $param = new IPF_Server_Reflection_Parameter($params[$key], $type, $this->_paramDesc[$key]);
- $param->setPosition($key);
- $tmp[] = $param;
- }
-
- $this->_prototypes[] = new IPF_Server_Reflection_Prototype($return, $tmp);
- }
- }
-
- protected function _reflect()
- {
- $function = $this->_reflection;
- $helpText = '';
- $signatures = array();
- $returnDesc = '';
- $paramCount = $function->getNumberOfParameters();
- $paramCountRequired = $function->getNumberOfRequiredParameters();
- $parameters = $function->getParameters();
- $docBlock = $function->getDocComment();
-
- if (!empty($docBlock)) {
- // Get help text
- if (preg_match(':/\*\*\s*\r?\n\s*\*\s(.*?)\r?\n\s*\*(\s@|/):s', $docBlock, $matches))
- {
- $helpText = $matches[1];
- $helpText = preg_replace('/(^\s*\*\s)/m', '', $helpText);
- $helpText = preg_replace('/\r?\n\s*\*\s*(\r?\n)*/s', "\n", $helpText);
- $helpText = trim($helpText);
- }
-
- // Get return type(s) and description
- $return = 'void';
- if (preg_match('/@return\s+(\S+)/', $docBlock, $matches)) {
- $return = explode('|', $matches[1]);
- if (preg_match('/@return\s+\S+\s+(.*?)(@|\*\/)/s', $docBlock, $matches))
- {
- $value = $matches[1];
- $value = preg_replace('/\s?\*\s/m', '', $value);
- $value = preg_replace('/\s{2,}/', ' ', $value);
- $returnDesc = trim($value);
- }
- }
-
- // Get param types and description
- if (preg_match_all('/@param\s+([^\s]+)/m', $docBlock, $matches)) {
- $paramTypesTmp = $matches[1];
- if (preg_match_all('/@param\s+\S+\s+(\$^\S+)\s+(.*?)(@|\*\/)/s', $docBlock, $matches))
- {
- $paramDesc = $matches[2];
- foreach ($paramDesc as $key => $value) {
- $value = preg_replace('/\s?\*\s/m', '', $value);
- $value = preg_replace('/\s{2,}/', ' ', $value);
- $paramDesc[$key] = trim($value);
- }
- }
- }
- } else {
- $helpText = $function->getName();
- $return = 'void';
- }
-
- // Set method description
- $this->setDescription($helpText);
-
- // Get all param types as arrays
- if (!isset($paramTypesTmp) && (0 < $paramCount)) {
- $paramTypesTmp = array_fill(0, $paramCount, 'mixed');
- } elseif (!isset($paramTypesTmp)) {
- $paramTypesTmp = array();
- } elseif (count($paramTypesTmp) < $paramCount) {
- $start = $paramCount - count($paramTypesTmp);
- for ($i = $start; $i < $paramCount; ++$i) {
- $paramTypesTmp[$i] = 'mixed';
- }
- }
-
- // Get all param descriptions as arrays
- if (!isset($paramDesc) && (0 < $paramCount)) {
- $paramDesc = array_fill(0, $paramCount, '');
- } elseif (!isset($paramDesc)) {
- $paramDesc = array();
- } elseif (count($paramDesc) < $paramCount) {
- $start = $paramCount - count($paramDesc);
- for ($i = $start; $i < $paramCount; ++$i) {
- $paramDesc[$i] = '';
- }
- }
-
-
- $paramTypes = array();
- foreach ($paramTypesTmp as $i => $param) {
- $tmp = explode('|', $param);
- if ($parameters[$i]->isOptional()) {
- array_unshift($tmp, null);
- }
- $paramTypes[] = $tmp;
- }
-
- $this->_buildSignatures($return, $returnDesc, $paramTypes, $paramDesc);
- }
-
- public function __call($method, $args)
- {
- if (method_exists($this->_reflection, $method)) {
- return call_user_func_array(array($this->_reflection, $method), $args);
- }
-
- throw new IPF_Exception('Invalid reflection method ("' .$method. '")');
- }
-
- public function __get($key)
- {
- if (isset($this->_config[$key])) {
- return $this->_config[$key];
- }
-
- return null;
- }
-
- public function __set($key, $value)
- {
- $this->_config[$key] = $value;
- }
-
- public function setNamespace($namespace)
- {
- if (empty($namespace)) {
- $this->_namespace = '';
- return;
- }
-
- if (!is_string($namespace) || !preg_match('/[a-z0-9_\.]+/i', $namespace)) {
- throw new IPF_Exception('Invalid namespace');
- }
-
- $this->_namespace = $namespace;
- }
-
- public function getNamespace()
- {
- return $this->_namespace;
- }
-
- public function setDescription($string)
- {
- if (!is_string($string)) {
- throw new IPF_Exception('Invalid description');
- }
-
- $this->_description = $string;
- }
-
- public function getDescription()
- {
- return $this->_description;
- }
-
- public function getPrototypes()
- {
- return $this->_prototypes;
- }
-
- public function getInvokeArguments()
- {
- return $this->_argv;
- }
-
- public function __wakeup()
- {
- if ($this->_reflection instanceof ReflectionMethod) {
- $class = new ReflectionClass($this->_class);
- $this->_reflection = new ReflectionMethod($class->newInstance(), $this->getName());
- } else {
- $this->_reflection = new ReflectionFunction($this->getName());
- }
- }
-}
+++ /dev/null
-<?php
-
-class IPF_Server_Reflection_Method extends IPF_Server_Reflection_Function_Abstract
-{
- protected $_class;
- protected $_classReflection;
-
- public function __construct(IPF_Server_Reflection_Class $class, ReflectionMethod $r, $namespace = null, $argv = array())
- {
- $this->_classReflection = $class;
- $this->_reflection = $r;
-
- $classNamespace = $class->getNamespace();
-
- // Determine namespace
- if (!empty($namespace)) {
- $this->setNamespace($namespace);
- } elseif (!empty($classNamespace)) {
- $this->setNamespace($classNamespace);
- }
-
- // Determine arguments
- if (is_array($argv)) {
- $this->_argv = $argv;
- }
-
- // If method call, need to store some info on the class
- $this->_class = $class->getName();
-
- // Perform some introspection
- $this->_reflect();
- }
-
- public function getDeclaringClass()
- {
- return $this->_classReflection;
- }
-
- public function __wakeup()
- {
- $this->_classReflection = new IPF_Server_Reflection_Class(new ReflectionClass($this->_class), $this->getNamespace(), $this->getInvokeArguments());
- $this->_reflection = new ReflectionMethod($this->_classReflection->getName(), $this->getName());
- }
-
-}
+++ /dev/null
-<?php
-
-class IPF_Server_Reflection_Node
-{
- protected $_value = null;
- protected $_children = array();
- protected $_parent = null;
-
- public function __construct($value, IPF_Server_Reflection_Node $parent = null)
- {
- $this->_value = $value;
- if (null !== $parent) {
- $this->setParent($parent, true);
- }
-
- return $this;
- }
-
- public function setParent(IPF_Server_Reflection_Node $node, $new = false)
- {
- $this->_parent = $node;
-
- if ($new) {
- $node->attachChild($this);
- return;
- }
- }
-
- public function createChild($value)
- {
- $child = new self($value, $this);
-
- return $child;
- }
-
- public function attachChild(IPF_Server_Reflection_Node $node)
- {
- $this->_children[] = $node;
-
- if ($node->getParent() !== $this) {
- $node->setParent($this);
- }
- }
-
- public function getChildren()
- {
- return $this->_children;
- }
-
- public function hasChildren()
- {
- return count($this->_children) > 0;
- }
-
- public function getParent()
- {
- return $this->_parent;
- }
-
- public function getValue()
- {
- return $this->_value;
- }
-
- public function setValue($value)
- {
- $this->_value = $value;
- }
-
- public function getEndPoints()
- {
- $endPoints = array();
- if (!$this->hasChildren()) {
- return $endPoints;
- }
-
- foreach ($this->_children as $child) {
- $value = $child->getValue();
-
- if (null === $value) {
- $endPoints[] = $this;
- } elseif ((null !== $value)
- && $child->hasChildren())
- {
- $childEndPoints = $child->getEndPoints();
- if (!empty($childEndPoints)) {
- $endPoints = array_merge($endPoints, $childEndPoints);
- }
- } elseif ((null !== $value) && !$child->hasChildren()) {
- $endPoints[] = $child;
- }
- }
-
- return $endPoints;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_Server_Reflection_Parameter
-{
- protected $_reflection;
- protected $_position;
- protected $_type;
- protected $_description;
-
- public function __construct(ReflectionParameter $r, $type = 'mixed', $description = '')
- {
- $this->_reflection = $r;
- $this->setType($type);
- $this->setDescription($description);
- }
-
- public function __call($method, $args)
- {
- if (method_exists($this->_reflection, $method)) {
- return call_user_func_array(array($this->_reflection, $method), $args);
- }
- throw new IPF_Exception('Invalid reflection method');
- }
-
- public function getType()
- {
- return $this->_type;
- }
-
- public function setType($type)
- {
- if (!is_string($type) && (null !== $type)) {
- throw new IPF_Exception('Invalid parameter type');
- }
- $this->_type = $type;
- }
-
- public function getDescription()
- {
- return $this->_description;
- }
-
- public function setDescription($description)
- {
- if (!is_string($description) && (null !== $description)) {
- throw new IPF_Exception('Invalid parameter description');
- }
- $this->_description = $description;
- }
-
- public function setPosition($index)
- {
- $this->_position = (int) $index;
- }
-
- public function getPosition()
- {
- return $this->_position;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_Server_Reflection_Prototype
-{
- public function __construct(IPF_Server_Reflection_ReturnValue $return, $params = null)
- {
- $this->_return = $return;
-
- if (!is_array($params) && (null !== $params)) {
- throw new IPF_Exception('Invalid parameters');
- }
-
- if (is_array($params)) {
- foreach ($params as $param) {
- if (!$param instanceof IPF_Server_Reflection_Parameter) {
- throw new IPF_Exception('One or more params are invalid');
- }
- }
- }
-
- $this->_params = $params;
- }
-
- public function getReturnType()
- {
- return $this->_return->getType();
- }
-
- public function getReturnValue()
- {
- return $this->_return;
- }
-
- public function getParameters()
- {
- return $this->_params;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_Server_Reflection_ReturnValue
-{
- protected $_type;
- protected $_description;
-
- public function __construct($type = 'mixed', $description = '')
- {
- $this->setType($type);
- $this->setDescription($description);
- }
-
- public function getType()
- {
- return $this->_type;
- }
-
- public function setType($type)
- {
- if (!is_string($type) && (null !== $type)) {
- throw new IPF_Exception('Invalid parameter type');
- }
- $this->_type = $type;
- }
-
- public function getDescription()
- {
- return $this->_description;
- }
-
- public function setDescription($description)
- {
- if (!is_string($description) && (null !== $description)) {
- throw new IPF_Exception('Invalid parameter description');
- }
- $this->_description = $description;
- }
-}
+++ /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;
- }
-}
-
--- /dev/null
+<?php
+
+abstract class IPF_Server_Abstract implements IPF_Server_Interface
+{
+ protected static $magic_methods = array(
+ '__construct',
+ '__destruct',
+ '__get',
+ '__set',
+ '__call',
+ '__sleep',
+ '__wakeup',
+ '__isset',
+ '__unset',
+ '__tostring',
+ '__clone',
+ '__set_state',
+ );
+
+ public static function lowerCase(&$value, &$key)
+ {
+ return $value = strtolower($value);
+ }
+}
--- /dev/null
+<?php
+
+interface IPF_Server_Interface
+{
+ public function addFunction($function, $namespace = '');
+ public function setClass($class, $namespace = '', $argv = null);
+ public function fault($fault = null, $code = 404);
+ public function handle($request = false);
+ public function getFunctions();
+ public function loadFunctions($definition);
+ public function setPersistence($mode);
+}
--- /dev/null
+<?php
+
+class IPF_Server_Reflection
+{
+ public static function reflectClass($class, $argv = false, $namespace = '')
+ {
+ if (is_object($class)) {
+ $reflection = new ReflectionObject($class);
+ } elseif (class_exists($class)) {
+ $reflection = new ReflectionClass($class);
+ } else {
+ throw new IPF_Exception('Invalid class or object passed to attachClass()');
+ }
+
+ if ($argv && !is_array($argv)) {
+ throw new IPF_Exception('Invalid argv argument passed to reflectClass');
+ }
+ return new IPF_Server_Reflection_Class($reflection, $namespace, $argv);
+ }
+
+ public static function reflectFunction($function, $argv = false, $namespace = '')
+ {
+ if (!is_string($function) || !function_exists($function)) {
+ throw new IPF_Exception('Invalid function "' . $function . '" passed to reflectFunction');
+ }
+ if ($argv && !is_array($argv)) {
+ throw new IPF_Exception('Invalid argv argument passed to reflectClass');
+ }
+ return new IPF_Server_Reflection_Function(new ReflectionFunction($function), $namespace, $argv);
+ }
+}
--- /dev/null
+<?php
+
+class IPF_Server_Reflection_Class
+{
+ protected $_config = array();
+ protected $_methods = array();
+ protected $_namespace = null;
+ protected $_reflection;
+ public function __construct(ReflectionClass $reflection, $namespace = null, $argv = false)
+ {
+ $this->_reflection = $reflection;
+ $this->setNamespace($namespace);
+
+ foreach ($reflection->getMethods() as $method) {
+ // Don't aggregate magic methods
+ if ('__' == substr($method->getName(), 0, 2)) {
+ continue;
+ }
+
+ if ($method->isPublic()) {
+ // Get signatures and description
+ $this->_methods[] = new IPF_Server_Reflection_Method($this, $method, $this->getNamespace(), $argv);
+ }
+ }
+ }
+
+ public function __call($method, $args)
+ {
+ if (method_exists($this->_reflection, $method)) {
+ return call_user_func_array(array($this->_reflection, $method), $args);
+ }
+ throw new IPF_Exception('Invalid reflection method');
+ }
+
+ public function __get($key)
+ {
+ if (isset($this->_config[$key])) {
+ return $this->_config[$key];
+ }
+
+ return null;
+ }
+
+ public function __set($key, $value)
+ {
+ $this->_config[$key] = $value;
+ }
+
+ public function getMethods()
+ {
+ return $this->_methods;
+ }
+
+ public function getNamespace()
+ {
+ return $this->_namespace;
+ }
+
+ public function setNamespace($namespace)
+ {
+ if (empty($namespace)) {
+ $this->_namespace = '';
+ return;
+ }
+
+ if (!is_string($namespace) || !preg_match('/[a-z0-9_\.]+/i', $namespace)) {
+ throw new IPF_Exception('Invalid namespace');
+ }
+
+ $this->_namespace = $namespace;
+ }
+
+ public function __wakeup()
+ {
+ $this->_reflection = new ReflectionClass($this->getName());
+ }
+}
--- /dev/null
+<?php
+
+class IPF_Server_Reflection_Function extends IPF_Server_Reflection_Function_Abstract{
+}
--- /dev/null
+<?php
+
+class IPF_Server_Reflection_Function_Abstract
+{
+ protected $_reflection;
+ protected $_argv = array();
+ protected $_config = array();
+ protected $_class;
+ protected $_description = '';
+ protected $_namespace;
+
+ protected $_prototypes = array();
+
+ private $_return;
+ private $_returnDesc;
+ private $_paramDesc;
+ private $_sigParams;
+ private $_sigParamsDepth;
+
+ public function __construct(Reflector $r, $namespace = null, $argv = array())
+ {
+ // In PHP 5.1.x, ReflectionMethod extends ReflectionFunction. In 5.2.x,
+ // both extend ReflectionFunctionAbstract. So, we can't do normal type
+ // hinting in the prototype, but instead need to do some explicit
+ // testing here.
+ if ((!$r instanceof ReflectionFunction)
+ && (!$r instanceof ReflectionMethod)) {
+ throw new IPF_Exception('Invalid reflection class');
+ }
+ $this->_reflection = $r;
+
+ // Determine namespace
+ if (null !== $namespace){
+ $this->setNamespace($namespace);
+ }
+
+ // Determine arguments
+ if (is_array($argv)) {
+ $this->_argv = $argv;
+ }
+
+ // If method call, need to store some info on the class
+ if ($r instanceof ReflectionMethod) {
+ $this->_class = $r->getDeclaringClass()->getName();
+ }
+
+ // Perform some introspection
+ $this->_reflect();
+ }
+
+ protected function _addTree(IPF_Server_Reflection_Node $parent, $level = 0)
+ {
+ if ($level >= $this->_sigParamsDepth) {
+ return;
+ }
+
+ foreach ($this->_sigParams[$level] as $value) {
+ $node = new IPF_Server_Reflection_Node($value, $parent);
+ if ((null !== $value) && ($this->_sigParamsDepth > $level + 1)) {
+ $this->_addTree($node, $level + 1);
+ }
+ }
+ }
+
+ protected function _buildTree()
+ {
+ $returnTree = array();
+ foreach ((array) $this->_return as $value) {
+ $node = new IPF_Server_Reflection_Node($value);
+ $this->_addTree($node);
+ $returnTree[] = $node;
+ }
+
+ return $returnTree;
+ }
+
+ protected function _buildSignatures($return, $returnDesc, $paramTypes, $paramDesc)
+ {
+ $this->_return = $return;
+ $this->_returnDesc = $returnDesc;
+ $this->_paramDesc = $paramDesc;
+ $this->_sigParams = $paramTypes;
+ $this->_sigParamsDepth = count($paramTypes);
+ $signatureTrees = $this->_buildTree();
+ $signatures = array();
+
+ $endPoints = array();
+ foreach ($signatureTrees as $root) {
+ $tmp = $root->getEndPoints();
+ if (empty($tmp)) {
+ $endPoints = array_merge($endPoints, array($root));
+ } else {
+ $endPoints = array_merge($endPoints, $tmp);
+ }
+ }
+
+ foreach ($endPoints as $node) {
+ if (!$node instanceof IPF_Server_Reflection_Node) {
+ continue;
+ }
+
+ $signature = array();
+ do {
+ array_unshift($signature, $node->getValue());
+ $node = $node->getParent();
+ } while ($node instanceof IPF_Server_Reflection_Node);
+
+ $signatures[] = $signature;
+ }
+
+ // Build prototypes
+ $params = $this->_reflection->getParameters();
+ foreach ($signatures as $signature) {
+ $return = new IPF_Server_Reflection_ReturnValue(array_shift($signature), $this->_returnDesc);
+ $tmp = array();
+ foreach ($signature as $key => $type) {
+ $param = new IPF_Server_Reflection_Parameter($params[$key], $type, $this->_paramDesc[$key]);
+ $param->setPosition($key);
+ $tmp[] = $param;
+ }
+
+ $this->_prototypes[] = new IPF_Server_Reflection_Prototype($return, $tmp);
+ }
+ }
+
+ protected function _reflect()
+ {
+ $function = $this->_reflection;
+ $helpText = '';
+ $signatures = array();
+ $returnDesc = '';
+ $paramCount = $function->getNumberOfParameters();
+ $paramCountRequired = $function->getNumberOfRequiredParameters();
+ $parameters = $function->getParameters();
+ $docBlock = $function->getDocComment();
+
+ if (!empty($docBlock)) {
+ // Get help text
+ if (preg_match(':/\*\*\s*\r?\n\s*\*\s(.*?)\r?\n\s*\*(\s@|/):s', $docBlock, $matches))
+ {
+ $helpText = $matches[1];
+ $helpText = preg_replace('/(^\s*\*\s)/m', '', $helpText);
+ $helpText = preg_replace('/\r?\n\s*\*\s*(\r?\n)*/s', "\n", $helpText);
+ $helpText = trim($helpText);
+ }
+
+ // Get return type(s) and description
+ $return = 'void';
+ if (preg_match('/@return\s+(\S+)/', $docBlock, $matches)) {
+ $return = explode('|', $matches[1]);
+ if (preg_match('/@return\s+\S+\s+(.*?)(@|\*\/)/s', $docBlock, $matches))
+ {
+ $value = $matches[1];
+ $value = preg_replace('/\s?\*\s/m', '', $value);
+ $value = preg_replace('/\s{2,}/', ' ', $value);
+ $returnDesc = trim($value);
+ }
+ }
+
+ // Get param types and description
+ if (preg_match_all('/@param\s+([^\s]+)/m', $docBlock, $matches)) {
+ $paramTypesTmp = $matches[1];
+ if (preg_match_all('/@param\s+\S+\s+(\$^\S+)\s+(.*?)(@|\*\/)/s', $docBlock, $matches))
+ {
+ $paramDesc = $matches[2];
+ foreach ($paramDesc as $key => $value) {
+ $value = preg_replace('/\s?\*\s/m', '', $value);
+ $value = preg_replace('/\s{2,}/', ' ', $value);
+ $paramDesc[$key] = trim($value);
+ }
+ }
+ }
+ } else {
+ $helpText = $function->getName();
+ $return = 'void';
+ }
+
+ // Set method description
+ $this->setDescription($helpText);
+
+ // Get all param types as arrays
+ if (!isset($paramTypesTmp) && (0 < $paramCount)) {
+ $paramTypesTmp = array_fill(0, $paramCount, 'mixed');
+ } elseif (!isset($paramTypesTmp)) {
+ $paramTypesTmp = array();
+ } elseif (count($paramTypesTmp) < $paramCount) {
+ $start = $paramCount - count($paramTypesTmp);
+ for ($i = $start; $i < $paramCount; ++$i) {
+ $paramTypesTmp[$i] = 'mixed';
+ }
+ }
+
+ // Get all param descriptions as arrays
+ if (!isset($paramDesc) && (0 < $paramCount)) {
+ $paramDesc = array_fill(0, $paramCount, '');
+ } elseif (!isset($paramDesc)) {
+ $paramDesc = array();
+ } elseif (count($paramDesc) < $paramCount) {
+ $start = $paramCount - count($paramDesc);
+ for ($i = $start; $i < $paramCount; ++$i) {
+ $paramDesc[$i] = '';
+ }
+ }
+
+
+ $paramTypes = array();
+ foreach ($paramTypesTmp as $i => $param) {
+ $tmp = explode('|', $param);
+ if ($parameters[$i]->isOptional()) {
+ array_unshift($tmp, null);
+ }
+ $paramTypes[] = $tmp;
+ }
+
+ $this->_buildSignatures($return, $returnDesc, $paramTypes, $paramDesc);
+ }
+
+ public function __call($method, $args)
+ {
+ if (method_exists($this->_reflection, $method)) {
+ return call_user_func_array(array($this->_reflection, $method), $args);
+ }
+
+ throw new IPF_Exception('Invalid reflection method ("' .$method. '")');
+ }
+
+ public function __get($key)
+ {
+ if (isset($this->_config[$key])) {
+ return $this->_config[$key];
+ }
+
+ return null;
+ }
+
+ public function __set($key, $value)
+ {
+ $this->_config[$key] = $value;
+ }
+
+ public function setNamespace($namespace)
+ {
+ if (empty($namespace)) {
+ $this->_namespace = '';
+ return;
+ }
+
+ if (!is_string($namespace) || !preg_match('/[a-z0-9_\.]+/i', $namespace)) {
+ throw new IPF_Exception('Invalid namespace');
+ }
+
+ $this->_namespace = $namespace;
+ }
+
+ public function getNamespace()
+ {
+ return $this->_namespace;
+ }
+
+ public function setDescription($string)
+ {
+ if (!is_string($string)) {
+ throw new IPF_Exception('Invalid description');
+ }
+
+ $this->_description = $string;
+ }
+
+ public function getDescription()
+ {
+ return $this->_description;
+ }
+
+ public function getPrototypes()
+ {
+ return $this->_prototypes;
+ }
+
+ public function getInvokeArguments()
+ {
+ return $this->_argv;
+ }
+
+ public function __wakeup()
+ {
+ if ($this->_reflection instanceof ReflectionMethod) {
+ $class = new ReflectionClass($this->_class);
+ $this->_reflection = new ReflectionMethod($class->newInstance(), $this->getName());
+ } else {
+ $this->_reflection = new ReflectionFunction($this->getName());
+ }
+ }
+}
--- /dev/null
+<?php
+
+class IPF_Server_Reflection_Method extends IPF_Server_Reflection_Function_Abstract
+{
+ protected $_class;
+ protected $_classReflection;
+
+ public function __construct(IPF_Server_Reflection_Class $class, ReflectionMethod $r, $namespace = null, $argv = array())
+ {
+ $this->_classReflection = $class;
+ $this->_reflection = $r;
+
+ $classNamespace = $class->getNamespace();
+
+ // Determine namespace
+ if (!empty($namespace)) {
+ $this->setNamespace($namespace);
+ } elseif (!empty($classNamespace)) {
+ $this->setNamespace($classNamespace);
+ }
+
+ // Determine arguments
+ if (is_array($argv)) {
+ $this->_argv = $argv;
+ }
+
+ // If method call, need to store some info on the class
+ $this->_class = $class->getName();
+
+ // Perform some introspection
+ $this->_reflect();
+ }
+
+ public function getDeclaringClass()
+ {
+ return $this->_classReflection;
+ }
+
+ public function __wakeup()
+ {
+ $this->_classReflection = new IPF_Server_Reflection_Class(new ReflectionClass($this->_class), $this->getNamespace(), $this->getInvokeArguments());
+ $this->_reflection = new ReflectionMethod($this->_classReflection->getName(), $this->getName());
+ }
+
+}
--- /dev/null
+<?php
+
+class IPF_Server_Reflection_Node
+{
+ protected $_value = null;
+ protected $_children = array();
+ protected $_parent = null;
+
+ public function __construct($value, IPF_Server_Reflection_Node $parent = null)
+ {
+ $this->_value = $value;
+ if (null !== $parent) {
+ $this->setParent($parent, true);
+ }
+
+ return $this;
+ }
+
+ public function setParent(IPF_Server_Reflection_Node $node, $new = false)
+ {
+ $this->_parent = $node;
+
+ if ($new) {
+ $node->attachChild($this);
+ return;
+ }
+ }
+
+ public function createChild($value)
+ {
+ $child = new self($value, $this);
+
+ return $child;
+ }
+
+ public function attachChild(IPF_Server_Reflection_Node $node)
+ {
+ $this->_children[] = $node;
+
+ if ($node->getParent() !== $this) {
+ $node->setParent($this);
+ }
+ }
+
+ public function getChildren()
+ {
+ return $this->_children;
+ }
+
+ public function hasChildren()
+ {
+ return count($this->_children) > 0;
+ }
+
+ public function getParent()
+ {
+ return $this->_parent;
+ }
+
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+ public function setValue($value)
+ {
+ $this->_value = $value;
+ }
+
+ public function getEndPoints()
+ {
+ $endPoints = array();
+ if (!$this->hasChildren()) {
+ return $endPoints;
+ }
+
+ foreach ($this->_children as $child) {
+ $value = $child->getValue();
+
+ if (null === $value) {
+ $endPoints[] = $this;
+ } elseif ((null !== $value)
+ && $child->hasChildren())
+ {
+ $childEndPoints = $child->getEndPoints();
+ if (!empty($childEndPoints)) {
+ $endPoints = array_merge($endPoints, $childEndPoints);
+ }
+ } elseif ((null !== $value) && !$child->hasChildren()) {
+ $endPoints[] = $child;
+ }
+ }
+
+ return $endPoints;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_Server_Reflection_Parameter
+{
+ protected $_reflection;
+ protected $_position;
+ protected $_type;
+ protected $_description;
+
+ public function __construct(ReflectionParameter $r, $type = 'mixed', $description = '')
+ {
+ $this->_reflection = $r;
+ $this->setType($type);
+ $this->setDescription($description);
+ }
+
+ public function __call($method, $args)
+ {
+ if (method_exists($this->_reflection, $method)) {
+ return call_user_func_array(array($this->_reflection, $method), $args);
+ }
+ throw new IPF_Exception('Invalid reflection method');
+ }
+
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ public function setType($type)
+ {
+ if (!is_string($type) && (null !== $type)) {
+ throw new IPF_Exception('Invalid parameter type');
+ }
+ $this->_type = $type;
+ }
+
+ public function getDescription()
+ {
+ return $this->_description;
+ }
+
+ public function setDescription($description)
+ {
+ if (!is_string($description) && (null !== $description)) {
+ throw new IPF_Exception('Invalid parameter description');
+ }
+ $this->_description = $description;
+ }
+
+ public function setPosition($index)
+ {
+ $this->_position = (int) $index;
+ }
+
+ public function getPosition()
+ {
+ return $this->_position;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_Server_Reflection_Prototype
+{
+ public function __construct(IPF_Server_Reflection_ReturnValue $return, $params = null)
+ {
+ $this->_return = $return;
+
+ if (!is_array($params) && (null !== $params)) {
+ throw new IPF_Exception('Invalid parameters');
+ }
+
+ if (is_array($params)) {
+ foreach ($params as $param) {
+ if (!$param instanceof IPF_Server_Reflection_Parameter) {
+ throw new IPF_Exception('One or more params are invalid');
+ }
+ }
+ }
+
+ $this->_params = $params;
+ }
+
+ public function getReturnType()
+ {
+ return $this->_return->getType();
+ }
+
+ public function getReturnValue()
+ {
+ return $this->_return;
+ }
+
+ public function getParameters()
+ {
+ return $this->_params;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_Server_Reflection_ReturnValue
+{
+ protected $_type;
+ protected $_description;
+
+ public function __construct($type = 'mixed', $description = '')
+ {
+ $this->setType($type);
+ $this->setDescription($description);
+ }
+
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ public function setType($type)
+ {
+ if (!is_string($type) && (null !== $type)) {
+ throw new IPF_Exception('Invalid parameter type');
+ }
+ $this->_type = $type;
+ }
+
+ public function getDescription()
+ {
+ return $this->_description;
+ }
+
+ public function setDescription($description)
+ {
+ if (!is_string($description) && (null !== $description)) {
+ throw new IPF_Exception('Invalid parameter description');
+ }
+ $this->_description = $description;
+ }
+}
--- /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;
+ }
+}
+