Nette Documentation Preview

syntax
Bootstrap
*********

<div class=perex>

Bootstrap je zaváděcí kód, který inicializuje prostředí, vytvoří dependency injection (DI) kontejner a spustí aplikaci. Řekneme si:

- jak se konfiguruje pomocí NEON souborů
- jak rozlišit produkční a vývojářský režim
- jak vytvořit DI kontejner

</div>


Aplikace, ať už jde o ty webové nebo skripty spouštěné z příkazové řádky, začínají svůj běh nějakou formou inicializace prostředí. V dávných dobách to míval na starosti soubor s názvem třeba `include.inc.php`, který prvotní soubor inkludoval.
V moderních Nette aplikacích jej nahradila třída `Bootstrap`, kterou jakožto součást aplikace najdete v souboru `app/Bootstrap.php`. Může vypadat kupříkladu takto:

```php
use Nette\Bootstrap\Configurator;

class Bootstrap
{
	private Configurator $configurator;
	private string $rootDir;

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// Konfigurátor je zodpovědný za nastavení prostředí aplikace a služeb.
		$this->configurator = new Configurator;
		// Nastaví adresář pro dočasné soubory generované Nette (např. zkompilované šablony)
		$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 je chytré a vývojový režim se zapíná automaticky,
		// nebo jej můžete povolit pro konkrétní IP adresu odkomentováním následujícího řádku:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Aktivuje Tracy: ultimátní "švýcarský nůž" pro ladění.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: automaticky načítá všechny třídy ve zvoleném adresáři
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Načte konfigurační soubory
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}
```


index.php
=========

Prvotní soubor je v případě webových aplikací `index.php`, který se nachází ve veřejném adresáři `www/`. Ten si nechá od třídy Bootstrap inicializovat prostředí a vyrobit DI kontejner. Poté z něj získá službu `Application`, která spustí webovou aplikaci:

```php
$bootstrap = new App\Bootstrap;
// Inicializace prostředí + vytvoření DI kontejneru
$container = $bootstrap->bootWebApplication();
// DI kontejner vytvoří objekt Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Spuštění aplikace Nette a zpracování příchozího požadavku
$application->run();
```

Jak vidno, s nastavením prostředí a vytvořením dependency injection (DI) kontejneru pomáhá třída [api:Nette\Bootstrap\Configurator], kterou si nyní blíže představíme.


Vývojářský vs produkční režim
=============================

Nette rozlišuje dva základní režimy, ve kterých se požadavek vykoná: vývojářský a produkční. Vývojářský je zaměřen na maximální pohodlí programátora, zobrazuje se Tracy, automaticky se aktualizuje cache při změně šablon nebo konfigurace DI kontejneru, atd. Produkční je zaměřený na výkon a ostré nasazení, Tracy chyby pouze loguje a změny šablon a dalších souborů se netestují.

Volba režimu se provádí autodetekcí, takže obvykle není potřeba nic konfigurovat nebo ručně přepínat. Režim je vývojářský tehdy, pokud je aplikace spuštěna na localhostu (tj. IP adresa `127.0.0.1` nebo `::1`) a není přitomna proxy (tj. její HTTP hlavička). Jinak běží v produkčním režimu.

Pokud chceme vývojářský režim povolit i v dalších případech, například programátorům přistupujícím z konkrétní IP adresy, použijeme `setDebugMode()`:

```php
$this->configurator->setDebugMode('23.75.345.200'); // lze uvést i pole IP adres
```

Rozhodně doporučujeme kombinovat IP adresu s cookie. Do cookie `nette-debug` uložíme tajný token, např. `secret1234`, a tímto způsobem aktivujeme vývojářský režim pro programátory přistupující z konkrétní IP adresy a zároveň mající v cookie zmíněný token:

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

Vývojářský režim můžeme také vypnout úplně, i pro localhost:

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

Pozor, hodnota `true` zapne vývojářský režim natvrdo, což se nikdy nesmí stát na produkčním serveru.


Debugovací nástroj Tracy
========================

Pro snadné debugování ještě zapneme skvělý nástroj [Tracy |tracy:]. Ve vývojářském režimu chyby vizualizuje a v produkčním režimu chyby loguje do uvedeného adresáře:

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


Dočasné soubory
===============

Nette využívá cache pro DI kontejner, RobotLoader, šablony atd. Proto je nutné nastavit cestu k adresáři, kam se bude cache ukládat:

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

Na Linuxu nebo macOS nastavte adresářům `log/` a `temp/` [práva pro zápis |nette:troubleshooting#Nastavení práv adresářů].


RobotLoader
===========

Zpravidla budeme chtít automaticky načítat třídy pomocí [RobotLoaderu |robot-loader:], musíme ho tedy nastartovat a necháme jej načítat třídy z adresáře, kde je umístěný `Bootstrap.php` (tj. `__DIR__`), a všech podadresářů:

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

Alternativní přístup je nechat třídy načítat pouze přes [Composer |best-practices:composer] při dodržení PSR-4.


Timezone
========

Přes konfigurátor můžete nastavit výchozí časovou zónu.

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


Konfigurace DI kontejneru
=========================

Součástí bootovacího procesu je vytvoření DI kontejneru neboli továrny na objekty, což je srdce celé aplikace. Jde vlastně o PHP třídu, kterou vygeneruje Nette a uloží do adresáře s cache. Továrna vyrábí klíčové objekty aplikace a pomocí konfiguračních souborů jí instruujeme, jak je má vytvářet a nastavovat, čímž ovlivňujeme chování celé aplikace.

Konfigurační soubory se obvykle zapisují ve formátu [NEON |neon:format]. V samostatné kapitole se dočtete, [co vše lze konfigurovat |nette:configuring].

.[tip]
Ve vývojářském režimu se kontejner automaticky aktualizuje při každé změně kódu nebo konfiguračních souborů. V produkčním režimu se vygeneruje jen jednou a změny se kvůli maximalizaci výkonu nekontrolují.

Konfigurační soubory načteme pomocí `addConfig()`:

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

Pokud chceme přidat více konfiguračních souborů, můžeme funkci `addConfig()` zavolat vícekrát.

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

Název `cli.php` není překlep, konfigurace může být zapsaná také v PHP souboru, který ji vrátí jako pole.

Také můžeme přidat další konfigurační soubory v [sekci `includes` |dependency-injection:configuration#Vkládání souborů].

Pokud se v konfiguračních souborech objeví prvky se stejnými klíči, budou přepsány, nebo v případě [polí sloučeny |dependency-injection:configuration#Slučování]. Později vkládaný soubor má vyšší prioritu než předchozí. Soubor, ve kterém je sekce `includes` uvedena, má vyšší prioritu než v něm inkludované soubory.


Statické parametry
------------------

Parametry používané v konfiguračních souborech můžeme definovat [v sekci `parameters`|dependency-injection:configuration#parametry] a také je předávat (či přepisovat) metodou `addStaticParameters()` (má alias `addParameters()`). Důležité je, že různé hodnoty parametrů způsobí vygenerování dalších DI kontejnerů, tedy dalších tříd.

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

Na parametr `projectId` se lze v konfiguraci odkázat obvyklým zápisem `%projectId%`.


Dynamické parametry
-------------------

Do kontejneru můžeme přidat i dynamické parametry, jejichž různé hodnoty na rozdíl od statických parameterů nezpůsobí generování nových DI kontejnerů.

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

Jednoduše tak můžeme přidat např. environmentální proměnné, na které se pak lze v konfiguraci odkázat zápisem `%env.variable%`.

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


Výchozí parametry
-----------------

V konfiguračních souborech můžete využít tyto statické parametry:

- `%appDir%` je absolutní cesta k adresáři se souborem `Bootstrap.php`
- `%wwwDir%` je absolutní cesta k adresáři se vstupním souborem `index.php`
- `%tempDir%` je absolutní cesta k adresáři pro dočasné soubory
- `%vendorDir%` je absolutní cesta k adresáři, kam Composer instaluje knihovny
- `%rootDir%` je absolutní cesta ke kořenovému adresáři projektu
- `%debugMode%` udává, zda je aplikace v debugovacím režimu
- `%consoleMode%` udává, zda request přišel přes příkazovou řádku


Importované služby
------------------

Nyní už jdeme hlouběji. Ačkoliv je smyslem DI kontejneru objekty vyrábet, výjimečně může vzniknout potřeba do kontejneru existující objekt vložit. Uděláme to tak, že službu definujeme s příznakem `imported: true`.

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

A v bootstrapu do kontejneru vložíme objekt:

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


Odlišné prostředí
=================

Nebojte se upravit třídu Bootstrap podle svých potřeb. Metodě `bootWebApplication()` můžete přidat parametry pro rozlišení webových projektů. Nebo můžeme doplnit další metody, například `bootTestEnvironment()`, která inicializuje prostředí pro jednotkové testy, `bootConsoleApplication()` pro skripty volané z příkazové řádky atd.

```php
public function bootTestEnvironment(): Nette\DI\Container
{
	Tester\Environment::setup(); // inicializace Nette Testeru
	$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 je zaváděcí kód, který inicializuje prostředí, vytvoří dependency injection (DI) kontejner a spustí aplikaci. Řekneme si:

  • jak se konfiguruje pomocí NEON souborů
  • jak rozlišit produkční a vývojářský režim
  • jak vytvořit DI kontejner

Aplikace, ať už jde o ty webové nebo skripty spouštěné z příkazové řádky, začínají svůj běh nějakou formou inicializace prostředí. V dávných dobách to míval na starosti soubor s názvem třeba include.inc.php, který prvotní soubor inkludoval. V moderních Nette aplikacích jej nahradila třída Bootstrap, kterou jakožto součást aplikace najdete v souboru app/Bootstrap.php. Může vypadat kupříkladu takto:

use Nette\Bootstrap\Configurator;

class Bootstrap
{
	private Configurator $configurator;
	private string $rootDir;

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// Konfigurátor je zodpovědný za nastavení prostředí aplikace a služeb.
		$this->configurator = new Configurator;
		// Nastaví adresář pro dočasné soubory generované Nette (např. zkompilované šablony)
		$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 je chytré a vývojový režim se zapíná automaticky,
		// nebo jej můžete povolit pro konkrétní IP adresu odkomentováním následujícího řádku:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Aktivuje Tracy: ultimátní "švýcarský nůž" pro ladění.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: automaticky načítá všechny třídy ve zvoleném adresáři
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Načte konfigurační soubory
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}

index.php

Prvotní soubor je v případě webových aplikací index.php, který se nachází ve veřejném adresáři www/. Ten si nechá od třídy Bootstrap inicializovat prostředí a vyrobit DI kontejner. Poté z něj získá službu Application, která spustí webovou aplikaci:

$bootstrap = new App\Bootstrap;
// Inicializace prostředí + vytvoření DI kontejneru
$container = $bootstrap->bootWebApplication();
// DI kontejner vytvoří objekt Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Spuštění aplikace Nette a zpracování příchozího požadavku
$application->run();

Jak vidno, s nastavením prostředí a vytvořením dependency injection (DI) kontejneru pomáhá třída Nette\Bootstrap\Configurator, kterou si nyní blíže představíme.

Vývojářský vs produkční režim

Nette rozlišuje dva základní režimy, ve kterých se požadavek vykoná: vývojářský a produkční. Vývojářský je zaměřen na maximální pohodlí programátora, zobrazuje se Tracy, automaticky se aktualizuje cache při změně šablon nebo konfigurace DI kontejneru, atd. Produkční je zaměřený na výkon a ostré nasazení, Tracy chyby pouze loguje a změny šablon a dalších souborů se netestují.

Volba režimu se provádí autodetekcí, takže obvykle není potřeba nic konfigurovat nebo ručně přepínat. Režim je vývojářský tehdy, pokud je aplikace spuštěna na localhostu (tj. IP adresa 127.0.0.1 nebo ::1) a není přitomna proxy (tj. její HTTP hlavička). Jinak běží v produkčním režimu.

Pokud chceme vývojářský režim povolit i v dalších případech, například programátorům přistupujícím z konkrétní IP adresy, použijeme setDebugMode():

$this->configurator->setDebugMode('23.75.345.200'); // lze uvést i pole IP adres

Rozhodně doporučujeme kombinovat IP adresu s cookie. Do cookie nette-debug uložíme tajný token, např. secret1234, a tímto způsobem aktivujeme vývojářský režim pro programátory přistupující z konkrétní IP adresy a zároveň mající v cookie zmíněný token:

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

Vývojářský režim můžeme také vypnout úplně, i pro localhost:

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

Pozor, hodnota true zapne vývojářský režim natvrdo, což se nikdy nesmí stát na produkčním serveru.

Debugovací nástroj Tracy

Pro snadné debugování ještě zapneme skvělý nástroj Tracy. Ve vývojářském režimu chyby vizualizuje a v produkčním režimu chyby loguje do uvedeného adresáře:

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

Dočasné soubory

Nette využívá cache pro DI kontejner, RobotLoader, šablony atd. Proto je nutné nastavit cestu k adresáři, kam se bude cache ukládat:

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

Na Linuxu nebo macOS nastavte adresářům log/ a temp/ práva pro zápis.

RobotLoader

Zpravidla budeme chtít automaticky načítat třídy pomocí RobotLoaderu, musíme ho tedy nastartovat a necháme jej načítat třídy z adresáře, kde je umístěný Bootstrap.php (tj. __DIR__), a všech podadresářů:

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

Alternativní přístup je nechat třídy načítat pouze přes Composer při dodržení PSR-4.

Timezone

Přes konfigurátor můžete nastavit výchozí časovou zónu.

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

Konfigurace DI kontejneru

Součástí bootovacího procesu je vytvoření DI kontejneru neboli továrny na objekty, což je srdce celé aplikace. Jde vlastně o PHP třídu, kterou vygeneruje Nette a uloží do adresáře s cache. Továrna vyrábí klíčové objekty aplikace a pomocí konfiguračních souborů jí instruujeme, jak je má vytvářet a nastavovat, čímž ovlivňujeme chování celé aplikace.

Konfigurační soubory se obvykle zapisují ve formátu NEON. V samostatné kapitole se dočtete, co vše lze konfigurovat.

Ve vývojářském režimu se kontejner automaticky aktualizuje při každé změně kódu nebo konfiguračních souborů. V produkčním režimu se vygeneruje jen jednou a změny se kvůli maximalizaci výkonu nekontrolují.

Konfigurační soubory načteme pomocí addConfig():

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

Pokud chceme přidat více konfiguračních souborů, můžeme funkci addConfig() zavolat vícekrát.

$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');
}

Název cli.php není překlep, konfigurace může být zapsaná také v PHP souboru, který ji vrátí jako pole.

Také můžeme přidat další konfigurační soubory v sekci includes.

Pokud se v konfiguračních souborech objeví prvky se stejnými klíči, budou přepsány, nebo v případě polí sloučeny. Později vkládaný soubor má vyšší prioritu než předchozí. Soubor, ve kterém je sekce includes uvedena, má vyšší prioritu než v něm inkludované soubory.

Statické parametry

Parametry používané v konfiguračních souborech můžeme definovat v sekci parameters a také je předávat (či přepisovat) metodou addStaticParameters() (má alias addParameters()). Důležité je, že různé hodnoty parametrů způsobí vygenerování dalších DI kontejnerů, tedy dalších tříd.

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

Na parametr projectId se lze v konfiguraci odkázat obvyklým zápisem %projectId%.

Dynamické parametry

Do kontejneru můžeme přidat i dynamické parametry, jejichž různé hodnoty na rozdíl od statických parameterů nezpůsobí generování nových DI kontejnerů.

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

Jednoduše tak můžeme přidat např. environmentální proměnné, na které se pak lze v konfiguraci odkázat zápisem %env.variable%.

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

Výchozí parametry

V konfiguračních souborech můžete využít tyto statické parametry:

  • %appDir% je absolutní cesta k adresáři se souborem Bootstrap.php
  • %wwwDir% je absolutní cesta k adresáři se vstupním souborem index.php
  • %tempDir% je absolutní cesta k adresáři pro dočasné soubory
  • %vendorDir% je absolutní cesta k adresáři, kam Composer instaluje knihovny
  • %rootDir% je absolutní cesta ke kořenovému adresáři projektu
  • %debugMode% udává, zda je aplikace v debugovacím režimu
  • %consoleMode% udává, zda request přišel přes příkazovou řádku

Importované služby

Nyní už jdeme hlouběji. Ačkoliv je smyslem DI kontejneru objekty vyrábet, výjimečně může vzniknout potřeba do kontejneru existující objekt vložit. Uděláme to tak, že službu definujeme s příznakem imported: true.

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

A v bootstrapu do kontejneru vložíme objekt:

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

Odlišné prostředí

Nebojte se upravit třídu Bootstrap podle svých potřeb. Metodě bootWebApplication() můžete přidat parametry pro rozlišení webových projektů. Nebo můžeme doplnit další metody, například bootTestEnvironment(), která inicializuje prostředí pro jednotkové testy, bootConsoleApplication() pro skripty volané z příkazové řádky atd.

public function bootTestEnvironment(): Nette\DI\Container
{
	Tester\Environment::setup(); // inicializace Nette Testeru
	$this->setupContainer();
	return $this->configurator->createContainer();
}

public function bootConsoleApplication(): Nette\DI\Container
{
	$this->configurator->setDebugMode(false);
	$this->initializeEnvironment();
	$this->setupContainer();
	return $this->configurator->createContainer();
}