FestinHegre/vendor/symfony/runtime/SymfonyRuntime.php
2024-09-26 17:26:04 +02:00

227 lines
9.0 KiB
PHP

<?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;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArgvInput;
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\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(MissingDotenv::class, false) || class_exists(Dotenv::class) || class_exists(MissingDotenv::class);
/**
* Knows the basic conventions to run Symfony apps.
*
* 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.)
* - "dotenv_overload" to tell Dotenv to override existing vars
*
* 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 registered in debug mode.
*
* On top of the base arguments provided by GenericRuntime,
* this runtime can feed the app-callable with arguments of type:
* - Request from "symfony/http-foundation" if the component is installed;
* - Application, Command, InputInterface and/or OutputInterface
* from "symfony/console" if the component is installed.
*
* This runtime can handle app-callables that return instances of either:
* - HttpKernelInterface,
* - Response,
* - Application,
* - Command,
* - int|string|null as handled by GenericRuntime.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class SymfonyRuntime extends GenericRuntime
{
private readonly ArgvInput $input;
private readonly ConsoleOutput $output;
private readonly Application $console;
private readonly Command $command;
/**
* @param array {
* debug?: ?bool,
* env?: ?string,
* disable_dotenv?: ?bool,
* project_dir?: ?string,
* prod_envs?: ?string[],
* dotenv_path?: ?string,
* test_envs?: ?string[],
* use_putenv?: ?bool,
* runtimes?: ?array,
* error_handler?: string|false,
* env_var_name?: string,
* debug_var_name?: string,
* dotenv_overload?: ?bool,
* } $options
*/
public function __construct(array $options = [])
{
$envKey = $options['env_var_name'] ??= 'APP_ENV';
$debugKey = $options['debug_var_name'] ??= 'APP_DEBUG';
if (isset($options['env'])) {
$_SERVER[$envKey] = $options['env'];
} elseif (isset($_SERVER['argv']) && class_exists(ArgvInput::class)) {
$this->options = $options;
$this->getInput();
}
if (!($options['disable_dotenv'] ?? false) && isset($options['project_dir']) && !class_exists(MissingDotenv::class, false)) {
(new Dotenv($envKey, $debugKey))
->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['dotenv_overload'] ?? false);
if (isset($this->input) && ($options['dotenv_overload'] ?? false)) {
if ($this->input->getParameterOption(['--env', '-e'], $_SERVER[$envKey], true) !== $_SERVER[$envKey]) {
throw new \LogicException(sprintf('Cannot use "--env" or "-e" when the "%s" file defines "%s" and the "dotenv_overload" runtime option is true.', $options['dotenv_path'] ?? '.env', $envKey));
}
if ($_SERVER[$debugKey] && $this->input->hasParameterOption('--no-debug', true)) {
putenv($debugKey.'='.$_SERVER[$debugKey] = $_ENV[$debugKey] = '0');
}
}
$options['debug'] ??= '1' === $_SERVER[$debugKey];
$options['disable_dotenv'] = true;
} else {
$_SERVER[$envKey] ??= $_ENV[$envKey] ?? 'dev';
$_SERVER[$debugKey] ??= $_ENV[$debugKey] ?? !\in_array($_SERVER[$envKey], (array) ($options['prod_envs'] ?? ['prod']), true);
}
$options['error_handler'] ??= SymfonyErrorHandler::class;
parent::__construct($options);
}
public function getRunner(?object $application): RunnerInterface
{
if ($application instanceof HttpKernelInterface) {
return new HttpKernelRunner($application, Request::createFromGlobals(), $this->options['debug'] ?? false);
}
if ($application instanceof Response) {
return new ResponseRunner($application);
}
if ($application instanceof Command) {
$console = $this->console ??= new Application();
$console->setName($application->getName() ?: $console->getName());
if (!$application->getName() || !$console->has($application->getName())) {
$application->setName($_SERVER['argv'][0]);
$console->add($application);
}
$console->setDefaultCommand($application->getName(), true);
$console->getDefinition()->addOptions($application->getDefinition()->getOptions());
return $this->getRunner($console);
}
if ($application instanceof Application) {
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
echo 'Warning: The console should be invoked via the CLI version of PHP, not the '.\PHP_SAPI.' SAPI'.\PHP_EOL;
}
set_time_limit(0);
$defaultEnv = !isset($this->options['env']) ? ($_SERVER[$this->options['env_var_name']] ?? 'dev') : null;
$output = $this->output ??= new ConsoleOutput();
return new ConsoleApplicationRunner($application, $defaultEnv, $this->getInput(), $output);
}
if (isset($this->command)) {
$this->getInput()->bind($this->command->getDefinition());
}
return parent::getRunner($application);
}
protected function getArgument(\ReflectionParameter $parameter, ?string $type): mixed
{
return match ($type) {
Request::class => Request::createFromGlobals(),
InputInterface::class => $this->getInput(),
OutputInterface::class => $this->output ??= new ConsoleOutput(),
Application::class => $this->console ??= new Application(),
Command::class => $this->command ??= new Command(),
default => parent::getArgument($parameter, $type),
};
}
protected static function register(GenericRuntime $runtime): GenericRuntime
{
$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 (isset($this->input)) {
return $this->input;
}
$input = new ArgvInput();
if (isset($this->options['env'])) {
return $this->input = $input;
}
if (null !== $env = $input->getParameterOption(['--env', '-e'], null, true)) {
putenv($this->options['env_var_name'].'='.$_SERVER[$this->options['env_var_name']] = $_ENV[$this->options['env_var_name']] = $env);
}
if ($input->hasParameterOption('--no-debug', true)) {
putenv($this->options['debug_var_name'].'='.$_SERVER[$this->options['debug_var_name']] = $_ENV[$this->options['debug_var_name']] = '0');
}
return $this->input = $input;
}
}