Nette Documentation Preview

syntax
Bootstrap
*********

<div class=perex>

Bootstrap е кодът на bootstrap, който инициализира средата, създава контейнера за изпълнение на зависимости (DI) и стартира приложението. Обсъждаме:

- Как да настроите приложение, използващо файлове NEON
- Как да работите с режими за производство и разработка
- Как да създадете контейнер DI

</div>


Приложенията, независимо дали са уеб приложения или скриптове от команден ред, започват с инициализиране на средата под една или друга форма. В древни времена за това може да е отговарял файл, наречен например `include.inc.php`, който е бил включен в изходния файл.
В съвременните приложения на Nette той е заменен с класа `Bootstrap`, който се намира като част от приложението във файла `app/Bootstrap.php`. Това може да изглежда например така:

```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 .[#toc-index-php]
===========================

Началният файл за уеб приложенията е `index.php`, разположен в публичната директория `www/`. Той използва класа `Bootstrap` за инициализиране на средата и създаване на контейнер DI. След това получава услугата `Application` от контейнера, която стартира уеб приложението:

```php
$bootstrap = new App\Bootstrap;
// Иницииране на средата + създаване на контейнер DI
$container = $bootstrap->bootWebApplication();
// Контейнерът DI създава обект Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Стартирайте приложението Nette и обработете входящата заявка
$application->run();
```

Както виждате, класът [api:Nette\Bootstrap\Configurator], който сега ще представим по-подробно, помага при настройването на средата и създаването на контейнера за инжектиране на зависимости (DI).


Режим на разработка и производствен режим .[#toc-development-vs-production-mode]
================================================================================

Nette прави разграничение между два основни режима, в които се изпълнява заявката: разработка и производство. Режимът за разработка е насочен към максимално удобство за програмиста, показва се Tracy, кешът се обновява автоматично при промяна на шаблоните или конфигурацията на DI контейнера и т.н. Производственият режим е ориентиран към производителността, Tracy регистрира само грешки, а промените в шаблоните и другите файлове не се проверяват.

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

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

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

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

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

Можете да деактивирате напълно режима за разработчици, дори за localhost:

```php
$this->configurator->setDebugMode(false);
```

Обърнете внимание, че стойността `true` активира плътно режима за разработчици, което никога не трябва да се случва на производствен сървър.


Инструмент за отстраняване на грешки в Tracy .[#toc-debugging-tool-tracy]
=========================================================================

За да улесним дебъгването, ще включим чудесния инструмент [Tracy |tracy:]. В режим за разработчици той визуализира грешките, а в производствен режим записва грешките в определена директория:

```php
$this->configurator->enableTracy($this->rootDir . '/log');
```


Временни файлове .[#toc-temporary-files]
========================================

Nette използва кеш за DI-контейнер, RobotLoader, шаблони и др. Затова е необходимо да се зададе пътят до директорията, в която се съхранява кешът:

```php
$this->configurator->setTempDirectory($this->rootDir . '/temp');
```

В Linux или macOS задайте [разрешения за запис |nette:troubleshooting#Setting-Directory-Permissions] за директориите `log/` и `temp/`.


RobotLoader .[#toc-robotloader]
===============================

Обикновено искаме да заредим класовете автоматично с помощта на [RobotLoader |robot-loader:], така че трябва да го стартираме и да му позволим да зареди класовете от директорията, в която се намира `Bootstrap.php` (т.е. `__DIR__`) и всички негови поддиректории:

```php
$this->configurator->createRobotLoader()
	->addDirectory(__DIR__)
	->register();
```

Алтернативен начин е да се използва само автозадаващото устройство PSR-4 [Composer |best-practices:composer].


Часова зона .[#toc-timezone]
============================

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

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


Конфигурация на DI-контейнер .[#toc-di-container-configuration]
===============================================================

Част от процеса на изтегляне е създаването на контейнера DI, т.е. фабриката за обекти, която е сърцето на цялото приложение. Всъщност това е клас на PHP, създаден от Nette и съхраняван в директория за кеш. Фабриката създава ключовите обекти на приложението, а конфигурационните файлове я инструктират как да ги създава и конфигурира, като по този начин влияем върху поведението на цялото приложение.

Файловете за конфигурация обикновено се записват във формат [NEON |neon:format]. Можете да прочетете [какво може да се конфигурира тук |nette:configuring].

.[tip]
В режим на разработка контейнерът се актуализира автоматично при всяка промяна на кода или конфигурационните файлове. В производствен режим той се генерира само веднъж и промените във файловете не се проверяват за максимална производителност.

Файловете за конфигурация се зареждат с помощта на `addConfig()`:

```php
$this->configurator->addConfig($this->rootDir . '/config/common.neon');
```

Методът `addConfig()` може да се извика няколко пъти, за да се добавят няколко файла.

```php
$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` |dependency-injection:configuration#Including-Files] за зареждане на конфигурационни файлове.

Ако в конфигурационните файлове се появят елементи със същите ключове, те ще бъдат [презаписани или обединени в |dependency-injection:configuration#Merging] случай на масиви. По-късно включен файл има по-висок приоритет от предишния. Файлът, посочен в раздела `includes`, има по-висок приоритет от файловете, включени в него.


Статични параметри .[#toc-static-parameters]
--------------------------------------------

Параметрите, използвани в конфигурационните файлове, могат да бъдат дефинирани [в раздела `parameters` |dependency-injection:configuration#parameters] и да бъдат взети (или презаписани) от метода `addStaticParameters()` (той има псевдоним `addParameters()`). Важно е, че различните стойности на параметрите водят до генериране на допълнителни DI-контейнери, т.е. допълнителни класове.

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

В конфигурационните файлове можем да запишем обичайната нотация `%projectId%`, за да получим достъп до параметъра с име `projectId`.


Динамични параметри .[#toc-dynamic-parameters]
----------------------------------------------

Възможно е също така да се добавят динамични параметри към контейнер. Различните им стойности, за разлика от статичните параметри, не генерират нови контейнери DI.

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

Достъпът до променливите на средата е лесен с помощта на динамични параметри. Достъпът до тях се осъществява чрез `%env.variable%` в конфигурационните файлове.

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


Параметри по подразбиране .[#toc-default-parameters]
----------------------------------------------------

Можете да използвате следните статични параметри в конфигурационните файлове:

- `%appDir%` е абсолютният път до директорията на файла `Bootstrap.php`
- `%wwwDir%` е абсолютният път до директорията, съдържаща входния файл `index.php`
- `%tempDir%` е абсолютният път до директорията за временни файлове
- `%vendorDir%` е абсолютният път до директорията, в която Composer инсталира библиотеки
- `%rootDir%` е абсолютният път до главната директория на проекта
- `%debugMode%` показва дали приложението е в режим на отстраняване на грешки
- `%consoleMode%` показва дали заявката е постъпила от командния ред


Внесени услуги .[#toc-imported-services]
----------------------------------------

Нека се задълбочим. Въпреки че целта на контейнера DI е да създава обекти, може да се наложи в контейнера да се вмъкне съществуващ обект. Това става чрез дефиниране на услуга с атрибута `imported: true`:

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

Създайте нов екземпляр и го вмъкнете в Bootstrap:

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


Различни среди .[#toc-different-environments]
=============================================

Не се колебайте да персонализирате класа `Bootstrap` според нуждите си. Можете да добавите параметри към метода `bootWebApplication()`, за да разграничите отделните уеб проекти. Като алтернатива можете да добавите и други методи, например `bootTestEnvironment()` за инициализиране на средата за unit тестове, `bootConsoleApplication()` за скриптове, извикани от командния ред, и т.н.

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

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