[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Zend Framework (http://framework.zend.com/) 4 * 5 * @link http://github.com/zendframework/zf2 for the canonical source repository 6 * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com) 7 * @license http://framework.zend.com/license/new-bsd New BSD License 8 */ 9 10 namespace Zend\Code\Reflection; 11 12 use ReflectionMethod as PhpReflectionMethod; 13 use Zend\Code\Annotation\AnnotationManager; 14 use Zend\Code\Scanner\AnnotationScanner; 15 use Zend\Code\Scanner\CachingFileScanner; 16 17 use function array_shift; 18 use function array_slice; 19 use function class_exists; 20 use function count; 21 use function file; 22 use function file_exists; 23 use function implode; 24 use function is_array; 25 use function rtrim; 26 use function strlen; 27 use function substr; 28 use function token_get_all; 29 use function token_name; 30 use function var_export; 31 32 class MethodReflection extends PhpReflectionMethod implements ReflectionInterface 33 { 34 /** 35 * Constant use in @MethodReflection to display prototype as an array 36 */ 37 const PROTOTYPE_AS_ARRAY = 'prototype_as_array'; 38 39 /** 40 * Constant use in @MethodReflection to display prototype as a string 41 */ 42 const PROTOTYPE_AS_STRING = 'prototype_as_string'; 43 44 /** 45 * @var AnnotationScanner 46 */ 47 protected $annotations; 48 49 /** 50 * Retrieve method DocBlock reflection 51 * 52 * @return DocBlockReflection|false 53 */ 54 public function getDocBlock() 55 { 56 if ('' == $this->getDocComment()) { 57 return false; 58 } 59 60 $instance = new DocBlockReflection($this); 61 62 return $instance; 63 } 64 65 /** 66 * @param AnnotationManager $annotationManager 67 * @return AnnotationScanner|false 68 */ 69 public function getAnnotations(AnnotationManager $annotationManager) 70 { 71 if (($docComment = $this->getDocComment()) == '') { 72 return false; 73 } 74 75 if ($this->annotations) { 76 return $this->annotations; 77 } 78 79 $cachingFileScanner = $this->createFileScanner($this->getFileName()); 80 $nameInformation = $cachingFileScanner->getClassNameInformation($this->getDeclaringClass()->getName()); 81 82 if (! $nameInformation) { 83 return false; 84 } 85 86 $this->annotations = new AnnotationScanner($annotationManager, $docComment, $nameInformation); 87 88 return $this->annotations; 89 } 90 91 /** 92 * Get start line (position) of method 93 * 94 * @param bool $includeDocComment 95 * @return int 96 */ 97 public function getStartLine($includeDocComment = false) 98 { 99 if ($includeDocComment) { 100 if ($this->getDocComment() != '') { 101 return $this->getDocBlock()->getStartLine(); 102 } 103 } 104 105 return parent::getStartLine(); 106 } 107 108 /** 109 * Get reflection of declaring class 110 * 111 * @return ClassReflection 112 */ 113 public function getDeclaringClass() 114 { 115 $phpReflection = parent::getDeclaringClass(); 116 $zendReflection = new ClassReflection($phpReflection->getName()); 117 unset($phpReflection); 118 119 return $zendReflection; 120 } 121 122 /** 123 * Get method prototype 124 * 125 * @param string $format 126 * @return array|string 127 */ 128 public function getPrototype($format = MethodReflection::PROTOTYPE_AS_ARRAY) 129 { 130 $returnType = 'mixed'; 131 $docBlock = $this->getDocBlock(); 132 if ($docBlock) { 133 $return = $docBlock->getTag('return'); 134 $returnTypes = $return->getTypes(); 135 $returnType = count($returnTypes) > 1 ? implode('|', $returnTypes) : $returnTypes[0]; 136 } 137 138 $declaringClass = $this->getDeclaringClass(); 139 $prototype = [ 140 'namespace' => $declaringClass->getNamespaceName(), 141 'class' => substr($declaringClass->getName(), strlen($declaringClass->getNamespaceName()) + 1), 142 'name' => $this->getName(), 143 'visibility' => $this->isPublic() ? 'public' : ($this->isPrivate() ? 'private' : 'protected'), 144 'return' => $returnType, 145 'arguments' => [], 146 ]; 147 148 $parameters = $this->getParameters(); 149 foreach ($parameters as $parameter) { 150 $prototype['arguments'][$parameter->getName()] = [ 151 'type' => $parameter->detectType(), 152 'required' => ! $parameter->isOptional(), 153 'by_ref' => $parameter->isPassedByReference(), 154 'default' => $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null, 155 ]; 156 } 157 158 if ($format == MethodReflection::PROTOTYPE_AS_STRING) { 159 $line = $prototype['visibility'] . ' ' . $prototype['return'] . ' ' . $prototype['name'] . '('; 160 $args = []; 161 foreach ($prototype['arguments'] as $name => $argument) { 162 $argsLine = ($argument['type'] ? 163 $argument['type'] . ' ' 164 : '') . ($argument['by_ref'] ? '&' : '') . '$' . $name; 165 if (! $argument['required']) { 166 $argsLine .= ' = ' . var_export($argument['default'], true); 167 } 168 $args[] = $argsLine; 169 } 170 $line .= implode(', ', $args); 171 $line .= ')'; 172 173 return $line; 174 } 175 176 return $prototype; 177 } 178 179 /** 180 * Get all method parameter reflection objects 181 * 182 * @return ParameterReflection[] 183 */ 184 public function getParameters() 185 { 186 $phpReflections = parent::getParameters(); 187 $zendReflections = []; 188 while ($phpReflections && ($phpReflection = array_shift($phpReflections))) { 189 $instance = new ParameterReflection( 190 [$this->getDeclaringClass()->getName(), $this->getName()], 191 $phpReflection->getName() 192 ); 193 $zendReflections[] = $instance; 194 unset($phpReflection); 195 } 196 unset($phpReflections); 197 198 return $zendReflections; 199 } 200 201 /** 202 * Get method contents 203 * 204 * @param bool $includeDocBlock 205 * @return string 206 */ 207 public function getContents($includeDocBlock = true) 208 { 209 $docComment = $this->getDocComment(); 210 $content = $includeDocBlock && ! empty($docComment) ? $docComment . "\n" : ''; 211 $content .= $this->extractMethodContents(); 212 213 return $content; 214 } 215 216 /** 217 * Get method body 218 * 219 * @return string 220 */ 221 public function getBody() 222 { 223 return $this->extractMethodContents(true); 224 } 225 226 /** 227 * Tokenize method string and return concatenated body 228 * 229 * @param bool $bodyOnly 230 * @return string 231 */ 232 protected function extractMethodContents($bodyOnly = false) 233 { 234 $fileName = $this->getFileName(); 235 236 if ((class_exists($this->class) && false === $fileName) || ! file_exists($fileName)) { 237 return ''; 238 } 239 240 $lines = array_slice( 241 file($fileName, FILE_IGNORE_NEW_LINES), 242 $this->getStartLine() - 1, 243 $this->getEndLine() - ($this->getStartLine() - 1), 244 true 245 ); 246 247 $functionLine = implode("\n", $lines); 248 $tokens = token_get_all('<?php ' . $functionLine); 249 250 //remove first entry which is php open tag 251 array_shift($tokens); 252 253 if (! count($tokens)) { 254 return ''; 255 } 256 257 $capture = false; 258 $firstBrace = false; 259 $body = ''; 260 261 foreach ($tokens as $key => $token) { 262 $tokenType = is_array($token) ? token_name($token[0]) : $token; 263 $tokenValue = is_array($token) ? $token[1] : $token; 264 265 switch ($tokenType) { 266 case 'T_FINAL': 267 case 'T_ABSTRACT': 268 case 'T_PUBLIC': 269 case 'T_PROTECTED': 270 case 'T_PRIVATE': 271 case 'T_STATIC': 272 case 'T_FUNCTION': 273 // check to see if we have a valid function 274 // then check if we are inside function and have a closure 275 if ($this->isValidFunction($tokens, $key, $this->getName())) { 276 if ($bodyOnly === false) { 277 //if first instance of tokenType grab prefixed whitespace 278 //and append to body 279 if ($capture === false) { 280 $body .= $this->extractPrefixedWhitespace($tokens, $key); 281 } 282 $body .= $tokenValue; 283 } 284 285 $capture = true; 286 } else { 287 //closure test 288 if ($firstBrace && $tokenType == 'T_FUNCTION') { 289 $body .= $tokenValue; 290 break; 291 } 292 $capture = false; 293 break; 294 } 295 break; 296 297 case '{': 298 if ($capture === false) { 299 break; 300 } 301 302 if ($firstBrace === false) { 303 $firstBrace = true; 304 if ($bodyOnly === true) { 305 break; 306 } 307 } 308 309 $body .= $tokenValue; 310 break; 311 312 case '}': 313 if ($capture === false) { 314 break; 315 } 316 317 //check to see if this is the last brace 318 if ($this->isEndingBrace($tokens, $key)) { 319 //capture the end brace if not bodyOnly 320 if ($bodyOnly === false) { 321 $body .= $tokenValue; 322 } 323 324 break 2; 325 } 326 327 $body .= $tokenValue; 328 break; 329 330 default: 331 if ($capture === false) { 332 break; 333 } 334 335 // if returning body only wait for first brace before capturing 336 if ($bodyOnly === true && $firstBrace !== true) { 337 break; 338 } 339 340 $body .= $tokenValue; 341 break; 342 } 343 } 344 345 //remove ending whitespace and return 346 return rtrim($body); 347 } 348 349 /** 350 * Take current position and find any whitespace 351 * 352 * @param array $haystack 353 * @param int $position 354 * @return string 355 */ 356 protected function extractPrefixedWhitespace($haystack, $position) 357 { 358 $content = ''; 359 $count = count($haystack); 360 if ($position + 1 == $count) { 361 return $content; 362 } 363 364 for ($i = $position - 1; $i >= 0; $i--) { 365 $tokenType = is_array($haystack[$i]) ? token_name($haystack[$i][0]) : $haystack[$i]; 366 $tokenValue = is_array($haystack[$i]) ? $haystack[$i][1] : $haystack[$i]; 367 368 //search only for whitespace 369 if ($tokenType == 'T_WHITESPACE') { 370 $content .= $tokenValue; 371 } else { 372 break; 373 } 374 } 375 376 return $content; 377 } 378 379 /** 380 * Test for ending brace 381 * 382 * @param array $haystack 383 * @param int $position 384 * @return bool 385 */ 386 protected function isEndingBrace($haystack, $position) 387 { 388 $count = count($haystack); 389 390 //advance one position 391 $position = $position + 1; 392 393 if ($position == $count) { 394 return true; 395 } 396 397 for ($i = $position; $i < $count; $i++) { 398 $tokenType = is_array($haystack[$i]) ? token_name($haystack[$i][0]) : $haystack[$i]; 399 switch ($tokenType) { 400 case 'T_FINAL': 401 case 'T_ABSTRACT': 402 case 'T_PUBLIC': 403 case 'T_PROTECTED': 404 case 'T_PRIVATE': 405 case 'T_STATIC': 406 return true; 407 408 case 'T_FUNCTION': 409 // If a function is encountered and that function is not a closure 410 // then return true. otherwise the function is a closure, return false 411 if ($this->isValidFunction($haystack, $i)) { 412 return true; 413 } 414 return false; 415 416 case '}': 417 case ';': 418 case 'T_BREAK': 419 case 'T_CATCH': 420 case 'T_DO': 421 case 'T_ECHO': 422 case 'T_ELSE': 423 case 'T_ELSEIF': 424 case 'T_EVAL': 425 case 'T_EXIT': 426 case 'T_FINALLY': 427 case 'T_FOR': 428 case 'T_FOREACH': 429 case 'T_GOTO': 430 case 'T_IF': 431 case 'T_INCLUDE': 432 case 'T_INCLUDE_ONCE': 433 case 'T_PRINT': 434 case 'T_STRING': 435 case 'T_STRING_VARNAME': 436 case 'T_THROW': 437 case 'T_USE': 438 case 'T_VARIABLE': 439 case 'T_WHILE': 440 case 'T_YIELD': 441 return false; 442 } 443 } 444 } 445 446 /** 447 * Test to see if current position is valid function or 448 * closure. Returns true if it's a function and NOT a closure 449 * 450 * @param array $haystack 451 * @param int $position 452 * @param string $functionName 453 * @return bool 454 */ 455 protected function isValidFunction($haystack, $position, $functionName = null) 456 { 457 $isValid = false; 458 $count = count($haystack); 459 for ($i = $position + 1; $i < $count; $i++) { 460 $tokenType = is_array($haystack[$i]) ? token_name($haystack[$i][0]) : $haystack[$i]; 461 $tokenValue = is_array($haystack[$i]) ? $haystack[$i][1] : $haystack[$i]; 462 463 //check for occurrence of ( or 464 if ($tokenType == 'T_STRING') { 465 //check to see if function name is passed, if so validate against that 466 if ($functionName !== null && $tokenValue != $functionName) { 467 $isValid = false; 468 break; 469 } 470 471 $isValid = true; 472 break; 473 } elseif ($tokenValue == '(') { 474 break; 475 } 476 } 477 478 return $isValid; 479 } 480 481 /** 482 * @return string 483 */ 484 public function toString() 485 { 486 return parent::__toString(); 487 } 488 489 /** 490 * @return string 491 */ 492 public function __toString() 493 { 494 return parent::__toString(); 495 } 496 497 /** 498 * Creates a new FileScanner instance. 499 * 500 * By having this as a separate method it allows the method to be overridden 501 * if a different FileScanner is needed. 502 * 503 * @param string $filename 504 * 505 * @return CachingFileScanner 506 */ 507 protected function createFileScanner($filename) 508 { 509 return new CachingFileScanner($filename); 510 } 511 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |