Nette Documentation Preview

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

<div class=perex>

Bootstrapping-ul este procesul de inițializare a mediului aplicației, crearea unui container de dependency injection (DI) și pornirea aplicației. Vom discuta:

- cum clasa Bootstrap inițializează mediul
- cum sunt configurate aplicațiile folosind fișiere NEON
- cum să distingem între modul de producție și dezvoltare
- cum să creăm și să configurăm containerul DI

</div>


Aplicațiile, fie că sunt web sau scripturi rulate din linia de comandă, își încep execuția cu o formă de inițializare a mediului. În trecut, acest lucru era de obicei gestionat de un fișier numit, de exemplu, `include.inc.php`, pe care fișierul inițial îl includea. În aplicațiile Nette moderne, acesta a fost înlocuit de clasa `Bootstrap`, pe care o veți găsi ca parte a aplicației în fișierul `app/Bootstrap.php`. Poate arăta, de exemplu, astfel:

```php
use Nette\Bootstrap\Configurator;

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

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// Configuratorul este responsabil pentru setarea mediului aplicației și a serviciilor.
		$this->configurator = new Configurator;
		// Setează directorul pentru fișierele temporare generate de Nette (de ex., șabloane compilate)
		$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 este inteligent și modul de dezvoltare se activează automat,
		// sau îl puteți activa pentru o anumită adresă IP decomentând următoarea linie:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Activează Tracy: "briceagul elvețian" suprem pentru depanare.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: încarcă automat toate clasele din directorul selectat
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Încarcă fișierele de configurare
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}
```


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

Fișierul inițial în cazul aplicațiilor web este `index.php`, care se află în [directorul public |directory-structure#Director public www] `www/`. Acesta solicită clasei Bootstrap să inițializeze mediul și să creeze containerul DI. Apoi, obține din acesta serviciul `Application`, care pornește aplicația web:

```php
$bootstrap = new App\Bootstrap;
// Inițializarea mediului + crearea containerului DI
$container = $bootstrap->bootWebApplication();
// Containerul DI creează obiectul Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Pornirea aplicației Nette și procesarea cererii primite
$application->run();
```

După cum se poate vedea, clasa [api:Nette\Bootstrap\Configurator] ajută la setarea mediului și la crearea containerului de dependency injection (DI), pe care o vom prezenta acum mai detaliat.


Modul de dezvoltare vs producție
================================

Nette se comportă diferit în funcție de dacă rulează pe un server de dezvoltare sau de producție:

🛠️ Modul de dezvoltare (Development):
	- Afișează bara de depanare Tracy cu informații utile (interogări SQL, timp de execuție, memorie utilizată)
	- În caz de eroare, afișează o pagină de eroare detaliată cu apelurile de funcții și conținutul variabilelor
	- Reînnoiește automat cache-ul la modificarea șabloanelor Latte, editarea fișierelor de configurare etc.


🚀 Modul de producție (Production):
	- Nu afișează nicio informație de depanare, toate erorile sunt scrise în log
	- În caz de eroare, afișează ErrorPresenter sau pagina generică "Server Error"
	- Cache-ul nu se reînnoiește niciodată automat!
	- Optimizat pentru viteză și securitate


Alegerea modului se face prin autodetecție, deci de obicei nu este necesar să configurați sau să comutați manual:

- modul de dezvoltare: pe localhost (adresa IP `127.0.0.1` sau `::1`) dacă nu este prezent un proxy (adică antetul său HTTP)
- modul de producție: oriunde altundeva

Dacă dorim să activăm modul de dezvoltare și în alte cazuri, de exemplu pentru programatorii care accesează de la o anumită adresă IP, folosim `setDebugMode()`:

```php
$this->configurator->setDebugMode('23.75.345.200'); // se poate specifica și un array de adrese IP
```

Recomandăm cu tărie combinarea adresei IP cu un cookie. În cookie-ul `nette-debug` salvăm un token secret, de ex. `secret1234`, și astfel activăm modul de dezvoltare pentru programatorii care accesează de la o anumită adresă IP și au în același timp tokenul menționat în cookie:

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

Putem, de asemenea, să dezactivăm complet modul de dezvoltare, chiar și pentru localhost:

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

Atenție, valoarea `true` activează modul de dezvoltare forțat, ceea ce nu trebuie să se întâmple niciodată pe un server de producție.


Instrumentul de depanare Tracy
==============================

Pentru o depanare ușoară, vom activa și excelentul instrument [Tracy |tracy:]. În modul de dezvoltare, vizualizează erorile, iar în modul de producție, le înregistrează în directorul specificat:

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


Fișiere temporare
=================

Nette utilizează cache pentru containerul DI, RobotLoader, șabloane etc. Prin urmare, este necesar să setați calea către directorul unde se va stoca cache-ul:

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

Pe Linux sau macOS, setați [permisiuni de scriere |nette:troubleshooting#Setarea permisiunilor pentru directoare] pentru directoarele `log/` și `temp/`.


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

De regulă, vom dori să încărcăm automat clasele folosind [RobotLoader |robot-loader:], deci trebuie să îl pornim și să îl lăsăm să încarce clasele din directorul unde este plasat `Bootstrap.php` (adică `__DIR__`), și din toate subdirectoarele:

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

O abordare alternativă este să lăsăm clasele să fie încărcate doar prin [Composer |best-practices:composer] respectând PSR-4.


Timezone
========

Prin intermediul configuratorului puteți seta fusul orar implicit.

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


Configurarea containerului DI
=============================

Parte a procesului de inițializare este crearea containerului DI sau a fabricii de obiecte, care este inima întregii aplicații. Este de fapt o clasă PHP, generată de Nette și salvată în directorul de cache. Fabrica produce obiectele cheie ale aplicației și, folosind fișierele de configurare, o instruim cum să le creeze și să le seteze, influențând astfel comportamentul întregii aplicații.

Fișierele de configurare sunt de obicei scrise în formatul [NEON |neon:format]. Într-un capitol separat veți afla [ce poate fi configurat |nette:configuring].

.[tip]
În modul de dezvoltare, containerul se actualizează automat la fiecare modificare a codului sau a fișierelor de configurare. În modul de producție, se generează o singură dată și modificările nu sunt verificate pentru a maximiza performanța.

Fișierele de configurare le încărcăm folosind `addConfig()`:

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

Dacă dorim să adăugăm mai multe fișiere de configurare, putem apela funcția `addConfig()` de mai multe ori.

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

Numele `cli.php` nu este o greșeală de tipar, configurația poate fi scrisă și într-un fișier PHP, care o returnează ca array.

De asemenea, putem adăuga alte fișiere de configurare în [secțiunea `includes` |dependency-injection:configuration#Includerea fișierelor].

Dacă în fișierele de configurare apar elemente cu aceleași chei, acestea vor fi suprascrise sau, în cazul [array-urilor, combinate |dependency-injection:configuration#Combinare]. Fișierul inclus ulterior are prioritate mai mare decât cel anterior. Fișierul în care este specificată secțiunea `includes` are prioritate mai mare decât fișierele incluse în el.


Parametri statici
-----------------

Parametrii utilizați în fișierele de configurare pot fi definiți [în secțiunea `parameters` |dependency-injection:configuration#Parametri] și, de asemenea, pot fi transmiși (sau suprascriși) prin metoda `addStaticParameters()` (are aliasul `addParameters()`). Este important că valorile diferite ale parametrilor determină generarea altor containere DI, adică a altor clase.

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

La parametrul `projectId` se poate face referire în configurație prin notația obișnuită `%projectId%`.


Parametri dinamici
------------------

În container putem adăuga și parametri dinamici, ale căror valori diferite, spre deosebire de parametrii statici, nu determină generarea de noi containere DI.

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

Astfel, putem adăuga simplu, de exemplu, variabile de mediu, la care se poate face referire ulterior în configurație prin notația `%env.variable%`.

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


Parametri impliciți
-------------------

În fișierele de configurare puteți utiliza acești parametri statici:

- `%appDir%` este calea absolută către directorul cu fișierul `Bootstrap.php`
- `%wwwDir%` este calea absolută către directorul cu fișierul de intrare `index.php`
- `%tempDir%` este calea absolută către directorul pentru fișiere temporare
- `%vendorDir%` este calea absolută către directorul unde Composer instalează bibliotecile
- `%rootDir%` este calea absolută către directorul rădăcină al proiectului
- `%debugMode%` indică dacă aplicația este în modul de depanare
- `%consoleMode%` indică dacă cererea a venit prin linia de comandă


Servicii importate
------------------

Acum intrăm mai în profunzime. Deși scopul containerului DI este să producă obiecte, în mod excepțional poate apărea nevoia de a introduce un obiect existent în container. Facem acest lucru definind serviciul cu flag-ul `imported: true`.

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

Și în bootstrap introducem obiectul în container:

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


Medii diferite
==============

Nu vă fie teamă să modificați clasa Bootstrap conform nevoilor dvs. Puteți adăuga parametri metodei `bootWebApplication()` pentru a distinge proiectele web. Sau putem completa cu alte metode, de exemplu `bootTestEnvironment()`, care inițializează mediul pentru testele unitare, `bootConsoleApplication()` pentru scripturile apelate din linia de comandă etc.

```php
public function bootTestEnvironment(): Nette\DI\Container
{
	Tester\Environment::setup(); // inițializarea 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();
}
```

Bootstrapping

Bootstrapping-ul este procesul de inițializare a mediului aplicației, crearea unui container de dependency injection (DI) și pornirea aplicației. Vom discuta:

  • cum clasa Bootstrap inițializează mediul
  • cum sunt configurate aplicațiile folosind fișiere NEON
  • cum să distingem între modul de producție și dezvoltare
  • cum să creăm și să configurăm containerul DI

Aplicațiile, fie că sunt web sau scripturi rulate din linia de comandă, își încep execuția cu o formă de inițializare a mediului. În trecut, acest lucru era de obicei gestionat de un fișier numit, de exemplu, include.inc.php, pe care fișierul inițial îl includea. În aplicațiile Nette moderne, acesta a fost înlocuit de clasa Bootstrap, pe care o veți găsi ca parte a aplicației în fișierul app/Bootstrap.php. Poate arăta, de exemplu, astfel:

use Nette\Bootstrap\Configurator;

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

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// Configuratorul este responsabil pentru setarea mediului aplicației și a serviciilor.
		$this->configurator = new Configurator;
		// Setează directorul pentru fișierele temporare generate de Nette (de ex., șabloane compilate)
		$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 este inteligent și modul de dezvoltare se activează automat,
		// sau îl puteți activa pentru o anumită adresă IP decomentând următoarea linie:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Activează Tracy: "briceagul elvețian" suprem pentru depanare.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: încarcă automat toate clasele din directorul selectat
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Încarcă fișierele de configurare
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}

index.php

Fișierul inițial în cazul aplicațiilor web este index.php, care se află în directorul public www/. Acesta solicită clasei Bootstrap să inițializeze mediul și să creeze containerul DI. Apoi, obține din acesta serviciul Application, care pornește aplicația web:

$bootstrap = new App\Bootstrap;
// Inițializarea mediului + crearea containerului DI
$container = $bootstrap->bootWebApplication();
// Containerul DI creează obiectul Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Pornirea aplicației Nette și procesarea cererii primite
$application->run();

După cum se poate vedea, clasa Nette\Bootstrap\Configurator ajută la setarea mediului și la crearea containerului de dependency injection (DI), pe care o vom prezenta acum mai detaliat.

Modul de dezvoltare vs producție

Nette se comportă diferit în funcție de dacă rulează pe un server de dezvoltare sau de producție:

🛠️ Modul de dezvoltare (Development)
Afișează bara de depanare Tracy cu informații utile (interogări SQL, timp de execuție, memorie utilizată)
În caz de eroare, afișează o pagină de eroare detaliată cu apelurile de funcții și conținutul variabilelor
Reînnoiește automat cache-ul la modificarea șabloanelor Latte, editarea fișierelor de configurare etc.
🚀 Modul de producție (Production)
Nu afișează nicio informație de depanare, toate erorile sunt scrise în log
În caz de eroare, afișează ErrorPresenter sau pagina generică „Server Error“
Cache-ul nu se reînnoiește niciodată automat!
Optimizat pentru viteză și securitate

Alegerea modului se face prin autodetecție, deci de obicei nu este necesar să configurați sau să comutați manual:

  • modul de dezvoltare: pe localhost (adresa IP 127.0.0.1 sau ::1) dacă nu este prezent un proxy (adică antetul său HTTP)
  • modul de producție: oriunde altundeva

Dacă dorim să activăm modul de dezvoltare și în alte cazuri, de exemplu pentru programatorii care accesează de la o anumită adresă IP, folosim setDebugMode():

$this->configurator->setDebugMode('23.75.345.200'); // se poate specifica și un array de adrese IP

Recomandăm cu tărie combinarea adresei IP cu un cookie. În cookie-ul nette-debug salvăm un token secret, de ex. secret1234, și astfel activăm modul de dezvoltare pentru programatorii care accesează de la o anumită adresă IP și au în același timp tokenul menționat în cookie:

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

Putem, de asemenea, să dezactivăm complet modul de dezvoltare, chiar și pentru localhost:

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

Atenție, valoarea true activează modul de dezvoltare forțat, ceea ce nu trebuie să se întâmple niciodată pe un server de producție.

Instrumentul de depanare Tracy

Pentru o depanare ușoară, vom activa și excelentul instrument Tracy. În modul de dezvoltare, vizualizează erorile, iar în modul de producție, le înregistrează în directorul specificat:

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

Fișiere temporare

Nette utilizează cache pentru containerul DI, RobotLoader, șabloane etc. Prin urmare, este necesar să setați calea către directorul unde se va stoca cache-ul:

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

Pe Linux sau macOS, setați permisiuni de scriere pentru directoarele log/ și temp/.

RobotLoader

De regulă, vom dori să încărcăm automat clasele folosind RobotLoader, deci trebuie să îl pornim și să îl lăsăm să încarce clasele din directorul unde este plasat Bootstrap.php (adică __DIR__), și din toate subdirectoarele:

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

O abordare alternativă este să lăsăm clasele să fie încărcate doar prin Composer respectând PSR-4.

Timezone

Prin intermediul configuratorului puteți seta fusul orar implicit.

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

Configurarea containerului DI

Parte a procesului de inițializare este crearea containerului DI sau a fabricii de obiecte, care este inima întregii aplicații. Este de fapt o clasă PHP, generată de Nette și salvată în directorul de cache. Fabrica produce obiectele cheie ale aplicației și, folosind fișierele de configurare, o instruim cum să le creeze și să le seteze, influențând astfel comportamentul întregii aplicații.

Fișierele de configurare sunt de obicei scrise în formatul NEON. Într-un capitol separat veți afla ce poate fi configurat.

În modul de dezvoltare, containerul se actualizează automat la fiecare modificare a codului sau a fișierelor de configurare. În modul de producție, se generează o singură dată și modificările nu sunt verificate pentru a maximiza performanța.

Fișierele de configurare le încărcăm folosind addConfig():

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

Dacă dorim să adăugăm mai multe fișiere de configurare, putem apela funcția addConfig() de mai multe ori.

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

Numele cli.php nu este o greșeală de tipar, configurația poate fi scrisă și într-un fișier PHP, care o returnează ca array.

De asemenea, putem adăuga alte fișiere de configurare în secțiunea includes.

Dacă în fișierele de configurare apar elemente cu aceleași chei, acestea vor fi suprascrise sau, în cazul array-urilor, combinate. Fișierul inclus ulterior are prioritate mai mare decât cel anterior. Fișierul în care este specificată secțiunea includes are prioritate mai mare decât fișierele incluse în el.

Parametri statici

Parametrii utilizați în fișierele de configurare pot fi definiți în secțiunea parameters și, de asemenea, pot fi transmiși (sau suprascriși) prin metoda addStaticParameters() (are aliasul addParameters()). Este important că valorile diferite ale parametrilor determină generarea altor containere DI, adică a altor clase.

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

La parametrul projectId se poate face referire în configurație prin notația obișnuită %projectId%.

Parametri dinamici

În container putem adăuga și parametri dinamici, ale căror valori diferite, spre deosebire de parametrii statici, nu determină generarea de noi containere DI.

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

Astfel, putem adăuga simplu, de exemplu, variabile de mediu, la care se poate face referire ulterior în configurație prin notația %env.variable%.

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

Parametri impliciți

În fișierele de configurare puteți utiliza acești parametri statici:

  • %appDir% este calea absolută către directorul cu fișierul Bootstrap.php
  • %wwwDir% este calea absolută către directorul cu fișierul de intrare index.php
  • %tempDir% este calea absolută către directorul pentru fișiere temporare
  • %vendorDir% este calea absolută către directorul unde Composer instalează bibliotecile
  • %rootDir% este calea absolută către directorul rădăcină al proiectului
  • %debugMode% indică dacă aplicația este în modul de depanare
  • %consoleMode% indică dacă cererea a venit prin linia de comandă

Servicii importate

Acum intrăm mai în profunzime. Deși scopul containerului DI este să producă obiecte, în mod excepțional poate apărea nevoia de a introduce un obiect existent în container. Facem acest lucru definind serviciul cu flag-ul imported: true.

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

Și în bootstrap introducem obiectul în container:

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

Medii diferite

Nu vă fie teamă să modificați clasa Bootstrap conform nevoilor dvs. Puteți adăuga parametri metodei bootWebApplication() pentru a distinge proiectele web. Sau putem completa cu alte metode, de exemplu bootTestEnvironment(), care inițializează mediul pentru testele unitare, bootConsoleApplication() pentru scripturile apelate din linia de comandă etc.

public function bootTestEnvironment(): Nette\DI\Container
{
	Tester\Environment::setup(); // inițializarea 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();
}