Побудова власного фреймворку з MicroKernelTrait
Дата оновлення перекладу 2024-05-09
Побудова власного фреймворку з MicroKernelTrait
Клас Kernel
за замовчуванням додоаний у додатки Symfony використовує
MicroKernelTrait, щоб
конфігурувати пакети, маршрути та сервіс-контейнер в одному класі.
Цей підхід мікро-ядра настільки гнучкий, що дозволяє вам контролювати структуру вашого додатку та функції достатньо легко.
Додаток Symfony з одного файлу
Почніть з абсолютно пороженього каталогу. Та встановіть ці компоненти Symfony через Composer:
1 2 3
$ composer require symfony/config symfony/http-kernel \
symfony/http-foundation symfony/routing \
symfony/dependency-injection symfony/framework-bundle
Далі, створіть файл index.php
, що визначає клас ядра та виконує його:
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 39 40 41 42 43
// index.php
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Annotation\Route;
require __DIR__.'/vendor/autoload.php';
class Kernel extends BaseKernel
{
use MicroKernelTrait;
public function registerBundles(): array
{
return [
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
];
}
protected function configureContainer(ContainerConfigurator $container): void
{
// PHP-еквівалент config/packages/framework.yaml
$container->extension('framework', [
'secret' => 'S0ME_SECRET'
]);
}
#[Route('/random/{limit}', name: 'random_number')]
public function randomNumber(int $limit): JsonResponse
{
return new JsonResponse([
'number' => random_int(0, $limit),
]);
}
}
$kernel = new Kernel('dev', true);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
Ось і все! Щоб протестувати, запустіть Локальний веб-сервер Symfony:
1
$ symfony server:start
Потім подивіться на JSON-відповідь у вашому браузері: http://localhost:8000/random/10
Методи "мікро" ядра
Коли ви використовуєте MicroKernelTrait
, ваше ядро вимагає рівно три методи,
що визначають ваші пакети, сервіси та маршрути:
- registerBundles()
-
Той же
registerBundles()
, який ви бачите у звичайному ядрі. - configureContainer(ContainerConfigurator $container)
-
Цей метод будує та конфігурує контейнер. На практиці, ви використовуватимете
extension()
, щоб сконфігурувати різні пакети (це еквівалент того, що ви бачите у нормальному файліconfig/packages/*
). Ви можете також зареєструвати сервіси напряму в PHP або завантажити зовнішні файли конфігурації (продемонстровано нижче). - configureRoutes(RoutingConfigurator $routes)
-
Ваша робота в цьому методі - додати маршрути до додатку.
RoutingConfigurator
має методи, які роблять додавання маршрутів в PHP веселішим. Ви також можете завантажувати зовнішні файли маршрутизації (продемонстровано нижче).
Додавання інтерфейсів до "мікро" ядра
При використанні MicroKernelTrait
, ви також можете реалізувати CompilerPassInterface
,
щоб автоматично реєструвати саме ядро як пропуск компілятора, як пояснюється у відповідному
розділі про передачу компілятора .
Якщо ExtensionInterface
реалізовано при використанні MicroKernelTrait
, то ядро буде автоматично зареєстровано як
розширення. Більш детально про це можна дізнатися у спеціальному розділі про
керування конфігурацією з розширеннями .
Також можливо реалізувати EventSubscriberInterface
, щоб обробляти події напряму з
ядра, і знову він буде зареєстрований автоматично:
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
// ...
use App\Exception\Danger;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class Kernel extends BaseKernel implements EventSubscriberInterface
{
use MicroKernelTrait;
// ...
public function onKernelException(ExceptionEvent $event): void
{
if ($event->getThrowable() instanceof Danger) {
$event->setResponse(new Response('It\'s dangerous to go alone. Take this ⚔'));
}
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::EXCEPTION => 'onKernelException',
];
}
}
Просунутий приклад: Twig, анотації та панель інструментів веб-налагодження
Ціль MicroKernelTrait
не в тому, щоб мати додаток з одного файлу. Вона в тому, щоб
надати вам мможливість обирати ваші пакети та структуру.
Спочатку ви напевно захочете помістити ваші PHP-класи в каталог src/
. Сконфігуруйте ваш
файл composer.json
так, щоб він завантажував звідти:
1 2 3 4 5 6 7 8 9 10
{
"require": {
"...": "..."
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
Потім, виконайте composer dump-autoload
, щоб скинути вашу нову конфігурацію автозавантаження.
Тепер, уявіть, що ви хочете використати Twig і завантажувати маршрути через анотації. Замість того,
щоб розміщувати все у index.php
, створіть новий src/Kernel.php
, щоб утримувати ядро. Тепер
він виглядає так:
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
// src/Kernel.php
namespace App;
use App\DependencyInjection\AppExtension;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
public function registerBundles(): array
{
$bundles = [
new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new \Symfony\Bundle\TwigBundle\TwigBundle(),
];
if ('dev' === $this->getEnvironment()) {
$bundles[] = new \Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
}
return $bundles;
}
protected function build(ContainerBuilder $containerBuilder): void
{
$containerBuilder->registerExtension(new AppExtension());
}
protected function configureContainer(ContainerConfigurator $container): void
{
$container->import(__DIR__.'/../config/framework.yaml');
// зареєструвати всі класи у /src/ як сервіси
$container->services()
->load('App\\', __DIR__.'/*')
->autowire()
->autoconfigure()
;
// сконфігурувати WebProfilerBundle лише якщо пакет підключено
if (isset($this->bundles['WebProfilerBundle'])) {
$container->extension('web_profiler', [
'toolbar' => true,
'intercept_redirects' => false,
]);
}
}
protected function configureRoutes(RoutingConfigurator $routes): void
{
// імпортувати WebProfilerRoutes, лише якщо пакет підключено
if (isset($this->bundles['WebProfilerBundle'])) {
$routes->import('@WebProfilerBundle/Resources/config/routing/wdt.xml')->prefix('/_wdt');
$routes->import('@WebProfilerBundle/Resources/config/routing/profiler.xml')->prefix('/_profiler');
}
// завантажити маршрути, визначені як PHP-атрибути
// (використайте 'annotation' в якості другого аргументу, якщо ви визначаєте маршрути як анотації)
$routes->import(__DIR__.'/Controller/', 'attribute');
}
// опціонально, щоб використати стандартний каталог кеша Symfony
public function getCacheDir(): string
{
return __DIR__.'/../var/cache/'.$this->getEnvironment();
}
// опціонально, щоб використати стандартний каталог логів Symfony
public function getLogDir(): string
{
return __DIR__.'/../var/log';
}
}
Перед тим, як продовжувати, виконайте цю команду, щоб додати підтримку для нових залежностей:
1
$ composer require symfony/yaml symfony/twig-bundle symfony/web-profiler-bundle
Далі, створіть новий клас розширень, який визначає конфігурацію вашого додатку та
додайте сервіс, умовно заснований на значенні foo
:
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
// src/DependencyInjection/AppExtension.php
namespace App\DependencyInjection;
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\AbstractExtension;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
class AppExtension extends AbstractExtension
{
public function configure(DefinitionConfigurator $definition): void
{
$definition->rootNode()
->children()
->booleanNode('foo')->defaultTrue()->end()
->end();
}
public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void
{
if ($config['foo']) {
$containerBuilder->register('foo_service', \stdClass::class);
}
}
}
На відміну від попереднього ядра, це завантажує зовнішній файл app/config/config.yml
, так
як конфігурація стає більшою:
1 2 3 4
# config/framework.yaml
framework:
secret: S0ME_SECRET
profiler: { only_exceptions: false }
Також завантажує маршрути атрибутів з каталогу src/Controller/
, який містить в собі
один файл:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// src/Controller/MicroController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class MicroController extends AbstractController
{
#[Route('/random/{limit}')]
public function randomNumber(int $limit): Response
{
$number = random_int(0, $limit);
return $this->render('micro/random.html.twig', [
'number' => $number,
]);
}
}
Файли шаблонів повинні жити у каталозі Resources/views/
того ж каталогу, в якому знаходиться
ваше ядро. Так як Kernel
живе в src/
, то цей шаблон знаходиться в
src/Resources/views/micro/random.html.twig
:
1 2 3 4 5 6 7 8 9 10
<!-- src/Resources/views/micro/random.html.twig -->
<!DOCTYPE html>
<html>
<head>
<title>Random action</title>
</head>
<body>
<p>{{ number }}</p>
</body>
</html>
Нарешті, вам потрібен фронт-контролер для завантаження та запуску додатку. Створіть
public/index.php
:
1 2 3 4 5 6 7 8 9 10 11
// public/index.php
use App\Kernel;
use Symfony\Component\HttpFoundation\Request;
require __DIR__.'/../vendor/autoload.php';
$kernel = new Kernel('dev', true);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
Ось і все! Цей URL /random/10
працюватиме, Twig відображатиме, і ви навіть побачите знизу
панель інструментів веб-налагодження. Підсумкова структура виглядає так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
your-project/
├─ config/
│ └─ framework.yaml
├─ public/
| └─ index.php
├─ src/
| ├─ Kernel.php
| ├─ Controller
| | └─ MicroController.php
│ └─ Resources
| └─ views
| └─ micro
| └─ random.html.twig
├─ var/
| ├─ cache/
│ └─ log/
├─ vendor/
│ └─ ...
├─ composer.json
└─ composer.lock
Як і раніше, ви можете використати вбудований PHP-сервер:
1
$ symfony server:start
Потім відвідайте сторінку у вашому браузері: http://localhost:8000/random/10