Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
MethodReflection.php
001 <?php
002 /**
003 * Zend Framework (http://framework.zend.com/)
004 *
005 * @link http://github.com/zendframework/zf2 for the canonical source repository
006 * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
007 * @license http://framework.zend.com/license/new-bsd New BSD License
008 */
009
010 namespace Zend\Code\Reflection;
011
012 use ReflectionMethod as PhpReflectionMethod;
013 use Zend\Code\Annotation\AnnotationManager;
014 use Zend\Code\Scanner\AnnotationScanner;
015 use Zend\Code\Scanner\CachingFileScanner;
016
017 use function array_shift;
018 use function array_slice;
019 use function class_exists;
020 use function count;
021 use function file;
022 use function file_exists;
023 use function implode;
024 use function is_array;
025 use function rtrim;
026 use function strlen;
027 use function substr;
028 use function token_get_all;
029 use function token_name;
030 use function var_export;
031
032 class MethodReflection extends PhpReflectionMethod implements ReflectionInterface
033 {
034 /**
035 * Constant use in @MethodReflection to display prototype as an array
036 */
037 const PROTOTYPE_AS_ARRAY = 'prototype_as_array';
038
039 /**
040 * Constant use in @MethodReflection to display prototype as a string
041 */
042 const PROTOTYPE_AS_STRING = 'prototype_as_string';
043
044 /**
045 * @var AnnotationScanner
046 */
047 protected $annotations;
048
049 /**
050 * Retrieve method DocBlock reflection
051 *
052 * @return DocBlockReflection|false
053 */
054 public function getDocBlock()
055 {
056 if ('' == $this->getDocComment()) {
057 return false;
058 }
059
060 $instance = new DocBlockReflection($this);
061
062 return $instance;
063 }
064
065 /**
066 * @param AnnotationManager $annotationManager
067 * @return AnnotationScanner|false
068 */
069 public function getAnnotations(AnnotationManager $annotationManager)
070 {
071 if (($docComment = $this->getDocComment()) == '') {
072 return false;
073 }
074
075 if ($this->annotations) {
076 return $this->annotations;
077 }
078
079 $cachingFileScanner = $this->createFileScanner($this->getFileName());
080 $nameInformation = $cachingFileScanner->getClassNameInformation($this->getDeclaringClass()->getName());
081
082 if (! $nameInformation) {
083 return false;
084 }
085
086 $this->annotations = new AnnotationScanner($annotationManager, $docComment, $nameInformation);
087
088 return $this->annotations;
089 }
090
091 /**
092 * Get start line (position) of method
093 *
094 * @param bool $includeDocComment
095 * @return int
096 */
097 public function getStartLine($includeDocComment = false)
098 {
099 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 }
512