Skip to content

Commit b58229a

Browse files
bug #14345 [FrameworkBundle] Fix Routing\DelegatingLoader resiliency to fatal errors (nicolas-grekas)
This PR was merged into the 2.3 branch. Discussion ---------- [FrameworkBundle] Fix Routing\DelegatingLoader resiliency to fatal errors | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #14342 | License | MIT | Doc PR | - Commits ------- 7e95a24 [FrameworkBundle] Fix Routing\DelegatingLoader resiliency to fatal errors
2 parents bb5f0b3 + 94ea68e commit b58229a

File tree

1 file changed

+32
-8
lines changed

1 file changed

+32
-8
lines changed

Routing/DelegatingLoader.php

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
namespace Symfony\Bundle\FrameworkBundle\Routing;
1313

1414
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
15+
use Symfony\Component\Config\Exception\FileLoaderLoadException;
1516
use Symfony\Component\Config\Loader\DelegatingLoader as BaseDelegatingLoader;
1617
use Symfony\Component\Config\Loader\LoaderResolverInterface;
1718
use Psr\Log\LoggerInterface;
18-
use Symfony\Component\Routing\RouteCollection;
1919

2020
/**
2121
* DelegatingLoader delegates route loading to other loaders using a loader resolver.
@@ -29,6 +29,7 @@ class DelegatingLoader extends BaseDelegatingLoader
2929
{
3030
protected $parser;
3131
protected $logger;
32+
private $loading = false;
3233

3334
/**
3435
* Constructor.
@@ -46,16 +47,39 @@ public function __construct(ControllerNameParser $parser, LoggerInterface $logge
4647
}
4748

4849
/**
49-
* Loads a resource.
50-
*
51-
* @param mixed $resource A resource
52-
* @param string $type The resource type
53-
*
54-
* @return RouteCollection A RouteCollection instance
50+
* {@inheritdoc}
5551
*/
5652
public function load($resource, $type = null)
5753
{
58-
$collection = parent::load($resource, $type);
54+
if ($this->loading) {
55+
// This can happen if a fatal error occurs in parent::load().
56+
// Here is the scenario:
57+
// - while routes are being loaded by parent::load() below, a fatal error
58+
// occurs (e.g. parse error in a controller while loading annotations);
59+
// - PHP abruptly empties the stack trace, bypassing all catch blocks;
60+
// it then calls the registered shutdown functions;
61+
// - the ErrorHandler catches the fatal error and re-injects it for rendering
62+
// thanks to HttpKernel->terminateWithException() (that calls handleException());
63+
// - at this stage, if we try to load the routes again, we must prevent
64+
// the fatal error from occurring a second time,
65+
// otherwise the PHP process would be killed immediately;
66+
// - while rendering the exception page, the router can be required
67+
// (by e.g. the web profiler that needs to generate an URL);
68+
// - this handles the case and prevents the second fatal error
69+
// by triggering an exception beforehand.
70+
71+
throw new FileLoaderLoadException($resource);
72+
}
73+
$this->loading = true;
74+
75+
try {
76+
$collection = parent::load($resource, $type);
77+
} catch (\Exception $e) {
78+
$this->loading = false;
79+
throw $e;
80+
}
81+
82+
$this->loading = false;
5983

6084
foreach ($collection->all() as $route) {
6185
if ($controller = $route->getDefault('_controller')) {

0 commit comments

Comments
 (0)