vendor/symfony/framework-bundle/Routing/DelegatingLoader.php line 70

Open in your IDE?
  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\Bundle\FrameworkBundle\Routing;
  11. use Symfony\Component\Config\Exception\LoaderLoadException;
  12. use Symfony\Component\Config\Loader\DelegatingLoader as BaseDelegatingLoader;
  13. use Symfony\Component\Config\Loader\LoaderResolverInterface;
  14. use Symfony\Component\Routing\RouteCollection;
  15. /**
  16. * DelegatingLoader delegates route loading to other loaders using a loader resolver.
  17. *
  18. * This implementation resolves the _controller attribute from the short notation
  19. * to the fully-qualified form (from a:b:c to class::method).
  20. *
  21. * @author Fabien Potencier <fabien@symfony.com>
  22. *
  23. * @final
  24. */
  25. class DelegatingLoader extends BaseDelegatingLoader
  26. {
  27. private $loading = false;
  28. private $defaultOptions;
  29. private $defaultRequirements;
  30. public function __construct(LoaderResolverInterface $resolver, array $defaultOptions = [], array $defaultRequirements = [])
  31. {
  32. $this->defaultOptions = $defaultOptions;
  33. $this->defaultRequirements = $defaultRequirements;
  34. parent::__construct($resolver);
  35. }
  36. /**
  37. * {@inheritdoc}
  38. */
  39. public function load($resource, ?string $type = null): RouteCollection
  40. {
  41. if ($this->loading) {
  42. // This can happen if a fatal error occurs in parent::load().
  43. // Here is the scenario:
  44. // - while routes are being loaded by parent::load() below, a fatal error
  45. // occurs (e.g. parse error in a controller while loading annotations);
  46. // - PHP abruptly empties the stack trace, bypassing all catch/finally blocks;
  47. // it then calls the registered shutdown functions;
  48. // - the ErrorHandler catches the fatal error and re-injects it for rendering
  49. // thanks to HttpKernel->terminateWithException() (that calls handleException());
  50. // - at this stage, if we try to load the routes again, we must prevent
  51. // the fatal error from occurring a second time,
  52. // otherwise the PHP process would be killed immediately;
  53. // - while rendering the exception page, the router can be required
  54. // (by e.g. the web profiler that needs to generate an URL);
  55. // - this handles the case and prevents the second fatal error
  56. // by triggering an exception beforehand.
  57. throw new LoaderLoadException($resource, null, 0, null, $type);
  58. }
  59. $this->loading = true;
  60. try {
  61. $collection = parent::load($resource, $type);
  62. } finally {
  63. $this->loading = false;
  64. }
  65. foreach ($collection->all() as $route) {
  66. if ($this->defaultOptions) {
  67. $route->setOptions($route->getOptions() + $this->defaultOptions);
  68. }
  69. if ($this->defaultRequirements) {
  70. $route->setRequirements($route->getRequirements() + $this->defaultRequirements);
  71. }
  72. if (!\is_string($controller = $route->getDefault('_controller'))) {
  73. continue;
  74. }
  75. if (str_contains($controller, '::')) {
  76. continue;
  77. }
  78. $route->setDefault('_controller', $controller);
  79. }
  80. return $collection;
  81. }
  82. }