Як створити додаток Symfony з декількома ядрами
Дата оновлення перекладу 2022-11-17
Як створити додаток Symfony з декількома ядрами
Caution
Створення додатків з декількома ядрами більше не рекомендується Symfony. Розгляньте варіант створення декількох маленьких додатків замість цього.
У більшості додатків Symfony вхідні запити обробляються фронт-контролером public/index.php
,
який інстанціює клас src/Kernel.php
для створення ядра додатку, що завантажує
пакети і працює з запитом для генерування відповіді.
Підхід одного ядра - це зручне рішення, але додатки Symfony можуть визначати будь-яку кількість ядер. У той час, як середовища виконують один і той же додаток з різними конфігураціями, ядра можуть виконувати різні частини одного і того ж додатку.
Ось найрозповсюдженіші випадки застосування створення багатьох ядер:
- Додаток, який визначає API, може визначити два ядра з міркувань продуктивності. Перше ядро служитиме звичайному додатку, а друге - реагувати лише на API-запити, завантажуючи пакети і підключачи менше функцій;
- Високочутливий додаток може також визначати два ядра. Перше буде лише завантажувати маршрути, що співпадають з публічними частинами додатку. Друге - завантажувати решту додатку, і його доступ буде захищений веб-сервером;
- Додаток, орієнтований на мікросервіси, може визначати декілька ядер для вибіркового підключення або відключення сервісів, перетворюючи традиційний монолітний додаток на декілька мікро-додатків.
Додавання нового ядра у додаток
Створення нового ядра у додатку - це процес, що складається з трьох кроків:
- Створіть новий фронт-контролер для завантаження нового ядра;
- Створіть новий клас ядра;
- Визначіть конфігурацію, що завантажується новим ядром.
Наступний приклад демонструє, як створити нове ядро для API заданого додатку Symfony.
Крок 1) Створіть новий фронт-контролер
Замість створення нового фронт-контролера з нуля, легше буде дублювати вже існуючий.
Наприклад, створіть public/api.php
з public/index.php
.
Далі, оновіть код нового фронт-контролера, щоб інстанціювати новий клас ядра, замість
звичайного класу Kernel
:
1 2 3 4 5 6 7
// public/api.php
// ...
$kernel = new ApiKernel(
$_SERVER['APP_ENV'] ?? 'dev',
$_SERVER['APP_DEBUG'] ?? ('prod' !== ($_SERVER['APP_ENV'] ?? 'dev'))
);
// ...
Tip
Інший підхід полягає в тому, щоб залишити існуючий фронт-контролер index.php
, але
додати ствердження if
для завантаження іншого ядра, заснованого на URL (наприклад,
якщо URL починається з /api
, використайте ApiKernel
).
Крок 2) Створіть новий клас ядра
Тепер вам потрібно визначити клас ApiKernel
, використовуваний новим фронт-контролером.
Легше за все це зробити дублювавши існуючий файл src/Kernel.php
та додавши до нього всі
необхідні зміни.
У цьому прикладі, ApiKernel
завантажуватиме менше пакетів, ніж ядро за замовчуванням.
Переконайтеся в тому, що ви також змінили локацію кешу, логів та файлів конфігурації, щоб
вони не зіштовхувалися з файлами з 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
// src/ApiKernel.php
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 ApiKernel extends Kernel
{
use MicroKernelTrait;
public function getProjectDir(): string
{
return \dirname(__DIR__);
}
public function getCacheDir(): string
{
return $this->getProjectDir().'/var/cache/api/'.$this->environment;
}
public function getLogDir(): string
{
return $this->getProjectDir().'/var/log/api';
}
protected function configureContainer(ContainerConfigurator $container): void
{
$container->import('../config/api/{packages}/*.yaml');
$container->import('../config/api/{packages}/'.$this->environment.'/*.yaml');
if (is_file(\dirname(__DIR__).'/config/api/services.yaml')) {
$container->import('../config/api/services.yaml');
$container->import('../config/api/{services}_'.$this->environment.'.yaml');
} else {
$container->import('../config/api/{services}.php');
}
}
protected function configureRoutes(RoutingConfigurator $routes): void
{
$routes->import('../config/api/{routes}/'.$this->environment.'/*.yaml');
$routes->import('../config/api/{routes}/*.yaml');
// ... завантажувати лише якщо маршрути конфігурації потрібні для API
}
// Якщо вам потрібно виконати деяку логіку, щоб вирішити, які пакети завантажувати,
// ви можете захотіти використати замість цього метод registerBundles()
private function getBundlesPath(): string
{
// завантажувати лише пакети, суворо необхідні для API
return $this->getProjectDir().'/config/api_bundles.php';
}
}
Крок 3) Визначіть конфігурацію ядра
Нарешті, визначіть файли конфігурації, які буде завантажувати новий ApiKernel
.
Дотримуючись коду вище, ця конфігурація житиме в одному з багатьох файлів, що
зберігаються у каталогах config/api/
і config/api/ENVIRONMENT_NAME/
.
Нові файли конфігурації можуть бути створені з нуля при завантаженні всього декількох
пакетів, так як це дуже просто. В інших випадках, дублюйте існуючі у config/packages/
файли конфігурації, або, що ще краще - імпортуйте їх та перевизначіть необхідні опції:
Виконання команд з іншим ядром
Скрипт bin/console
, використовуваний для запуску команд Symfony завжди використовує
клас за замовчуванням Kernel
, щоб побудувати додаток та завантажити команди. Якщо вам
потрібно виконати консольні команди, використовуючи нове ядро, дублюйте скрипт bin/console
та переіменуйте його (наприклад, bin/api
).
Далі, замініть інстанціювання Kernel
вашим вланим інстанціюванням ядра (наприклад,
ApiKernel
) і тепер ви можете виконувати команди, використовуючи нове ядро (наприклад,
php bin/api cache:clear
). Тепер ви можете виконувати команд, використовуючи нове ядре=.
Note
Команди, доступні для кожного консольного скрипту (наприклад, bin/console
і
bin/api
) можуть відрізнятися, так як вони залежать від пакетів, підключених
для кожного ядра, які можуть відрізнятися.
Відображення шаблонів, визначених в іншому ядрі
Якщо ви дотримуєтесь Кращих практик Symfony, то шаблони нового ядра будуть зберігатися
у templates/
. Спроба відобразити ці шаблони в іншому ядрі призведе до помилки Не
існує зареєстрованих шляхів для простору імен "__main__".
Щоб вирішити цю проблему, додайте наступну конфігурацію до вашого ядра:
1 2 3 4 5
# config/api/twig.yaml
twig:
paths:
# дозволяє використовувати api/templates/ dir в ApiKernel
"%kernel.project_dir%/api/templates": ~
Виконання тестів, використовуючи інше ядро
У додатках Symfony функціональні тести за замовчуванням розширюються з класу
WebTestCase. Всередині цього класу,
метод під назвою getKernelClass()
намагається знайти клас ядра для запуску додатку
під час тестування. Логіка цього методу не підтримує додатки з декількома ядрами, так
що ваші тести не будуть використовувати правильне ядро.
Рішенням буде створити користувацький базовий клас для функціональних тестів, що
розширюється з класу WebTestCase
і перевизначає метод getKernelClass()
, щоб
повертати повне імʼя класу ядра для використання:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
// тести, яким потрібно, щоб ApiKernel працював, тепер повинні розширювати цей
// клас ApiTestCase, замість класу WebTestCase за замовчуванням
class ApiTestCase extends WebTestCase
{
protected static function getKernelClass()
{
return 'App\ApiKernel';
}
// це потрібно, так як клас KernelTestCase містить посилання на створене раніше
// ядро та його статичну властивість $kernel. Отже, якщо ваші функціональні тести
// не запускають ізольований процес, пізніший запусктесту для іншого ядра буде
// повторно використовувати створений раніше екземпляр, що вказує на інше ядро
protected function tearDown()
{
parent::tearDown();
static::$class = null;
}
}
Додавання більшої кількості ядер у додаток
Якщо ваш додаток дуже складний і ви створюєте декілька ядер, то краще зберігати їх
у їх власних каталогах, замість того, щоб плутатися з купою файлів у каталозі src/
за замовчуванням:
1 2 3 4 5 6 7 8 9 10 11 12
project/
├─ src/
│ ├─ ...
│ └─ Kernel.php
├─ api/
│ ├─ ...
│ └─ ApiKernel.php
├─ ...
└─ public/
├─ ...
├─ api.php
└─ index.php