Компонент HttpKernel
Дата оновлення перекладу 2025-01-10
Компонент HttpKernel
Компонент HttpKernel надає структурний процес для перетворення
Request
вResponse
, використовуючи компонент EventDispatcher. Він достатньо гнучкий, щоб створювати повний фреймворк (Symfony), мікро-фреймворк (Silex) або просунуту CMS-систему (Drupal).
Установка
1
$ composer require symfony/http-kernel
Note
Якщо ви встановлюєте цей компонент поза додатком Symfony, вам потрібно підключити
файл vendor/autoload.php
у вашому коді для включення механізму автозавантаження
класів, наданих Composer. Детальніше можна прочитати у цій статті.
Робочий процес запиту
See also
Ця стаття пояснює, як використовувати функції HttpKernel в якості незалежного компоненту в будь-якому додатку PHP. В додатках Symfony все вже налаштовано і готово до використання. Прочитайте статті Контролер та Події та слухачі подій, для розуміння того, як використовувати ці функції при створенні контролерів та визначенні подій в додатках Symfony.
Кожна веб-взаємодія HTTP починається із запиту та завершується відповіддю. Ваша робота, як розробника, - створити PHP-код, який читає інформацію запиту (наприклад, URL) та створює і повертає відповідь (наприклад, сторінку HTML або рядок JSON). Ось спрощений огляд робочого процесу запиту в додатках Symfony:
- Користувач запитує джерело в браузері;
- Браузер відправляє запит серверу;
- Symfony дає додатку об'єкт Запит;
- Застосунок генерує об'єкт Відподвідь, використовуючи дані об'єкту Запит;
- Сервер відправляє запит назад браузеру;
- Браузер відображає джерело користувачу.
Зазвичай, якийсь фреймворк або система будуются для обробки всіх задач, що повторюються (наприклад, маршрутизаці, безпеки і т.д.), щоб розробник міг з легкістю побудувати кожну сторінку додатку. Те, як саме ци системи будуються, дуже відрізняється. Компонент HttpKernel надає інтерфейс, який формалізує процес початку запиту та створення відповідної відповіді. Компонент має бути серцем будь- якого додатку фреймворку, незалежно від того, наскільки змінено архітектуру цієї системи:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
namespace Symfony\Component\HttpKernel;
use Symfony\Component\HttpFoundation\Request;
interface HttpKernelInterface
{
// ...
/**
* @return Response A Response instance
*/
public function handle(
Request $request,
int $type = self::MAIN_REQUEST,
bool $catch = true
): Response;
}
Внутрішньо, метод HttpKernel::handle() - тверда реалізація HttpKernelInterface::handle() - визначає робочий процес, який починається з Request, та закінчується Response.
Точні деталі цього робочого процесу - ключ до розуміння того, як працює ядро (та фреймворк Symfony або будь-яка інша бібліотека, що використовує ядро).
HttpKernel: Керований подіями
Метод HttpKernel::handle()
працює внутрішньо, оголошуючи події. Це робить
метод як гнучким, так і трохи абстрактним, так як вся "робота" фреймфорку/додатку,
побудована за допомогою HttpKernel, насправді здійснюється слухачами подій.
Щоб допомогти пояснити цей процес, даний документ розглядає кожен крок процесу, та пояснює, як працює одна особлива реалізація HttpKernel - фреймворк Symfony.
Початково, використання HttpKernel дуже просте і потребує створення диспетчеру подій та контролера і розпізнавача аргументу (пояснюється нижче). Щоб розпізнати ваше ядро, що працює, ви додасте більше слухачів подій до подій, яки обговорюються нижче:
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
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
use Symfony\Component\HttpKernel\HttpKernel;
// створити об'єкт Request
$request = Request::createFromGlobals();
$dispatcher = new EventDispatcher();
// ... додати якихось слухачів події
// створити ваш контролер та розділювач аргументів
$controllerResolver = new ControllerResolver();
$argumentResolver = new ArgumentResolver();
// інастанціювати ядро
$kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);
// насправді запустит ядро, що перетворює запит у відповідь,
// оголошуючи події, викликаючи контролер та повертаючи відповідь
$response = $kernel->handle($request);
// відправити заголовки та відобразити зміст
$response->send();
// запустити подію kernel.terminate
$kernel->terminate($request, $response);
Див. "повний робочий приклад ", щоб побачити більш конкретну реалізацію.
Щоб дізнатися загальну інформацію про додавання слухачів до подій нижче, див. Створення слухача подій .
See also
Існує низка чудових туторіалів по використанню компоненту HttpKernel та інших компонентів Symfony для створення вашого власного фреймворку. Див. Вступ.
1) Подія kernel.request
Типові цілі: Додавати більше інформації в Request
, ініціалізувати частини
системи, або повертати Response
, якщо це можливо (наприклад, шар безпеки, що
відмовляє у доступі).
Інформаційна таблиця подій ядра
Перша подія, яка оголошується всередині HttpKernel::handle
- це kernel.request
, яка може мати безліч різних слухачів.
Слухачі цієї події можуть сильно відрізнятися. Деякі слухачі, наприклад, слухач
безпеки, можуть мати достатньо інформації, щоб створити об'єкт Response
негайно.
Наприклад, якщо слухач безпеки визначив, що користувач не має доступу, цей слухач
може повернути RedirectResponse на сторінку
входу або відповідь Відмова у доступі 403.
Якщо Response
повертається на цьому етапі, то процес переходить напряму до
події kernel.response .
Інші слухачі просто ініціалізують щось або додають більше інформації в запит.
Наприклад, слухач може визначити та встановити локаль в об'єкті Request
.
Інший розповсюджений слухач - маршрутизатор. Слухач маршрутизатору може обробляти
Request
та визначати контролер, який має бути відображено (див. наступний розділ).
Насправді, об'єкт Request
має набір "атрибутів ",
що є ідеальним місцем для зберігання додаткових даних про запит, що відносяться до нього.
Це означає, що якщо ваш слухач маршрутизатору якимось чином визначає контролер, він може
зберігати його в атрибутах Request
(що може бути використано вашим розпізнавачем
контролера).
Врешті-решт, ціль слухача kernel.request
- або створити та повернути Response
напряму, або додати інформацію до Request
(наприклад, встановити локаль або
встановити якусь іншу інформацію в атрибутах Request
).
Note
При встановленні відповіді для події kernel.request
, розповсюдження зупиняється.
Це означає, що слухачі з більш низьким пріоритетом, не будуть виконуватися.
2) Розвʼязання контролера
Якщо припустити, що жоден слухач kernel.request
не зміг створити Response
,
наступний крок в HttpKernel - визначити та підготувати (тобто розпізнати) контролер.
Контролер - це частина коду кінцевого додатку, яка відповідає за створення та
повернення Response
для конкретної сторінки. Єдина умова - щоб PHP був таким, що
викликається, тобто функцією, методом, об'єктом або Closure
.
Але те, як ви визначаєте певний контролер для запиту, залежить повністю від
вашого додатку. Це робота "розпізнавача контролера" - класу, що реалізує
ControllerResolverInterface,
і є одним з аргументів конструктору HttpKernel
.
Ваша робота полягає в створенні класу, що реалізує інтерфейс, та заповенні його
методу: getController()
. Насправді, одна реалізація за замовчуванням вже існує,
і ви можете використовувати її напряму, або навчитися у:
ControllerResolver. Ця реалізація
пояснюється більше у виносці нижче:
1 2 3 4 5 6 7 8
namespace Symfony\Component\HttpKernel\Controller;
use Symfony\Component\HttpFoundation\Request;
interface ControllerResolverInterface
{
public function getController(Request $request): callable|false;
}
Внутрішньо, метод HttpKernel::handle()
спочатку викликає
getController()
в розвʼязувачі контролера. Цей метод передається в Request
та відповідає за визначення та
повернення PHP, що викликається (контролера), заснованого на інформації запиту.
3) Подія kernel.controller
Типові цілі: Ініціалізувати щось або змінити контролер прямо перед його виконанням.
Інформаційна таблиця подій ядра
Після того, як було визначено викликане контролера, HttpKernel::handle()
запускає
подію kernel.controller
. Слухачі цієї події можуть ініціалізувати якусь частину
систему, яку необхідно ініціалізувати після визначення деяких речей (наприклад, контролера,
інформації маршрутизації), але перед виконанням контролера. Щоб побачити приклади, дивіться
розділ Symfony нижче.
Ще один типовий випадок використання - вилучення атрибутів з контролера, використовуючи метод getAttributes(). Див. розділ Symfony нижче, щоб побачити приклади.
Слухачі цієї події можуть також змінюватися викликане контролера повністю, викликавши ControllerEvent::setController в об'єкті події, який передається слухачам цієї події.
4) Отримання аргументів контролера
Далі, HttpKernel::handle()
викликає
ArgumentResolverInterface::getArguments().
Пам'ятайте, що контролер, повернений в getController()
- викликане. Ціль
getArguments()
- повернути масив аргументів, який має бути передано цьому
контролера. Те, яка саме це буде зроблено, залежить тільки від вас, хоча вбудований
ArgumentResolver є гарним
прикладом.
На цьому етапі, ядро має PHP-викликане (контролер) та масив аргументів, який має бути переданий при виконанні цього викликаного.
5) Виклик контролера
Наступний крок дуже простий! HttpKernel::handle()
виконує контролер.
Робота контролера - побудувати відповідь для заданого джерела. Це може бути HTML-сторінка, рядок JSON або щось інше. На відміну від будь-якої іншої частини процесу до цього часу, цей крок реалізується "кінцевим-розробником" для кожної сторінки, яка будується.
Зазвичай, контролер повертає об'єкт Response
. Якщо це так, то робота ядра
вже майже завершена! В такому випадку, наступний крок - подія
kernel.response .
Але якщо контролер повертає будь-що, окрім Response
, то ядру належить
зробити ще трохи роботи - kernel.view
(так як кінцева ціль завжди - згенерувати об'єкт Response
).
Note
Контролер має повернути щось. Якщо контролер повертає null
, то
негайно буде викликано виключення.
6) Подія kernel.view
Типові цілі: Перетворити зворотнє значення не-Response
з контролера в
Response
Інформаційна таблиця подій ядра
Якщо контролер не повертає об'єкт Response
, то ядро запускає іншу подію
- kernel.view
. Робота слухача цієї події - повернути значення контролера
(наприклад, масив даних або об'єкт), щоб створити Response
.
Це може бути корисно, якщо ви хочете використовувати шар "перегляду": замість
повернення Response
з контролера, ви можете повертати дані, які представляють
сторінку. Слухач цієї події потім може використовувати ці дані, щоб створити
Response
в правильному форматі (наприклад, HTML, JSON, та ін.).
На цьому етапі, якщо жоден слухач не встановив у події відповідь, викликається
виключення: або контролер, або один зі слухачів перегляду має завжди повертати
Response
.
Note
При встановелнні відповіді для події kernel.view
, розсповсюдження зупиняється.
Це означає, що слухачі з більш низьким пріоритетм не будуть виконані.
7) Подія kernel.response
Типові цілі: Змінити об'єкт Response
прямо перед його відправленням
Інформаційна таблиця подій ядра
Кінцевою метою ядра є перетворення Request
в Response
. Response
може
бути створений на весь час події kernel.request ,
повернено з контролера , або
повернено одним зі слухачів події kernel.view .
Незалежно від того, хто створює Response
, інша подія - kernel.response
,
запускається одразу після цього. Типовий слухач цієї події змінить об'єкт
Response
якимось чином, наприкла, змінить заголовки, додасть куки, або навіть
змінить зміст самого Response
(наприклад, впровадивши JavaScript до кінця
тегу </body>
HTML-відповіді).
Після цього, як оголошено цю подію, фінальний об'єкт Response
повертається з
handle(). У найчастіших
випадках застосування, після цього ви викликаєте метод
send(), який надсилає
заголовки та відображує зміст Response
.
8) Подія kernel.terminate
Типові цілі: Виконати якісь "важкі" дії після відправки відповіді користувачу
Інформаційна таблиця подій ядра
Фінальною подією процесу HttpKernel є kernel.terminate
і вона унікальнка,
так як відбувається після методу HttpKernel::handle()
, і після того, як
відповідь надіслана користувачу. Згадайте з прикладів вище, в такому випадку
код, що використовує ядро, закінчується так:
1 2 3 4 5
// відправляє заголовки та відображує зміст
$response->send();
// оголошує подію kernel.terminate
$kernel->terminate($request, $response);
Як ви бачите, викликавши $kernel->terminate
після відправки відповіді, ви запустите
подію kernel.terminate
, де ви зможете виконати деякі дії, які ви відкладали, щоб
повернути відповідь клієнту максимально швидко (наприклад, відправка електронних листів).
Warning
Внутрішньо, HttpKernel використовує PHP-функцію fastcgi_finish_request.
Це означає, що в цей момент, тільки API сервер PHP FPM може відправляти відповідь
клієнту, в той час як процес PHP серверу все ще виконує якісь завдання. З усіма іншими
API серверу, слухачі kernel.terminate
всеодно виконуються, але відповідь не
відправляється клієнту, поки вони всі не будуть виконані.
Note
Використання події kernel.terminate
необов'язкове, і має бути викликане лише
якщо ваше ядро реалізує TerminableInterface.
9) Обробка виключень: подія kernel.exception
Типові цілі: Обробити якесь виключення та створити відповідний
Response
для повернення виключенню
Інформаційна таблиця подій ядра
Якщо в якийсь момент всередині HttpKernel::handle()
викликається виключення,
то оголошується інша подія - kernel.exception
. Внутрішньо, тіло функції
handle()
обгорнуто в блок "спробуй спіймай". Коли викликається виключення,
оголошується подія kernel.exception
, щоб ваша система могла якось відповісти
на виключення.
Кожному слухачу цієї події передається об'єкт
ExceptionEvent,
який ви можете використати для доступу до початкового виключення через метод
getThrowable().
Типовий слухач цієї події перевірить наявність певного типу виключень, та створить
відповідну помилку Response
.
Наприклад, щоб згенерувати сторінку 404, ви можете викликати спеціальний тип виключення,
а потім додати слухача цієї події, який виглядає як виключення, та створює і повертає
404 Response
. Насправді, компонент HttpKernel надається з
ErrorListener, який, якщо ви
вирішите його використовувати, зробить це і багато іншого за замовчуванням (див.
виноску нижче, щоб дізнатися більше).
Клас ExceptionEvent розкриває метод isKernelTerminating(), за допомогою якого можна визначити, чи завершує роботу ядро в момент, коли було викликано виключення.
7.1
Метод isKernelTerminating() було представлено в Symfony 7.1.
Note
При встановленні відповіді для події kernel.exception
, розповсюдження
зупиняється. Це означає, що слухачі з більш низьким пріоритетом не будуть
виконані.
Створення слухача подій
Як ви бачили, ви можете створювати та приєднувати слухачів подій до будь-яких подій,
що оголошені під час циклу HttpKernel::handle()
. Зазвичай слухач - це PHP-клас
з методом, що виконується, але він може бути чим завгодно. Щоб дізнатися більше про
створення та приєднання слухачів подій, див. Компонент EventDispatcher.
Ім'я кожної події "ядра" визначається у вигляді константи в класі KernelEvents. Крім того, кожному слухачу подій передається один аргумент, який є підкласом KernelEvent. Цей об'єкт містить інформацію про поточний стан системи, і кожна подія має свій власний об'єкт події:
Повний робочий приклад
При використанні компонента HttpKernel, ви можете приєднувати будь-яких слухачів до базових подій, використовувати будь-який розпізнавач контролера, який реалізує ControllerResolverInterface та використовувати будь-який розпізнавач аргументів, який реалізує ArgumentResolverInterface. Однак, компонент HttpKernel постачається з деякими вбудованими слухачами та всім іншим, що може бути використано для створення робочого прикладу:
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 Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
use Symfony\Component\HttpKernel\EventListener\RouterListener;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
$routes = new RouteCollection();
$routes->add('hello', new Route('/hello/{name}', [
'_controller' => function (Request $request): Response {
return new Response(
sprintf("Hello %s", $request->get('name'))
);
}]
));
$request = Request::createFromGlobals();
$matcher = new UrlMatcher($routes, new RequestContext());
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new RouterListener($matcher, new RequestStack()));
$controllerResolver = new ControllerResolver();
$argumentResolver = new ArgumentResolver();
$kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
Підзапити
На додаток до "головного" запиту, який відправляється в HttpKernel::handle()
, ви
можете також відправити так званий "підзапит". Підзапит виглядає та поводить себе так
само, як будь-який інший запит, але зазвичай служить для відображення однієї маленької
частини сторінки замість цілої. Ви частіше за все будете робити підзапити з вашого
контролера (або, можливо, з шаблону, який відображається вашим контролером).
Щоб виконати підзапит, використовуйте HttpKernel::handle()
, але змініть
другий аргумент наступним чином:
1 2 3 4 5 6 7 8 9 10 11 12
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
// ...
// створити якийсь інший запит вручну, як це необхідно
$request = new Request();
// наприклад, встановити its _controller вручну
$request->attributes->set('_controller', '...');
$response = $kernel->handle($request, HttpKernelInterface::SUB_REQUEST);
// зробити щось з відповіддю
Це створює це один повний цикл запит-відповідь, де новий Request
перетворюється
в Response
. Єдина відміннсть внутрішньо в тому, що деякі слухачі (наприклад, безпеке)
можуть діяти лише в головному запиті. Кожному слухачу передається деякий підклас
KernelEvent, метод
метод isMainRequest() якого
може бути використаний для перевірки, чи є поточний запит "головним" або "під-" запитом.
Наприклад, слухач, якому необхідно діяти лише по головному запиту, може виглядати так:
1 2 3 4 5 6 7 8 9 10 11
use Symfony\Component\HttpKernel\Event\RequestEvent;
// ...
public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMainRequest()) {
return;
}
// ...
}
Note
За замовчуванням значенням атрибута запиту _format
є html
. Якщо ваш
підзапит повертає інший формат (наприклад, json
), ви можете встановити його
визначивши атрибут _format
явно у запиті:
1
$request->attributes->set('_format', 'json');
Розташування джерел
Компонент HttpKernel відповідає за механізм пакету, що використовується в додатках Symfony. Ключова функція пакетів полягає в тому, що вони дозволяють перевизначати будь-яке джерело, що використовується додатком (файли конфігурації, шаблони, контролери, файли перекладів та ін.).
Цей механізм перевизначення працює, так як на джерела посилаються не по їх фізичному
шляху, а по логічному. Наприклад, на файл services.xml
, що зберігається в каталозі
Resources/config/
пакету за іменем FooBundle, посилаються так:
@FooBundle/Resources/config/services.xml
. Цей логічний шлях буде працювати, коли
застосунок перевизначить цей файл, і навіть якщо ви зміните каталог FooBundle.
Компонент HttpKernel надає метод під назвою locateResource(), який може бути використаний для перетворення логічних шляхів у фізичні:
1
$path = $kernel->locateResource('@FooBundle/Resources/config/services.xml');