File indexing completed on 2025-01-19 05:21:28
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Server 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 */ 0020 0021 /** 0022 * Zend_Server_Reflection_Node 0023 */ 0024 // require_once 'Zend/Server/Reflection/Node.php'; 0025 0026 /** 0027 * Zend_Server_Reflection_Parameter 0028 */ 0029 // require_once 'Zend/Server/Reflection/Parameter.php'; 0030 0031 /** 0032 * Zend_Server_Reflection_Prototype 0033 */ 0034 // require_once 'Zend/Server/Reflection/Prototype.php'; 0035 0036 /** 0037 * Function/Method Reflection 0038 * 0039 * Decorates a ReflectionFunction. Allows setting and retrieving an alternate 0040 * 'service' name (i.e., the name to be used when calling via a service), 0041 * setting and retrieving the description (originally set using the docblock 0042 * contents), retrieving the callback and callback type, retrieving additional 0043 * method invocation arguments, and retrieving the 0044 * method {@link Zend_Server_Reflection_Prototype prototypes}. 0045 * 0046 * @category Zend 0047 * @package Zend_Server 0048 * @subpackage Reflection 0049 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0050 * @license http://framework.zend.com/license/new-bsd New BSD License 0051 * @version $Id$ 0052 */ 0053 abstract class Zend_Server_Reflection_Function_Abstract 0054 { 0055 /** 0056 * @var ReflectionFunction 0057 */ 0058 protected $_reflection; 0059 0060 /** 0061 * Additional arguments to pass to method on invocation 0062 * @var array 0063 */ 0064 protected $_argv = array(); 0065 0066 /** 0067 * Used to store extra configuration for the method (typically done by the 0068 * server class, e.g., to indicate whether or not to instantiate a class). 0069 * Associative array; access is as properties via {@link __get()} and 0070 * {@link __set()} 0071 * @var array 0072 */ 0073 protected $_config = array(); 0074 0075 /** 0076 * Declaring class (needed for when serialization occurs) 0077 * @var string 0078 */ 0079 protected $_class; 0080 0081 /** 0082 * Function/method description 0083 * @var string 0084 */ 0085 protected $_description = ''; 0086 0087 /** 0088 * Namespace with which to prefix function/method name 0089 * @var string 0090 */ 0091 protected $_namespace; 0092 0093 /** 0094 * Prototypes 0095 * @var array 0096 */ 0097 protected $_prototypes = array(); 0098 0099 private $_return; 0100 private $_returnDesc; 0101 private $_paramDesc; 0102 private $_sigParams; 0103 private $_sigParamsDepth; 0104 0105 /** 0106 * Constructor 0107 * 0108 * @param ReflectionFunction $r 0109 */ 0110 public function __construct(Reflector $r, $namespace = null, $argv = array()) 0111 { 0112 // In PHP 5.1.x, ReflectionMethod extends ReflectionFunction. In 5.2.x, 0113 // both extend ReflectionFunctionAbstract. So, we can't do normal type 0114 // hinting in the prototype, but instead need to do some explicit 0115 // testing here. 0116 if ((!$r instanceof ReflectionFunction) 0117 && (!$r instanceof ReflectionMethod)) { 0118 // require_once 'Zend/Server/Reflection/Exception.php'; 0119 throw new Zend_Server_Reflection_Exception('Invalid reflection class'); 0120 } 0121 $this->_reflection = $r; 0122 0123 // Determine namespace 0124 if (null !== $namespace){ 0125 $this->setNamespace($namespace); 0126 } 0127 0128 // Determine arguments 0129 if (is_array($argv)) { 0130 $this->_argv = $argv; 0131 } 0132 0133 // If method call, need to store some info on the class 0134 if ($r instanceof ReflectionMethod) { 0135 $this->_class = $r->getDeclaringClass()->getName(); 0136 } 0137 0138 // Perform some introspection 0139 $this->_reflect(); 0140 } 0141 0142 /** 0143 * Create signature node tree 0144 * 0145 * Recursive method to build the signature node tree. Increments through 0146 * each array in {@link $_sigParams}, adding every value of the next level 0147 * to the current value (unless the current value is null). 0148 * 0149 * @param Zend_Server_Reflection_Node $parent 0150 * @param int $level 0151 * @return void 0152 */ 0153 protected function _addTree(Zend_Server_Reflection_Node $parent, $level = 0) 0154 { 0155 if ($level >= $this->_sigParamsDepth) { 0156 return; 0157 } 0158 0159 foreach ($this->_sigParams[$level] as $value) { 0160 $node = new Zend_Server_Reflection_Node($value, $parent); 0161 if ((null !== $value) && ($this->_sigParamsDepth > $level + 1)) { 0162 $this->_addTree($node, $level + 1); 0163 } 0164 } 0165 } 0166 0167 /** 0168 * Build the signature tree 0169 * 0170 * Builds a signature tree starting at the return values and descending 0171 * through each method argument. Returns an array of 0172 * {@link Zend_Server_Reflection_Node}s. 0173 * 0174 * @return array 0175 */ 0176 protected function _buildTree() 0177 { 0178 $returnTree = array(); 0179 foreach ((array) $this->_return as $value) { 0180 $node = new Zend_Server_Reflection_Node($value); 0181 $this->_addTree($node); 0182 $returnTree[] = $node; 0183 } 0184 0185 return $returnTree; 0186 } 0187 0188 /** 0189 * Build method signatures 0190 * 0191 * Builds method signatures using the array of return types and the array of 0192 * parameters types 0193 * 0194 * @param array $return Array of return types 0195 * @param string $returnDesc Return value description 0196 * @param array $params Array of arguments (each an array of types) 0197 * @param array $paramDesc Array of parameter descriptions 0198 * @return array 0199 */ 0200 protected function _buildSignatures($return, $returnDesc, $paramTypes, $paramDesc) 0201 { 0202 $this->_return = $return; 0203 $this->_returnDesc = $returnDesc; 0204 $this->_paramDesc = $paramDesc; 0205 $this->_sigParams = $paramTypes; 0206 $this->_sigParamsDepth = count($paramTypes); 0207 $signatureTrees = $this->_buildTree(); 0208 $signatures = array(); 0209 0210 $endPoints = array(); 0211 foreach ($signatureTrees as $root) { 0212 $tmp = $root->getEndPoints(); 0213 if (empty($tmp)) { 0214 $endPoints = array_merge($endPoints, array($root)); 0215 } else { 0216 $endPoints = array_merge($endPoints, $tmp); 0217 } 0218 } 0219 0220 foreach ($endPoints as $node) { 0221 if (!$node instanceof Zend_Server_Reflection_Node) { 0222 continue; 0223 } 0224 0225 $signature = array(); 0226 do { 0227 array_unshift($signature, $node->getValue()); 0228 $node = $node->getParent(); 0229 } while ($node instanceof Zend_Server_Reflection_Node); 0230 0231 $signatures[] = $signature; 0232 } 0233 0234 // Build prototypes 0235 $params = $this->_reflection->getParameters(); 0236 foreach ($signatures as $signature) { 0237 $return = new Zend_Server_Reflection_ReturnValue(array_shift($signature), $this->_returnDesc); 0238 $tmp = array(); 0239 foreach ($signature as $key => $type) { 0240 $param = new Zend_Server_Reflection_Parameter($params[$key], $type, (isset($this->_paramDesc[$key]) ? $this->_paramDesc[$key] : null)); 0241 $param->setPosition($key); 0242 $tmp[] = $param; 0243 } 0244 0245 $this->_prototypes[] = new Zend_Server_Reflection_Prototype($return, $tmp); 0246 } 0247 } 0248 0249 /** 0250 * Use code reflection to create method signatures 0251 * 0252 * Determines the method help/description text from the function DocBlock 0253 * comment. Determines method signatures using a combination of 0254 * ReflectionFunction and parsing of DocBlock @param and @return values. 0255 * 0256 * @param ReflectionFunction $function 0257 * @return array 0258 */ 0259 protected function _reflect() 0260 { 0261 $function = $this->_reflection; 0262 $helpText = ''; 0263 $signatures = array(); 0264 $returnDesc = ''; 0265 $paramCount = $function->getNumberOfParameters(); 0266 $paramCountRequired = $function->getNumberOfRequiredParameters(); 0267 $parameters = $function->getParameters(); 0268 $docBlock = $function->getDocComment(); 0269 0270 if (!empty($docBlock)) { 0271 // Get help text 0272 if (preg_match(':/\*\*\s*\r?\n\s*\*\s(.*?)\r?\n\s*\*(\s@|/):s', $docBlock, $matches)) 0273 { 0274 $helpText = $matches[1]; 0275 $helpText = preg_replace('/(^\s*\*\s)/m', '', $helpText); 0276 $helpText = preg_replace('/\r?\n\s*\*\s*(\r?\n)*/s', "\n", $helpText); 0277 $helpText = trim($helpText); 0278 } 0279 0280 // Get return type(s) and description 0281 $return = 'void'; 0282 if (preg_match('/@return\s+(\S+)/', $docBlock, $matches)) { 0283 $return = explode('|', $matches[1]); 0284 if (preg_match('/@return\s+\S+\s+(.*?)(@|\*\/)/s', $docBlock, $matches)) 0285 { 0286 $value = $matches[1]; 0287 $value = preg_replace('/\s?\*\s/m', '', $value); 0288 $value = preg_replace('/\s{2,}/', ' ', $value); 0289 $returnDesc = trim($value); 0290 } 0291 } 0292 0293 // Get param types and description 0294 if (preg_match_all('/@param\s+([^\s]+)/m', $docBlock, $matches)) { 0295 $paramTypesTmp = $matches[1]; 0296 if (preg_match_all('/@param\s+\S+\s+(\$\S+)\s+(.*?)(?=@|\*\/)/s', $docBlock, $matches)) 0297 { 0298 $paramDesc = $matches[2]; 0299 foreach ($paramDesc as $key => $value) { 0300 $value = preg_replace('/\s?\*\s/m', '', $value); 0301 $value = preg_replace('/\s{2,}/', ' ', $value); 0302 $paramDesc[$key] = trim($value); 0303 } 0304 } 0305 } 0306 } else { 0307 $helpText = $function->getName(); 0308 $return = 'void'; 0309 0310 // Try and auto-determine type, based on reflection 0311 $paramTypesTmp = array(); 0312 foreach ($parameters as $i => $param) { 0313 $paramType = 'mixed'; 0314 if ($param->isArray()) { 0315 $paramType = 'array'; 0316 } 0317 $paramTypesTmp[$i] = $paramType; 0318 } 0319 } 0320 0321 // Set method description 0322 $this->setDescription($helpText); 0323 0324 // Get all param types as arrays 0325 if (!isset($paramTypesTmp) && (0 < $paramCount)) { 0326 $paramTypesTmp = array_fill(0, $paramCount, 'mixed'); 0327 } elseif (!isset($paramTypesTmp)) { 0328 $paramTypesTmp = array(); 0329 } elseif (count($paramTypesTmp) < $paramCount) { 0330 $start = $paramCount - count($paramTypesTmp); 0331 for ($i = $start; $i < $paramCount; ++$i) { 0332 $paramTypesTmp[$i] = 'mixed'; 0333 } 0334 } 0335 0336 // Get all param descriptions as arrays 0337 if (!isset($paramDesc) && (0 < $paramCount)) { 0338 $paramDesc = array_fill(0, $paramCount, ''); 0339 } elseif (!isset($paramDesc)) { 0340 $paramDesc = array(); 0341 } elseif (count($paramDesc) < $paramCount) { 0342 $start = $paramCount - count($paramDesc); 0343 for ($i = $start; $i < $paramCount; ++$i) { 0344 $paramDesc[$i] = ''; 0345 } 0346 } 0347 0348 if (count($paramTypesTmp) != $paramCount) { 0349 // require_once 'Zend/Server/Reflection/Exception.php'; 0350 throw new Zend_Server_Reflection_Exception( 0351 'Variable number of arguments is not supported for services (except optional parameters). ' 0352 . 'Number of function arguments in ' . $function->getDeclaringClass()->getName() . '::' 0353 . $function->getName() . '() must correspond to actual number of arguments described in the ' 0354 . 'docblock.'); 0355 } 0356 0357 $paramTypes = array(); 0358 foreach ($paramTypesTmp as $i => $param) { 0359 $tmp = explode('|', $param); 0360 if ($parameters[$i]->isOptional()) { 0361 array_unshift($tmp, null); 0362 } 0363 $paramTypes[] = $tmp; 0364 } 0365 0366 $this->_buildSignatures($return, $returnDesc, $paramTypes, $paramDesc); 0367 } 0368 0369 0370 /** 0371 * Proxy reflection calls 0372 * 0373 * @param string $method 0374 * @param array $args 0375 * @return mixed 0376 */ 0377 public function __call($method, $args) 0378 { 0379 if (method_exists($this->_reflection, $method)) { 0380 return call_user_func_array(array($this->_reflection, $method), $args); 0381 } 0382 0383 // require_once 'Zend/Server/Reflection/Exception.php'; 0384 throw new Zend_Server_Reflection_Exception('Invalid reflection method ("' .$method. '")'); 0385 } 0386 0387 /** 0388 * Retrieve configuration parameters 0389 * 0390 * Values are retrieved by key from {@link $_config}. Returns null if no 0391 * value found. 0392 * 0393 * @param string $key 0394 * @return mixed 0395 */ 0396 public function __get($key) 0397 { 0398 if (isset($this->_config[$key])) { 0399 return $this->_config[$key]; 0400 } 0401 0402 return null; 0403 } 0404 0405 /** 0406 * Set configuration parameters 0407 * 0408 * Values are stored by $key in {@link $_config}. 0409 * 0410 * @param string $key 0411 * @param mixed $value 0412 * @return void 0413 */ 0414 public function __set($key, $value) 0415 { 0416 $this->_config[$key] = $value; 0417 } 0418 0419 /** 0420 * Set method's namespace 0421 * 0422 * @param string $namespace 0423 * @return void 0424 */ 0425 public function setNamespace($namespace) 0426 { 0427 if (empty($namespace)) { 0428 $this->_namespace = ''; 0429 return; 0430 } 0431 0432 if (!is_string($namespace) || !preg_match('/[a-z0-9_\.]+/i', $namespace)) { 0433 // require_once 'Zend/Server/Reflection/Exception.php'; 0434 throw new Zend_Server_Reflection_Exception('Invalid namespace'); 0435 } 0436 0437 $this->_namespace = $namespace; 0438 } 0439 0440 /** 0441 * Return method's namespace 0442 * 0443 * @return string 0444 */ 0445 public function getNamespace() 0446 { 0447 return $this->_namespace; 0448 } 0449 0450 /** 0451 * Set the description 0452 * 0453 * @param string $string 0454 * @return void 0455 */ 0456 public function setDescription($string) 0457 { 0458 if (!is_string($string)) { 0459 // require_once 'Zend/Server/Reflection/Exception.php'; 0460 throw new Zend_Server_Reflection_Exception('Invalid description'); 0461 } 0462 0463 $this->_description = $string; 0464 } 0465 0466 /** 0467 * Retrieve the description 0468 * 0469 * @return void 0470 */ 0471 public function getDescription() 0472 { 0473 return $this->_description; 0474 } 0475 0476 /** 0477 * Retrieve all prototypes as array of 0478 * {@link Zend_Server_Reflection_Prototype Zend_Server_Reflection_Prototypes} 0479 * 0480 * @return array 0481 */ 0482 public function getPrototypes() 0483 { 0484 return $this->_prototypes; 0485 } 0486 0487 /** 0488 * Retrieve additional invocation arguments 0489 * 0490 * @return array 0491 */ 0492 public function getInvokeArguments() 0493 { 0494 return $this->_argv; 0495 } 0496 0497 /** 0498 * Wakeup from serialization 0499 * 0500 * Reflection needs explicit instantiation to work correctly. Re-instantiate 0501 * reflection object on wakeup. 0502 * 0503 * @return void 0504 */ 0505 public function __wakeup() 0506 { 0507 if ($this->_reflection instanceof ReflectionMethod) { 0508 $class = new ReflectionClass($this->_class); 0509 $this->_reflection = new ReflectionMethod($class->newInstance(), $this->getName()); 0510 } else { 0511 $this->_reflection = new ReflectionFunction($this->getName()); 0512 } 0513 } 0514 }