ReflectionClassResource.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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\Contracts\Service\ServiceSubscriberInterface;
  14. /**
  15. * @author Nicolas Grekas <p@tchwork.com>
  16. */
  17. class ReflectionClassResource implements SelfCheckingResourceInterface, \Serializable
  18. {
  19. private $files = [];
  20. private $className;
  21. private $classReflector;
  22. private $excludedVendors = [];
  23. private $hash;
  24. public function __construct(\ReflectionClass $classReflector, array $excludedVendors = [])
  25. {
  26. $this->className = $classReflector->name;
  27. $this->classReflector = $classReflector;
  28. $this->excludedVendors = $excludedVendors;
  29. }
  30. public function isFresh($timestamp)
  31. {
  32. if (null === $this->hash) {
  33. $this->hash = $this->computeHash();
  34. $this->loadFiles($this->classReflector);
  35. }
  36. foreach ($this->files as $file => $v) {
  37. if (false === $filemtime = @filemtime($file)) {
  38. return false;
  39. }
  40. if ($filemtime > $timestamp) {
  41. return $this->hash === $this->computeHash();
  42. }
  43. }
  44. return true;
  45. }
  46. public function __toString()
  47. {
  48. return 'reflection.'.$this->className;
  49. }
  50. /**
  51. * @internal
  52. */
  53. public function serialize()
  54. {
  55. if (null === $this->hash) {
  56. $this->hash = $this->computeHash();
  57. $this->loadFiles($this->classReflector);
  58. }
  59. return serialize([$this->files, $this->className, $this->hash]);
  60. }
  61. /**
  62. * @internal
  63. */
  64. public function unserialize($serialized)
  65. {
  66. list($this->files, $this->className, $this->hash) = unserialize($serialized);
  67. }
  68. private function loadFiles(\ReflectionClass $class)
  69. {
  70. foreach ($class->getInterfaces() as $v) {
  71. $this->loadFiles($v);
  72. }
  73. do {
  74. $file = $class->getFileName();
  75. if (false !== $file && file_exists($file)) {
  76. foreach ($this->excludedVendors as $vendor) {
  77. if (0 === strpos($file, $vendor) && false !== strpbrk(substr($file, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
  78. $file = false;
  79. break;
  80. }
  81. }
  82. if ($file) {
  83. $this->files[$file] = null;
  84. }
  85. }
  86. foreach ($class->getTraits() as $v) {
  87. $this->loadFiles($v);
  88. }
  89. } while ($class = $class->getParentClass());
  90. }
  91. private function computeHash()
  92. {
  93. if (null === $this->classReflector) {
  94. try {
  95. $this->classReflector = new \ReflectionClass($this->className);
  96. } catch (\ReflectionException $e) {
  97. // the class does not exist anymore
  98. return false;
  99. }
  100. }
  101. $hash = hash_init('md5');
  102. foreach ($this->generateSignature($this->classReflector) as $info) {
  103. hash_update($hash, $info);
  104. }
  105. return hash_final($hash);
  106. }
  107. private function generateSignature(\ReflectionClass $class)
  108. {
  109. yield $class->getDocComment();
  110. yield (int) $class->isFinal();
  111. yield (int) $class->isAbstract();
  112. if ($class->isTrait()) {
  113. yield print_r(class_uses($class->name), true);
  114. } else {
  115. yield print_r(class_parents($class->name), true);
  116. yield print_r(class_implements($class->name), true);
  117. yield print_r($class->getConstants(), true);
  118. }
  119. if (!$class->isInterface()) {
  120. $defaults = $class->getDefaultProperties();
  121. foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) {
  122. yield $p->getDocComment().$p;
  123. yield print_r($defaults[$p->name], true);
  124. }
  125. }
  126. foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
  127. yield preg_replace('/^ @@.*/m', '', $m);
  128. $defaults = [];
  129. foreach ($m->getParameters() as $p) {
  130. $defaults[$p->name] = $p->isDefaultValueAvailable() ? $p->getDefaultValue() : null;
  131. }
  132. yield print_r($defaults, true);
  133. }
  134. if ($class->isAbstract() || $class->isInterface() || $class->isTrait()) {
  135. return;
  136. }
  137. if (interface_exists(EventSubscriberInterface::class, false) && $class->isSubclassOf(EventSubscriberInterface::class)) {
  138. yield EventSubscriberInterface::class;
  139. yield print_r($class->name::getSubscribedEvents(), true);
  140. }
  141. if (interface_exists(LegacyServiceSubscriberInterface::class, false) && $class->isSubclassOf(LegacyServiceSubscriberInterface::class)) {
  142. yield LegacyServiceSubscriberInterface::class;
  143. yield print_r([$class->name, 'getSubscribedServices'](), true);
  144. } elseif (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) {
  145. yield ServiceSubscriberInterface::class;
  146. yield print_r($class->name::getSubscribedServices(), true);
  147. }
  148. }
  149. }