composer package updates
[openemr.git] / vendor / zendframework / zend-server / src / Reflection / AbstractFunction.php
blobf9adfc2e9e92bfc1f559e2243bfc03f337424a73
1 <?php
2 /**
3 * @see https://github.com/zendframework/zend-server for the canonical source repository
4 * @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (https://www.zend.com)
5 * @license https://github.com/zendframework/zend-server/blob/master/LICENSE.md New BSD License
6 */
8 namespace Zend\Server\Reflection;
10 use ReflectionClass as PhpReflectionClass;
11 use ReflectionFunction as PhpReflectionFunction;
12 use ReflectionFunctionAbstract;
13 use ReflectionMethod as PhpReflectionMethod;
14 use Zend\Code\Reflection\DocBlockReflection;
16 /**
17 * Function/Method Reflection
19 * Decorates a ReflectionFunction. Allows setting and retrieving an alternate
20 * 'service' name (i.e., the name to be used when calling via a service),
21 * setting and retrieving the description (originally set using the docblock
22 * contents), retrieving the callback and callback type, retrieving additional
23 * method invocation arguments, and retrieving the
24 * method {@link \Zend\Server\Reflection\Prototype prototypes}.
26 abstract class AbstractFunction
28 /**
29 * @var ReflectionFunctionAbstract
31 protected $reflection;
33 /**
34 * Additional arguments to pass to method on invocation
35 * @var array
37 protected $argv = [];
39 /**
40 * Used to store extra configuration for the method (typically done by the
41 * server class, e.g., to indicate whether or not to instantiate a class).
42 * Associative array; access is as properties via {@link __get()} and
43 * {@link __set()}
44 * @var array
46 protected $config = [];
48 /**
49 * Declaring class (needed for when serialization occurs)
50 * @var string
52 protected $class;
54 /**
55 * Function/method description
56 * @var string
58 protected $description = '';
60 /**
61 * Namespace with which to prefix function/method name
62 * @var string
64 protected $namespace;
66 /**
67 * Prototypes
68 * @var array
70 protected $prototypes = [];
72 /**
73 * Phpdoc comment
74 * @var string
76 protected $docComment = '';
78 private $return;
79 private $returnDesc;
80 private $paramDesc;
81 private $sigParams;
82 private $sigParamsDepth;
84 /**
85 * Constructor
87 * @param ReflectionFunctionAbstract $r
88 * @param null|string $namespace
89 * @param null|array $argv
90 * @throws Exception\InvalidArgumentException
91 * @throws Exception\RuntimeException
93 public function __construct(ReflectionFunctionAbstract $r, $namespace = null, $argv = [])
95 $this->reflection = $r;
97 // Determine namespace
98 if (null !== $namespace) {
99 $this->setNamespace($namespace);
102 // Determine arguments
103 if (is_array($argv)) {
104 $this->argv = $argv;
107 // If method call, need to store some info on the class
108 if ($r instanceof PhpReflectionMethod) {
109 $this->class = $r->getDeclaringClass()->getName();
112 // Perform some introspection
113 $this->reflect();
117 * Create signature node tree
119 * Recursive method to build the signature node tree. Increments through
120 * each array in {@link $sigParams}, adding every value of the next level
121 * to the current value (unless the current value is null).
123 * @param \Zend\Server\Reflection\Node $parent
124 * @param int $level
125 * @return void
127 protected function addTree(Node $parent, $level = 0)
129 if ($level >= $this->sigParamsDepth) {
130 return;
133 foreach ($this->sigParams[$level] as $value) {
134 $node = new Node($value, $parent);
135 if ((null !== $value) && ($this->sigParamsDepth > $level + 1)) {
136 $this->addTree($node, $level + 1);
142 * Build the signature tree
144 * Builds a signature tree starting at the return values and descending
145 * through each method argument. Returns an array of
146 * {@link \Zend\Server\Reflection\Node}s.
148 * @return array
150 protected function buildTree()
152 $returnTree = [];
153 foreach ($this->return as $value) {
154 $node = new Node($value);
155 $this->addTree($node);
156 $returnTree[] = $node;
159 return $returnTree;
163 * Build method signatures
165 * Builds method signatures using the array of return types and the array of
166 * parameters types
168 * @param array $return Array of return types
169 * @param string $returnDesc Return value description
170 * @param array $paramTypes Array of arguments (each an array of types)
171 * @param array $paramDesc Array of parameter descriptions
172 * @return array
174 protected function buildSignatures($return, $returnDesc, $paramTypes, $paramDesc)
176 $this->return = $return;
177 $this->returnDesc = $returnDesc;
178 $this->paramDesc = $paramDesc;
179 $this->sigParams = $paramTypes;
180 $this->sigParamsDepth = count($paramTypes);
181 $signatureTrees = $this->buildTree();
182 $signatures = [];
184 $endPoints = [];
185 foreach ($signatureTrees as $root) {
186 $tmp = $root->getEndPoints();
187 if (empty($tmp)) {
188 $endPoints = array_merge($endPoints, [$root]);
189 } else {
190 $endPoints = array_merge($endPoints, $tmp);
194 foreach ($endPoints as $node) {
195 if (! $node instanceof Node) {
196 continue;
199 $signature = [];
200 do {
201 array_unshift($signature, $node->getValue());
202 $node = $node->getParent();
203 } while ($node instanceof Node);
205 $signatures[] = $signature;
208 // Build prototypes
209 $params = $this->reflection->getParameters();
210 foreach ($signatures as $signature) {
211 $return = new ReflectionReturnValue(array_shift($signature), $this->returnDesc);
212 $tmp = [];
213 foreach ($signature as $key => $type) {
214 $param = new ReflectionParameter(
215 $params[$key],
216 $type,
217 (isset($this->paramDesc[$key]) ? $this->paramDesc[$key] : null)
219 $param->setPosition($key);
220 $tmp[] = $param;
223 $this->prototypes[] = new Prototype($return, $tmp);
228 * Use code reflection to create method signatures
230 * Determines the method help/description text from the function DocBlock
231 * comment. Determines method signatures using a combination of
232 * ReflectionFunction and parsing of DocBlock @param and @return values.
234 * @throws Exception\RuntimeException
235 * @return array
237 protected function reflect()
239 $function = $this->reflection;
240 $paramCount = $function->getNumberOfParameters();
241 $parameters = $function->getParameters();
243 if (! $this->docComment) {
244 $this->docComment = $function->getDocComment();
247 $scanner = new DocBlockReflection(($this->docComment) ? : '/***/');
248 $helpText = $scanner->getLongDescription();
249 /* @var \Zend\Code\Reflection\DocBlock\Tag\ParamTag[] $paramTags */
250 $paramTags = $scanner->getTags('param');
251 /* @var \Zend\Code\Reflection\DocBlock\Tag\ReturnTag $returnTag */
252 $returnTag = $scanner->getTag('return');
254 if (empty($helpText)) {
255 $helpText = $scanner->getShortDescription();
256 if (empty($helpText)) {
257 $helpText = $function->getName();
260 $this->setDescription($helpText);
262 if ($returnTag) {
263 $return = [];
264 $returnDesc = $returnTag->getDescription();
265 foreach ($returnTag->getTypes() as $type) {
266 $return[] = $type;
268 } else {
269 $return = ['void'];
270 $returnDesc = '';
273 $paramTypesTmp = [];
274 $paramDesc = [];
275 if (empty($paramTags)) {
276 foreach ($parameters as $param) {
277 $paramTypesTmp[] = [($param->isArray()) ? 'array' : 'mixed'];
278 $paramDesc[] = '';
280 } else {
281 $paramDesc = [];
282 foreach ($paramTags as $paramTag) {
283 $paramTypesTmp[] = $paramTag->getTypes();
284 $paramDesc[] = ($paramTag->getDescription()) ? : '';
288 // Get all param types as arrays
289 $nParamTypesTmp = count($paramTypesTmp);
290 if ($nParamTypesTmp < $paramCount) {
291 $start = $paramCount - $nParamTypesTmp;
292 for ($i = $start; $i < $paramCount; ++$i) {
293 $paramTypesTmp[$i] = ['mixed'];
294 $paramDesc[$i] = '';
296 } elseif ($nParamTypesTmp != $paramCount) {
297 throw new Exception\RuntimeException(
298 'Variable number of arguments is not supported for services (except optional parameters). '
299 . 'Number of function arguments must correspond to actual number of arguments described in a docblock.'
303 $paramTypes = [];
304 foreach ($paramTypesTmp as $i => $param) {
305 if ($parameters[$i]->isOptional()) {
306 array_unshift($param, null);
308 $paramTypes[] = $param;
311 $this->buildSignatures($return, $returnDesc, $paramTypes, $paramDesc);
315 * Proxy reflection calls
317 * @param string $method
318 * @param array $args
319 * @throws Exception\BadMethodCallException
320 * @return mixed
322 public function __call($method, $args)
324 if (method_exists($this->reflection, $method)) {
325 return call_user_func_array([$this->reflection, $method], $args);
328 throw new Exception\BadMethodCallException('Invalid reflection method ("' . $method . '")');
332 * Retrieve configuration parameters
334 * Values are retrieved by key from {@link $config}. Returns null if no
335 * value found.
337 * @param string $key
338 * @return mixed
340 public function __get($key)
342 if (isset($this->config[$key])) {
343 return $this->config[$key];
346 return;
350 * Set configuration parameters
352 * Values are stored by $key in {@link $config}.
354 * @param string $key
355 * @param mixed $value
356 * @return void
358 public function __set($key, $value)
360 $this->config[$key] = $value;
364 * Set method's namespace
366 * @param string $namespace
367 * @throws Exception\InvalidArgumentException
368 * @return void
370 public function setNamespace($namespace)
372 if (empty($namespace)) {
373 $this->namespace = '';
374 return;
377 if (! is_string($namespace) || ! preg_match('/[a-z0-9_\.]+/i', $namespace)) {
378 throw new Exception\InvalidArgumentException('Invalid namespace');
381 $this->namespace = $namespace;
385 * Return method's namespace
387 * @return string
389 public function getNamespace()
391 return $this->namespace;
395 * Set the description
397 * @param string $string
398 * @throws Exception\InvalidArgumentException
399 * @return void
401 public function setDescription($string)
403 if (! is_string($string)) {
404 throw new Exception\InvalidArgumentException('Invalid description');
407 $this->description = $string;
411 * Retrieve the description
413 * @return string
415 public function getDescription()
417 return $this->description;
421 * Retrieve all prototypes as array of
422 * {@link \Zend\Server\Reflection\Prototype}s
424 * @return Prototype[]
426 public function getPrototypes()
428 return $this->prototypes;
432 * Retrieve additional invocation arguments
434 * @return array
436 public function getInvokeArguments()
438 return $this->argv;
442 * Wakeup from serialization
444 * Reflection needs explicit instantiation to work correctly. Re-instantiate
445 * reflection object on wakeup.
447 * @return void
449 public function __wakeup()
451 if ($this->reflection instanceof PhpReflectionMethod) {
452 $class = new PhpReflectionClass($this->class);
453 $this->reflection = new PhpReflectionMethod($class->newInstance(), $this->getName());
454 } else {
455 $this->reflection = new PhpReflectionFunction($this->getName());