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__);
// Конфигуратор отвечает за настройку среды и служб приложения.
$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();
Как вы видите, класс Nette\Bootstrap\Configurator, который мы сейчас представим более подробно, помогает в настройке окружения и создании контейнера внедрения зависимостей (DI).
Режим разработки и режим производства
Nette ведет себя по-разному в зависимости от того, работает ли он на сервере разработки или на рабочем сервере:
- 🛠️ Режим разработки
- Отображает панель отладки Tracy с полезной информацией (например, SQL-запросы, время выполнения, использование памяти).
- При возникновении ошибки показывает подробную страницу ошибки с трассировкой вызовов функций и содержимым переменных.
- Автоматически обновляет кэш при изменении шаблонов Latte, файлов конфигурации и т. д.
- 🚀 Производственный режим
- Не отображает никакой отладочной информации; все ошибки записываются в журнал.
- При возникновении ошибки показывает
ErrorPresenter
или общую страницу “Ошибка сервера”. - Кэш никогда не обновляется автоматически!
- Оптимизирован для скорости и безопасности.
Режим определяется автоматически, поэтому в большинстве случаев нет необходимости настраивать или переключать его вручную:
- Режим разработки: Активен на 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();
Альтернативный способ — использовать только автозагрузку PSR-4 Composer.
Часовой пояс
Configurator позволяет указать часовой пояс для вашего приложения.
$this->configurator->setTimeZone('Europe/Prague');
Конфигурация 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();
}