Bootstrap

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, кешът се обновява автоматично при промяна на шаблоните или конфигурацията на DI контейнера и т.н. Производственият режим е ориентиран към производителността, Tracy регистрира само грешки, а промените в шаблоните и другите файлове не се проверяват.

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

Ако искате да активирате режима за разработка в други случаи, например за програмисти, които имат достъп от определен IP адрес, можете да използвате setDebugMode():

$this->configurator->setDebugMode('23.75.345.200'); // един или повече IP адреси

Определено препоръчваме да комбинирате IP адреса с “бисквитка”. Ще съхраним тайния токен в “бисквитката” nette-debug', например, `secret1234, а режимът за разработка ще бъде активиран за програмистите с тази комбинация от IP и “бисквитка”.

$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.

Часова зона

Конфигураторът ви позволява да зададете часовата зона за вашето приложение.

$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() за инициализиране на средата за unit тестове, 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();
}