ReflectionClassResource.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Config\Resource;
  11. use Symfony\Component\DependencyInjection\ServiceSubscriberInterface as LegacyServiceSubscriberInterface;
  12. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  13. use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
  14. use Symfony\Contracts\Service\ServiceSubscriberInterface;
  15. /**
  16. * @author Nicolas Grekas <p@tchwork.com>
  17. *
  18. * @final since Symfony 4.3
  19. */
  20. class ReflectionClassResource implements SelfCheckingResourceInterface
  21. {
  22. private $files = [];
  23. private $className;
  24. private $classReflector;
  25. private $excludedVendors = [];
  26. private $hash;
  27. public function __construct(\ReflectionClass $classReflector, array $excludedVendors = [])
  28. {
  29. $this->className = $classReflector->name;
  30. $this->classReflector = $classReflector;
  31. $this->excludedVendors = $excludedVendors;
  32. }
  33. public function isFresh($timestamp)
  34. {
  35. if (null === $this->hash) {
  36. $this->hash = $this->computeHash();
  37. $this->loadFiles($this->classReflector);
  38. }
  39. foreach ($this->files as $file => $v) {
  40. if (false === $filemtime = @filemtime($file)) {
  41. return false;
  42. }
  43. if ($filemtime > $timestamp) {
  44. return $this->hash === $this->computeHash();
  45. }
  46. }
  47. return true;
  48. }
  49. public function __toString()
  50. {
  51. return 'reflection.'.$this->className;
  52. }
  53. /**
  54. * @internal
  55. */
  56. public function __sleep(): array
  57. {
  58. if (null === $this->hash) {
  59. $this->hash = $this->computeHash();
  60. $this->loadFiles($this->classReflector);
  61. }
  62. return ['files', 'className', 'hash'];
  63. }
  64. private function loadFiles(\ReflectionClass $class)
  65. {
  66. foreach ($class->getInterfaces() as $v) {
  67. $this->loadFiles($v);
  68. }
  69. do {
  70. $file = $class->getFileName();
  71. if (false !== $file && file_exists($file)) {
  72. foreach ($this->excludedVendors as $vendor) {
  73. if (0 === strpos($file, $vendor) && false !== strpbrk(substr($file, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
  74. $file = false;
  75. break;
  76. }
  77. }
  78. if ($file) {
  79. $this->files[$file] = null;
  80. }
  81. }
  82. foreach ($class->getTraits() as $v) {
  83. $this->loadFiles($v);
  84. }
  85. } while ($class = $class->getParentClass());
  86. }
  87. private function computeHash(): string
  88. {
  89. if (null === $this->classReflector) {
  90. try {
  91. $this->classReflector = new \ReflectionClass($this->className);
  92. } catch (\ReflectionException $e) {
  93. // the class does not exist anymore
  94. return false;
  95. }
  96. }
  97. $hash = hash_init('md5');
  98. foreach ($this->generateSignature($this->classReflector) as $info) {
  99. hash_update($hash, $info);
  100. }
  101. return hash_final($hash);
  102. }
  103. private function generateSignature(\ReflectionClass $class): iterable
  104. {
  105. yield $class->getDocComment();
  106. yield (int) $class->isFinal();
  107. yield (int) $class->isAbstract();
  108. if ($class->isTrait()) {
  109. yield print_r(class_uses($class->name), true);
  110. } else {
  111. yield print_r(class_parents($class->name), true);
  112. yield print_r(class_implements($class->name), true);
  113. yield print_r($class->getConstants(), true);
  114. }
  115. if (!$class->isInterface()) {
  116. $defaults = $class->getDefaultProperties();
  117. foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) {
  118. yield $p->getDocComment();
  119. yield $p->isDefault() ? '<default>' : '';
  120. yield $p->isPublic() ? 'public' : 'protected';
  121. yield $p->isStatic() ? 'static' : '';
  122. yield '$'.$p->name;
  123. yield print_r(isset($defaults[$p->name]) && !\is_object($defaults[$p->name]) ? $defaults[$p->name] : null, true);
  124. }
  125. }
  126. foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
  127. $defaults = [];
  128. $parametersWithUndefinedConstants = [];
  129. foreach ($m->getParameters() as $p) {
  130. if (!$p->isDefaultValueAvailable()) {
  131. $defaults[$p->name] = null;
  132. continue;
  133. }
  134. if (!$p->isDefaultValueConstant() || \defined($p->getDefaultValueConstantName())) {
  135. $defaults[$p->name] = $p->getDefaultValue();
  136. continue;
  137. }
  138. $defaults[$p->name] = $p->getDefaultValueConstantName();
  139. $parametersWithUndefinedConstants[$p->name] = true;
  140. }
  141. if (!$parametersWithUndefinedConstants) {
  142. yield preg_replace('/^ @@.*/m', '', $m);
  143. } else {
  144. $t = $m->getReturnType();
  145. $stack = [
  146. $m->getDocComment(),
  147. $m->getName(),
  148. $m->isAbstract(),
  149. $m->isFinal(),
  150. $m->isStatic(),
  151. $m->isPublic(),
  152. $m->isPrivate(),
  153. $m->isProtected(),
  154. $m->returnsReference(),
  155. $t instanceof \ReflectionNamedType ? ((string) $t->allowsNull()).$t->getName() : (string) $t,
  156. ];
  157. foreach ($m->getParameters() as $p) {
  158. if (!isset($parametersWithUndefinedConstants[$p->name])) {
  159. $stack[] = (string) $p;
  160. } else {
  161. $t = $p->getType();
  162. $stack[] = $p->isOptional();
  163. $stack[] = $t instanceof \ReflectionNamedType ? ((string) $t->allowsNull()).$t->getName() : (string) $t;
  164. $stack[] = $p->isPassedByReference();
  165. $stack[] = $p->isVariadic();
  166. $stack[] = $p->getName();
  167. }
  168. }
  169. yield implode(',', $stack);
  170. }
  171. yield print_r($defaults, true);
  172. }
  173. if ($class->isAbstract() || $class->isInterface() || $class->isTrait()) {
  174. return;
  175. }
  176. if (interface_exists(EventSubscriberInterface::class, false) && $class->isSubclassOf(EventSubscriberInterface::class)) {
  177. yield EventSubscriberInterface::class;
  178. yield print_r($class->name::getSubscribedEvents(), true);
  179. }
  180. if (interface_exists(MessageSubscriberInterface::class, false) && $class->isSubclassOf(MessageSubscriberInterface::class)) {
  181. yield MessageSubscriberInterface::class;
  182. foreach ($class->name::getHandledMessages() as $key => $value) {
  183. yield $key.print_r($value, true);
  184. }
  185. }
  186. if (interface_exists(LegacyServiceSubscriberInterface::class, false) && $class->isSubclassOf(LegacyServiceSubscriberInterface::class)) {
  187. yield LegacyServiceSubscriberInterface::class;
  188. yield print_r([$class->name, 'getSubscribedServices'](), true);
  189. } elseif (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) {
  190. yield ServiceSubscriberInterface::class;
  191. yield print_r($class->name::getSubscribedServices(), true);
  192. }
  193. }
  194. }