CacheWarmerAggregate.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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\HttpKernel\CacheWarmer;
  11. use Symfony\Component\Console\Style\SymfonyStyle;
  12. /**
  13. * Aggregates several cache warmers into a single one.
  14. *
  15. * @author Fabien Potencier <fabien@symfony.com>
  16. *
  17. * @final
  18. */
  19. class CacheWarmerAggregate implements CacheWarmerInterface
  20. {
  21. private bool $optionalsEnabled = false;
  22. private bool $onlyOptionalsEnabled = false;
  23. /**
  24. * @param iterable<mixed, CacheWarmerInterface> $warmers
  25. */
  26. public function __construct(
  27. private iterable $warmers = [],
  28. private bool $debug = false,
  29. private ?string $deprecationLogsFilepath = null,
  30. ) {
  31. }
  32. public function enableOptionalWarmers(): void
  33. {
  34. $this->optionalsEnabled = true;
  35. }
  36. public function enableOnlyOptionalWarmers(): void
  37. {
  38. $this->onlyOptionalsEnabled = $this->optionalsEnabled = true;
  39. }
  40. public function warmUp(string $cacheDir, ?string $buildDir = null, ?SymfonyStyle $io = null): array
  41. {
  42. if ($collectDeprecations = $this->debug && !\defined('PHPUNIT_COMPOSER_INSTALL')) {
  43. $collectedLogs = [];
  44. $previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) {
  45. if (\E_USER_DEPRECATED !== $type && \E_DEPRECATED !== $type) {
  46. return $previousHandler ? $previousHandler($type, $message, $file, $line) : false;
  47. }
  48. if (isset($collectedLogs[$message])) {
  49. ++$collectedLogs[$message]['count'];
  50. return null;
  51. }
  52. $backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 3);
  53. // Clean the trace by removing first frames added by the error handler itself.
  54. for ($i = 0; isset($backtrace[$i]); ++$i) {
  55. if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
  56. $backtrace = \array_slice($backtrace, 1 + $i);
  57. break;
  58. }
  59. }
  60. $collectedLogs[$message] = [
  61. 'type' => $type,
  62. 'message' => $message,
  63. 'file' => $file,
  64. 'line' => $line,
  65. 'trace' => $backtrace,
  66. 'count' => 1,
  67. ];
  68. return null;
  69. });
  70. }
  71. $preload = [];
  72. try {
  73. foreach ($this->warmers as $warmer) {
  74. if (!$this->optionalsEnabled && $warmer->isOptional()) {
  75. continue;
  76. }
  77. if ($this->onlyOptionalsEnabled && !$warmer->isOptional()) {
  78. continue;
  79. }
  80. $start = microtime(true);
  81. foreach ($warmer->warmUp($cacheDir, $buildDir) as $item) {
  82. if (is_dir($item) || (str_starts_with($item, \dirname($cacheDir)) && !is_file($item)) || ($buildDir && str_starts_with($item, \dirname($buildDir)) && !is_file($item))) {
  83. throw new \LogicException(\sprintf('"%s::warmUp()" should return a list of files or classes but "%s" is none of them.', $warmer::class, $item));
  84. }
  85. $preload[] = $item;
  86. }
  87. if ($io?->isDebug()) {
  88. $io->info(\sprintf('"%s" completed in %0.2fms.', $warmer::class, 1000 * (microtime(true) - $start)));
  89. }
  90. }
  91. } finally {
  92. if ($collectDeprecations) {
  93. restore_error_handler();
  94. if (is_file($this->deprecationLogsFilepath)) {
  95. $previousLogs = unserialize(file_get_contents($this->deprecationLogsFilepath));
  96. if (\is_array($previousLogs)) {
  97. $collectedLogs = array_merge($previousLogs, $collectedLogs);
  98. }
  99. }
  100. file_put_contents($this->deprecationLogsFilepath, serialize(array_values($collectedLogs)));
  101. }
  102. }
  103. return array_values(array_unique($preload));
  104. }
  105. public function isOptional(): bool
  106. {
  107. return false;
  108. }
  109. }