Preloader.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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\DependencyInjection\Dumper;
  11. /**
  12. * @author Nicolas Grekas <p@tchwork.com>
  13. */
  14. final class Preloader
  15. {
  16. public static function append(string $file, array $list): void
  17. {
  18. if (!file_exists($file)) {
  19. throw new \LogicException(sprintf('File "%s" does not exist.', $file));
  20. }
  21. $cacheDir = \dirname($file);
  22. $classes = [];
  23. foreach ($list as $item) {
  24. if (0 === strpos($item, $cacheDir)) {
  25. file_put_contents($file, sprintf("require_once __DIR__.%s;\n", var_export(strtr(substr($item, \strlen($cacheDir)), \DIRECTORY_SEPARATOR, '/'), true)), \FILE_APPEND);
  26. continue;
  27. }
  28. $classes[] = sprintf("\$classes[] = %s;\n", var_export($item, true));
  29. }
  30. file_put_contents($file, sprintf("\n\$classes = [];\n%sPreloader::preload(\$classes);\n", implode('', $classes)), \FILE_APPEND);
  31. }
  32. public static function preload(array $classes): void
  33. {
  34. set_error_handler(function ($t, $m, $f, $l) {
  35. if (error_reporting() & $t) {
  36. if (__FILE__ !== $f) {
  37. throw new \ErrorException($m, 0, $t, $f, $l);
  38. }
  39. throw new \ReflectionException($m);
  40. }
  41. });
  42. $prev = [];
  43. $preloaded = [];
  44. try {
  45. while ($prev !== $classes) {
  46. $prev = $classes;
  47. foreach ($classes as $c) {
  48. if (!isset($preloaded[$c])) {
  49. self::doPreload($c, $preloaded);
  50. }
  51. }
  52. $classes = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits());
  53. }
  54. } finally {
  55. restore_error_handler();
  56. }
  57. }
  58. private static function doPreload(string $class, array &$preloaded): void
  59. {
  60. if (isset($preloaded[$class]) || \in_array($class, ['self', 'static', 'parent'], true)) {
  61. return;
  62. }
  63. $preloaded[$class] = true;
  64. try {
  65. $r = new \ReflectionClass($class);
  66. if ($r->isInternal()) {
  67. return;
  68. }
  69. $r->getConstants();
  70. $r->getDefaultProperties();
  71. if (\PHP_VERSION_ID >= 70400) {
  72. foreach ($r->getProperties(\ReflectionProperty::IS_PUBLIC) as $p) {
  73. self::preloadType($p->getType(), $preloaded);
  74. }
  75. }
  76. foreach ($r->getMethods(\ReflectionMethod::IS_PUBLIC) as $m) {
  77. foreach ($m->getParameters() as $p) {
  78. if ($p->isDefaultValueAvailable() && $p->isDefaultValueConstant()) {
  79. $c = $p->getDefaultValueConstantName();
  80. if ($i = strpos($c, '::')) {
  81. self::doPreload(substr($c, 0, $i), $preloaded);
  82. }
  83. }
  84. self::preloadType($p->getType(), $preloaded);
  85. }
  86. self::preloadType($m->getReturnType(), $preloaded);
  87. }
  88. } catch (\Throwable $e) {
  89. // ignore missing classes
  90. }
  91. }
  92. private static function preloadType(?\ReflectionType $t, array &$preloaded): void
  93. {
  94. if (!$t) {
  95. return;
  96. }
  97. foreach ($t instanceof \ReflectionUnionType ? $t->getTypes() : [$t] as $t) {
  98. if (!$t->isBuiltin()) {
  99. self::doPreload($t instanceof \ReflectionNamedType ? $t->getName() : $t, $preloaded);
  100. }
  101. }
  102. }
  103. }