Nette Documentation Preview

syntax
Bootstrap
*********

<div class=perex>

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 тільки реєструє помилки, а зміни шаблонів та інших файлів не перевіряються.

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

Якщо ви хочете ввімкнути режим розробки в інших випадках, наприклад, для програмістів, які отримують доступ з певної IP-адреси, ви можете використовувати `setDebugMode()`:

```php
$this->configurator->setDebugMode('23.75.345.200'); // одна або більше IP-адрес
```

Ми безумовно рекомендуємо поєднувати IP-адресу з файлом cookie. Ми зберігатимемо секретний токен у cookie `nette-debug', например, `secret1234`, і режим розробки буде активовано для програмістів із такою комбінацією IP і cookie.

```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]
=============================

Configurator дає змогу вказати часовий пояс для вашого застосунку.

```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()` для ініціалізації середовища для модульних тестів, `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 – це завантажувальний код, який ініціалізує середовище, створює контейнер впровадження залежностей (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 тільки реєструє помилки, а зміни шаблонів та інших файлів не перевіряються.

Вибір режиму здійснюється шляхом автовизначення, тому зазвичай немає необхідності налаштовувати або перемикати що-небудь вручну. Режим розробки використовується, якщо застосунок запущено на 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();
}