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 ReflectionClass as InternalReflectionClass, ReflectionProperty as InternalReflectionProperty, ReflectionMethod as InternalReflectionMethod;
20:
21: 22: 23:
24: class ReflectionClass extends ReflectionElement implements IReflectionClass
25: {
26: 27: 28: 29: 30: 31:
32: const IS_INTERFACE = 0x80;
33:
34: 35: 36: 37: 38: 39:
40: const IS_TRAIT = 0x120;
41:
42: 43: 44: 45: 46: 47: 48:
49: const IMPLEMENTS_INTERFACES = 0x80000;
50:
51: 52: 53: 54: 55: 56: 57:
58: const IMPLEMENTS_TRAITS = 0x400000;
59:
60: 61: 62: 63: 64:
65: private $namespaceName;
66:
67: 68: 69: 70: 71:
72: private $modifiers = 0;
73:
74: 75: 76: 77: 78:
79: private $type = 0;
80:
81: 82: 83: 84: 85:
86: private $modifiersComplete = false;
87:
88: 89: 90: 91: 92:
93: private $parentClassName;
94:
95: 96: 97: 98: 99:
100: private $interfaces = array();
101:
102: 103: 104: 105: 106:
107: private $traits = array();
108:
109: 110: 111: 112: 113: 114: 115:
116: private $traitAliases = array();
117:
118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128:
129: private $traitImports = array();
130:
131: 132: 133: 134: 135:
136: private $methods = array();
137:
138: 139: 140: 141: 142:
143: private $constants = array();
144:
145: 146: 147: 148: 149:
150: private $properties = array();
151:
152: 153: 154: 155: 156:
157: private $definitionComplete = false;
158:
159: 160: 161: 162: 163:
164: private $aliases = array();
165:
166: 167: 168: 169: 170:
171: public function getShortName()
172: {
173: $name = $this->getName();
174: if ($this->namespaceName !== ReflectionNamespace::NO_NAMESPACE_NAME) {
175: $name = substr($name, strlen($this->namespaceName) + 1);
176: }
177:
178: return $name;
179: }
180:
181: 182: 183: 184: 185:
186: public function getNamespaceName()
187: {
188: return $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName;
189: }
190:
191: 192: 193: 194: 195:
196: public function inNamespace()
197: {
198: return null !== $this->namespaceName && ReflectionNamespace::NO_NAMESPACE_NAME !== $this->namespaceName;
199: }
200:
201: 202: 203: 204: 205:
206: public function getModifiers()
207: {
208: if (false === $this->modifiersComplete) {
209: if (($this->modifiers & InternalReflectionClass::IS_EXPLICIT_ABSTRACT) && !($this->modifiers & InternalReflectionClass::IS_IMPLICIT_ABSTRACT)) {
210: foreach ($this->getMethods() as $reflectionMethod) {
211: if ($reflectionMethod->isAbstract()) {
212: $this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT;
213: }
214: }
215:
216: if (!empty($this->interfaces)) {
217: $this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT;
218: }
219: }
220:
221: if (!empty($this->interfaces)) {
222: $this->modifiers |= self::IMPLEMENTS_INTERFACES;
223: }
224:
225: if ($this->isInterface() && !empty($this->methods)) {
226: $this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT;
227: }
228:
229: if (!empty($this->traits)) {
230: $this->modifiers |= self::IMPLEMENTS_TRAITS;
231: }
232:
233: $this->modifiersComplete = null === $this->parentClassName || $this->getParentClass()->isComplete();
234:
235: if ($this->modifiersComplete) {
236: foreach ($this->getInterfaces() as $interface) {
237: if (!$interface->isComplete()) {
238: $this->modifiersComplete = false;
239: break;
240: }
241: }
242: }
243: if ($this->modifiersComplete) {
244: foreach ($this->getTraits() as $trait) {
245: if (!$trait->isComplete()) {
246: $this->modifiersComplete = false;
247: break;
248: }
249: }
250: }
251: }
252:
253: return $this->modifiers;
254: }
255:
256: 257: 258: 259: 260:
261: public function isAbstract()
262: {
263: if ($this->modifiers & InternalReflectionClass::IS_EXPLICIT_ABSTRACT) {
264: return true;
265: } elseif ($this->isInterface() && !empty($this->methods)) {
266: return true;
267: }
268:
269: return false;
270: }
271:
272: 273: 274: 275: 276:
277: public function isFinal()
278: {
279: return (bool) ($this->modifiers & InternalReflectionClass::IS_FINAL);
280: }
281:
282: 283: 284: 285: 286:
287: public function isInterface()
288: {
289: return (bool) ($this->modifiers & self::IS_INTERFACE);
290: }
291:
292: 293: 294: 295: 296:
297: public function isException()
298: {
299: return 'Exception' === $this->name || $this->isSubclassOf('Exception');
300: }
301:
302: 303: 304: 305: 306:
307: public function isInstantiable()
308: {
309: if ($this->isInterface() || $this->isAbstract()) {
310: return false;
311: }
312:
313: if (null === ($constructor = $this->getConstructor())) {
314: return true;
315: }
316:
317: return $constructor->isPublic();
318: }
319:
320: 321: 322: 323: 324: 325: 326: 327:
328: public function isCloneable()
329: {
330: if ($this->isInterface() || $this->isAbstract()) {
331: return false;
332: }
333:
334: if ($this->hasMethod('__clone')) {
335: return $this->getMethod('__clone')->isPublic();
336: }
337:
338: return true;
339: }
340:
341: 342: 343: 344: 345: 346: 347: 348:
349: public function isIterateable()
350: {
351: return $this->implementsInterface('Traversable');
352: }
353:
354: 355: 356: 357: 358: 359: 360:
361: public function isSubclassOf($class)
362: {
363: if (is_object($class)) {
364: if ($class instanceof InternalReflectionClass || $class instanceof IReflectionClass) {
365: $class = $class->getName();
366: } else {
367: $class = get_class($class);
368: }
369: }
370:
371: if ($class === $this->parentClassName) {
372: return true;
373: }
374:
375: $parent = $this->getParentClass();
376: return false === $parent ? false : $parent->isSubclassOf($class);
377: }
378:
379: 380: 381: 382: 383:
384: public function getParentClass()
385: {
386: $className = $this->getParentClassName();
387: if (null === $className) {
388: return false;
389: }
390:
391: return $this->getBroker()->getClass($className);
392: }
393:
394: 395: 396: 397: 398:
399: public function getParentClassName()
400: {
401: return $this->parentClassName;
402: }
403:
404: 405: 406: 407: 408:
409: public function getParentClasses()
410: {
411: $parent = $this->getParentClass();
412: if (false === $parent) {
413: return array();
414: }
415:
416: return array_merge(array($parent->getName() => $parent), $parent->getParentClasses());
417: }
418:
419: 420: 421: 422: 423:
424: public function getParentClassNameList()
425: {
426: $parent = $this->getParentClass();
427: if (false === $parent) {
428: return array();
429: }
430:
431: return array_merge(array($parent->getName()), $parent->getParentClassNameList());
432: }
433:
434: 435: 436: 437: 438: 439: 440:
441: public function implementsInterface($interface)
442: {
443: if (is_object($interface)) {
444: if (!$interface instanceof InternalReflectionClass && !$interface instanceof IReflectionClass) {
445: throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of class reflection, "%s" provided.', get_class($interface)), Exception\RuntimeException::INVALID_ARGUMENT, $this);
446: }
447:
448: if (!$interface->isInterface()) {
449: throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interfaceName), Exception\RuntimeException::INVALID_ARGUMENT, $this);
450: }
451:
452: $interfaceName = $interface->getName();
453: } else {
454: $interfaceName = $interface;
455: }
456:
457: return in_array($interfaceName, $this->getInterfaceNames());
458: }
459:
460: 461: 462: 463: 464:
465: public function getInterfaces()
466: {
467: $interfaceNames = $this->getInterfaceNames();
468: if (empty($interfaceNames)) {
469: return array();
470: }
471:
472: $broker = $this->getBroker();
473: return array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) {
474: return $broker->getClass($interfaceName);
475: }, $interfaceNames));
476: }
477:
478: 479: 480: 481: 482:
483: public function getInterfaceNames()
484: {
485: $parentClass = $this->getParentClass();
486:
487: $names = false !== $parentClass ? array_reverse(array_flip($parentClass->getInterfaceNames())) : array();
488: foreach ($this->interfaces as $interfaceName) {
489: $names[$interfaceName] = true;
490: foreach (array_reverse($this->getBroker()->getClass($interfaceName)->getInterfaceNames()) as $parentInterfaceName) {
491: $names[$parentInterfaceName] = true;
492: }
493: }
494:
495: return array_keys($names);
496: }
497:
498: 499: 500: 501: 502:
503: public function getOwnInterfaces()
504: {
505: $interfaceNames = $this->getOwnInterfaceNames();
506: if (empty($interfaceNames)) {
507: return array();
508: }
509:
510: $broker = $this->getBroker();
511: return array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) {
512: return $broker->getClass($interfaceName);
513: }, $interfaceNames));
514: }
515:
516: 517: 518: 519: 520:
521: public function getOwnInterfaceNames()
522: {
523: return $this->interfaces;
524: }
525:
526: 527: 528: 529: 530:
531: public function getConstructor()
532: {
533: foreach ($this->getMethods() as $method) {
534: if ($method->isConstructor()) {
535: return $method;
536: }
537: }
538:
539: return null;
540: }
541:
542: 543: 544: 545: 546:
547: public function getDestructor()
548: {
549: foreach ($this->getMethods() as $method) {
550: if ($method->isDestructor()) {
551: return $method;
552: }
553: }
554:
555: return null;
556: }
557:
558: 559: 560: 561: 562: 563:
564: public function hasMethod($name)
565: {
566: foreach ($this->getMethods() as $method) {
567: if ($name === $method->getName()) {
568: return true;
569: }
570: }
571:
572: return false;
573: }
574:
575: 576: 577: 578: 579: 580: 581:
582: public function getMethod($name)
583: {
584: if (isset($this->methods[$name])) {
585: return $this->methods[$name];
586: }
587:
588: foreach ($this->getMethods() as $method) {
589: if ($name === $method->getName()) {
590: return $method;
591: }
592: }
593:
594: throw new Exception\RuntimeException(sprintf('There is no method "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
595: }
596:
597: 598: 599: 600: 601: 602:
603: public function getMethods($filter = null)
604: {
605: $methods = $this->methods;
606:
607: foreach ($this->getTraitMethods() as $traitMethod) {
608: if (!isset($methods[$traitMethod->getName()])) {
609: $methods[$traitMethod->getName()] = $traitMethod;
610: }
611: }
612:
613: if (null !== $this->parentClassName) {
614: foreach ($this->getParentClass()->getMethods(null) as $parentMethod) {
615: if (!isset($methods[$parentMethod->getName()])) {
616: $methods[$parentMethod->getName()] = $parentMethod;
617: }
618: }
619: }
620: foreach ($this->getOwnInterfaces() as $interface) {
621: foreach ($interface->getMethods(null) as $parentMethod) {
622: if (!isset($methods[$parentMethod->getName()])) {
623: $methods[$parentMethod->getName()] = $parentMethod;
624: }
625: }
626: }
627:
628: if (null !== $filter) {
629: $methods = array_filter($methods, function(IReflectionMethod $method) use ($filter) {
630: return $method->is($filter);
631: });
632: }
633:
634: return array_values($methods);
635: }
636:
637: 638: 639: 640: 641: 642:
643: public function hasOwnMethod($name)
644: {
645: return isset($this->methods[$name]);
646: }
647:
648: 649: 650: 651: 652: 653:
654: public function getOwnMethods($filter = null)
655: {
656: $methods = $this->methods;
657:
658: if (null !== $filter) {
659: $methods = array_filter($methods, function(ReflectionMethod $method) use ($filter) {
660: return $method->is($filter);
661: });
662: }
663:
664: return array_values($methods);
665: }
666:
667: 668: 669: 670: 671: 672:
673: public function hasTraitMethod($name)
674: {
675: if (isset($this->methods[$name])) {
676: return false;
677: }
678:
679: foreach ($this->getOwnTraits() as $trait) {
680: if ($trait->hasMethod($name)) {
681: return true;
682: }
683: }
684:
685: return false;
686: }
687:
688: 689: 690: 691: 692: 693: 694:
695: public function getTraitMethods($filter = null)
696: {
697: $methods = array();
698:
699: foreach ($this->getOwnTraits() as $trait) {
700: $traitName = $trait->getName();
701: foreach ($trait->getMethods(null) as $traitMethod) {
702: $methodName = $traitMethod->getName();
703:
704: $imports = array();
705: if (isset($this->traitImports[$traitName . '::' . $methodName])) {
706: $imports = $this->traitImports[$traitName . '::' . $methodName];
707: }
708: if (isset($this->traitImports[$methodName])) {
709: $imports = empty($imports) ? $this->traitImports[$methodName] : array_merge($imports, $this->traitImports[$methodName]);
710: }
711:
712: foreach ($imports as $import) {
713: if (null !== $import) {
714: list($newName, $accessLevel) = $import;
715:
716: if ('' === $newName) {
717: $newName = $methodName;
718: $imports[] = null;
719: }
720:
721: if (!isset($this->methods[$newName])) {
722: if (isset($methods[$newName])) {
723: throw new Exception\RuntimeException(sprintf('Trait method "%s" was already imported.', $newName), Exception\RuntimeException::ALREADY_EXISTS, $this);
724: }
725:
726: $methods[$newName] = $traitMethod->alias($this, $newName, $accessLevel);
727: }
728: }
729: }
730:
731: if (!in_array(null, $imports)) {
732: if (!isset($this->methods[$methodName])) {
733: if (isset($methods[$methodName])) {
734: throw new Exception\RuntimeException(sprintf('Trait method "%s" was already imported.', $methodName), Exception\RuntimeException::ALREADY_EXISTS, $this);
735: }
736:
737: $methods[$methodName] = $traitMethod->alias($this);
738: }
739: }
740: }
741: }
742:
743: if (null !== $filter) {
744: $methods = array_filter($methods, function(IReflectionMethod $method) use ($filter) {
745: return (bool) ($method->getModifiers() & $filter);
746: });
747: }
748:
749: return array_values($methods);
750: }
751:
752: 753: 754: 755: 756: 757:
758: public function hasConstant($name)
759: {
760: if (isset($this->constants[$name])) {
761: return true;
762: }
763:
764: foreach ($this->getConstantReflections() as $constant) {
765: if ($name === $constant->getName()) {
766: return true;
767: }
768: }
769:
770: return false;
771: }
772:
773: 774: 775: 776: 777: 778:
779: public function getConstant($name)
780: {
781: try {
782: return $this->getConstantReflection($name)->getValue();
783: } catch (Exception\BaseException $e) {
784: return false;
785: }
786: }
787:
788: 789: 790: 791: 792: 793: 794:
795: public function getConstantReflection($name)
796: {
797: if (isset($this->constants[$name])) {
798: return $this->constants[$name];
799: }
800:
801: foreach ($this->getConstantReflections() as $constant) {
802: if ($name === $constant->getName()) {
803: return $constant;
804: }
805: }
806:
807: throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
808: }
809:
810: 811: 812: 813: 814:
815: public function getConstants()
816: {
817: $constants = array();
818: foreach ($this->getConstantReflections() as $constant) {
819: $constants[$constant->getName()] = $constant->getValue();
820: }
821: return $constants;
822: }
823:
824: 825: 826: 827: 828:
829: public function getConstantReflections()
830: {
831: if (null === $this->parentClassName && empty($this->interfaces)) {
832: return array_values($this->constants);
833: } else {
834: $reflections = array_values($this->constants);
835:
836: if (null !== $this->parentClassName) {
837: $reflections = array_merge($reflections, $this->getParentClass()->getConstantReflections());
838: }
839: foreach ($this->getOwnInterfaces() as $interface) {
840: $reflections = array_merge($reflections, $interface->getConstantReflections());
841: }
842:
843: return $reflections;
844: }
845: }
846:
847: 848: 849: 850: 851: 852:
853: public function hasOwnConstant($name)
854: {
855: return isset($this->constants[$name]);
856: }
857:
858: 859: 860: 861: 862:
863: public function getOwnConstants()
864: {
865: return array_map(function(ReflectionConstant $constant) {
866: return $constant->getValue();
867: }, $this->constants);
868: }
869:
870: 871: 872: 873: 874:
875: public function getOwnConstantReflections()
876: {
877: return array_values($this->constants);
878: }
879:
880: 881: 882: 883: 884: 885:
886: public function hasProperty($name)
887: {
888: foreach ($this->getProperties() as $property) {
889: if ($name === $property->getName()) {
890: return true;
891: }
892: }
893:
894: return false;
895: }
896:
897: 898: 899: 900: 901: 902: 903:
904: public function getProperty($name)
905: {
906: if (isset($this->properties[$name])) {
907: return $this->properties[$name];
908: }
909:
910: foreach ($this->getProperties() as $property) {
911: if ($name === $property->getName()) {
912: return $property;
913: }
914: }
915:
916: throw new Exception\RuntimeException(sprintf('There is no property "%s".', $name, $this->name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
917: }
918:
919: 920: 921: 922: 923: 924:
925: public function getProperties($filter = null)
926: {
927: $properties = $this->properties;
928:
929: foreach ($this->getTraitProperties(null) as $traitProperty) {
930: if (!isset($properties[$traitProperty->getName()])) {
931: $properties[$traitProperty->getName()] = $traitProperty->alias($this);
932: }
933: }
934:
935: if (null !== $this->parentClassName) {
936: foreach ($this->getParentClass()->getProperties(null) as $parentProperty) {
937: if (!isset($properties[$parentProperty->getName()])) {
938: $properties[$parentProperty->getName()] = $parentProperty;
939: }
940: }
941: }
942:
943: if (null !== $filter) {
944: $properties = array_filter($properties, function(IReflectionProperty $property) use ($filter) {
945: return (bool) ($property->getModifiers() & $filter);
946: });
947: }
948:
949: return array_values($properties);
950: }
951:
952: 953: 954: 955: 956: 957:
958: public function hasOwnProperty($name)
959: {
960: return isset($this->properties[$name]);
961: }
962:
963: 964: 965: 966: 967: 968:
969: public function getOwnProperties($filter = null)
970: {
971: $properties = $this->properties;
972:
973: if (null !== $filter) {
974: $properties = array_filter($properties, function(ReflectionProperty $property) use ($filter) {
975: return (bool) ($property->getModifiers() & $filter);
976: });
977: }
978:
979: return array_values($properties);
980: }
981:
982: 983: 984: 985: 986: 987:
988: public function hasTraitProperty($name)
989: {
990: if (isset($this->properties[$name])) {
991: return false;
992: }
993:
994: foreach ($this->getOwnTraits() as $trait) {
995: if ($trait->hasProperty($name)) {
996: return true;
997: }
998: }
999:
1000: return false;
1001: }
1002:
1003: 1004: 1005: 1006: 1007: 1008:
1009: public function getTraitProperties($filter = null)
1010: {
1011: $properties = array();
1012:
1013: foreach ($this->getOwnTraits() as $trait) {
1014: foreach ($trait->getProperties(null) as $traitProperty) {
1015: if (!isset($this->properties[$traitProperty->getName()]) && !isset($properties[$traitProperty->getName()])) {
1016: $properties[$traitProperty->getName()] = $traitProperty->alias($this);
1017: }
1018: }
1019: }
1020:
1021: if (null !== $filter) {
1022: $properties = array_filter($properties, function(IReflectionProperty $property) use ($filter) {
1023: return (bool) ($property->getModifiers() & $filter);
1024: });
1025: }
1026:
1027: return array_values($properties);
1028: }
1029:
1030: 1031: 1032: 1033: 1034:
1035: public function getDefaultProperties()
1036: {
1037: static $accessLevels = array(InternalReflectionProperty::IS_PUBLIC, InternalReflectionProperty::IS_PROTECTED, InternalReflectionProperty::IS_PRIVATE);
1038:
1039: $defaults = array();
1040: $properties = $this->getProperties();
1041: foreach (array(true, false) as $static) {
1042: foreach ($properties as $property) {
1043: foreach ($accessLevels as $level) {
1044: if ($property->isStatic() === $static && ($property->getModifiers() & $level)) {
1045: $defaults[$property->getName()] = $property->getDefaultValue();
1046: }
1047: }
1048: }
1049: }
1050:
1051: return $defaults;
1052: }
1053:
1054: 1055: 1056: 1057: 1058:
1059: public function getStaticProperties()
1060: {
1061: $defaults = array();
1062: foreach ($this->getProperties(InternalReflectionProperty::IS_STATIC) as $property) {
1063: if ($property instanceof ReflectionProperty) {
1064: $defaults[$property->getName()] = $property->getDefaultValue();
1065: }
1066: }
1067:
1068: return $defaults;
1069: }
1070:
1071: 1072: 1073: 1074: 1075: 1076: 1077: 1078: 1079:
1080: public function getStaticPropertyValue($name, $default = null)
1081: {
1082: if ($this->hasProperty($name) && ($property = $this->getProperty($name)) && $property->isStatic()) {
1083: if (!$property->isPublic() && !$property->isAccessible()) {
1084: throw new Exception\RuntimeException(sprintf('Static property "%s" is not accessible.', $name), Exception\RuntimeException::NOT_ACCESSBILE, $this);
1085: }
1086:
1087: return $property->getDefaultValue();
1088: }
1089:
1090: throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
1091: }
1092:
1093: 1094: 1095: 1096: 1097:
1098: public function getTraits()
1099: {
1100: $traitNames = $this->getTraitNames();
1101: if (empty($traitNames)) {
1102: return array();
1103: }
1104:
1105: $broker = $this->getBroker();
1106: return array_combine($traitNames, array_map(function($traitName) use ($broker) {
1107: return $broker->getClass($traitName);
1108: }, $traitNames));
1109: }
1110:
1111: 1112: 1113: 1114: 1115:
1116: public function getOwnTraits()
1117: {
1118: $ownTraitNames = $this->getOwnTraitNames();
1119: if (empty($ownTraitNames)) {
1120: return array();
1121: }
1122:
1123: $broker = $this->getBroker();
1124: return array_combine($ownTraitNames, array_map(function($traitName) use ($broker) {
1125: return $broker->getClass($traitName);
1126: }, $ownTraitNames));
1127: }
1128:
1129: 1130: 1131: 1132: 1133:
1134: public function getTraitNames()
1135: {
1136: $parentClass = $this->getParentClass();
1137:
1138: $names = $parentClass ? $parentClass->getTraitNames() : array();
1139: foreach ($this->traits as $traitName) {
1140: $names[] = $traitName;
1141: }
1142:
1143: return array_unique($names);
1144: }
1145:
1146: 1147: 1148: 1149: 1150:
1151: public function getOwnTraitNames()
1152: {
1153: return $this->traits;
1154: }
1155:
1156: 1157: 1158: 1159: 1160:
1161: public function getTraitAliases()
1162: {
1163: return $this->traitAliases;
1164: }
1165:
1166: 1167: 1168: 1169: 1170:
1171: public function isTrait()
1172: {
1173: return self::IS_TRAIT === $this->type;
1174: }
1175:
1176: 1177: 1178: 1179: 1180:
1181: public function isValid()
1182: {
1183: if (null !== $this->parentClassName && !$this->getParentClass()->isValid()) {
1184: return false;
1185: }
1186:
1187: foreach ($this->getInterfaces() as $interface) {
1188: if (!$interface->isValid()) {
1189: return false;
1190: }
1191: }
1192:
1193: foreach ($this->getTraits() as $trait) {
1194: if (!$trait->isValid()) {
1195: return false;
1196: }
1197: }
1198:
1199: return true;
1200: }
1201:
1202: 1203: 1204: 1205: 1206: 1207: 1208:
1209: public function usesTrait($trait)
1210: {
1211: if (is_object($trait)) {
1212: if (!$trait instanceof InternalReflectionClass && !$trait instanceof IReflectionClass) {
1213: throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of trait reflection, "%s" provided.', get_class($trait)), Exception\RuntimeException::INVALID_ARGUMENT, $this);
1214: }
1215:
1216: $traitName = $trait->getName();
1217:
1218: if (!$trait->isTrait()) {
1219: throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $traitName), Exception\RuntimeException::INVALID_ARGUMENT, $this);
1220: }
1221: } else {
1222: $reflection = $this->getBroker()->getClass($trait);
1223: if (!$reflection->isTrait()) {
1224: throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $trait), Exception\RuntimeException::INVALID_ARGUMENT, $this);
1225: }
1226:
1227: $traitName = $trait;
1228: }
1229:
1230: return in_array($traitName, $this->getTraitNames());
1231: }
1232:
1233: 1234: 1235: 1236: 1237:
1238: public function getDirectSubclasses()
1239: {
1240: $that = $this->name;
1241: return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
1242: if (!$class->isSubclassOf($that)) {
1243: return false;
1244: }
1245:
1246: return null === $class->getParentClassName() || !$class->getParentClass()->isSubClassOf($that);
1247: });
1248: }
1249:
1250: 1251: 1252: 1253: 1254:
1255: public function getDirectSubclassNames()
1256: {
1257: return array_keys($this->getDirectSubclasses());
1258: }
1259:
1260: 1261: 1262: 1263: 1264:
1265: public function getIndirectSubclasses()
1266: {
1267: $that = $this->name;
1268: return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
1269: if (!$class->isSubclassOf($that)) {
1270: return false;
1271: }
1272:
1273: return null !== $class->getParentClassName() && $class->getParentClass()->isSubClassOf($that);
1274: });
1275: }
1276:
1277: 1278: 1279: 1280: 1281:
1282: public function getIndirectSubclassNames()
1283: {
1284: return array_keys($this->getIndirectSubclasses());
1285: }
1286:
1287: 1288: 1289: 1290: 1291:
1292: public function getDirectImplementers()
1293: {
1294: if (!$this->isInterface()) {
1295: return array();
1296: }
1297:
1298: $that = $this->name;
1299: return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
1300: if ($class->isInterface() || !$class->implementsInterface($that)) {
1301: return false;
1302: }
1303:
1304: return null === $class->getParentClassName() || !$class->getParentClass()->implementsInterface($that);
1305: });
1306: }
1307:
1308: 1309: 1310: 1311: 1312:
1313: public function getDirectImplementerNames()
1314: {
1315: return array_keys($this->getDirectImplementers());
1316: }
1317:
1318: 1319: 1320: 1321: 1322:
1323: public function getIndirectImplementers()
1324: {
1325: if (!$this->isInterface()) {
1326: return array();
1327: }
1328:
1329: $that = $this->name;
1330: return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
1331: if ($class->isInterface() || !$class->implementsInterface($that)) {
1332: return false;
1333: }
1334:
1335: return null !== $class->getParentClassName() && $class->getParentClass()->implementsInterface($that);
1336: });
1337: }
1338:
1339: 1340: 1341: 1342: 1343:
1344: public function getIndirectImplementerNames()
1345: {
1346: return array_keys($this->getIndirectImplementers());
1347: }
1348:
1349: 1350: 1351: 1352: 1353: 1354: 1355:
1356: public function isInstance($object)
1357: {
1358: if (!is_object($object)) {
1359: throw new Exception\RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)), Exception\RuntimeException::INVALID_ARGUMENT, $this);
1360: }
1361:
1362: return $this->name === get_class($object) || is_subclass_of($object, $this->getName());
1363: }
1364:
1365: 1366: 1367: 1368: 1369: 1370:
1371: public function newInstanceWithoutConstructor()
1372: {
1373: if (!class_exists($this->name, true)) {
1374: throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this);
1375: }
1376:
1377: $reflection = new \TokenReflection\Php\ReflectionClass($this->getName(), $this->getBroker());
1378: return $reflection->newInstanceWithoutConstructor();
1379: }
1380:
1381: 1382: 1383: 1384: 1385: 1386: 1387: 1388:
1389: public function newInstance($args)
1390: {
1391: return $this->newInstanceArgs(func_get_args());
1392: }
1393:
1394: 1395: 1396: 1397: 1398: 1399: 1400:
1401: public function newInstanceArgs(array $args = array())
1402: {
1403: if (!class_exists($this->name, true)) {
1404: throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this);
1405: }
1406:
1407: $reflection = new InternalReflectionClass($this->name);
1408: return $reflection->newInstanceArgs($args);
1409: }
1410:
1411: 1412: 1413: 1414: 1415: 1416: 1417: 1418:
1419: public function setStaticPropertyValue($name, $value)
1420: {
1421: if ($this->hasProperty($name) && ($property = $this->getProperty($name)) && $property->isStatic()) {
1422: if (!$property->isPublic() && !$property->isAccessible()) {
1423: throw new Exception\RuntimeException(sprintf('Static property "%s" is not accessible.', $name), Exception\RuntimeException::NOT_ACCESSBILE, $this);
1424: }
1425:
1426: $property->setDefaultValue($value);
1427: return;
1428: }
1429:
1430: throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
1431: }
1432:
1433: 1434: 1435: 1436: 1437:
1438: public function __toString()
1439: {
1440: $implements = '';
1441: $interfaceNames = $this->getInterfaceNames();
1442: if (count($interfaceNames) > 0) {
1443: $implements = sprintf(
1444: ' %s %s',
1445: $this->isInterface() ? 'extends' : 'implements',
1446: implode(', ', $interfaceNames)
1447: );
1448: }
1449:
1450: $buffer = '';
1451: $count = 0;
1452: foreach ($this->getConstantReflections() as $constant) {
1453: $buffer .= ' ' . $constant->__toString();
1454: $count++;
1455: }
1456: $constants = sprintf("\n\n - Constants [%d] {\n%s }", $count, $buffer);
1457:
1458: $sBuffer = '';
1459: $sCount = 0;
1460: $buffer = '';
1461: $count = 0;
1462: foreach ($this->getProperties() as $property) {
1463: $string = ' ' . preg_replace('~\n(?!$)~', "\n ", $property->__toString());
1464: if ($property->isStatic()) {
1465: $sBuffer .= $string;
1466: $sCount++;
1467: } else {
1468: $buffer .= $string;
1469: $count++;
1470: }
1471: }
1472: $staticProperties = sprintf("\n\n - Static properties [%d] {\n%s }", $sCount, $sBuffer);
1473: $properties = sprintf("\n\n - Properties [%d] {\n%s }", $count, $buffer);
1474:
1475: $sBuffer = '';
1476: $sCount = 0;
1477: $buffer = '';
1478: $count = 0;
1479: foreach ($this->getMethods() as $method) {
1480:
1481: if ($method->getDeclaringClassName() !== $this->getName() && $method->isPrivate()) {
1482: continue;
1483: }
1484:
1485: $string = "\n ";
1486:
1487: $string .= preg_replace('~\n(?!$|\n|\s*\*)~', "\n ", $method->__toString());
1488:
1489: if ($method->getDeclaringClassName() !== $this->getName()) {
1490: $string = preg_replace(
1491: array('~Method [ <[\w:]+~', '~, overwrites[^,]+~'),
1492: array('\0, inherits ' . $method->getDeclaringClassName(), ''),
1493: $string
1494: );
1495: }
1496: if ($method->isStatic()) {
1497: $sBuffer .= $string;
1498: $sCount++;
1499: } else {
1500: $buffer .= $string;
1501: $count++;
1502: }
1503: }
1504: $staticMethods = sprintf("\n\n - Static methods [%d] {\n%s }", $sCount, ltrim($sBuffer, "\n"));
1505: $methods = sprintf("\n\n - Methods [%d] {\n%s }", $count, ltrim($buffer, "\n"));
1506:
1507: return sprintf(
1508: "%s%s [ <user>%s %s%s%s %s%s%s ] {\n @@ %s %d-%d%s%s%s%s%s\n}\n",
1509: $this->getDocComment() ? $this->getDocComment() . "\n" : '',
1510: $this->isInterface() ? 'Interface' : 'Class',
1511: $this->isIterateable() ? ' <iterateable>' : '',
1512: $this->isAbstract() && !$this->isInterface() ? 'abstract ' : '',
1513: $this->isFinal() ? 'final ' : '',
1514: $this->isInterface() ? 'interface' : 'class',
1515: $this->getName(),
1516: null !== $this->getParentClassName() ? ' extends ' . $this->getParentClassName() : '',
1517: $implements,
1518: $this->getFileName(),
1519: $this->getStartLine(),
1520: $this->getEndLine(),
1521: $constants,
1522: $staticProperties,
1523: $staticMethods,
1524: $properties,
1525: $methods
1526: );
1527: }
1528:
1529: 1530: 1531: 1532: 1533: 1534: 1535: 1536: 1537:
1538: public static function export(Broker $broker, $className, $return = false)
1539: {
1540: if (is_object($className)) {
1541: $className = get_class($className);
1542: }
1543:
1544: $class = $broker->getClass($className);
1545: if ($class instanceof Invalid\ReflectionClass) {
1546: throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED);
1547: } elseif ($class instanceof Dummy\ReflectionClass) {
1548: throw new Exception\RuntimeException('Class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST);
1549: }
1550:
1551: if ($return) {
1552: return $class->__toString();
1553: }
1554:
1555: echo $class->__toString();
1556: }
1557:
1558: 1559: 1560: 1561: 1562:
1563: public function isComplete()
1564: {
1565: if (!$this->definitionComplete) {
1566: if (null !== $this->parentClassName && !$this->getParentClass()->isComplete()) {
1567: return false;
1568: }
1569:
1570: foreach ($this->getOwnInterfaces() as $interface) {
1571: if (!$interface->isComplete()) {
1572: return false;
1573: }
1574: }
1575:
1576: $this->definitionComplete = true;
1577: }
1578:
1579: return $this->definitionComplete;
1580: }
1581:
1582: 1583: 1584: 1585: 1586:
1587: public function getNamespaceAliases()
1588: {
1589: return $this->aliases;
1590: }
1591:
1592: 1593: 1594: 1595: 1596: 1597: 1598: 1599:
1600: protected function processParent(IReflection $parent, Stream $tokenStream)
1601: {
1602: if (!$parent instanceof ReflectionFileNamespace) {
1603: throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid parent reflection provided: "%s".', get_class($parent)), Exception\ParseException::INVALID_PARENT);
1604: }
1605:
1606: $this->namespaceName = $parent->getName();
1607: $this->aliases = $parent->getNamespaceAliases();
1608: return parent::processParent($parent, $tokenStream);
1609: }
1610:
1611: 1612: 1613: 1614: 1615: 1616: 1617:
1618: protected function parse(Stream $tokenStream, IReflection $parent)
1619: {
1620: return $this
1621: ->parseModifiers($tokenStream)
1622: ->parseName($tokenStream)
1623: ->parseParent($tokenStream, $parent)
1624: ->parseInterfaces($tokenStream, $parent);
1625: }
1626:
1627: 1628: 1629: 1630: 1631: 1632:
1633: private function parseModifiers(Stream $tokenStream)
1634: {
1635: while (true) {
1636: switch ($tokenStream->getType()) {
1637: case null:
1638: break 2;
1639: case T_ABSTRACT:
1640: $this->modifiers = InternalReflectionClass::IS_EXPLICIT_ABSTRACT;
1641: break;
1642: case T_FINAL:
1643: $this->modifiers = InternalReflectionClass::IS_FINAL;
1644: break;
1645: case T_INTERFACE:
1646: $this->modifiers = self::IS_INTERFACE;
1647: $this->type = self::IS_INTERFACE;
1648: $tokenStream->skipWhitespaces(true);
1649: break 2;
1650: case T_TRAIT:
1651: $this->modifiers = self::IS_TRAIT;
1652: $this->type = self::IS_TRAIT;
1653: $tokenStream->skipWhitespaces(true);
1654: break 2;
1655: case T_CLASS:
1656: $tokenStream->skipWhitespaces(true);
1657: break 2;
1658: default:
1659: break;
1660: }
1661:
1662: $tokenStream->skipWhitespaces(true);
1663: }
1664:
1665: return $this;
1666: }
1667:
1668: 1669: 1670: 1671: 1672: 1673: 1674:
1675: protected function parseName(Stream $tokenStream)
1676: {
1677: if (!$tokenStream->is(T_STRING)) {
1678: throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
1679: }
1680:
1681: if ($this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME) {
1682: $this->name = $tokenStream->getTokenValue();
1683: } else {
1684: $this->name = $this->namespaceName . '\\' . $tokenStream->getTokenValue();
1685: }
1686:
1687: $tokenStream->skipWhitespaces(true);
1688:
1689: return $this;
1690: }
1691:
1692: 1693: 1694: 1695: 1696: 1697: 1698:
1699: private function parseParent(Stream $tokenStream, ReflectionElement $parent = null)
1700: {
1701: if (!$tokenStream->is(T_EXTENDS)) {
1702: return $this;
1703: }
1704:
1705: while (true) {
1706: $tokenStream->skipWhitespaces(true);
1707:
1708: $parentClassName = '';
1709: while (true) {
1710: switch ($tokenStream->getType()) {
1711: case T_STRING:
1712: case T_NS_SEPARATOR:
1713: $parentClassName .= $tokenStream->getTokenValue();
1714: break;
1715: default:
1716: break 2;
1717: }
1718:
1719: $tokenStream->skipWhitespaces(true);
1720: }
1721:
1722: $parentClassName = Resolver::resolveClassFQN($parentClassName, $this->aliases, $this->namespaceName);
1723:
1724: if ($this->isInterface()) {
1725: $this->interfaces[] = $parentClassName;
1726:
1727: if (',' === $tokenStream->getTokenValue()) {
1728: continue;
1729: }
1730: } else {
1731: $this->parentClassName = $parentClassName;
1732: }
1733:
1734: break;
1735: }
1736:
1737: return $this;
1738: }
1739:
1740: 1741: 1742: 1743: 1744: 1745: 1746: 1747:
1748: private function parseInterfaces(Stream $tokenStream, ReflectionElement $parent = null)
1749: {
1750: if (!$tokenStream->is(T_IMPLEMENTS)) {
1751: return $this;
1752: }
1753:
1754: if ($this->isInterface()) {
1755: throw new Exception\ParseException($this, $tokenStream, 'Interfaces cannot implement interfaces.', Exception\ParseException::LOGICAL_ERROR);
1756: }
1757:
1758: while (true) {
1759: $interfaceName = '';
1760:
1761: $tokenStream->skipWhitespaces(true);
1762: while (true) {
1763: switch ($tokenStream->getType()) {
1764: case T_STRING:
1765: case T_NS_SEPARATOR:
1766: $interfaceName .= $tokenStream->getTokenValue();
1767: break;
1768: default:
1769: break 2;
1770: }
1771:
1772: $tokenStream->skipWhitespaces(true);
1773: }
1774:
1775: $this->interfaces[] = Resolver::resolveClassFQN($interfaceName, $this->aliases, $this->namespaceName);
1776:
1777: $type = $tokenStream->getType();
1778: if ('{' === $type) {
1779: break;
1780: } elseif (',' !== $type) {
1781: throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found, expected "{" or ";".', Exception\ParseException::UNEXPECTED_TOKEN);
1782: }
1783: }
1784:
1785: return $this;
1786: }
1787:
1788: 1789: 1790: 1791: 1792: 1793: 1794: 1795:
1796: protected function parseChildren(Stream $tokenStream, IReflection $parent)
1797: {
1798: while (true) {
1799: switch ($type = $tokenStream->getType()) {
1800: case null:
1801: break 2;
1802: case T_COMMENT:
1803: case T_DOC_COMMENT:
1804: $docblock = $tokenStream->getTokenValue();
1805: if (preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $docblock)) {
1806: array_unshift($this->docblockTemplates, new ReflectionAnnotation($this, $docblock));
1807: } elseif (self::DOCBLOCK_TEMPLATE_END === $docblock) {
1808: array_shift($this->docblockTemplates);
1809: }
1810: $tokenStream->next();
1811: break;
1812: case '}':
1813: break 2;
1814: case T_PUBLIC:
1815: case T_PRIVATE:
1816: case T_PROTECTED:
1817: case T_STATIC:
1818: case T_VAR:
1819: case T_VARIABLE:
1820: static $searching = array(T_VARIABLE => true, T_FUNCTION => true);
1821:
1822: if (T_VAR !== $tokenStream->getType()) {
1823: $position = $tokenStream->key();
1824: while (null !== ($type = $tokenStream->getType($position)) && !isset($searching[$type])) {
1825: $position++;
1826: }
1827: }
1828:
1829: if (T_VARIABLE === $type || T_VAR === $type) {
1830: $property = new ReflectionProperty($tokenStream, $this->getBroker(), $this);
1831: $this->properties[$property->getName()] = $property;
1832: $tokenStream->next();
1833: break;
1834: }
1835:
1836: case T_FINAL:
1837: case T_ABSTRACT:
1838: case T_FUNCTION:
1839: $method = new ReflectionMethod($tokenStream, $this->getBroker(), $this);
1840: $this->methods[$method->getName()] = $method;
1841: $tokenStream->next();
1842: break;
1843: case T_CONST:
1844: $tokenStream->skipWhitespaces(true);
1845: while ($tokenStream->is(T_STRING)) {
1846: $constant = new ReflectionConstant($tokenStream, $this->getBroker(), $this);
1847: $this->constants[$constant->getName()] = $constant;
1848: if ($tokenStream->is(',')) {
1849: $tokenStream->skipWhitespaces(true);
1850: } else {
1851: $tokenStream->next();
1852: }
1853: }
1854: break;
1855: case T_USE:
1856: $tokenStream->skipWhitespaces(true);
1857:
1858: while (true) {
1859: $traitName = '';
1860: $type = $tokenStream->getType();
1861: while (T_STRING === $type || T_NS_SEPARATOR === $type) {
1862: $traitName .= $tokenStream->getTokenValue();
1863: $type = $tokenStream->skipWhitespaces(true)->getType();
1864: }
1865:
1866: if ('' === trim($traitName, '\\')) {
1867: throw new Exception\ParseException($this, $tokenStream, 'An empty trait name found.', Exception\ParseException::LOGICAL_ERROR);
1868: }
1869:
1870: $this->traits[] = Resolver::resolveClassFQN($traitName, $this->aliases, $this->namespaceName);
1871:
1872: if (';' === $type) {
1873:
1874: $tokenStream->skipWhitespaces();
1875: break;
1876: } elseif (',' === $type) {
1877:
1878: $tokenStream->skipWhitespaces();
1879: continue;
1880: } elseif ('{' !== $type) {
1881:
1882: throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found: "%s".', Exception\ParseException::UNEXPECTED_TOKEN);
1883: }
1884:
1885:
1886: $type = $tokenStream->skipWhitespaces(true)->getType();
1887: while (true) {
1888: if ('}' === $type) {
1889: $tokenStream->skipWhitespaces();
1890: break 2;
1891: }
1892:
1893: $leftSide = '';
1894: $rightSide = array('', null);
1895: $alias = true;
1896:
1897: while (T_STRING === $type || T_NS_SEPARATOR === $type || T_DOUBLE_COLON === $type) {
1898: $leftSide .= $tokenStream->getTokenValue();
1899: $type = $tokenStream->skipWhitespaces(true)->getType();
1900: }
1901:
1902: if (T_INSTEADOF === $type) {
1903: $alias = false;
1904: } elseif (T_AS !== $type) {
1905: throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
1906: }
1907:
1908: $type = $tokenStream->skipWhitespaces(true)->getType();
1909:
1910: if (T_PUBLIC === $type || T_PROTECTED === $type || T_PRIVATE === $type) {
1911: if (!$alias) {
1912: throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
1913: }
1914:
1915: switch ($type) {
1916: case T_PUBLIC:
1917: $type = InternalReflectionMethod::IS_PUBLIC;
1918: break;
1919: case T_PROTECTED:
1920: $type = InternalReflectionMethod::IS_PROTECTED;
1921: break;
1922: case T_PRIVATE:
1923: $type = InternalReflectionMethod::IS_PRIVATE;
1924: break;
1925: default:
1926: break;
1927: }
1928:
1929: $rightSide[1] = $type;
1930: $type = $tokenStream->skipWhitespaces(true)->getType();
1931: }
1932:
1933: while (T_STRING === $type || (T_NS_SEPARATOR === $type && !$alias)) {
1934: $rightSide[0] .= $tokenStream->getTokenValue();
1935: $type = $tokenStream->skipWhitespaces(true)->getType();
1936: }
1937:
1938: if (empty($leftSide)) {
1939: throw new Exception\ParseException($this, $tokenStream, 'An empty method name was found.', Exception\ParseException::LOGICAL_ERROR);
1940: }
1941:
1942: if ($alias) {
1943:
1944: if ($pos = strpos($leftSide, '::')) {
1945: $methodName = substr($leftSide, $pos + 2);
1946: $className = Resolver::resolveClassFQN(substr($leftSide, 0, $pos), $this->aliases, $this->namespaceName);
1947: $leftSide = $className . '::' . $methodName;
1948:
1949: $this->traitAliases[$rightSide[0]] = $leftSide;
1950: } else {
1951: $this->traitAliases[$rightSide[0]] = '(null)::' . $leftSide;
1952: }
1953:
1954: $this->traitImports[$leftSide][] = $rightSide;
1955: } else {
1956:
1957: if ($pos = strpos($leftSide, '::')) {
1958: $methodName = substr($leftSide, $pos + 2);
1959: } else {
1960: throw new Exception\ParseException($this, $tokenStream, 'A T_DOUBLE_COLON has to be present when using T_INSTEADOF.', Exception\ParseException::UNEXPECTED_TOKEN);
1961: }
1962:
1963: $this->traitImports[Resolver::resolveClassFQN($rightSide[0], $this->aliases, $this->namespaceName) . '::' . $methodName][] = null;
1964: }
1965:
1966: if (',' === $type) {
1967: $tokenStream->skipWhitespaces(true);
1968: continue;
1969: } elseif (';' !== $type) {
1970: throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
1971: }
1972:
1973: $type = $tokenStream->skipWhitespaces()->getType();
1974: }
1975: }
1976:
1977: break;
1978: default:
1979: $tokenStream->next();
1980: break;
1981: }
1982: }
1983:
1984: return $this;
1985: }
1986: }
1987: