]> git.andy128k.dev Git - ipf-xmlrpc.git/commitdiff
IPF Server
authoravl <alex.litovchenko@gmail.com>
Mon, 29 Sep 2008 12:16:07 +0000 (15:16 +0300)
committeravl <alex.litovchenko@gmail.com>
Mon, 29 Sep 2008 12:16:07 +0000 (15:16 +0300)
ipf/server/abstract.php [new file with mode: 0644]
ipf/server/interface.php [new file with mode: 0644]
ipf/server/reflection.php [new file with mode: 0644]
ipf/server/reflection/class.php [new file with mode: 0644]
ipf/server/reflection/function.php [new file with mode: 0644]
ipf/server/reflection/function/abstract.php [new file with mode: 0644]
ipf/server/reflection/method.php [new file with mode: 0644]
ipf/server/reflection/node.php [new file with mode: 0644]
ipf/server/reflection/parameter.php [new file with mode: 0644]
ipf/server/reflection/prototype.php [new file with mode: 0644]
ipf/server/reflection/returnvalue.php [new file with mode: 0644]

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