From 0520dda6cbcbca4f8c7317baca5326e798d9b6c2 Mon Sep 17 00:00:00 2001 From: avl Date: Mon, 29 Sep 2008 15:16:07 +0300 Subject: [PATCH] IPF Server --- ipf/server/abstract.php | 24 ++ ipf/server/interface.php | 12 + ipf/server/reflection.php | 31 +++ ipf/server/reflection/class.php | 77 +++++ ipf/server/reflection/function.php | 4 + ipf/server/reflection/function/abstract.php | 293 ++++++++++++++++++++ ipf/server/reflection/method.php | 45 +++ ipf/server/reflection/node.php | 96 +++++++ ipf/server/reflection/parameter.php | 60 ++++ ipf/server/reflection/prototype.php | 38 +++ ipf/server/reflection/returnvalue.php | 39 +++ 11 files changed, 719 insertions(+) create mode 100644 ipf/server/abstract.php create mode 100644 ipf/server/interface.php create mode 100644 ipf/server/reflection.php create mode 100644 ipf/server/reflection/class.php create mode 100644 ipf/server/reflection/function.php create mode 100644 ipf/server/reflection/function/abstract.php create mode 100644 ipf/server/reflection/method.php create mode 100644 ipf/server/reflection/node.php create mode 100644 ipf/server/reflection/parameter.php create mode 100644 ipf/server/reflection/prototype.php create mode 100644 ipf/server/reflection/returnvalue.php diff --git a/ipf/server/abstract.php b/ipf/server/abstract.php new file mode 100644 index 0000000..b0cf6a1 --- /dev/null +++ b/ipf/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/ipf/server/reflection/function.php b/ipf/server/reflection/function.php new file mode 100644 index 0000000..a4cd63c --- /dev/null +++ b/ipf/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/ipf/server/reflection/method.php b/ipf/server/reflection/method.php new file mode 100644 index 0000000..9aaee14 --- /dev/null +++ b/ipf/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/ipf/server/reflection/node.php b/ipf/server/reflection/node.php new file mode 100644 index 0000000..5ee1a2a --- /dev/null +++ b/ipf/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/ipf/server/reflection/parameter.php b/ipf/server/reflection/parameter.php new file mode 100644 index 0000000..d043314 --- /dev/null +++ b/ipf/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/ipf/server/reflection/prototype.php b/ipf/server/reflection/prototype.php new file mode 100644 index 0000000..c34375d --- /dev/null +++ b/ipf/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/ipf/server/reflection/returnvalue.php b/ipf/server/reflection/returnvalue.php new file mode 100644 index 0000000..c33fb2c --- /dev/null +++ b/ipf/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; + } +} -- 2.49.0