1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15:
16: namespace TokenReflection;
17:
18: use TokenReflection\Exception, TokenReflection\Stream\StreamBase as Stream;
19: use ReflectionMethod as InternalReflectionMethod, ReflectionClass as InternalReflectionClass;
20:
21: 22: 23:
24: class ReflectionMethod extends ReflectionFunctionBase implements IReflectionMethod
25: {
26: 27: 28: 29: 30: 31: 32: 33:
34: const IS_IMPLEMENTED_ABSTRACT = 0x08;
35:
36: 37: 38: 39: 40: 41: 42: 43:
44: const ACCESS_LEVEL_CHANGED = 0x800;
45:
46: 47: 48: 49: 50: 51: 52: 53: 54: 55:
56: const IS_CONSTRUCTOR = 0x2000;
57:
58: 59: 60: 61: 62: 63: 64: 65:
66: const IS_DESTRUCTOR = 0x4000;
67:
68: 69: 70: 71: 72: 73: 74: 75:
76: const IS_CLONE = 0x8000;
77:
78: 79: 80: 81: 82: 83: 84: 85:
86: const IS_ALLOWED_STATIC = 0x10000;
87:
88: 89: 90: 91: 92:
93: private $declaringClassName;
94:
95: 96: 97: 98: 99:
100: private $prototype;
101:
102: 103: 104: 105: 106:
107: protected $modifiers = 0;
108:
109: 110: 111: 112: 113:
114: private $accessible = false;
115:
116: 117: 118: 119: 120:
121: private $modifiersComplete = false;
122:
123: 124: 125: 126: 127:
128: private $originalName = null;
129:
130: 131: 132: 133: 134:
135: private $original = null;
136:
137: 138: 139: 140: 141:
142: private $originalModifiers = null;
143:
144: 145: 146: 147: 148:
149: private $declaringTraitName;
150:
151: 152: 153: 154: 155:
156: public function getDeclaringClass()
157: {
158: return null === $this->declaringClassName ? null : $this->getBroker()->getClass($this->declaringClassName);
159: }
160:
161: 162: 163: 164: 165:
166: public function getDeclaringClassName()
167: {
168: return $this->declaringClassName;
169: }
170:
171: 172: 173: 174: 175:
176: public function getModifiers()
177: {
178: if (!$this->modifiersComplete && !($this->modifiers & (self::ACCESS_LEVEL_CHANGED | self::IS_IMPLEMENTED_ABSTRACT))) {
179: $declaringClass = $this->getDeclaringClass();
180: $parentClass = $declaringClass->getParentClass();
181: if (false !== $parentClass && $parentClass->hasMethod($this->name)) {
182: $parentClassMethod = $parentClass->getMethod($this->name);
183:
184:
185: if (($this->isPublic() || $this->isProtected()) && $parentClassMethod->is(self::ACCESS_LEVEL_CHANGED | InternalReflectionMethod::IS_PRIVATE)) {
186: $this->modifiers |= self::ACCESS_LEVEL_CHANGED;
187: }
188:
189:
190: if ($parentClassMethod->isAbstract() && !$this->isAbstract()) {
191: $this->modifiers |= self::IS_IMPLEMENTED_ABSTRACT;
192: }
193: } else {
194:
195: foreach ($declaringClass->getInterfaces() as $interface) {
196: if ($interface->hasOwnMethod($this->name)) {
197: $this->modifiers |= self::IS_IMPLEMENTED_ABSTRACT;
198: break;
199: }
200: }
201: }
202:
203:
204: $this->modifiersComplete = $this->isComplete() || (($this->modifiers & self::IS_IMPLEMENTED_ABSTRACT) && ($this->modifiers & self::ACCESS_LEVEL_CHANGED));
205: }
206:
207: return $this->modifiers;
208: }
209:
210: 211: 212: 213: 214:
215: public function isAbstract()
216: {
217: return (bool) ($this->modifiers & InternalReflectionMethod::IS_ABSTRACT);
218: }
219:
220: 221: 222: 223: 224:
225: public function isFinal()
226: {
227: return (bool) ($this->modifiers & InternalReflectionMethod::IS_FINAL);
228: }
229:
230: 231: 232: 233: 234:
235: public function isPrivate()
236: {
237: return (bool) ($this->modifiers & InternalReflectionMethod::IS_PRIVATE);
238: }
239:
240: 241: 242: 243: 244:
245: public function isProtected()
246: {
247: return (bool) ($this->modifiers & InternalReflectionMethod::IS_PROTECTED);
248: }
249:
250: 251: 252: 253: 254:
255: public function isPublic()
256: {
257: return (bool) ($this->modifiers & InternalReflectionMethod::IS_PUBLIC);
258: }
259:
260: 261: 262: 263: 264:
265: public function isStatic()
266: {
267: return (bool) ($this->modifiers & InternalReflectionMethod::IS_STATIC);
268: }
269:
270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283:
284: public function is($filter = null)
285: {
286:
287: static $computedModifiers = 0x808;
288:
289: if (null === $filter || ($this->modifiers & $filter)) {
290: return true;
291: } elseif (($filter & $computedModifiers) && !$this->modifiersComplete) {
292: return (bool) ($this->getModifiers() & $filter);
293: }
294:
295: return false;
296: }
297:
298: 299: 300: 301: 302:
303: public function isConstructor()
304: {
305: return (bool) ($this->modifiers & self::IS_CONSTRUCTOR);
306: }
307:
308: 309: 310: 311: 312:
313: public function isDestructor()
314: {
315: return (bool) ($this->modifiers & self::IS_DESTRUCTOR);
316: }
317:
318: 319: 320: 321: 322: 323:
324: public function getPrototype()
325: {
326: if (null === $this->prototype) {
327: $prototype = null;
328:
329: $declaring = $this->getDeclaringClass();
330: if (($parent = $declaring->getParentClass()) && $parent->hasMethod($this->name)) {
331: $method = $parent->getMethod($this->name);
332:
333: if (!$method->isPrivate()) {
334: try {
335: $prototype = $method->getPrototype();
336: } catch (Exception\RuntimeException $e) {
337: $prototype = $method;
338: }
339: }
340: }
341:
342: if (null === $prototype) {
343: foreach ($declaring->getOwnInterfaces() as $interface) {
344: if ($interface->hasMethod($this->name)) {
345: $prototype = $interface->getMethod($this->name);
346: break;
347: }
348: }
349: }
350:
351: $this->prototype = $prototype ?: ($this->isComplete() ? false : null);
352: }
353:
354: if (empty($this->prototype)) {
355: throw new Exception\RuntimeException('Method has no prototype.', Exception\RuntimeException::DOES_NOT_EXIST, $this);
356: }
357:
358: return $this->prototype;
359: }
360:
361: 362: 363: 364: 365:
366: public function getPrettyName()
367: {
368: return sprintf('%s::%s', $this->declaringClassName ?: $this->declaringTraitName, parent::getPrettyName());
369: }
370:
371: 372: 373: 374: 375:
376: public function __toString()
377: {
378: $internal = '';
379: $overwrite = '';
380: $prototype = '';
381:
382: $declaringClassParent = $this->getDeclaringClass()->getParentClass();
383: try {
384: $prototype = ', prototype ' . $this->getPrototype()->getDeclaringClassName();
385: } catch (Exception\RuntimeException $e) {
386: if ($declaringClassParent && $declaringClassParent->isInternal()) {
387: $internal = 'internal:' . $parentClass->getExtensionName();
388: }
389: }
390:
391: if ($declaringClassParent && $declaringClassParent->hasMethod($this->name)) {
392: $parentMethod = $declaringClassParent->getMethod($this->name);
393: $overwrite = ', overwrites ' . $parentMethod->getDeclaringClassName();
394: }
395:
396: if ($this->isConstructor()) {
397: $cdtor = ', ctor';
398: } elseif ($this->isDestructor()) {
399: $cdtor = ', dtor';
400: } else {
401: $cdtor = '';
402: }
403:
404: $parameters = '';
405: if ($this->getNumberOfParameters() > 0) {
406: $buffer = '';
407: foreach ($this->getParameters() as $parameter) {
408: $buffer .= "\n " . $parameter->__toString();
409: }
410: $parameters = sprintf(
411: "\n\n - Parameters [%d] {%s\n }",
412: $this->getNumberOfParameters(),
413: $buffer
414: );
415: }
416:
417: return sprintf(
418: "%sMethod [ <%s%s%s%s> %s%s%s%s%s%s method %s%s ] {\n @@ %s %d - %d%s\n}\n",
419: $this->getDocComment() ? $this->getDocComment() . "\n" : '',
420: !empty($internal) ? $internal : 'user',
421: $overwrite,
422: $prototype,
423: $cdtor,
424: $this->isAbstract() ? 'abstract ' : '',
425: $this->isFinal() ? 'final ' : '',
426: $this->isStatic() ? 'static ' : '',
427: $this->isPublic() ? 'public' : '',
428: $this->isPrivate() ? 'private' : '',
429: $this->isProtected() ? 'protected' : '',
430: $this->returnsReference() ? '&' : '',
431: $this->getName(),
432: $this->getFileName(),
433: $this->getStartLine(),
434: $this->getEndLine(),
435: $parameters
436: );
437: }
438:
439: 440: 441: 442: 443: 444: 445: 446: 447: 448:
449: public static function export(Broker $broker, $class, $method, $return = false)
450: {
451: $className = is_object($class) ? get_class($class) : $class;
452: $methodName = $method;
453:
454: $class = $broker->getClass($className);
455: if ($class instanceof Invalid\ReflectionClass) {
456: throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED);
457: } elseif ($class instanceof Dummy\ReflectionClass) {
458: throw new Exception\RuntimeException(sprintf('Class %s does not exist.', $className), Exception\RuntimeException::DOES_NOT_EXIST);
459: }
460: $method = $class->getMethod($methodName);
461:
462: if ($return) {
463: return $method->__toString();
464: }
465:
466: echo $method->__toString();
467: }
468:
469: 470: 471: 472: 473: 474: 475:
476: public function invoke($object, $args)
477: {
478: $params = func_get_args();
479: return $this->invokeArgs(array_shift($params), $params);
480: }
481:
482: 483: 484: 485: 486: 487: 488: 489:
490: public function invokeArgs($object, array $args = array())
491: {
492: $declaringClass = $this->getDeclaringClass();
493: if (!$declaringClass->isInstance($object)) {
494: throw new Exception\RuntimeException(sprintf('Expected instance of or subclass of "%s".', $this->declaringClassName), Exception\RuntimeException::INVALID_ARGUMENT, $this);
495: }
496:
497: if ($this->isPublic()) {
498: return call_user_func_array(array($object, $this->getName()), $args);
499: } elseif ($this->isAccessible()) {
500: $refClass = new InternalReflectionClass($object);
501: $refMethod = $refClass->getMethod($this->name);
502:
503: $refMethod->setAccessible(true);
504: $value = $refMethod->invokeArgs($object, $args);
505: $refMethod->setAccessible(false);
506:
507: return $value;
508: }
509:
510: throw new Exception\RuntimeException('Only public methods can be invoked.', Exception\RuntimeException::NOT_ACCESSBILE, $this);
511: }
512:
513: 514: 515: 516: 517:
518: public function isAccessible()
519: {
520: return $this->accessible;
521: }
522:
523: 524: 525: 526: 527:
528: public function setAccessible($accessible)
529: {
530: $this->accessible = (bool) $accessible;
531: }
532:
533: 534: 535: 536: 537: 538: 539:
540: private function isComplete()
541: {
542: return $this->getDeclaringClass()->isComplete();
543: }
544:
545: 546: 547: 548: 549:
550: public function getNamespaceAliases()
551: {
552: return $this->getDeclaringClass()->getNamespaceAliases();
553: }
554:
555: 556: 557: 558: 559: 560:
561: public function getClosure($object)
562: {
563: $declaringClass = $this->getDeclaringClass();
564: if (!$declaringClass->isInstance($object)) {
565: throw new Exception\RuntimeException(sprintf('Expected instance of or subclass of "%s".', $this->declaringClassName), Exception\RuntimeException::INVALID_ARGUMENT, $this);
566: }
567:
568: $that = $this;
569: return function() use ($object, $that) {
570: return $that->invokeArgs($object, func_get_args());
571: };
572: }
573:
574: 575: 576: 577: 578: 579: 580: 581: 582:
583: public function alias(ReflectionClass $parent, $name = null, $accessLevel = null)
584: {
585: static $possibleLevels = array(InternalReflectionMethod::IS_PUBLIC => true, InternalReflectionMethod::IS_PROTECTED => true, InternalReflectionMethod::IS_PRIVATE => true);
586:
587: $method = clone $this;
588:
589: $method->declaringClassName = $parent->getName();
590: if (null !== $name) {
591: $method->originalName = $this->name;
592: $method->name = $name;
593: }
594: if (null !== $accessLevel) {
595: if (!isset($possibleLevels[$accessLevel])) {
596: throw new Exception\RuntimeException(sprintf('Invalid method access level: "%s".', $accessLevel), Exception\RuntimeException::INVALID_ARGUMENT, $this);
597: }
598:
599: $method->modifiers &= ~(InternalReflectionMethod::IS_PUBLIC | InternalReflectionMethod::IS_PROTECTED | InternalReflectionMethod::IS_PRIVATE);
600: $method->modifiers |= $accessLevel;
601:
602: $method->originalModifiers = $this->getModifiers();
603: }
604:
605: foreach ($this->parameters as $parameterName => $parameter) {
606: $method->parameters[$parameterName] = $parameter->alias($method);
607: }
608:
609: return $method;
610: }
611:
612: 613: 614: 615: 616:
617: public function getOriginalName()
618: {
619: return $this->originalName;
620: }
621:
622: 623: 624: 625: 626:
627: public function getOriginal()
628: {
629: return $this->original;
630: }
631:
632: 633: 634: 635: 636:
637: public function getOriginalModifiers()
638: {
639: return $this->originalModifiers;
640: }
641:
642: 643: 644: 645: 646:
647: public function getDeclaringTrait()
648: {
649: return null === $this->declaringTraitName ? null : $this->getBroker()->getClass($this->declaringTraitName);
650: }
651:
652: 653: 654: 655: 656:
657: public function getDeclaringTraitName()
658: {
659: return $this->declaringTraitName;
660: }
661:
662: 663: 664: 665: 666: 667: 668: 669:
670: protected function processParent(IReflection $parent, Stream $tokenStream)
671: {
672: if (!$parent instanceof ReflectionClass) {
673: throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionClass.', Exception\ParseException::INVALID_PARENT);
674: }
675:
676: $this->declaringClassName = $parent->getName();
677: if ($parent->isTrait()) {
678: $this->declaringTraitName = $parent->getName();
679: }
680: return parent::processParent($parent, $tokenStream);
681: }
682:
683: 684: 685: 686: 687: 688: 689: 690:
691: protected function parse(Stream $tokenStream, IReflection $parent)
692: {
693: return $this
694: ->parseBaseModifiers($tokenStream)
695: ->parseReturnsReference($tokenStream)
696: ->parseName($tokenStream)
697: ->parseInternalModifiers($parent);
698: }
699:
700: 701: 702: 703: 704: 705:
706: private function parseBaseModifiers(Stream $tokenStream)
707: {
708: while (true) {
709: switch ($tokenStream->getType()) {
710: case T_ABSTRACT:
711: $this->modifiers |= InternalReflectionMethod::IS_ABSTRACT;
712: break;
713: case T_FINAL:
714: $this->modifiers |= InternalReflectionMethod::IS_FINAL;
715: break;
716: case T_PUBLIC:
717: $this->modifiers |= InternalReflectionMethod::IS_PUBLIC;
718: break;
719: case T_PRIVATE:
720: $this->modifiers |= InternalReflectionMethod::IS_PRIVATE;
721: break;
722: case T_PROTECTED:
723: $this->modifiers |= InternalReflectionMethod::IS_PROTECTED;
724: break;
725: case T_STATIC:
726: $this->modifiers |= InternalReflectionMethod::IS_STATIC;
727: break;
728: case T_FUNCTION:
729: case null:
730: break 2;
731: default:
732: break;
733: }
734:
735: $tokenStream->skipWhitespaces();
736: }
737:
738: if (!($this->modifiers & (InternalReflectionMethod::IS_PRIVATE | InternalReflectionMethod::IS_PROTECTED))) {
739: $this->modifiers |= InternalReflectionMethod::IS_PUBLIC;
740: }
741:
742: return $this;
743: }
744:
745: 746: 747: 748: 749: 750:
751: private function parseInternalModifiers(ReflectionClass $class)
752: {
753: $name = strtolower($this->name);
754:
755: if ('__construct' === $name || ((!$class->inNamespace() || PHP_VERSION_ID < 50303) && strtolower($class->getShortName()) === $name)) {
756: $this->modifiers |= self::IS_CONSTRUCTOR;
757: } elseif ('__destruct' === $name) {
758: $this->modifiers |= self::IS_DESTRUCTOR;
759: } elseif ('__clone' === $name) {
760: $this->modifiers |= self::IS_CLONE;
761: }
762:
763: if ($class->isInterface()) {
764: $this->modifiers |= InternalReflectionMethod::IS_ABSTRACT;
765: } else {
766:
767: static $notAllowed = array('__clone' => true, '__tostring' => true, '__get' => true, '__set' => true, '__isset' => true, '__unset' => true);
768: if (!$this->isStatic() && !$this->isConstructor() && !$this->isDestructor() && !isset($notAllowed[$name])) {
769: $this->modifiers |= self::IS_ALLOWED_STATIC;
770: }
771: }
772:
773: return $this;
774: }
775: }
776: