[Console][DI] Fail gracefully
This commit is contained in:
parent
b9fb27cab2
commit
4a5a3f52ab
@ -40,6 +40,7 @@ use Symfony\Component\Console\Event\ConsoleExceptionEvent;
|
|||||||
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
||||||
use Symfony\Component\Console\Exception\CommandNotFoundException;
|
use Symfony\Component\Console\Exception\CommandNotFoundException;
|
||||||
use Symfony\Component\Console\Exception\LogicException;
|
use Symfony\Component\Console\Exception\LogicException;
|
||||||
|
use Symfony\Component\Debug\ErrorHandler;
|
||||||
use Symfony\Component\Debug\Exception\FatalThrowableError;
|
use Symfony\Component\Debug\Exception\FatalThrowableError;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
|
|
||||||
@ -118,6 +119,25 @@ class Application
|
|||||||
$output = new ConsoleOutput();
|
$output = new ConsoleOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$renderException = function ($e) use ($output) {
|
||||||
|
if (!$e instanceof \Exception) {
|
||||||
|
$e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
|
||||||
|
}
|
||||||
|
if ($output instanceof ConsoleOutputInterface) {
|
||||||
|
$this->renderException($e, $output->getErrorOutput());
|
||||||
|
} else {
|
||||||
|
$this->renderException($e, $output);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if ($phpHandler = set_exception_handler($renderException)) {
|
||||||
|
restore_exception_handler();
|
||||||
|
if (!is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) {
|
||||||
|
$debugHandler = true;
|
||||||
|
} elseif ($debugHandler = $phpHandler[0]->setExceptionHandler($renderException)) {
|
||||||
|
$phpHandler[0]->setExceptionHandler($debugHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (null !== $this->dispatcher && $this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) {
|
if (null !== $this->dispatcher && $this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) {
|
||||||
@trigger_error(sprintf('The "ConsoleEvents::EXCEPTION" event is deprecated since Symfony 3.3 and will be removed in 4.0. Listen to the "ConsoleEvents::ERROR" event instead.'), E_USER_DEPRECATED);
|
@trigger_error(sprintf('The "ConsoleEvents::EXCEPTION" event is deprecated since Symfony 3.3 and will be removed in 4.0. Listen to the "ConsoleEvents::ERROR" event instead.'), E_USER_DEPRECATED);
|
||||||
}
|
}
|
||||||
@ -125,21 +145,13 @@ class Application
|
|||||||
$this->configureIO($input, $output);
|
$this->configureIO($input, $output);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$e = null;
|
|
||||||
$exitCode = $this->doRun($input, $output);
|
$exitCode = $this->doRun($input, $output);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
}
|
|
||||||
|
|
||||||
if (null !== $e) {
|
|
||||||
if (!$this->catchExceptions) {
|
if (!$this->catchExceptions) {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($output instanceof ConsoleOutputInterface) {
|
$renderException($e);
|
||||||
$this->renderException($e, $output->getErrorOutput());
|
|
||||||
} else {
|
|
||||||
$this->renderException($e, $output);
|
|
||||||
}
|
|
||||||
|
|
||||||
$exitCode = $e->getCode();
|
$exitCode = $e->getCode();
|
||||||
if (is_numeric($exitCode)) {
|
if (is_numeric($exitCode)) {
|
||||||
@ -150,6 +162,12 @@ class Application
|
|||||||
} else {
|
} else {
|
||||||
$exitCode = 1;
|
$exitCode = 1;
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
if (!$phpHandler) {
|
||||||
|
restore_exception_handler();
|
||||||
|
} elseif (!$debugHandler) {
|
||||||
|
$phpHandler[0]->setExceptionHandler(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->autoExit) {
|
if ($this->autoExit) {
|
||||||
|
@ -217,12 +217,16 @@ EOF;
|
|||||||
{$namespaceLine}
|
{$namespaceLine}
|
||||||
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
|
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
|
||||||
|
|
||||||
if (!class_exists(\\Container{$hash}\\{$options['class']}::class, false)) {
|
if (\\class_exists(\\Container{$hash}\\{$options['class']}::class, false)) {
|
||||||
require __DIR__.'/Container{$hash}/{$options['class']}.php';
|
// no-op
|
||||||
|
} elseif (!include __DIR__.'/Container{$hash}/{$options['class']}.php') {
|
||||||
|
touch(__DIR__.'/Container{$hash}.legacy');
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!class_exists({$options['class']}::class, false)) {
|
if (!\\class_exists({$options['class']}::class, false)) {
|
||||||
class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false);
|
\\class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new \\Container{$hash}\\{$options['class']}();
|
return new \\Container{$hash}\\{$options['class']}();
|
||||||
@ -428,13 +432,13 @@ EOTXT;
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) {
|
foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) {
|
||||||
$code .= sprintf(" require_once %s;\n", $file);
|
$code .= sprintf(" include_once %s;\n", $file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($inlinedDefinitions as $def) {
|
foreach ($inlinedDefinitions as $def) {
|
||||||
if ($file = $def->getFile()) {
|
if ($file = $def->getFile()) {
|
||||||
$code .= sprintf(" require_once %s;\n", $this->dumpValue($file));
|
$code .= sprintf(" include_once %s;\n", $this->dumpValue($file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1233,7 +1237,7 @@ EOF;
|
|||||||
foreach ($lineage as $file) {
|
foreach ($lineage as $file) {
|
||||||
if (!isset($this->inlinedRequires[$file])) {
|
if (!isset($this->inlinedRequires[$file])) {
|
||||||
$this->inlinedRequires[$file] = true;
|
$this->inlinedRequires[$file] = true;
|
||||||
$code .= sprintf(" require_once %s;\n", $file);
|
$code .= sprintf(" include_once %s;\n", $file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,7 +297,7 @@ class ProjectServiceContainer extends Container
|
|||||||
*/
|
*/
|
||||||
protected function getMethodCall1Service()
|
protected function getMethodCall1Service()
|
||||||
{
|
{
|
||||||
require_once '%path%foo.php';
|
include_once '%path%foo.php';
|
||||||
|
|
||||||
$this->services['method_call1'] = $instance = new \Bar\FooClass();
|
$this->services['method_call1'] = $instance = new \Bar\FooClass();
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
|
|||||||
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
|
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
|
||||||
// Returns the public 'method_call1' shared service.
|
// Returns the public 'method_call1' shared service.
|
||||||
|
|
||||||
require_once ($this->targetDirs[0].'/Fixtures/includes/foo.php');
|
include_once ($this->targetDirs[0].'/Fixtures/includes/foo.php');
|
||||||
|
|
||||||
$this->services['method_call1'] = $instance = new \Bar\FooClass();
|
$this->services['method_call1'] = $instance = new \Bar\FooClass();
|
||||||
|
|
||||||
@ -471,12 +471,16 @@ class ProjectServiceContainer extends Container
|
|||||||
|
|
||||||
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
|
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
|
||||||
|
|
||||||
if (!class_exists(\Container%s\ProjectServiceContainer::class, false)) {
|
if (\class_exists(\Container%s\ProjectServiceContainer::class, false)) {
|
||||||
require __DIR__.'/Container%s/ProjectServiceContainer.php';
|
// no-op
|
||||||
|
} elseif (!include __DIR__.'/Container%s/ProjectServiceContainer.php') {
|
||||||
|
touch(__DIR__.'/Container%s.legacy');
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!class_exists(ProjectServiceContainer::class, false)) {
|
if (!\class_exists(ProjectServiceContainer::class, false)) {
|
||||||
class_alias(\Container%s\ProjectServiceContainer::class, ProjectServiceContainer::class, false);
|
\class_alias(\Container%s\ProjectServiceContainer::class, ProjectServiceContainer::class, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new \Container%s\ProjectServiceContainer();
|
return new \Container%s\ProjectServiceContainer();
|
||||||
|
@ -307,7 +307,7 @@ class ProjectServiceContainer extends Container
|
|||||||
*/
|
*/
|
||||||
protected function getMethodCall1Service()
|
protected function getMethodCall1Service()
|
||||||
{
|
{
|
||||||
require_once '%path%foo.php';
|
include_once '%path%foo.php';
|
||||||
|
|
||||||
$this->services['method_call1'] = $instance = new \Bar\FooClass();
|
$this->services['method_call1'] = $instance = new \Bar\FooClass();
|
||||||
|
|
||||||
|
@ -46,10 +46,10 @@ class ProjectServiceContainer extends Container
|
|||||||
|
|
||||||
$this->aliases = array();
|
$this->aliases = array();
|
||||||
|
|
||||||
require_once $this->targetDirs[1].'/includes/HotPath/I1.php';
|
include_once $this->targetDirs[1].'/includes/HotPath/I1.php';
|
||||||
require_once $this->targetDirs[1].'/includes/HotPath/P1.php';
|
include_once $this->targetDirs[1].'/includes/HotPath/P1.php';
|
||||||
require_once $this->targetDirs[1].'/includes/HotPath/T1.php';
|
include_once $this->targetDirs[1].'/includes/HotPath/T1.php';
|
||||||
require_once $this->targetDirs[1].'/includes/HotPath/C1.php';
|
include_once $this->targetDirs[1].'/includes/HotPath/C1.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRemovedIds()
|
public function getRemovedIds()
|
||||||
@ -104,8 +104,8 @@ class ProjectServiceContainer extends Container
|
|||||||
*/
|
*/
|
||||||
protected function getC2Service()
|
protected function getC2Service()
|
||||||
{
|
{
|
||||||
require_once $this->targetDirs[1].'/includes/HotPath/C2.php';
|
include_once $this->targetDirs[1].'/includes/HotPath/C2.php';
|
||||||
require_once $this->targetDirs[1].'/includes/HotPath/C3.php';
|
include_once $this->targetDirs[1].'/includes/HotPath/C3.php';
|
||||||
|
|
||||||
return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3()) && false ?: '_'});
|
return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3()) && false ?: '_'});
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ class ProjectServiceContainer extends Container
|
|||||||
*/
|
*/
|
||||||
protected function getC3Service()
|
protected function getC3Service()
|
||||||
{
|
{
|
||||||
require_once $this->targetDirs[1].'/includes/HotPath/C3.php';
|
include_once $this->targetDirs[1].'/includes/HotPath/C3.php';
|
||||||
|
|
||||||
return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3();
|
return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3();
|
||||||
}
|
}
|
||||||
|
@ -582,7 +582,13 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
|
|||||||
$cacheDir = $this->warmupDir ?: $this->getCacheDir();
|
$cacheDir = $this->warmupDir ?: $this->getCacheDir();
|
||||||
$cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug);
|
$cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug);
|
||||||
if ($fresh = $cache->isFresh()) {
|
if ($fresh = $cache->isFresh()) {
|
||||||
$this->container = require $cache->getPath();
|
// Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors
|
||||||
|
$errorLevel = error_reporting(\E_ALL ^ \E_WARNING);
|
||||||
|
try {
|
||||||
|
$this->container = include $cache->getPath();
|
||||||
|
} finally {
|
||||||
|
error_reporting($errorLevel);
|
||||||
|
}
|
||||||
$fresh = \is_object($this->container);
|
$fresh = \is_object($this->container);
|
||||||
}
|
}
|
||||||
if (!$fresh) {
|
if (!$fresh) {
|
||||||
@ -590,7 +596,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
|
|||||||
$collectedLogs = array();
|
$collectedLogs = array();
|
||||||
$previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) {
|
$previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) {
|
||||||
if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
|
if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
|
||||||
return $previousHandler ? $previousHandler($type, $message, $file, $line) : false;
|
return $previousHandler ? $previousHandler($type & ~E_WARNING, $message, $file, $line) : E_WARNING === $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($collectedLogs[$message])) {
|
if (isset($collectedLogs[$message])) {
|
||||||
@ -617,23 +623,27 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
|
|||||||
'count' => 1,
|
'count' => 1,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
$errorLevel = error_reporting(\E_ALL ^ \E_WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$container = null;
|
$container = null;
|
||||||
$container = $this->buildContainer();
|
$container = $this->buildContainer();
|
||||||
$container->compile();
|
$container->compile();
|
||||||
|
|
||||||
|
$oldContainer = file_exists($cache->getPath()) && is_object($oldContainer = include $cache->getPath()) ? new \ReflectionClass($oldContainer) : false;
|
||||||
} finally {
|
} finally {
|
||||||
if ($this->debug) {
|
if ($this->debug) {
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
|
|
||||||
file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs)));
|
file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs)));
|
||||||
file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
|
file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
|
||||||
|
} else {
|
||||||
|
error_reporting($errorLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$oldContainer = file_exists($cache->getPath()) && is_object($oldContainer = @include $cache->getPath()) ? new \ReflectionClass($oldContainer) : false;
|
|
||||||
|
|
||||||
$this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());
|
$this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());
|
||||||
$this->container = require $cache->getPath();
|
$this->container = require $cache->getPath();
|
||||||
}
|
}
|
||||||
@ -649,13 +659,13 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
|
|||||||
// old container files are not removed immediately,
|
// old container files are not removed immediately,
|
||||||
// but on a next dump of the container.
|
// but on a next dump of the container.
|
||||||
$oldContainerDir = dirname($oldContainer->getFileName());
|
$oldContainerDir = dirname($oldContainer->getFileName());
|
||||||
foreach (glob(dirname($oldContainerDir).'/*.legacyContainer') as $legacyContainer) {
|
foreach (glob(dirname($oldContainerDir).'/*.legacy') as $legacyContainer) {
|
||||||
if ($oldContainerDir.'.legacyContainer' !== $legacyContainer && @unlink($legacyContainer)) {
|
if ($oldContainerDir.'.legacy' !== $legacyContainer && @unlink($legacyContainer)) {
|
||||||
(new Filesystem())->remove(substr($legacyContainer, 0, -16));
|
(new Filesystem())->remove(substr($legacyContainer, 0, -16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
touch($oldContainerDir.'.legacyContainer');
|
touch($oldContainerDir.'.legacy');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->container->has('cache_warmer')) {
|
if ($this->container->has('cache_warmer')) {
|
||||||
|
@ -833,7 +833,7 @@ EOF;
|
|||||||
|
|
||||||
$this->assertTrue(get_class($kernel->getContainer()) !== $containerClass);
|
$this->assertTrue(get_class($kernel->getContainer()) !== $containerClass);
|
||||||
$this->assertFileExists($containerFile);
|
$this->assertFileExists($containerFile);
|
||||||
$this->assertFileExists(dirname($containerFile).'.legacyContainer');
|
$this->assertFileExists(dirname($containerFile).'.legacy');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testKernelPass()
|
public function testKernelPass()
|
||||||
|
Reference in New Issue
Block a user