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/local.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. По умолчанию конфигуратор заполняет следующие параметры: appDir, wwwDir, tempDir, vendorDir, debugMode и consoleMode.

Динамические параметры

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

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

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

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

Импортированные сервисы

Углубимся дальше. Хотя цель 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;
}