From: Andrey Kutejko Date: Sun, 28 Jul 2013 11:32:54 +0000 (+0300) Subject: move to root X-Git-Url: https://git.andy128k.dev/?a=commitdiff_plain;h=c96a3117a6064229d5e7ec72fed0f5e9de019026;p=ipf-xmlrpc.git move to root --- diff --git a/ipf/server/abstract.php b/ipf/server/abstract.php deleted file mode 100644 index b0cf6a1..0000000 --- a/ipf/server/abstract.php +++ /dev/null @@ -1,24 +0,0 @@ -_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()); - } -} diff --git a/ipf/server/reflection/function.php b/ipf/server/reflection/function.php deleted file mode 100644 index a4cd63c..0000000 --- a/ipf/server/reflection/function.php +++ /dev/null @@ -1,4 +0,0 @@ -_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()); - } - } -} diff --git a/ipf/server/reflection/method.php b/ipf/server/reflection/method.php deleted file mode 100644 index 9aaee14..0000000 --- a/ipf/server/reflection/method.php +++ /dev/null @@ -1,45 +0,0 @@ -_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()); - } - -} diff --git a/ipf/server/reflection/node.php b/ipf/server/reflection/node.php deleted file mode 100644 index 5ee1a2a..0000000 --- a/ipf/server/reflection/node.php +++ /dev/null @@ -1,96 +0,0 @@ -_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; - } -} diff --git a/ipf/server/reflection/parameter.php b/ipf/server/reflection/parameter.php deleted file mode 100644 index d043314..0000000 --- a/ipf/server/reflection/parameter.php +++ /dev/null @@ -1,60 +0,0 @@ -_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; - } -} diff --git a/ipf/server/reflection/prototype.php b/ipf/server/reflection/prototype.php deleted file mode 100644 index c34375d..0000000 --- a/ipf/server/reflection/prototype.php +++ /dev/null @@ -1,38 +0,0 @@ -_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; - } -} diff --git a/ipf/server/reflection/returnvalue.php b/ipf/server/reflection/returnvalue.php deleted file mode 100644 index c33fb2c..0000000 --- a/ipf/server/reflection/returnvalue.php +++ /dev/null @@ -1,39 +0,0 @@ -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; - } -} diff --git a/ipf/xmlrpc/fault.php b/ipf/xmlrpc/fault.php deleted file mode 100644 index 7b1d7b7..0000000 --- a/ipf/xmlrpc/fault.php +++ /dev/null @@ -1,183 +0,0 @@ - '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(); - } -} diff --git a/ipf/xmlrpc/request.php b/ipf/xmlrpc/request.php deleted file mode 100644 index 39c4edc..0000000 --- a/ipf/xmlrpc/request.php +++ /dev/null @@ -1,257 +0,0 @@ -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(); - } -} diff --git a/ipf/xmlrpc/request/http.php b/ipf/xmlrpc/request/http.php deleted file mode 100644 index f66114e..0000000 --- a/ipf/xmlrpc/request/http.php +++ /dev/null @@ -1,57 +0,0 @@ -_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; - } -} diff --git a/ipf/xmlrpc/request/stdin.php b/ipf/xmlrpc/request/stdin.php deleted file mode 100644 index 0538d2b..0000000 --- a/ipf/xmlrpc/request/stdin.php +++ /dev/null @@ -1,30 +0,0 @@ -_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; - } -} diff --git a/ipf/xmlrpc/response.php b/ipf/xmlrpc/response.php deleted file mode 100644 index 5f2f78a..0000000 --- a/ipf/xmlrpc/response.php +++ /dev/null @@ -1,121 +0,0 @@ -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(); - } -} diff --git a/ipf/xmlrpc/response/http.php b/ipf/xmlrpc/response/http.php deleted file mode 100644 index fa767b5..0000000 --- a/ipf/xmlrpc/response/http.php +++ /dev/null @@ -1,12 +0,0 @@ -getEncoding())); - } - return parent::__toString(); - } -} diff --git a/ipf/xmlrpc/server.php b/ipf/xmlrpc/server.php deleted file mode 100644 index a3a5a3a..0000000 --- a/ipf/xmlrpc/server.php +++ /dev/null @@ -1,428 +0,0 @@ - '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; - } -} diff --git a/ipf/xmlrpc/server/cache.php b/ipf/xmlrpc/server/cache.php deleted file mode 100644 index aa6c7e7..0000000 --- a/ipf/xmlrpc/server/cache.php +++ /dev/null @@ -1,60 +0,0 @@ -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; - } -} diff --git a/ipf/xmlrpc/server/fault.php b/ipf/xmlrpc/server/fault.php deleted file mode 100644 index 50db5ed..0000000 --- a/ipf/xmlrpc/server/fault.php +++ /dev/null @@ -1,95 +0,0 @@ - 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; - } -} diff --git a/ipf/xmlrpc/value.php b/ipf/xmlrpc/value.php deleted file mode 100644 index 5201bed..0000000 --- a/ipf/xmlrpc/value.php +++ /dev/null @@ -1,240 +0,0 @@ -_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 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 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; - } - -} - - diff --git a/ipf/xmlrpc/value/array.php b/ipf/xmlrpc/value/array.php deleted file mode 100644 index ead3419..0000000 --- a/ipf/xmlrpc/value/array.php +++ /dev/null @@ -1,33 +0,0 @@ -_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; - } -} - diff --git a/ipf/xmlrpc/value/base64.php b/ipf/xmlrpc/value/base64.php deleted file mode 100644 index 0583f9f..0000000 --- a/ipf/xmlrpc/value/base64.php +++ /dev/null @@ -1,36 +0,0 @@ -_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; - } -} - diff --git a/ipf/xmlrpc/value/boolean.php b/ipf/xmlrpc/value/boolean.php deleted file mode 100644 index c0cf52c..0000000 --- a/ipf/xmlrpc/value/boolean.php +++ /dev/null @@ -1,31 +0,0 @@ -_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; - } -} - diff --git a/ipf/xmlrpc/value/collection.php b/ipf/xmlrpc/value/collection.php deleted file mode 100644 index a720f10..0000000 --- a/ipf/xmlrpc/value/collection.php +++ /dev/null @@ -1,32 +0,0 @@ - $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; - } - -} - diff --git a/ipf/xmlrpc/value/datetime.php b/ipf/xmlrpc/value/datetime.php deleted file mode 100644 index 569cd75..0000000 --- a/ipf/xmlrpc/value/datetime.php +++ /dev/null @@ -1,33 +0,0 @@ -_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; - } - -} - diff --git a/ipf/xmlrpc/value/double.php b/ipf/xmlrpc/value/double.php deleted file mode 100644 index e0a8536..0000000 --- a/ipf/xmlrpc/value/double.php +++ /dev/null @@ -1,17 +0,0 @@ -_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; - } - -} - diff --git a/ipf/xmlrpc/value/integer.php b/ipf/xmlrpc/value/integer.php deleted file mode 100644 index 5de4c8d..0000000 --- a/ipf/xmlrpc/value/integer.php +++ /dev/null @@ -1,17 +0,0 @@ -_type = self::XMLRPC_TYPE_INTEGER; - $this->_value = (int)$value; // Make sure this value is integer - } - - public function getValue() - { - return $this->_value; - } - -} - diff --git a/ipf/xmlrpc/value/nil.php b/ipf/xmlrpc/value/nil.php deleted file mode 100644 index e04c670..0000000 --- a/ipf/xmlrpc/value/nil.php +++ /dev/null @@ -1,30 +0,0 @@ -_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; - } -} - diff --git a/ipf/xmlrpc/value/scalar.php b/ipf/xmlrpc/value/scalar.php deleted file mode 100644 index a950fb5..0000000 --- a/ipf/xmlrpc/value/scalar.php +++ /dev/null @@ -1,20 +0,0 @@ -_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; - } -} - diff --git a/ipf/xmlrpc/value/string.php b/ipf/xmlrpc/value/string.php deleted file mode 100644 index 635293a..0000000 --- a/ipf/xmlrpc/value/string.php +++ /dev/null @@ -1,24 +0,0 @@ -_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'); - } - -} - diff --git a/ipf/xmlrpc/value/struct.php b/ipf/xmlrpc/value/struct.php deleted file mode 100644 index f6ed6ce..0000000 --- a/ipf/xmlrpc/value/struct.php +++ /dev/null @@ -1,34 +0,0 @@ -_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; - } -} - diff --git a/server/abstract.php b/server/abstract.php new file mode 100644 index 0000000..b0cf6a1 --- /dev/null +++ b/server/abstract.php @@ -0,0 +1,24 @@ +_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()); + } +} diff --git a/server/reflection/function.php b/server/reflection/function.php new file mode 100644 index 0000000..a4cd63c --- /dev/null +++ b/server/reflection/function.php @@ -0,0 +1,4 @@ +_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()); + } + } +} diff --git a/server/reflection/method.php b/server/reflection/method.php new file mode 100644 index 0000000..9aaee14 --- /dev/null +++ b/server/reflection/method.php @@ -0,0 +1,45 @@ +_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()); + } + +} diff --git a/server/reflection/node.php b/server/reflection/node.php new file mode 100644 index 0000000..5ee1a2a --- /dev/null +++ b/server/reflection/node.php @@ -0,0 +1,96 @@ +_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; + } +} diff --git a/server/reflection/parameter.php b/server/reflection/parameter.php new file mode 100644 index 0000000..d043314 --- /dev/null +++ b/server/reflection/parameter.php @@ -0,0 +1,60 @@ +_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; + } +} diff --git a/server/reflection/prototype.php b/server/reflection/prototype.php new file mode 100644 index 0000000..c34375d --- /dev/null +++ b/server/reflection/prototype.php @@ -0,0 +1,38 @@ +_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; + } +} diff --git a/server/reflection/returnvalue.php b/server/reflection/returnvalue.php new file mode 100644 index 0000000..c33fb2c --- /dev/null +++ b/server/reflection/returnvalue.php @@ -0,0 +1,39 @@ +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; + } +} diff --git a/xmlrpc/fault.php b/xmlrpc/fault.php new file mode 100644 index 0000000..7b1d7b7 --- /dev/null +++ b/xmlrpc/fault.php @@ -0,0 +1,183 @@ + '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(); + } +} diff --git a/xmlrpc/request.php b/xmlrpc/request.php new file mode 100644 index 0000000..39c4edc --- /dev/null +++ b/xmlrpc/request.php @@ -0,0 +1,257 @@ +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(); + } +} diff --git a/xmlrpc/request/http.php b/xmlrpc/request/http.php new file mode 100644 index 0000000..f66114e --- /dev/null +++ b/xmlrpc/request/http.php @@ -0,0 +1,57 @@ +_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; + } +} diff --git a/xmlrpc/request/stdin.php b/xmlrpc/request/stdin.php new file mode 100644 index 0000000..0538d2b --- /dev/null +++ b/xmlrpc/request/stdin.php @@ -0,0 +1,30 @@ +_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; + } +} diff --git a/xmlrpc/response.php b/xmlrpc/response.php new file mode 100644 index 0000000..5f2f78a --- /dev/null +++ b/xmlrpc/response.php @@ -0,0 +1,121 @@ +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(); + } +} diff --git a/xmlrpc/response/http.php b/xmlrpc/response/http.php new file mode 100644 index 0000000..fa767b5 --- /dev/null +++ b/xmlrpc/response/http.php @@ -0,0 +1,12 @@ +getEncoding())); + } + return parent::__toString(); + } +} diff --git a/xmlrpc/server.php b/xmlrpc/server.php new file mode 100644 index 0000000..a3a5a3a --- /dev/null +++ b/xmlrpc/server.php @@ -0,0 +1,428 @@ + '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; + } +} diff --git a/xmlrpc/server/cache.php b/xmlrpc/server/cache.php new file mode 100644 index 0000000..aa6c7e7 --- /dev/null +++ b/xmlrpc/server/cache.php @@ -0,0 +1,60 @@ +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; + } +} diff --git a/xmlrpc/server/fault.php b/xmlrpc/server/fault.php new file mode 100644 index 0000000..50db5ed --- /dev/null +++ b/xmlrpc/server/fault.php @@ -0,0 +1,95 @@ + 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; + } +} diff --git a/xmlrpc/value.php b/xmlrpc/value.php new file mode 100644 index 0000000..5201bed --- /dev/null +++ b/xmlrpc/value.php @@ -0,0 +1,240 @@ +_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 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 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; + } + +} + + diff --git a/xmlrpc/value/array.php b/xmlrpc/value/array.php new file mode 100644 index 0000000..ead3419 --- /dev/null +++ b/xmlrpc/value/array.php @@ -0,0 +1,33 @@ +_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; + } +} + diff --git a/xmlrpc/value/base64.php b/xmlrpc/value/base64.php new file mode 100644 index 0000000..0583f9f --- /dev/null +++ b/xmlrpc/value/base64.php @@ -0,0 +1,36 @@ +_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; + } +} + diff --git a/xmlrpc/value/boolean.php b/xmlrpc/value/boolean.php new file mode 100644 index 0000000..c0cf52c --- /dev/null +++ b/xmlrpc/value/boolean.php @@ -0,0 +1,31 @@ +_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; + } +} + diff --git a/xmlrpc/value/collection.php b/xmlrpc/value/collection.php new file mode 100644 index 0000000..a720f10 --- /dev/null +++ b/xmlrpc/value/collection.php @@ -0,0 +1,32 @@ + $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; + } + +} + diff --git a/xmlrpc/value/datetime.php b/xmlrpc/value/datetime.php new file mode 100644 index 0000000..569cd75 --- /dev/null +++ b/xmlrpc/value/datetime.php @@ -0,0 +1,33 @@ +_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; + } + +} + diff --git a/xmlrpc/value/double.php b/xmlrpc/value/double.php new file mode 100644 index 0000000..e0a8536 --- /dev/null +++ b/xmlrpc/value/double.php @@ -0,0 +1,17 @@ +_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; + } + +} + diff --git a/xmlrpc/value/integer.php b/xmlrpc/value/integer.php new file mode 100644 index 0000000..5de4c8d --- /dev/null +++ b/xmlrpc/value/integer.php @@ -0,0 +1,17 @@ +_type = self::XMLRPC_TYPE_INTEGER; + $this->_value = (int)$value; // Make sure this value is integer + } + + public function getValue() + { + return $this->_value; + } + +} + diff --git a/xmlrpc/value/nil.php b/xmlrpc/value/nil.php new file mode 100644 index 0000000..e04c670 --- /dev/null +++ b/xmlrpc/value/nil.php @@ -0,0 +1,30 @@ +_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; + } +} + diff --git a/xmlrpc/value/scalar.php b/xmlrpc/value/scalar.php new file mode 100644 index 0000000..a950fb5 --- /dev/null +++ b/xmlrpc/value/scalar.php @@ -0,0 +1,20 @@ +_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; + } +} + diff --git a/xmlrpc/value/string.php b/xmlrpc/value/string.php new file mode 100644 index 0000000..635293a --- /dev/null +++ b/xmlrpc/value/string.php @@ -0,0 +1,24 @@ +_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'); + } + +} + diff --git a/xmlrpc/value/struct.php b/xmlrpc/value/struct.php new file mode 100644 index 0000000..f6ed6ce --- /dev/null +++ b/xmlrpc/value/struct.php @@ -0,0 +1,34 @@ +_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; + } +} +