Nette Documentation Preview

syntax
Bootstrapping
*************

<div class=perex>

A bootstrapping az alkalmazás környezetének inicializálása, egy dependency injection (DI) konténer létrehozása és az alkalmazás elindítása. A következőkről fogunk beszélni:

- hogyan inicializálja a Bootstrap osztály a környezetet
- hogyan konfigurálhatók az alkalmazások NEON fájlok használatával
- hogyan különböztessük meg a produkciós és fejlesztői módot
- hogyan hozzuk létre és konfiguráljuk a DI konténert

</div>


Az alkalmazások, legyenek azok webesek vagy parancssorból futtatott szkriptek, működésüket valamilyen környezet inicializálási formával kezdik. Régen ezt egy `include.inc.php` nevű fájl intézte, amelyet az elsődleges fájl inkludált. A modern Nette alkalmazásokban ezt a `Bootstrap` osztály váltotta fel, amelyet az alkalmazás részeként az `app/Bootstrap.php` fájlban találhat meg. Például így nézhet ki:

```php
use Nette\Bootstrap\Configurator;

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

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// A Configurator felelős az alkalmazás környezetének és szolgáltatásainak beállításáért.
		$this->configurator = new Configurator;
		// Beállítja a Nette által generált ideiglenes fájlok (pl. fordított sablonok) könyvtárát
		$this->configurator->setTempDirectory($this->rootDir . '/temp');
	}

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

	private function initializeEnvironment(): void
	{
		// A Nette okos, és a fejlesztői mód automatikusan bekapcsolódik,
		// vagy engedélyezheti egy adott IP-címre a következő sor kommentjének eltávolításával:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Aktiválja a Tracy-t: a végső "svájci bicska" a debuggoláshoz.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: automatikusan betölti az összes osztályt a kiválasztott könyvtárban
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Betölti a konfigurációs fájlokat
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}
```


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

A webalkalmazások esetében az elsődleges fájl az `index.php`, amely a [nyilvános könyvtárban |directory-structure#Nyilvános könyvtár www] (`www/`) található. Ez a Bootstrap osztálytól kéri a környezet inicializálását és a DI konténer létrehozását. Ezután ebből szerzi be az `Application` szolgáltatást, amely elindítja a webalkalmazást:

```php
$bootstrap = new App\Bootstrap;
// Környezet inicializálása + DI konténer létrehozása
$container = $bootstrap->bootWebApplication();
// A DI konténer létrehozza a Nette\Application\Application objektumot
$application = $container->getByType(Nette\Application\Application::class);
// A Nette alkalmazás elindítása és a bejövő kérés feldolgozása
$application->run();
```

Mint látható, a környezet beállításában és a dependency injection (DI) konténer létrehozásában a [api:Nette\Bootstrap\Configurator] osztály segít, amelyet most részletesebben bemutatunk.


Fejlesztői vs éles mód
======================

A Nette eltérően viselkedik attól függően, hogy fejlesztői vagy éles szerveren fut:

🛠️  Fejlesztői mód (Development):
	- Megjeleníti a Tracy debugbart hasznos információkkal (SQL lekérdezések, végrehajtási idő, felhasznált memória)
	- Hiba esetén részletes hibaoldalt jelenít meg a függvényhívásokkal és a változók tartalmával
	- Automatikusan frissíti a cache-t a Latte sablonok módosításakor, a konfigurációs fájlok szerkesztésekor stb.


🚀  Éles mód (Production):
	- Nem jelenít meg semmilyen debuggolási információt, minden hibát a logba ír
	- Hiba esetén az ErrorPresentert vagy egy általános "Server Error" oldalt jelenít meg
	- A cache soha nem frissül automatikusan!
	- Optimalizálva a sebességre és a biztonságra


A mód kiválasztása automatikus felismeréssel történik, így általában nincs szükség semmit konfigurálni vagy manuálisan átváltani:

- fejlesztői mód: localhoston (IP-cím `127.0.0.1` vagy `::1`), ha nincs proxy (azaz annak HTTP fejléce)
- éles mód: mindenhol máshol

Ha a fejlesztői módot más esetekben is engedélyezni szeretnénk, például egy adott IP-címről hozzáférő programozók számára, használjuk a `setDebugMode()` metódust:

```php
$this->configurator->setDebugMode('23.75.345.200'); // IP-címek tömbje is megadható
```

Határozottan javasoljuk az IP-cím és a cookie kombinálását. A `nette-debug` cookie-ba mentsünk el egy titkos tokent, pl. `secret1234`, és így aktiváljuk a fejlesztői módot az adott IP-címről hozzáférő és a cookie-ban említett tokennel rendelkező programozók számára:

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

A fejlesztői módot teljesen ki is kapcsolhatjuk, még localhostra is:

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

Figyelem, a `true` érték véglegesen bekapcsolja a fejlesztői módot, ami soha nem történhet meg éles szerveren.


Tracy debuggoló eszköz
======================

A könnyű debuggolás érdekében kapcsoljuk be a nagyszerű [Tracy |tracy:] eszközt. Fejlesztői módban vizualizálja a hibákat, éles módban pedig a hibákat a megadott könyvtárba logolja:

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


Ideiglenes fájlok
=================

A Nette cache-t használ a DI konténerhez, a RobotLoaderhez, a sablonokhoz stb. Ezért szükséges beállítani annak a könyvtárnak az elérési útját, ahová a cache mentésre kerül:

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

Linuxon vagy macOS-en állítsa be a `log/` és `temp/` könyvtáraknak az [írási jogokat |nette:troubleshooting#Könyvtárjogosultságok beállítása].


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

Általában szeretnénk automatikusan betölteni az osztályokat a [RobotLoader |robot-loader:] segítségével, ezért el kell indítanunk, és hagynunk kell, hogy betöltse az osztályokat abból a könyvtárból, ahol a `Bootstrap.php` található (azaz `__DIR__`), és az összes alkönyvtárából:

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

Alternatív megközelítés az osztályok betöltésének kizárólag a [Composer |best-practices:composer] segítségével történő engedélyezése a PSR-4 betartása mellett.


Időzóna
=======

A konfigurátoron keresztül beállíthatja az alapértelmezett időzónát.

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


DI konténer konfigurálása
=========================

Az indítási folyamat része a DI konténer, vagyis az objektumgyár létrehozása, amely az egész alkalmazás szíve. Ez valójában egy PHP osztály, amelyet a Nette generál és a cache könyvtárba ment. A gyár gyártja az alkalmazás kulcsfontosságú objektumait, és a konfigurációs fájlok segítségével utasítjuk, hogyan hozza létre és állítsa be őket, ezzel befolyásolva az egész alkalmazás viselkedését.

A konfigurációs fájlokat általában [NEON |neon:format] formátumban írják. Egy külön fejezetben olvashat arról, [mit lehet konfigurálni |nette:configuring].

.[tip]
Fejlesztői módban a konténer automatikusan frissül minden kód- vagy konfigurációs fájl módosításakor. Éles módban csak egyszer generálódik, és a változások a maximális teljesítmény érdekében nem kerülnek ellenőrzésre.

A konfigurációs fájlokat a `addConfig()` segítségével töltjük be:

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

Ha több konfigurációs fájlt szeretnénk hozzáadni, többször is meghívhatjuk az `addConfig()` függvényt.

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

A `cli.php` név nem elírás, a konfiguráció PHP fájlban is megadható, amely tömbként adja vissza.

További konfigurációs fájlokat is hozzáadhatunk az [`includes` szekcióban |dependency-injection:configuration#Fájlok beillesztése].

Ha a konfigurációs fájlokban azonos kulcsokkal rendelkező elemek jelennek meg, azok felülíródnak, vagy [tömbök esetén egyesülnek |dependency-injection:configuration#Összefésülés]. A később beillesztett fájlnak magasabb prioritása van, mint az előzőnek. Annak a fájlnak, amelyben az `includes` szekció szerepel, magasabb prioritása van, mint a benne inkludált fájloknak.


Statikus paraméterek
--------------------

A konfigurációs fájlokban használt paramétereket definiálhatjuk [a `parameters` szekcióban |dependency-injection:configuration#Paraméterek], és átadhatjuk (vagy felülírhatjuk) az `addStaticParameters()` metódussal (van `addParameters()` aliasa is). Fontos, hogy a paraméterek különböző értékei további DI konténerek, azaz további osztályok generálását eredményezik.

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

A `projectId` paraméterre a konfigurációban a szokásos `%projectId%` jelöléssel lehet hivatkozni.


Dinamikus paraméterek
---------------------

A konténerhez dinamikus paramétereket is hozzáadhatunk, amelyek különböző értékei, a statikus paraméterekkel ellentétben, nem okozzák új DI konténerek generálását.

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

Így egyszerűen hozzáadhatunk pl. környezeti változókat, amelyekre aztán a konfigurációban a `%env.variable%` jelöléssel lehet hivatkozni.

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


Alapértelmezett paraméterek
---------------------------

A konfigurációs fájlokban használhatja ezeket a statikus paramétereket:

- `%appDir%` az abszolút elérési út a `Bootstrap.php` fájlt tartalmazó könyvtárhoz
- `%wwwDir%` az abszolút elérési út a `index.php` bemeneti fájlt tartalmazó könyvtárhoz
- `%tempDir%` az abszolút elérési út az ideiglenes fájlok könyvtárához
- `%vendorDir%` az abszolút elérési út ahhoz a könyvtárhoz, ahová a Composer telepíti a könyvtárakat
- `%rootDir%` az abszolút elérési út a projekt gyökérkönyvtárához
- `%debugMode%` jelzi, hogy az alkalmazás debug módban van-e
- `%consoleMode%` jelzi, hogy a kérés parancssorból érkezett-e


Importált szolgáltatások
------------------------

Most mélyebbre megyünk. Bár a DI konténer célja az objektumok gyártása, kivételesen szükség lehet egy meglévő objektum beillesztésére a konténerbe. Ezt úgy tehetjük meg, hogy a szolgáltatást `imported: true` jelzővel definiáljuk.

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

És a bootstrapban beillesztjük az objektumot a konténerbe:

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


Eltérő környezet
================

Ne féljen módosítani a Bootstrap osztályt saját igényei szerint. A `bootWebApplication()` metódushoz hozzáadhat paramétereket a webprojektek megkülönböztetésére. Vagy kiegészíthetjük további metódusokkal, például `bootTestEnvironment()`, amely inicializálja a környezetet az egységtesztekhez, `bootConsoleApplication()` a parancssorból hívott szkriptekhez stb.

```php
public function bootTestEnvironment(): Nette\DI\Container
{
	Tester\Environment::setup(); // Nette Tester inicializálása
	$this->setupContainer();
	return $this->configurator->createContainer();
}

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

Bootstrapping

A bootstrapping az alkalmazás környezetének inicializálása, egy dependency injection (DI) konténer létrehozása és az alkalmazás elindítása. A következőkről fogunk beszélni:

  • hogyan inicializálja a Bootstrap osztály a környezetet
  • hogyan konfigurálhatók az alkalmazások NEON fájlok használatával
  • hogyan különböztessük meg a produkciós és fejlesztői módot
  • hogyan hozzuk létre és konfiguráljuk a DI konténert

Az alkalmazások, legyenek azok webesek vagy parancssorból futtatott szkriptek, működésüket valamilyen környezet inicializálási formával kezdik. Régen ezt egy include.inc.php nevű fájl intézte, amelyet az elsődleges fájl inkludált. A modern Nette alkalmazásokban ezt a Bootstrap osztály váltotta fel, amelyet az alkalmazás részeként az app/Bootstrap.php fájlban találhat meg. Például így nézhet ki:

use Nette\Bootstrap\Configurator;

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

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// A Configurator felelős az alkalmazás környezetének és szolgáltatásainak beállításáért.
		$this->configurator = new Configurator;
		// Beállítja a Nette által generált ideiglenes fájlok (pl. fordított sablonok) könyvtárát
		$this->configurator->setTempDirectory($this->rootDir . '/temp');
	}

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

	private function initializeEnvironment(): void
	{
		// A Nette okos, és a fejlesztői mód automatikusan bekapcsolódik,
		// vagy engedélyezheti egy adott IP-címre a következő sor kommentjének eltávolításával:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Aktiválja a Tracy-t: a végső "svájci bicska" a debuggoláshoz.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: automatikusan betölti az összes osztályt a kiválasztott könyvtárban
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Betölti a konfigurációs fájlokat
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}

index.php

A webalkalmazások esetében az elsődleges fájl az index.php, amely a nyilvános könyvtárban (www/) található. Ez a Bootstrap osztálytól kéri a környezet inicializálását és a DI konténer létrehozását. Ezután ebből szerzi be az Application szolgáltatást, amely elindítja a webalkalmazást:

$bootstrap = new App\Bootstrap;
// Környezet inicializálása + DI konténer létrehozása
$container = $bootstrap->bootWebApplication();
// A DI konténer létrehozza a Nette\Application\Application objektumot
$application = $container->getByType(Nette\Application\Application::class);
// A Nette alkalmazás elindítása és a bejövő kérés feldolgozása
$application->run();

Mint látható, a környezet beállításában és a dependency injection (DI) konténer létrehozásában a Nette\Bootstrap\Configurator osztály segít, amelyet most részletesebben bemutatunk.

Fejlesztői vs éles mód

A Nette eltérően viselkedik attól függően, hogy fejlesztői vagy éles szerveren fut:

🛠️ Fejlesztői mód (Development)
Megjeleníti a Tracy debugbart hasznos információkkal (SQL lekérdezések, végrehajtási idő, felhasznált memória)
Hiba esetén részletes hibaoldalt jelenít meg a függvényhívásokkal és a változók tartalmával
Automatikusan frissíti a cache-t a Latte sablonok módosításakor, a konfigurációs fájlok szerkesztésekor stb.
🚀 Éles mód (Production)
Nem jelenít meg semmilyen debuggolási információt, minden hibát a logba ír
Hiba esetén az ErrorPresentert vagy egy általános „Server Error“ oldalt jelenít meg
A cache soha nem frissül automatikusan!
Optimalizálva a sebességre és a biztonságra

A mód kiválasztása automatikus felismeréssel történik, így általában nincs szükség semmit konfigurálni vagy manuálisan átváltani:

  • fejlesztői mód: localhoston (IP-cím 127.0.0.1 vagy ::1), ha nincs proxy (azaz annak HTTP fejléce)
  • éles mód: mindenhol máshol

Ha a fejlesztői módot más esetekben is engedélyezni szeretnénk, például egy adott IP-címről hozzáférő programozók számára, használjuk a setDebugMode() metódust:

$this->configurator->setDebugMode('23.75.345.200'); // IP-címek tömbje is megadható

Határozottan javasoljuk az IP-cím és a cookie kombinálását. A nette-debug cookie-ba mentsünk el egy titkos tokent, pl. secret1234, és így aktiváljuk a fejlesztői módot az adott IP-címről hozzáférő és a cookie-ban említett tokennel rendelkező programozók számára:

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

A fejlesztői módot teljesen ki is kapcsolhatjuk, még localhostra is:

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

Figyelem, a true érték véglegesen bekapcsolja a fejlesztői módot, ami soha nem történhet meg éles szerveren.

Tracy debuggoló eszköz

A könnyű debuggolás érdekében kapcsoljuk be a nagyszerű Tracy eszközt. Fejlesztői módban vizualizálja a hibákat, éles módban pedig a hibákat a megadott könyvtárba logolja:

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

Ideiglenes fájlok

A Nette cache-t használ a DI konténerhez, a RobotLoaderhez, a sablonokhoz stb. Ezért szükséges beállítani annak a könyvtárnak az elérési útját, ahová a cache mentésre kerül:

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

Linuxon vagy macOS-en állítsa be a log/ és temp/ könyvtáraknak az írási jogokat.

RobotLoader

Általában szeretnénk automatikusan betölteni az osztályokat a RobotLoader segítségével, ezért el kell indítanunk, és hagynunk kell, hogy betöltse az osztályokat abból a könyvtárból, ahol a Bootstrap.php található (azaz __DIR__), és az összes alkönyvtárából:

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

Alternatív megközelítés az osztályok betöltésének kizárólag a Composer segítségével történő engedélyezése a PSR-4 betartása mellett.

Időzóna

A konfigurátoron keresztül beállíthatja az alapértelmezett időzónát.

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

DI konténer konfigurálása

Az indítási folyamat része a DI konténer, vagyis az objektumgyár létrehozása, amely az egész alkalmazás szíve. Ez valójában egy PHP osztály, amelyet a Nette generál és a cache könyvtárba ment. A gyár gyártja az alkalmazás kulcsfontosságú objektumait, és a konfigurációs fájlok segítségével utasítjuk, hogyan hozza létre és állítsa be őket, ezzel befolyásolva az egész alkalmazás viselkedését.

A konfigurációs fájlokat általában NEON formátumban írják. Egy külön fejezetben olvashat arról, mit lehet konfigurálni.

Fejlesztői módban a konténer automatikusan frissül minden kód- vagy konfigurációs fájl módosításakor. Éles módban csak egyszer generálódik, és a változások a maximális teljesítmény érdekében nem kerülnek ellenőrzésre.

A konfigurációs fájlokat a addConfig() segítségével töltjük be:

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

Ha több konfigurációs fájlt szeretnénk hozzáadni, többször is meghívhatjuk az addConfig() függvényt.

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

A cli.php név nem elírás, a konfiguráció PHP fájlban is megadható, amely tömbként adja vissza.

További konfigurációs fájlokat is hozzáadhatunk az includes szekcióban.

Ha a konfigurációs fájlokban azonos kulcsokkal rendelkező elemek jelennek meg, azok felülíródnak, vagy tömbök esetén egyesülnek. A később beillesztett fájlnak magasabb prioritása van, mint az előzőnek. Annak a fájlnak, amelyben az includes szekció szerepel, magasabb prioritása van, mint a benne inkludált fájloknak.

Statikus paraméterek

A konfigurációs fájlokban használt paramétereket definiálhatjuk a parameters szekcióban, és átadhatjuk (vagy felülírhatjuk) az addStaticParameters() metódussal (van addParameters() aliasa is). Fontos, hogy a paraméterek különböző értékei további DI konténerek, azaz további osztályok generálását eredményezik.

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

A projectId paraméterre a konfigurációban a szokásos %projectId% jelöléssel lehet hivatkozni.

Dinamikus paraméterek

A konténerhez dinamikus paramétereket is hozzáadhatunk, amelyek különböző értékei, a statikus paraméterekkel ellentétben, nem okozzák új DI konténerek generálását.

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

Így egyszerűen hozzáadhatunk pl. környezeti változókat, amelyekre aztán a konfigurációban a %env.variable% jelöléssel lehet hivatkozni.

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

Alapértelmezett paraméterek

A konfigurációs fájlokban használhatja ezeket a statikus paramétereket:

  • %appDir% az abszolút elérési út a Bootstrap.php fájlt tartalmazó könyvtárhoz
  • %wwwDir% az abszolút elérési út a index.php bemeneti fájlt tartalmazó könyvtárhoz
  • %tempDir% az abszolút elérési út az ideiglenes fájlok könyvtárához
  • %vendorDir% az abszolút elérési út ahhoz a könyvtárhoz, ahová a Composer telepíti a könyvtárakat
  • %rootDir% az abszolút elérési út a projekt gyökérkönyvtárához
  • %debugMode% jelzi, hogy az alkalmazás debug módban van-e
  • %consoleMode% jelzi, hogy a kérés parancssorból érkezett-e

Importált szolgáltatások

Most mélyebbre megyünk. Bár a DI konténer célja az objektumok gyártása, kivételesen szükség lehet egy meglévő objektum beillesztésére a konténerbe. Ezt úgy tehetjük meg, hogy a szolgáltatást imported: true jelzővel definiáljuk.

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

És a bootstrapban beillesztjük az objektumot a konténerbe:

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

Eltérő környezet

Ne féljen módosítani a Bootstrap osztályt saját igényei szerint. A bootWebApplication() metódushoz hozzáadhat paramétereket a webprojektek megkülönböztetésére. Vagy kiegészíthetjük további metódusokkal, például bootTestEnvironment(), amely inicializálja a környezetet az egységtesztekhez, bootConsoleApplication() a parancssorból hívott szkriptekhez stb.

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

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