Bootstrap

Bootstrap – це завантажувальний код, який ініціалізує середовище, створює контейнер впровадження залежностей (DI) і запускає додаток. Ми обговоримо:

  • як налаштувати застосунок за допомогою файлів NEON
  • як працювати з режимами виробництва та розробки
  • як створити контейнер DI

Додатки, чи то веб-додатки, чи то скрипти командного рядка, починаються з ініціалізації середовища в тій чи іншій формі. У стародавні часи за це міг відповідати файл з ім'ям, наприклад, include.inc.php, який включався у вихідний файл. У сучасних додатках Nette він замінений класом Bootstrap, який як частина додатка знаходиться у файлі app/Bootstrap.php. Це може виглядати, наприклад, так:

use Nette\Bootstrap\Configurator;

class Bootstrap
{
	public static function boot(): Configurator
	{
		$appDir = dirname(__DIR__);
		$configurator = new Configurator;
		//$configurator->setDebugMode('secret@23.75.345.200');
		$configurator->enableTracy($appDir . '/log');
		$configurator->setTempDirectory($appDir . '/temp');
		$configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
		$configurator->addConfig($appDir . '/config/common.neon');
		return $configurator;
	}
}

index.php

У випадку веб-додатків початковим файлом є index.php, який знаходиться у загальнодоступному каталозі www/. Він дозволяє класу Bootstrap ініціалізувати середовище і повертає $configurator, який створює контейнер DI. Потім він отримує сервіс Application, який запускає веб-додаток:

// ініціалізуємо середовище + отримуємо об'єкт Configurator
$configurator = App\Bootstrap::boot();
// створюємо DI-контейнер
$container = $configurator->createContainer();
// DI-контейнер створює об'єкт Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// запускаємо додаток Nette
$application->run();

Як ви бачите, клас Nette\Bootstrap\Configurator, який ми зараз представимо детальніше, допомагає в налаштуванні середовища та створенні контейнера впровадження залежностей (DI).

Режим розробки та режим виробництва

Nette розрізняє два основні режими, в яких виконується запит: розробка і виробництво. Режим розробки орієнтований на максимальну зручність програміста, відображається Tracy, кеш автоматично оновлюється у разі зміни шаблонів або конфігурації DI контейнера тощо. Режим виробництва орієнтований на продуктивність, Tracy тільки реєструє помилки, а зміни шаблонів та інших файлів не перевіряються.

Вибір режиму здійснюється шляхом автовизначення, тому зазвичай немає необхідності налаштовувати або перемикати що-небудь вручну. Режим розробки використовується, якщо застосунок запущено на localhost (тобто IP-адресу 127.0.0.1 або ::1) і відсутній проксі-сервер (тобто його HTTP-заголовок). В іншому разі застосунок працює у виробничому режимі.

Якщо ви хочете ввімкнути режим розробки в інших випадках, наприклад, для програмістів, які отримують доступ з певної IP-адреси, ви можете використовувати setDebugMode():

$configurator->setDebugMode('23.75.345.200'); // одна або більше IP-адрес

Ми безумовно рекомендуємо поєднувати IP-адресу з файлом cookie. Ми зберігатимемо секретний токен у cookie nette-debug', например, `secret1234, і режим розробки буде активовано для програмістів із такою комбінацією IP і cookie.

$configurator->setDebugMode('secret1234@23.75.345.200');

Можна повністю вимкнути режим розробника, навіть для localhost:

$configurator->setDebugMode(false);

Зверніть увагу, що значення true жорстко вмикає режим розробника, чого ніколи не повинно відбуватися на робочому сервері.

Налагоджувальний інструмент Tracy

Для полегшення налагодження ми увімкнемо чудовий інструмент Tracy. У режимі розробника він візуалізує помилки, а в режимі виробництва – записує помилки в зазначений каталог:

$configurator->enableTracy($appDir . '/log');

Тимчасові файли

Nette використовує кеш для DI-контейнера, RobotLoader, шаблонів тощо. Тому необхідно задати шлях до директорії, де зберігатиметься кеш:

$configurator->setTempDirectory($appDir . '/temp');

У Linux або macOS встановіть права на запис для каталогів log/ і temp/.

RobotLoader

Зазвичай ми хочемо автоматично завантажувати класи за допомогою RobotLoader, тому ми повинні запустити його і дозволити йому завантажити класи з каталогу, в якому знаходиться Bootstrap.php (тобто __DIR__) і всі його підкаталоги:

$configurator->createRobotLoader()
	->addDirectory(__DIR__)
	->register();

Альтернативний спосіб – використовувати лише автозавантаження PSR-4 Composer.

Часовий пояс

Configurator дає змогу вказати часовий пояс для вашого застосунку.

$configurator->setTimeZone('Europe/Prague');

Конфігурація DI-контейнера

Частиною процесу завантаження є створення DI-контейнера, тобто фабрики об'єктів, яка є серцем усього додатка. Насправді це PHP-клас, створений Nette, який зберігається в каталозі кешу. Фабрика створює ключові об'єкти застосунку, а конфігураційні файли інструктують її, як їх створювати та налаштовувати, і таким чином ми впливаємо на поведінку всього застосунку.

Файли конфігурації зазвичай записуються у форматі NEON. Ви можете прочитати що можна налаштувати тут.

У режимі розробки контейнер автоматично оновлюється щоразу, коли ви змінюєте код або конфігураційні файли. У виробничому режимі він генерується лише один раз, а зміни файлів не перевіряються для досягнення максимальної продуктивності.

Файли конфігурації завантажуються за допомогою addConfig():

$configurator->addConfig($appDir . '/config/common.neon');

Метод addConfig() може бути викликаний кілька разів для додавання декількох файлів.

$configurator->addConfig($appDir . '/config/common.neon');
$configurator->addConfig($appDir . '/config/services.neon');
if (PHP_SAPI === 'cli') {
	$configurator->addConfig($appDir . '/config/cli.php');
}

Підключення cli.php не є друкарською помилкою, конфігурацію також можна записати в PHP-файлі, який повертає її у вигляді масиву.

Альтернативно, ми можемо використовувати секцію includes для завантаження конфігураційних файлів.

Якщо елементи з однаковими ключами відображаються у файлах конфігурації, вони будуть перезаписані або об'єднані у випадку масивів. Пізніше включений файл має вищий пріоритет, ніж попередні. Файл, зазначений у секції includes, має вищий пріоритет, ніж файли, включені в нього.

Статичні параметри

Параметри, що використовуються у файлах конфігурації, можуть бути визначені в секції parameters і підхоплені (або перезаписані) методом addStaticParameters() (у нього є аліас addParameters()). Важливо, що різні значення параметрів викликають генерацію додаткових DI-контейнерів, тобто додаткових класів.

$configurator->addStaticParameters([
	'projectId' => 23,
]);

У конфігураційних файлах ми можемо записати звичайну нотацію %projectId% для доступу до параметра з ім'ям projectId.

Динамічні параметри

Можна також додати динамічні параметри в контейнер. Їхні різні значення, на відміну від статичних параметрів, не призведуть до генерації нових DI-контейнерів.

$configurator->addDynamicParameters([
	'remoteIp' => $_SERVER['REMOTE_ADDR'],
]);

Змінні середовища можуть бути легко доступні з використанням динамічних параметрів. Ми можемо отримати доступ до них через %env.variable% у файлах конфігурації.

$configurator->addDynamicParameters([
	'env' => getenv(),
]);

Параметри за замовчуванням

Ви можете використовувати наступні статичні параметри у файлах конфігурації:

  • %appDir% – абсолютний шлях до каталогу з файлом Bootstrap.php
  • %wwwDir% – абсолютний шлях до каталогу, в якому знаходиться файл запису index.php
  • %tempDir% – абсолютний шлях до каталогу для тимчасових файлів
  • %vendorDir% – абсолютний шлях до каталогу, куди Composer встановлює бібліотеки
  • %debugMode% вказує на те, чи перебуває програма у режимі налагодження
  • %consoleMode% вказує на те, що запит надійшов через командний рядок

Імпортовані сервіси

Заглибимося далі. Хоча мета DI-контейнера у створенні об'єктів, може виникнути необхідність вставити наявний об'єкт у контейнер. Це робиться визначенням сервісу з атрибутом imported: true:

services:
	myservice:
		type: App\Model\MyCustomService
		imported: true

Створюємо новий екземпляр і вставляємо його в Bootstrap:

$configurator->addServices([
	'myservice' => new App\Model\MyCustomService('foobar'),
]);

Різні середовища

Не соромтеся налаштувати клас Bootstrap відповідно до ваших потреб. Ви можете додавати параметри до методу boot() для розділення веб-проєктів, або додавати інші методи, як-от bootForTests(), які ініціалізують середовище для модульних тестів, bootForCli() для скриптів, що викликаються з командного рядка, і так далі.

public static function bootForTests(): Configurator
{
	$configurator = self::boot();
	Tester\Environment::setup(); // Инициализация Nette Tester
	return $configurator;
}