Bootstrap
Bootstrap — це завантажувальний код, який ініціалізує середовище, створює DI-контейнер (впровадження залежностей) і запускає застосунок. Розглянемо:
- як він налаштовується за допомогою NEON-файлів
- як розрізнити робочий і розробницький режими
- як створити DI-контейнер
Застосунки, чи то веб-застосунки, чи скрипти, що запускаються з
командного рядка, починають свою роботу з певної форми ініціалізації
середовища. У давні часи за це відповідав файл з назвою, наприклад,
include.inc.php
, який включався первинним файлом. У сучасних
застосунках Nette його замінив клас Bootstrap
, який як частину
застосунку ви знайдете у файлі app/Bootstrap.php
. Він може виглядати,
наприклад, так:
use Nette\Bootstrap\Configurator;
class Bootstrap
{
private Configurator $configurator;
private string $rootDir;
public function __construct()
{
$this->rootDir = dirname(__DIR__);
// Configurator відповідає за налаштування середовища застосунку та сервісів.
$this->configurator = new Configurator;
// Встановлює каталог для тимчасових файлів, що генеруються Nette (наприклад, скомпільовані шаблони)
$this->configurator->setTempDirectory($this->rootDir . '/temp');
}
public function bootWebApplication(): Nette\DI\Container
{
$this->initializeEnvironment();
$this->setupContainer();
return $this->configurator->createContainer();
}
private function initializeEnvironment(): void
{
// Nette розумний, і режим розробки вмикається автоматично,
// або ви можете ввімкнути його для конкретної IP-адреси, розкоментувавши наступний рядок:
// $this->configurator->setDebugMode('secret@23.75.345.200');
// Активує Tracy: неперевершений "швейцарський ніж" для налагодження.
$this->configurator->enableTracy($this->rootDir . '/log');
// RobotLoader: автоматично завантажує всі класи у вибраному каталозі
$this->configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
}
private function setupContainer(): void
{
// Завантажує конфігураційні файли
$this->configurator->addConfig($this->rootDir . '/config/common.neon');
}
}
index.php
Первинним файлом у випадку веб-застосунків є index.php
, який
знаходиться у публічному каталозі
www/
. Він отримує від класу Bootstrap ініціалізацію середовища та
створення DI-контейнера. Потім з нього отримує сервіс Application
,
який запускає веб-застосунок:
$bootstrap = new App\Bootstrap;
// Ініціалізація середовища + створення DI-контейнера
$container = $bootstrap->bootWebApplication();
// DI-контейнер створює об'єкт Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Запуск застосунку Nette та обробка вхідного запиту
$application->run();
Як бачимо, з налаштуванням середовища та створенням DI-контейнера (впровадження залежностей) допомагає клас Nette\Bootstrap\Configurator, який ми зараз детальніше розглянемо.
Режим розробки проти робочого режиму
Nette поводиться по-різному залежно від того, чи працює він на сервері розробки чи на робочому сервері:
- 🛠️ Режим розробки (Development)
- Показує панель налагодження Tracy з корисною інформацією (SQL-запити, час виконання, використана пам'ять)
- У разі помилки показує детальну сторінку помилки з викликами функцій та вмістом змінних
- Автоматично оновлює кеш при зміні шаблонів Latte, редагуванні конфігураційних файлів тощо.
- 🚀 Робочий режим (Production)
- Не показує жодної налагоджувальної інформації, всі помилки записує в лог
- У разі помилки показує ErrorPresenter або загальну сторінку “Server Error”
- Кеш ніколи автоматично не оновлюється!
- Оптимізований для швидкості та безпеки
Вибір режиму здійснюється автовизначенням, тому зазвичай не потрібно нічого налаштовувати або вручну перемикати:
- режим розробки: на localhost (IP-адреса
127.0.0.1
або::1
), якщо немає проксі (тобто її HTTP-заголовка) - робочий режим: скрізь в інших місцях
Якщо ми хочемо ввімкнути режим розробки і в інших випадках,
наприклад, для програмістів, що підключаються з конкретної IP-адреси,
використовуємо setDebugMode()
:
$this->configurator->setDebugMode('23.75.345.200'); // можна вказати і масив IP-адрес
Однозначно рекомендуємо комбінувати IP-адресу з cookie. У cookie
nette-debug
збережемо секретний токен, наприклад, secret1234
, і
таким чином активуємо режим розробки для програмістів, що
підключаються з конкретної IP-адреси та мають у cookie згаданий токен:
$this->configurator->setDebugMode('secret1234@23.75.345.200');
Режим розробки можна також повністю вимкнути, навіть для localhost:
$this->configurator->setDebugMode(false);
Увага, значення true
вмикає режим розробки примусово, що ніколи
не повинно статися на робочому сервері.
Інструмент налагодження Tracy
Для легкого налагодження ще ввімкнемо чудовий інструмент Tracy. У режимі розробки він візуалізує помилки, а в робочому режимі помилки логує до вказаного каталогу:
$this->configurator->enableTracy($this->rootDir . '/log');
Тимчасові файли
Nette використовує кеш для DI-контейнера, RobotLoader, шаблонів тощо. Тому необхідно встановити шлях до каталогу, куди буде зберігатися кеш:
$this->configurator->setTempDirectory($this->rootDir . '/temp');
На Linux або macOS встановіть для каталогів log/
та temp/
права на запис.
RobotLoader
Зазвичай ми захочемо автоматично завантажувати класи за допомогою RobotLoader, тому ми повинні його запустити і
дозволити йому завантажувати класи з каталогу, де знаходиться
Bootstrap.php
(тобто __DIR__
), та всіх підкаталогів:
$this->configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
Альтернативний підхід — дозволити завантажувати класи лише через Composer, дотримуючись PSR-4.
Часовий пояс
За допомогою конфігуратора ви можете встановити стандартний часовий пояс.
$this->configurator->setTimeZone('Europe/Kyiv');
Конфігурація DI-контейнера
Частиною процесу завантаження є створення DI-контейнера, або фабрики об'єктів, що є серцем усього застосунку. Це фактично PHP-клас, який генерує Nette і зберігає в каталозі з кешем. Фабрика виробляє ключові об'єкти застосунку, і за допомогою конфігураційних файлів ми інструктуємо її, як їх створювати та налаштовувати, чим впливаємо на поведінку всього застосунку.
Конфігураційні файли зазвичай записуються у форматі NEON. В окремому розділі ви дізнаєтеся, що можна налаштувати.
У режимі розробки контейнер автоматично оновлюється при кожній зміні коду або конфігураційних файлів. У робочому режимі він генерується лише один раз, і зміни не перевіряються для максимальної продуктивності.
Конфігураційні файли завантажуємо за допомогою addConfig()
:
$this->configurator->addConfig($this->rootDir . '/config/common.neon');
Якщо ми хочемо додати більше конфігураційних файлів, ми можемо
викликати функцію addConfig()
кілька разів.
$configDir = $this->rootDir . '/config';
$this->configurator->addConfig($configDir . '/common.neon');
$this->configurator->addConfig($configDir . '/services.neon');
if (PHP_SAPI === 'cli') {
$this->configurator->addConfig($configDir . '/cli.php');
}
Назва cli.php
не є помилкою, конфігурація може бути записана
також у PHP-файлі, який повертає її як масив.
Також ми можемо додати інші конфігураційні файли в секції
includes
.
Якщо в конфігураційних файлах з'являються елементи з однаковими
ключами, вони будуть перезаписані, або у випадку масивів об'єднані. Файл,
що завантажується пізніше, має вищий пріоритет, ніж попередній. Файл, у
якому вказана секція includes
, має вищий пріоритет, ніж файли, що в
ньому включені.
Статичні параметри
Параметри, що використовуються в конфігураційних файлах, ми можемо
визначити у секції
parameters
, а також передавати (чи перезаписувати) їх методом
addStaticParameters()
(має псевдонім addParameters()
). Важливо, що різні
значення параметрів спричинять генерацію додаткових DI-контейнерів,
тобто додаткових класів.
$this->configurator->addStaticParameters([
'projectId' => 23,
]);
На параметр projectId
можна посилатися в конфігурації звичайним
записом %projectId%
.
Динамічні параметри
До контейнера ми можемо додати й динамічні параметри, різні значення яких, на відміну від статичних параметрів, не спричиняють генерації нових DI-контейнерів.
$this->configurator->addDynamicParameters([
'remoteIp' => $_SERVER['REMOTE_ADDR'],
]);
Таким чином, ми можемо легко додати, наприклад, змінні середовища, на
які потім можна посилатися в конфігурації записом %env.variable%
.
$this->configurator->addDynamicParameters([
'env' => getenv(),
]);
Стандартні параметри
У конфігураційних файлах ви можете використовувати ці статичні параметри:
%appDir%
— абсолютний шлях до каталогу з файломBootstrap.php
%wwwDir%
— абсолютний шлях до каталогу з вхідним файломindex.php
%tempDir%
— абсолютний шлях до каталогу для тимчасових файлів%vendorDir%
— абсолютний шлях до каталогу, куди Composer встановлює бібліотеки%rootDir%
— абсолютний шлях до кореневого каталогу проєкту%debugMode%
— вказує, чи перебуває застосунок у режимі налагодження%consoleMode%
— вказує, чи прийшов запит через командний рядок
Імпортовані сервіси
Тепер ми заглиблюємося. Хоча сенс DI-контейнера полягає у створенні
об'єктів, винятково може виникнути потреба вставити в контейнер
існуючий об'єкт. Ми робимо це, визначаючи сервіс з прапорцем
imported: true
.
services:
myservice:
type: App\Model\MyCustomService
imported: true
І в bootstrap ми вставляємо об'єкт у контейнер:
$this->configurator->addServices([
'myservice' => new App\Model\MyCustomService('foobar'),
]);
Різне середовище
Не бійтеся змінювати клас Bootstrap відповідно до ваших потреб. Методу
bootWebApplication()
ви можете додати параметри для розрізнення
веб-проектів. Або ми можемо додати інші методи, наприклад
bootTestEnvironment()
, який ініціалізує середовище для юніт-тестів,
bootConsoleApplication()
для скриптів, що викликаються з командного
рядка, тощо.
public function bootTestEnvironment(): Nette\DI\Container
{
Tester\Environment::setup(); // ініціалізація Nette Tester
$this->setupContainer();
return $this->configurator->createContainer();
}
public function bootConsoleApplication(): Nette\DI\Container
{
$this->configurator->setDebugMode(false);
$this->initializeEnvironment();
$this->setupContainer();
return $this->configurator->createContainer();
}