Bootstrap

Bootstrap este codul de inițializare care inițializează mediul, creează containerul de dependency injection (DI) și pornește aplicația. Vom discuta despre:

  • cum se configurează folosind fișiere NEON
  • cum se distinge între modul de producție și cel de dezvoltare
  • cum se creează 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();
}
versiune: 4.0