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();
}