Source of file AbstractFunction.php
Size: 12,885 Bytes - Last Modified: 2014-03-12T23:21:18+01:00
/home/theseer/Downloads/ZendFramework-2.3.0/library/Zend/Server/Reflection/AbstractFunction.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 | <?php /** * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ namespace Zend\Server\Reflection; use ReflectionClass as PhpReflectionClass; use ReflectionFunction as PhpReflectionFunction; use ReflectionFunctionAbstract; use ReflectionMethod as PhpReflectionMethod; use Zend\Code\Reflection\DocBlockReflection; /** * Function/Method Reflection * * Decorates a ReflectionFunction. Allows setting and retrieving an alternate * 'service' name (i.e., the name to be used when calling via a service), * setting and retrieving the description (originally set using the docblock * contents), retrieving the callback and callback type, retrieving additional * method invocation arguments, and retrieving the * method {@link \Zend\Server\Reflection\Prototype prototypes}. */ abstract class AbstractFunction { /** * @var ReflectionFunctionAbstract */ protected $reflection; /** * Additional arguments to pass to method on invocation * @var array */ protected $argv = array(); /** * Used to store extra configuration for the method (typically done by the * server class, e.g., to indicate whether or not to instantiate a class). * Associative array; access is as properties via {@link __get()} and * {@link __set()} * @var array */ protected $config = array(); /** * Declaring class (needed for when serialization occurs) * @var string */ protected $class; /** * Function/method description * @var string */ protected $description = ''; /** * Namespace with which to prefix function/method name * @var string */ protected $namespace; /** * Prototypes * @var array */ protected $prototypes = array(); private $return; private $returnDesc; private $paramDesc; private $sigParams; private $sigParamsDepth; /** * Constructor * * @param ReflectionFunctionAbstract $r * @param null|string $namespace * @param null|array $argv * @throws Exception\InvalidArgumentException * @throws Exception\RuntimeException */ public function __construct(ReflectionFunctionAbstract $r, $namespace = null, $argv = array()) { $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 PhpReflectionMethod) { $this->class = $r->getDeclaringClass()->getName(); } // Perform some introspection $this->reflect(); } /** * Create signature node tree * * Recursive method to build the signature node tree. Increments through * each array in {@link $sigParams}, adding every value of the next level * to the current value (unless the current value is null). * * @param \Zend\Server\Reflection\Node $parent * @param int $level * @return void */ protected function addTree(Node $parent, $level = 0) { if ($level >= $this->sigParamsDepth) { return; } foreach ($this->sigParams[$level] as $value) { $node = new Node($value, $parent); if ((null !== $value) && ($this->sigParamsDepth > $level + 1)) { $this->addTree($node, $level + 1); } } } /** * Build the signature tree * * Builds a signature tree starting at the return values and descending * through each method argument. Returns an array of * {@link \Zend\Server\Reflection\Node}s. * * @return array */ protected function buildTree() { $returnTree = array(); foreach ($this->return as $value) { $node = new Node($value); $this->addTree($node); $returnTree[] = $node; } return $returnTree; } /** * Build method signatures * * Builds method signatures using the array of return types and the array of * parameters types * * @param array $return Array of return types * @param string $returnDesc Return value description * @param array $paramTypes Array of arguments (each an array of types) * @param array $paramDesc Array of parameter descriptions * @return array */ 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 Node) { continue; } $signature = array(); do { array_unshift($signature, $node->getValue()); $node = $node->getParent(); } while ($node instanceof Node); $signatures[] = $signature; } // Build prototypes $params = $this->reflection->getParameters(); foreach ($signatures as $signature) { $return = new ReflectionReturnValue(array_shift($signature), $this->returnDesc); $tmp = array(); foreach ($signature as $key => $type) { $param = new ReflectionParameter($params[$key], $type, (isset($this->paramDesc[$key]) ? $this->paramDesc[$key] : null)); $param->setPosition($key); $tmp[] = $param; } $this->prototypes[] = new Prototype($return, $tmp); } } /** * Use code reflection to create method signatures * * Determines the method help/description text from the function DocBlock * comment. Determines method signatures using a combination of * ReflectionFunction and parsing of DocBlock @param and @return values. * * @throws Exception\RuntimeException * @return array */ protected function reflect() { $function = $this->reflection; $paramCount = $function->getNumberOfParameters(); $parameters = $function->getParameters(); $scanner = new DocBlockReflection(($function->getDocComment()) ? : '/***/'); $helpText = $scanner->getLongDescription(); /* @var \Zend\Code\Reflection\DocBlock\Tag\ParamTag[] $paramTags */ $paramTags = $scanner->getTags('param'); /* @var \Zend\Code\Reflection\DocBlock\Tag\ReturnTag $returnTag */ $returnTag = $scanner->getTag('return'); if (empty($helpText)) { $helpText = $scanner->getShortDescription(); if (empty($helpText)) { $helpText = $function->getName(); } } $this->setDescription($helpText); if ($returnTag) { $return = array(); $returnDesc = $returnTag->getDescription(); foreach ($returnTag->getTypes() as $type) { $return[] = $type; } } else { $return = array('void'); $returnDesc = ''; } $paramTypesTmp = array(); $paramDesc = array(); if (empty($paramTags)) { foreach ($parameters as $param) { $paramTypesTmp[] = array(($param->isArray()) ? 'array' : 'mixed'); $paramDesc[] = ''; } } else { $paramDesc = array(); foreach ($paramTags as $paramTag) { $paramTypesTmp[] = $paramTag->getTypes(); $paramDesc[] = ($paramTag->getDescription()) ? : ''; } } // Get all param types as arrays $nParamTypesTmp = count($paramTypesTmp); if ($nParamTypesTmp < $paramCount) { $start = $paramCount - $nParamTypesTmp; for ($i = $start; $i < $paramCount; ++$i) { $paramTypesTmp[$i] = array('mixed'); $paramDesc[$i] = ''; } } elseif ($nParamTypesTmp != $paramCount) { throw new Exception\RuntimeException( 'Variable number of arguments is not supported for services (except optional parameters). ' . 'Number of function arguments must correspond to actual number of arguments described in a docblock.'); } $paramTypes = array(); foreach ($paramTypesTmp as $i => $param) { if ($parameters[$i]->isOptional()) { array_unshift($param, null); } $paramTypes[] = $param; } $this->buildSignatures($return, $returnDesc, $paramTypes, $paramDesc); } /** * Proxy reflection calls * * @param string $method * @param array $args * @throws Exception\BadMethodCallException * @return mixed */ public function __call($method, $args) { if (method_exists($this->reflection, $method)) { return call_user_func_array(array($this->reflection, $method), $args); } throw new Exception\BadMethodCallException('Invalid reflection method ("' . $method . '")'); } /** * Retrieve configuration parameters * * Values are retrieved by key from {@link $config}. Returns null if no * value found. * * @param string $key * @return mixed */ public function __get($key) { if (isset($this->config[$key])) { return $this->config[$key]; } return null; } /** * Set configuration parameters * * Values are stored by $key in {@link $config}. * * @param string $key * @param mixed $value * @return void */ public function __set($key, $value) { $this->config[$key] = $value; } /** * Set method's namespace * * @param string $namespace * @throws Exception\InvalidArgumentException * @return void */ public function setNamespace($namespace) { if (empty($namespace)) { $this->namespace = ''; return; } if (!is_string($namespace) || !preg_match('/[a-z0-9_\.]+/i', $namespace)) { throw new Exception\InvalidArgumentException('Invalid namespace'); } $this->namespace = $namespace; } /** * Return method's namespace * * @return string */ public function getNamespace() { return $this->namespace; } /** * Set the description * * @param string $string * @throws Exception\InvalidArgumentException * @return void */ public function setDescription($string) { if (!is_string($string)) { throw new Exception\InvalidArgumentException('Invalid description'); } $this->description = $string; } /** * Retrieve the description * * @return string */ public function getDescription() { return $this->description; } /** * Retrieve all prototypes as array of * {@link \Zend\Server\Reflection\Prototype}s * * @return Prototype[] */ public function getPrototypes() { return $this->prototypes; } /** * Retrieve additional invocation arguments * * @return array */ public function getInvokeArguments() { return $this->argv; } /** * Wakeup from serialization * * Reflection needs explicit instantiation to work correctly. Re-instantiate * reflection object on wakeup. * * @return void */ public function __wakeup() { if ($this->reflection instanceof PhpReflectionMethod) { $class = new PhpReflectionClass($this->class); $this->reflection = new PhpReflectionMethod($class->newInstance(), $this->getName()); } else { $this->reflection = new PhpReflectionFunction($this->getName()); } } } |