feature #40513 [Runtime] make GenericRuntime ... generic (nicolas-grekas)
This PR was merged into the 5.3-dev branch.
Discussion
----------
[Runtime] make GenericRuntime ... generic
| Q | A
| ------------- | ---
| Branch? | 5.x
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets | -
| License | MIT
| Doc PR | -
This PR will allow #40436 to move to https://github.com/symfony/psr-http-message-bridge
For the record, it builds on a prototype I wrote almost one year ago at https://github.com/tchwork/bootstrapper.
This PR makes the `GenericRuntime` implementation able to auto-discover runtime implementations for specific types.
It uses the autoloader for the discovery: when a closure-app requires or returns a type `Vendor\Foo`, it will use a convention and check if the class `Symfony\Runtime\Vendor\FooRuntime` exists. If yes, it will use it to resolve the corresponding type. Such runtime classes have to extend `GenericRuntime` so that they can use the protected API it provides. This requirement is aligned with the fact that the very convention proposed here is an implementation detail that works when using a `GenericRuntime` as the main runtime (This behavior can be overridden by providing explicit entries in the new `runtimes` option when booting the `GenericRuntime`.)
`SymfonyRuntime` can be used as both the main runtime or a type-specific runtime:
- when used as the main runtime, it configures the typical global-state for Symfony and has a fast codepath for Symfony types, while still being generic.
- it can also be used in another runtime as a way to resolve Symfony types (would typically be useful to #40436 for running Console apps in a PSR-based web app.)
Commits
-------
33e371e24d
[Runtime] make GenericRuntime ... generic
This commit is contained in:
commit
6c0786b579
@ -38,6 +38,8 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\HttpKernel\UriSigner;
|
||||
use Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner;
|
||||
use Symfony\Component\Runtime\Runner\Symfony\ResponseRunner;
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
use Symfony\Component\String\LazyString;
|
||||
use Symfony\Component\String\Slugger\AsciiSlugger;
|
||||
@ -79,6 +81,8 @@ return static function (ContainerConfigurator $container) {
|
||||
service('argument_resolver'),
|
||||
])
|
||||
->tag('container.hot_path')
|
||||
->tag('container.preload', ['class' => HttpKernelRunner::class])
|
||||
->tag('container.preload', ['class' => ResponseRunner::class])
|
||||
->tag('container.preload', ['class' => SymfonyRuntime::class])
|
||||
->alias(HttpKernelInterface::class, 'http_kernel')
|
||||
|
||||
|
@ -22,8 +22,12 @@ class_exists(ClosureResolver::class);
|
||||
/**
|
||||
* A runtime to do bare-metal PHP without using superglobals.
|
||||
*
|
||||
* One option named "debug" is supported; it toggles displaying errors
|
||||
* and defaults to the "APP_ENV" environment variable.
|
||||
* It supports the following options:
|
||||
* - "debug" toggles displaying errors and defaults
|
||||
* to the "APP_DEBUG" environment variable;
|
||||
* - "runtimes" maps types to a GenericRuntime implementation
|
||||
* that knows how to deal with each of them;
|
||||
* - "error_handler" defines the class to use to handle PHP errors.
|
||||
*
|
||||
* The app-callable can declare arguments among either:
|
||||
* - "array $context" to get a local array similar to $_SERVER;
|
||||
@ -42,42 +46,48 @@ class_exists(ClosureResolver::class);
|
||||
*/
|
||||
class GenericRuntime implements RuntimeInterface
|
||||
{
|
||||
private $debug;
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @param array {
|
||||
* debug?: ?bool,
|
||||
* runtimes?: ?array,
|
||||
* error_handler?: string|false,
|
||||
* } $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->debug = $options['debug'] ?? $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? true;
|
||||
$debug = $options['debug'] ?? $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? true;
|
||||
|
||||
if (!\is_bool($this->debug)) {
|
||||
$this->debug = filter_var($this->debug, \FILTER_VALIDATE_BOOLEAN);
|
||||
if (!\is_bool($debug)) {
|
||||
$debug = filter_var($debug, \FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
if ($this->debug) {
|
||||
if ($debug) {
|
||||
umask(0000);
|
||||
$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '1';
|
||||
$errorHandler = new BasicErrorHandler($this->debug);
|
||||
set_error_handler($errorHandler);
|
||||
|
||||
if (false !== $errorHandler = ($options['error_handler'] ?? BasicErrorHandler::class)) {
|
||||
$errorHandler::register($debug);
|
||||
$options['error_handler'] = false;
|
||||
}
|
||||
} else {
|
||||
$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0';
|
||||
}
|
||||
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getResolver(callable $callable): ResolverInterface
|
||||
public function getResolver(callable $callable, \ReflectionFunction $reflector = null): ResolverInterface
|
||||
{
|
||||
if (!$callable instanceof \Closure) {
|
||||
$callable = \Closure::fromCallable($callable);
|
||||
}
|
||||
|
||||
$function = new \ReflectionFunction($callable);
|
||||
$parameters = $function->getParameters();
|
||||
|
||||
$parameters = ($reflector ?? new \ReflectionFunction($callable))->getParameters();
|
||||
$arguments = function () use ($parameters) {
|
||||
$arguments = [];
|
||||
|
||||
@ -95,7 +105,7 @@ class GenericRuntime implements RuntimeInterface
|
||||
return $arguments;
|
||||
};
|
||||
|
||||
if ($this->debug) {
|
||||
if ($_SERVER['APP_DEBUG']) {
|
||||
return new DebugClosureResolver($callable, $arguments);
|
||||
}
|
||||
|
||||
@ -115,15 +125,19 @@ class GenericRuntime implements RuntimeInterface
|
||||
return $application;
|
||||
}
|
||||
|
||||
if (!$application instanceof \Closure) {
|
||||
if ($runtime = $this->resolveRuntime(\get_class($application))) {
|
||||
return $runtime->getRunner($application);
|
||||
}
|
||||
|
||||
if (!\is_callable($application)) {
|
||||
throw new \LogicException(sprintf('"%s" doesn\'t know how to handle apps of type "%s".', get_debug_type($this), get_debug_type($application)));
|
||||
}
|
||||
|
||||
if (!$application instanceof \Closure) {
|
||||
$application = \Closure::fromCallable($application);
|
||||
}
|
||||
|
||||
if ($this->debug && ($r = new \ReflectionFunction($application)) && $r->getNumberOfRequiredParameters()) {
|
||||
if ($_SERVER['APP_DEBUG'] && ($r = new \ReflectionFunction($application)) && $r->getNumberOfRequiredParameters()) {
|
||||
throw new \ArgumentCountError(sprintf('Zero argument should be required by the runner callable, but at least one is in "%s" on line "%d.', $r->getFileName(), $r->getStartLine()));
|
||||
}
|
||||
|
||||
@ -163,8 +177,56 @@ class GenericRuntime implements RuntimeInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (!$runtime = $this->getRuntime($type)) {
|
||||
$r = $parameter->getDeclaringFunction();
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Cannot resolve argument "%s $%s" in "%s" on line "%d": "%s" supports only arguments "array $context", "array $argv" and "array $request".', $type, $parameter->name, $r->getFileName(), $r->getStartLine(), get_debug_type($this)));
|
||||
throw new \InvalidArgumentException(sprintf('Cannot resolve argument "%s $%s" in "%s" on line "%d": "%s" supports only arguments "array $context", "array $argv" and "array $request", or a runtime named "Symfony\Runtime\%1$sRuntime".', $type, $parameter->name, $r->getFileName(), $r->getStartLine(), get_debug_type($this)));
|
||||
}
|
||||
|
||||
return $runtime->getArgument($parameter, $type);
|
||||
}
|
||||
|
||||
protected static function register(self $runtime): self
|
||||
{
|
||||
return $runtime;
|
||||
}
|
||||
|
||||
private function getRuntime(string $type): ?self
|
||||
{
|
||||
if (null === $runtime = ($this->options['runtimes'][$type] ?? null)) {
|
||||
$runtime = 'Symfony\Runtime\\'.$type.'Runtime';
|
||||
$runtime = class_exists($runtime) ? $runtime : $this->options['runtimes'][$type] = false;
|
||||
}
|
||||
|
||||
if (\is_string($runtime)) {
|
||||
$runtime = $runtime::register($this);
|
||||
}
|
||||
|
||||
if ($this === $runtime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $runtime ?: null;
|
||||
}
|
||||
|
||||
private function resolveRuntime(string $class): ?self
|
||||
{
|
||||
if ($runtime = $this->getRuntime($class)) {
|
||||
return $runtime;
|
||||
}
|
||||
|
||||
foreach (class_parents($class) as $type) {
|
||||
if ($runtime = $this->getRuntime($type)) {
|
||||
return $runtime;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (class_implements($class) as $type) {
|
||||
if ($runtime = $this->getRuntime($type)) {
|
||||
return $runtime;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ namespace Symfony\Component\Runtime\Internal;
|
||||
*/
|
||||
class BasicErrorHandler
|
||||
{
|
||||
public function __construct(bool $debug)
|
||||
public static function register(bool $debug): void
|
||||
{
|
||||
error_reporting(-1);
|
||||
|
||||
@ -32,10 +32,11 @@ class BasicErrorHandler
|
||||
if (0 <= ini_get('zend.assertions')) {
|
||||
ini_set('zend.assertions', 1);
|
||||
ini_set('assert.active', $debug);
|
||||
ini_set('assert.bail', 0);
|
||||
ini_set('assert.warning', 0);
|
||||
ini_set('assert.exception', 1);
|
||||
}
|
||||
|
||||
set_error_handler(new self());
|
||||
}
|
||||
|
||||
public function __invoke(int $type, string $message, string $file, int $line): bool
|
||||
|
@ -102,14 +102,12 @@ class ComposerPlugin implements PluginInterface, EventSubscriberInterface
|
||||
throw new \InvalidArgumentException(sprintf('Class "%s" listed under "extra.runtime.class" in your composer.json file '.(class_exists($runtimeClass) ? 'should implement "%s".' : 'not found.'), $runtimeClass, RuntimeInterface::class));
|
||||
}
|
||||
|
||||
if (!\is_array($runtimeOptions = $extra['options'] ?? [])) {
|
||||
throw new \InvalidArgumentException('The "extra.runtime.options" entry in your composer.json file must be an array.');
|
||||
}
|
||||
unset($extra['class'], $extra['autoload_template']);
|
||||
|
||||
$code = strtr(file_get_contents($autoloadTemplate), [
|
||||
'%project_dir%' => $projectDir,
|
||||
'%runtime_class%' => var_export($runtimeClass, true),
|
||||
'%runtime_options%' => '['.substr(var_export($runtimeOptions, true), 7, -1)." 'project_dir' => {$projectDir},\n]",
|
||||
'%runtime_options%' => '['.substr(var_export($extra, true), 7, -1)." 'project_dir' => {$projectDir},\n]",
|
||||
]);
|
||||
|
||||
file_put_contents(substr_replace($autoloadFile, '_runtime', -4, 0), $code);
|
||||
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\Console;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ApplicationRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\Console\Command;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class CommandRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\Console\Input;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class InputInterfaceRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\Console\Output;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class OutputInterfaceRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\HttpFoundation;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class RequestRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\HttpFoundation;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ResponseRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\HttKernel;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class HttpKernelInterfaceRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime\Internal;
|
||||
|
||||
use Symfony\Component\ErrorHandler\BufferingLogger;
|
||||
use Symfony\Component\ErrorHandler\DebugClassLoader;
|
||||
use Symfony\Component\ErrorHandler\ErrorHandler;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class SymfonyErrorHandler
|
||||
{
|
||||
public static function register(bool $debug): void
|
||||
{
|
||||
BasicErrorHandler::register($debug);
|
||||
|
||||
if (class_exists(ErrorHandler::class)) {
|
||||
DebugClassLoader::enable();
|
||||
restore_error_handler();
|
||||
ErrorHandler::register(new ErrorHandler(new BufferingLogger(), true));
|
||||
}
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ interface RuntimeInterface
|
||||
*
|
||||
* The callable itself should return an object that represents the application to pass to the getRunner() method.
|
||||
*/
|
||||
public function getResolver(callable $callable): ResolverInterface;
|
||||
public function getResolver(callable $callable, \ReflectionFunction $reflector = null): ResolverInterface;
|
||||
|
||||
/**
|
||||
* Returns a callable that knows how to run the passed object and that returns its exit status as int.
|
||||
|
@ -18,38 +18,35 @@ use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
use Symfony\Component\ErrorHandler\Debug;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\Runtime\Internal\MissingDotenv;
|
||||
use Symfony\Component\Runtime\Internal\SymfonyErrorHandler;
|
||||
use Symfony\Component\Runtime\Runner\Symfony\ConsoleApplicationRunner;
|
||||
use Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner;
|
||||
use Symfony\Component\Runtime\Runner\Symfony\ResponseRunner;
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(ResponseRunner::class);
|
||||
class_exists(HttpKernelRunner::class);
|
||||
class_exists(MissingDotenv::class, false) || class_exists(Dotenv::class) || class_exists(MissingDotenv::class);
|
||||
|
||||
/**
|
||||
* Knows the basic conventions to run Symfony apps.
|
||||
*
|
||||
* Accepts the following options:
|
||||
* - "debug" to toggle debugging features
|
||||
* - "env" to define the name of the environment the app runs in
|
||||
* - "disable_dotenv" to disable looking for .env files
|
||||
* - "dotenv_path" to define the path of dot-env files - defaults to ".env"
|
||||
* - "prod_envs" to define the names of the production envs - defaults to ["prod"]
|
||||
* - "test_envs" to define the names of the test envs - defaults to ["test"]
|
||||
* In addition to the options managed by GenericRuntime, it accepts the following options:
|
||||
* - "env" to define the name of the environment the app runs in;
|
||||
* - "disable_dotenv" to disable looking for .env files;
|
||||
* - "dotenv_path" to define the path of dot-env files - defaults to ".env";
|
||||
* - "prod_envs" to define the names of the production envs - defaults to ["prod"];
|
||||
* - "test_envs" to define the names of the test envs - defaults to ["test"];
|
||||
* - "use_putenv" to tell Dotenv to set env vars using putenv() (NOT RECOMMENDED.)
|
||||
*
|
||||
* When these options are not defined, they will fallback:
|
||||
* - to reading the "APP_DEBUG" and "APP_ENV" environment variables;
|
||||
* - to parsing the "--env|-e" and "--no-debug" command line arguments
|
||||
* if the "symfony/console" component is installed.
|
||||
* When the "debug" / "env" options are not defined, they will fallback to the
|
||||
* "APP_DEBUG" / "APP_ENV" environment variables, and to the "--env|-e" / "--no-debug"
|
||||
* command line arguments if "symfony/console" is installed.
|
||||
*
|
||||
* When the "symfony/dotenv" component is installed, .env files are loaded.
|
||||
* When "symfony/error-handler" is installed, it is registred in debug mode.
|
||||
* When "symfony/error-handler" is installed, it is registered in debug mode.
|
||||
*
|
||||
* On top of the base arguments provided by GenericRuntime,
|
||||
* this runtime can feed the app-callable with arguments of type:
|
||||
@ -74,7 +71,6 @@ class SymfonyRuntime extends GenericRuntime
|
||||
private $output;
|
||||
private $console;
|
||||
private $command;
|
||||
private $env;
|
||||
|
||||
/**
|
||||
* @param array {
|
||||
@ -85,31 +81,32 @@ class SymfonyRuntime extends GenericRuntime
|
||||
* prod_envs?: ?string[],
|
||||
* dotenv_path?: ?string,
|
||||
* test_envs?: ?string[],
|
||||
* use_putenv?: ?bool,
|
||||
* runtimes?: ?array,
|
||||
* error_handler?: string|false,
|
||||
* } $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->env = $options['env'] ?? null;
|
||||
$_SERVER['APP_ENV'] = $options['env'] ?? $_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null;
|
||||
|
||||
if (isset($_SERVER['argv']) && null === $this->env && class_exists(ArgvInput::class)) {
|
||||
if (isset($_SERVER['argv']) && !isset($options['env']) && class_exists(ArgvInput::class)) {
|
||||
$this->options = $options;
|
||||
$this->getInput();
|
||||
}
|
||||
|
||||
$_SERVER['APP_ENV'] = $options['env'] ?? $_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? 'dev';
|
||||
|
||||
if (!($options['disable_dotenv'] ?? false) && isset($options['project_dir']) && !class_exists(MissingDotenv::class, false)) {
|
||||
(new Dotenv())
|
||||
->setProdEnvs((array) ($options['prod_envs'] ?? ['prod']))
|
||||
->usePutenv($options['use_putenv'] ?? false)
|
||||
->bootEnv($options['project_dir'].'/'.($options['dotenv_path'] ?? '.env'), 'dev', (array) ($options['test_envs'] ?? ['test']));
|
||||
$options['debug'] ?? $options['debug'] = '1' === $_SERVER['APP_DEBUG'];
|
||||
$options['disable_dotenv'] = true;
|
||||
}
|
||||
|
||||
$options['error_handler'] ?? $options['error_handler'] = SymfonyErrorHandler::class;
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
if ($_SERVER['APP_DEBUG'] && class_exists(Debug::class)) {
|
||||
restore_error_handler();
|
||||
umask(0000);
|
||||
Debug::enable();
|
||||
}
|
||||
}
|
||||
|
||||
public function getRunner(?object $application): RunnerInterface
|
||||
@ -142,7 +139,7 @@ class SymfonyRuntime extends GenericRuntime
|
||||
}
|
||||
|
||||
set_time_limit(0);
|
||||
$defaultEnv = null === $this->env ? ($_SERVER['APP_ENV'] ?? 'dev') : null;
|
||||
$defaultEnv = !isset($this->options['env']) ? ($_SERVER['APP_ENV'] ?? 'dev') : null;
|
||||
$output = $this->output ?? $this->output = new ConsoleOutput();
|
||||
|
||||
return new ConsoleApplicationRunner($application, $defaultEnv, $this->getInput(), $output);
|
||||
@ -180,6 +177,23 @@ class SymfonyRuntime extends GenericRuntime
|
||||
return parent::getArgument($parameter, $type);
|
||||
}
|
||||
|
||||
protected static function register(parent $runtime): parent
|
||||
{
|
||||
$self = new self($runtime->options + ['runtimes' => []]);
|
||||
$self->options['runtimes'] += [
|
||||
HttpKernelInterface::class => $self,
|
||||
Request::class => $self,
|
||||
Response::class => $self,
|
||||
Application::class => $self,
|
||||
Command::class => $self,
|
||||
InputInterface::class => $self,
|
||||
OutputInterface::class => $self,
|
||||
];
|
||||
$runtime->options = $self->options;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
private function getInput(): ArgvInput
|
||||
{
|
||||
if (null !== $this->input) {
|
||||
@ -188,7 +202,7 @@ class SymfonyRuntime extends GenericRuntime
|
||||
|
||||
$input = new ArgvInput();
|
||||
|
||||
if (null !== $this->env) {
|
||||
if (isset($this->options['env'])) {
|
||||
return $this->input = $input;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,8 @@ if (file_exists(dirname(__DIR__, 2).'/vendor/autoload.php')) {
|
||||
}
|
||||
|
||||
$app = require $_SERVER['SCRIPT_FILENAME'];
|
||||
$runtime = new SymfonyRuntime($_SERVER['APP_RUNTIME_OPTIONS']);
|
||||
$runtime = $_SERVER['APP_RUNTIME'] ?? SymfonyRuntime::class;
|
||||
$runtime = new $runtime($_SERVER['APP_RUNTIME_OPTIONS']);
|
||||
[$app, $args] = $runtime->getResolver($app)->resolve();
|
||||
exit($runtime->getRunner($app(...$args))->run());
|
||||
}
|
||||
|
19
src/Symfony/Component/Runtime/Tests/phpt/generic-request.php
Normal file
19
src/Symfony/Component/Runtime/Tests/phpt/generic-request.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\Runtime\GenericRuntime;
|
||||
use Symfony\Runtime\Symfony\Component\HttpFoundation\RequestRuntime;
|
||||
use Symfony\Runtime\Symfony\Component\HttpFoundation\ResponseRuntime;
|
||||
|
||||
$_SERVER['APP_RUNTIME'] = GenericRuntime::class;
|
||||
require __DIR__.'/autoload.php';
|
||||
|
||||
return function (Request $request, array $context) {
|
||||
echo class_exists(RequestRuntime::class, false) ? 'OK request runtime' : 'KO request runtime', "\n";
|
||||
|
||||
return new StreamedResponse(function () use ($context) {
|
||||
echo 'OK Request '.$context['SOME_VAR'], "\n";
|
||||
echo class_exists(ResponseRuntime::class, false) ? 'KO response runtime' : 'OK response runtime', "\n";
|
||||
});
|
||||
};
|
@ -0,0 +1,14 @@
|
||||
--TEST--
|
||||
Test Request/Response
|
||||
--INI--
|
||||
display_errors=1
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require $_SERVER['SCRIPT_FILENAME'] = __DIR__.'/generic-request.php';
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
OK request runtime
|
||||
OK Request foo_bar
|
||||
OK response runtime
|
@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
use Symfony\Component\Runtime\Runner\ClosureRunner;
|
||||
use Symfony\Component\Runtime\RunnerInterface;
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
require __DIR__.'/autoload.php';
|
||||
|
||||
|
@ -30,7 +30,10 @@
|
||||
"symfony/dotenv": "<5.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\Runtime\\": "" },
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Runtime\\": "",
|
||||
"Symfony\\Runtime\\Symfony\\Component\\": "Internal/"
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
|
Reference in New Issue
Block a user