Компонент Runtime
Дата обновления перевода: 2024-07-10
Компонент Runtime
Компонент Runtime (среда исполнения) отделяет логику самозагрузки от любого глобального состояния, чтобы убедиться в том, что приложение может работать со средами вроде PHP-PM, ReactPHP, Swoole, и др. без изменений.
Установка
1
$ composer require symfony/runtime
Note
Если вы устанавливаете этот компонент вне приложения Symfony, вам нужно
подключить файл vendor/autoload.php
в вашем коде для включения механизма
автозагрузки классов, предоставляемых Composer. Детальнее читайте в
этой статье.
Использование
Компонент Runtime извлекает большинство логики самозагрузки как так называемые
среды исполнения, позволяя вам писать фронт-контроллеры общим образом. Например,
компонент Runtime позволяет public/index.php
Symfony выглядеть так:
1 2 3 4 5 6 7 8
// public/index.php
use App\Kernel;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (array $context): Kernel {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};
Итак, как же работает этот фронт-контроллер? Вначале, специальный файл
autoload_runtime.php
автоматически создается плагином Composer в компоненте.
Этот файл выолняет следующую логику:
- Инстанциирует RuntimeInterface;
- Вызываемое (возвращенное
public/index.php
) передается Runtime, работа которого заключается в распознании аргументов (в этом примере:array $context
); - Затем, это вызываемое вызывается для получения приложения (
App\Kernel
); - Наконец, Runtime используется для запуска приложения (т.e. вызов
$kernel->handle(Request::createFromGlobals())->send()
).
Caution
Если вы используете опцию Composer --no-plugins
, файл autoload_runtime.php
не будет создан.
Если вы используете опцию Composer --no-scripts
, убедитесь в том, что ваша версия
Composer - >=2.1.3
; иначе файл autoload_runtime.php
не будет создан.
Чтобы создать консольное приложение, код самозагрузки будет выглядеть так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#!/usr/bin/env php
<?php
// bin/console
use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (array $context): Application {
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
// возвращение "Application" заставляет Runtime запустить Консольное
// приложение вместо HTTP Ядра
return new Application($kernel);
};
Выбор среды исполнения
Среда исполнения по умолчанию - SymfonyRuntime. Она отлично работает в большинстве приложений, работающих с веб-сервером, использующим PHP-FPM вроде Nginx или Apache.
Компонент также предоставляет GenericRuntime,
которые использует суперглобальные $_SERVER
, $_POST
, $_GET
, $_FILES
и
$_SESSION
. Вы можете также использовать пользовательскую среду исполнения (например,
чтобы интегрироваться с Swoole или AWS Lambda).
Используйте переменную окружения APP_RUNTIME
или укажите extra.runtime.class
в composer.json
, чтобы изменить класс Runtime:
1 2 3 4 5 6 7 8 9 10
{
"require": {
"...": "..."
},
"extra": {
"runtime": {
"class": "Symfony\\Component\\Runtime\\GenericRuntime"
}
}
}
Использование Runtime
Runtime отвечает за передачу аргументов в замыкание и запуск приложения, которое возвращается этим замыканием. SymfonyRuntime и GenericRuntime поддерживает определенное количество аргументов и разных приложений, которые вы можете использовать в ваших фронт-контроллерах.
Разрешимые аргументы
Замыкание, возвращённое из фронт-контроллера, может иметь 0 или более аргументов:
1 2 3 4 5 6 7 8 9 10
// public/index.php
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (InputInterface $input, OutputInterface $output): Application {
// ...
};
Следующие аргументы поддерживаются SymfonyRuntime
:
- Request
- Запрос, созданный из глобальностей.
- InputInterface
- Ввод для чтения опций и аргументов.
- OutputInterface
- Вывод консоли для отображения в CLI со стилем.
- Application
- Приложение для создания CLI-приложений.
- Command
-
Для создания CLI-приложений однострочной команды (используя
Command::setCode()
).
А эти аргументы поддерживаются как SymfonyRuntime
, так и
GenericRuntime
(важны как тип, так и имя переменной):
array $context
-
То же самое, что и
$_SERVER
+$_ENV
. array $argv
-
Аргументы, переданные команде (то же самое, что и
$_SERVER['argv']
). array $request
-
С ключами
query
,body
,files
иsession
.
Разрешимые приложения
Приложение, возвращенное замыканием ниже, является Ядром Symfony. Однако, поддерживается ряд различных приложений:
1 2 3 4 5 6 7 8
// public/index.php
use App\Kernel;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return static function (): Kernel {
return new Kernel('prod', false);
};
SymfonyRuntime
может работать с такими приложениями:
- HttpKernelInterface
- Приложение будет запущено с HttpKernelRunner, как и "стандартное" приложение Symfony.
- Response
-
Ответ будет выведен ResponseRunner:
1 2 3 4 5 6 7 8
// public/index.php use Symfony\Component\HttpFoundation\Response; require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return static function (): Response { return new Response('Hello world'); };
- Command
-
Для написания приложений одной строки. Это будет использовать ConsoleApplicationRunner:
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return static function (Command $command): Command { $command->setCode(static function (InputInterface $input, OutputInterface $output): void { $output->write('Hello World'); }); return $command; };
- Application
-
Полезно для консольных приложений с более, чем одной командой. Это будет использовать ConsoleApplicationRunner:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return static function (array $context): Application { $command = new Command('hello'); $command->setCode(static function (InputInterface $input, OutputInterface $output): void { $output->write('Hello World'); }); $app = new Application(); $app->add($command); $app->setDefaultCommand('hello', true); return $app; };
GenericRuntime
и SymfonyRuntime
также поддерживают такие общие
приложения:
- RunnerInterface
-
RunnerInterface
- это способ использовать пользовательское приложение с общим Runtime:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// public/index.php use Symfony\Component\Runtime\RunnerInterface; require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return static function (): RunnerInterface { return new class implements RunnerInterface { public function run(): int { echo 'Hello World'; return 0; } }; };
callable
-
Ваше "приложение" также может быть
callable
. Первое вызываемое вернет "приложение", а второе вызываемое будет самим "приложением":1 2 3 4 5 6 7 8 9 10 11 12
// public/index.php require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return static function (): callable { $app = static function(): int { echo 'Hello World'; return 0; }; return $app; };
void
-
Если вызываемое ничего не возвращает,
SymfonyRuntime
предположит, что все хорошо:1 2 3 4 5
require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return function (): void { echo 'Hello world'; };
Использование опций
Некоторое поведение сред исполнения можно изменять с помощью их опций. Они
могут быть установлены, используя переменную окружения APP_RUNTIME_OPTIONS
:
1 2 3 4 5 6 7
$_SERVER['APP_RUNTIME_OPTIONS'] = [
'project_dir' => '/var/task',
];
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
// ...
Вы также можете сконфигурировать extra.runtime.options
в composer.json
:
1 2 3 4 5 6 7 8 9 10
{
"require": {
"...": "..."
},
"extra": {
"runtime": {
"project_dir": "/var/task"
}
}
}
Затем обновите файлы Composer (например, запустив composer dump-autoload
),
чтобы файлы vendor/autoload_runtime.php
регенерировались с новой опцией.
Следующие опции поддерживаются SymfonyRuntime
:
env
(по умолчанию: переменная окруженияAPP_ENV
или"dev"
)- Для определения названия окружения, в котором выполняется приложение.
disable_dotenv
(по умолчанию:false
)-
Для отключения поиска файлов
.env
. dotenv_path
(по умолчанию:.env
)- Для определения пути файлов dot-env.
dotenv_overload
(default:false
)-
Чтобы сообщить Dotenv, надо ли переопределять переменные
.env
на.env.local
(или другие файлы.env.*
) use_putenv
-
Для указания Dotenv установить переменные окружения, используя
putenv()
(НЕ РЕКОМЕНДУЕТСЯ). prod_envs
(по умолчанию:["prod"]
)- Для определения названий окружений производства.
test_envs
(по умолчанию:["test"]
)- Для определения названий тестовых окружений.
Кроме этого, GenericRuntime
и SymfonyRuntime
также поддерживают
такие опции:
debug
(по умолчанию: значение переменной окружения, определенной опцией-
debug_var_name
(обычно -APP_DEBUG
), илиtrue
, если такая переменная окружения не определена) Переключает режим отладки приложений Symfony (например, чтобы отображать ошибки) runtimes
-
Маршрутизирует "типы приложений" к реализации
GenericRuntime
, которая знает, как работать с каждым из них. error_handler
(по умолчанию: BasicErrorHandler или SymfonyErrorHandler дляSymfonyRuntime
)- Определяет класс, который нужно использовать для обработки PHP-ошибок.
env_var_name
(по умолчанию:"APP_ENV"
)- Определяет имя переменной окружения, которая хранит имя окружения конфигурации environment , чтобы использовать при запуске приложения.
debug_var_name
(по умолчанию:"APP_DEBUG"
)- Определяет имя переменной окружения, которая хранит значение флажка режима отладки , чтобы использовать при запуске приложения.
Создайте свою собственную среду исполнения
Это продвинутая тема, которая описывает внутренний процесс компонента Runtime.
Использование компонента Runtime даст преимущества сопровождающим, так как логика самозагрузки может быть версионирована как часть обычного пакета. Если автор приложения решит использовать этот компонент, сопровождающий пакета класса Runtime будет иметь больше контроля и сможет исправлять ошибки и добавлять функции.
Компонент Runtime создан так, чтобы быть максимально общим и иметь возможность запускать любое приложение вне глобального состоания за 6 шагов:
- Основная точка входа возвращает вызываемое ("app"), оборачивающее приложение;
- Вызываемое приложения передается
RuntimeInterface::getResolver()
, который возвращает ResolverInterface. Этот разрешитель возвращает массив с вызываемым приложения (или что-то, что оформляет это вызываемое) с индексом 0 и все его разрешенные аргументы с индексом 1. - Вызываемое приложения вызывается с его аргументами, и вернет объект, представляющий собой приложение.
- Этот объект приложения передаетя
RuntimeInterface::getRunner()
, который возвращает RunnerInterface: экземпляр, который знает, как "запускать" объект приложения. RunnerInterface::run(object $application)
вызывается и возвращает статус-код выхода какint
.- PHP-движок завершает работу с этим статус-кодом.
При создании новой среды исполнения, важно подумать о двух вещах: Во-первых, какие аргументы будет использовать конечный пользователь? Во-вторых, как будет выглядеть приложние пользователя?
Например, представьте, что вы хотите создать среду исполнения для ReactPHP:
Какие аргументы будет использовать конечный пользователь?
Для общего приложения ReactPHP, обычно не требуется каких-то особенных аргументов. Это означает, что вы можете использовать GenericRuntime.
Как будет выглядеть приложение пользователя?
Также не существует типичного приложения React, поэтому вы можете захотеть положиться на интерфейсы PSR-15 для обработки HTTP-запроса.
Однако, приложению ReactPHP будет необходима некоторая логика для запуска. Эта логика добавляется в новый класс, реализующий RunnerInterface:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use React\EventLoop\Factory as ReactFactory;
use React\Http\Server as ReactHttpServer;
use React\Socket\Server as ReactSocketServer;
use Symfony\Component\Runtime\RunnerInterface;
class ReactPHPRunner implements RunnerInterface
{
public function __construct(
private RequestHandlerInterface $application,
private int $port,
) {
}
public function run(): int
{
$application = $this->application;
$loop = ReactFactory::create();
// сконфигурируйте ReactPHP, чтобы корректно обработать приложение PSR-15
$server = new ReactHttpServer(
$loop,
function (ServerRequestInterface $request) use ($application): ResponseInterface {
return $application->handle($request);
}
);
// запустите сервер ReactPHP
$socket = new ReactSocketServer($this->port, $loop);
$server->listen($socket);
$loop->run();
return 0;
}
}
Расширяя GenericRuntime
, вы гарантируете, что приложение всегда будет использовать
ReactPHPRunner
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
use Symfony\Component\Runtime\GenericRuntime;
use Symfony\Component\Runtime\RunnerInterface;
class ReactPHPRuntime extends GenericRuntime
{
private int $port;
public function __construct(array $options)
{
$this->port = $options['port'] ?? 8080;
parent::__construct($options);
}
public function getRunner(?object $application): RunnerInterface
{
if ($application instanceof RequestHandlerInterface) {
return new ReactPHPRunner($application, $this->port);
}
// если это не приложение PSR-15, используйте GenericRuntime, чтобы
// запустить приложение (см. "Resolvable Applications" выше)
return parent::getRunner($application);
}
}
Конечный пользователь теперь сможет создавать фронт-контроллер вроде:
1 2 3 4 5
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (array $context): SomeCustomPsr15Application {
return new SomeCustomPsr15Application();
};