Побудова власного фреймворку з MicroKernelTrait
Дата оновлення перекладу 2025-01-11
Побудова власного фреймворку з MicroKernelTrait
Клас Kernel
за замовчуванням додоаний у додатки Symfony використовує
MicroKernelTrait, щоб
конфігурувати пакети, маршрути та сервіс-контейнер в одному класі.
Цей підхід мікро-ядра настільки гнучкий, що дозволяє вам контролювати структуру вашого додатку та функції достатньо легко.
Додаток Symfony з одного файлу
Почніть з абсолютно пороженього каталогу. Та встановіть ці компоненти Symfony через Composer:
1
$ composer require symfony/framework-bundle symfony/runtime
Далі, створіть файл 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
// 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\Attribute\Route;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
class Kernel extends BaseKernel
{
use MicroKernelTrait;
protected function configureContainer(ContainerConfigurator $container): void
{
// PHP equivalent of 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),
]);
}
}
return static function (array $context) {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};
Ось і все! Щоб протестувати, запустіть Локальний веб-сервер Symfony:
1
$ symfony server:start
Потім подивіться на JSON-відповідь у вашому браузері: http://localhost:8000/random/10
Tip
Якщо у ваше ядро визначає лише один контролер, ви можете використати метод, що викликається:
1 2 3 4 5 6 7 8 9 10 11 12
class Kernel extends BaseKernel
{
use MicroKernelTrait;
// ...
#[Route('/random/{limit}', name: 'random_number')]
public function __invoke(int $limit): JsonResponse
{
// ...
}
}
Методи "мікро" ядра
Коли ви використовуєте MicroKernelTrait
, ваше ядро вимагає рівно три методи,
що визначають ваші пакети, сервіси та маршрути:
- registerBundles()
-
Це те ж саме, що і
registerBundles()
, який ви бачите у звичайному ядрі. За замовчуванням мікроядро реєструє лишеFrameworkBundle
. Якщо вам потрібно зареєструвати більше пакетів, перевизначте цей метод:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\TwigBundle\TwigBundle; // ... class Kernel extends BaseKernel { use MicroKernelTrait; // ... public function registerBundles(): array { yield new FrameworkBundle(); yield new TwigBundle(); } }
- 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,
]);
}
}
Файли шаблонів повинні знаходитись у каталозі templates/
у корені вашого проекту.
Цей шаблон знаходиться за адресою templates/micro/random.html.twig
:
1 2 3 4 5 6 7 8 9 10
<!-- templates/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