DelegatingLoader.php 3.3 KB

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