Bootstrap

Bootstrap è il codice di avvio che inizializza l'ambiente, crea il container di dependency injection (DI) ed esegue l'applicazione. Parleremo di:

  • come configurarlo utilizzando file NEON
  • come distinguere tra modalità di produzione e sviluppo
  • come creare un container DI

Le applicazioni, siano esse web o script eseguiti dalla riga di comando, iniziano la loro esecuzione con una qualche forma di inizializzazione dell'ambiente. In passato, questo compito era spesso affidato a un file chiamato, ad esempio, include.inc.php, che il file iniziale includeva. Nelle moderne applicazioni Nette, questo è stato sostituito dalla classe Bootstrap, che, come parte dell'applicazione, si trova nel file app/Bootstrap.php. Potrebbe assomigliare, ad esempio, a questo:

use Nette\Bootstrap\Configurator;

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

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// Il Configurator è responsabile dell'impostazione dell'ambiente e dei servizi dell'applicazione.
		$this->configurator = new Configurator;
		// Imposta la directory per i file temporanei generati da Nette (es. template compilati)
		$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 è intelligente e la modalità di sviluppo si attiva automaticamente,
		// oppure puoi abilitarla per un indirizzo IP specifico decommentando la riga seguente:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Attiva Tracy: l'ultimo "coltellino svizzero" per il debug.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: carica automaticamente tutte le classi nella directory selezionata
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Carica i file di configurazione
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}

index.php

Il file iniziale nel caso delle applicazioni web è index.php, che si trova nella directory pubblica www/. Questo file fa inizializzare l'ambiente e creare il container DI dalla classe Bootstrap. Successivamente, ottiene il servizio Application da esso, che avvia l'applicazione web:

$bootstrap = new App\Bootstrap;
// Inizializzazione dell'ambiente + creazione del container DI
$container = $bootstrap->bootWebApplication();
// Il container DI crea l'oggetto Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Avvio dell'applicazione Nette ed elaborazione della richiesta in arrivo
$application->run();

Come si può vedere, la classe Nette\Bootstrap\Configurator aiuta con l'impostazione dell'ambiente e la creazione del container di dependency injection (DI), che ora presenteremo più in dettaglio.

Modalità Sviluppo vs Produzione

Nette si comporta diversamente a seconda che sia in esecuzione su un server di sviluppo o di produzione:

🛠️ Modalità Sviluppo (Development)
Mostra la debugbar di Tracy con informazioni utili (query SQL, tempo di esecuzione, memoria utilizzata)
In caso di errore, mostra una pagina di errore dettagliata con le chiamate alle funzioni e il contenuto delle variabili
Aggiorna automaticamente la cache quando vengono modificati i template Latte, i file di configurazione, ecc.
🚀 Modalità Produzione (Production)
Non mostra alcuna informazione di debug, tutti gli errori vengono scritti nel log
In caso di errore, mostra ErrorPresenter o una pagina generica “Server Error”
La cache non viene mai aggiornata automaticamente!
Ottimizzato per velocità e sicurezza

La selezione della modalità avviene tramite autodetect, quindi di solito non è necessario configurare nulla o passare manualmente:

  • modalità sviluppo: su localhost (indirizzo IP 127.0.0.1 o ::1) se non è presente un proxy (cioè il suo header HTTP)
  • modalità produzione: ovunque altrove

Se vogliamo abilitare la modalità di sviluppo anche in altri casi, ad esempio per i programmatori che accedono da un indirizzo IP specifico, utilizziamo setDebugMode():

$this->configurator->setDebugMode('23.75.345.200'); // è possibile specificare anche un array di indirizzi IP

Consigliamo vivamente di combinare l'indirizzo IP con un cookie. Memorizziamo un token segreto nel cookie nette-debug, ad esempio secret1234, e in questo modo attiviamo la modalità di sviluppo per i programmatori che accedono da un indirizzo IP specifico e che hanno anche il token menzionato nel cookie:

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

Possiamo anche disattivare completamente la modalità di sviluppo, anche per localhost:

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

Attenzione, il valore true attiva forzatamente la modalità di sviluppo, cosa che non deve mai accadere su un server di produzione.

Strumento di Debug Tracy

Per un facile debug, attiviamo anche l'ottimo strumento Tracy. In modalità sviluppo, visualizza gli errori e in modalità produzione, registra gli errori nella directory specificata:

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

File Temporanei

Nette utilizza la cache per il container DI, RobotLoader, template, ecc. Pertanto, è necessario impostare il percorso della directory in cui verrà memorizzata la cache:

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

Su Linux o macOS, imposta i permessi di scrittura per le directory log/ e temp/.

RobotLoader

Di solito, vorremo caricare automaticamente le classi utilizzando RobotLoader, quindi dobbiamo avviarlo e fargli caricare le classi dalla directory in cui si trova Bootstrap.php (cioè __DIR__), e da tutte le sottodirectory:

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

Un approccio alternativo è far caricare le classi solo tramite Composer rispettando PSR-4.

Timezone

Tramite il configuratore è possibile impostare il fuso orario predefinito.

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

Configurazione del Container DI

Parte del processo di avvio è la creazione del container DI, ovvero la factory di oggetti, che è il cuore dell'intera applicazione. Si tratta in realtà di una classe PHP generata da Nette e salvata nella directory della cache. La factory produce gli oggetti chiave dell'applicazione e, tramite i file di configurazione, le istruiamo su come crearli e impostarli, influenzando così il comportamento dell'intera applicazione.

I file di configurazione sono solitamente scritti nel formato NEON. In un capitolo separato, imparerai cosa può essere configurato.

In modalità sviluppo, il container viene aggiornato automaticamente ad ogni modifica del codice o dei file di configurazione. In modalità produzione, viene generato solo una volta e le modifiche non vengono controllate per massimizzare le prestazioni.

Carichiamo i file di configurazione utilizzando addConfig():

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

Se vogliamo aggiungere più file di configurazione, possiamo chiamare la funzione addConfig() più volte.

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

Il nome cli.php non è un errore di battitura, la configurazione può anche essere scritta in un file PHP che la restituisce come array.

Possiamo anche aggiungere altri file di configurazione nella sezione includes.

Se nei file di configurazione compaiono elementi con le stesse chiavi, verranno sovrascritti o, nel caso di array, uniti. Il file incluso successivamente ha una priorità maggiore rispetto al precedente. Il file in cui è specificata la sezione includes ha una priorità maggiore rispetto ai file inclusi in esso.

Parametri Statici

I parametri utilizzati nei file di configurazione possono essere definiti nella sezione parameters e anche passati (o sovrascritti) con il metodo addStaticParameters() (ha l'alias addParameters()). È importante notare che valori diversi dei parametri causeranno la generazione di ulteriori container DI, ovvero ulteriori classi.

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

Al parametro projectId si può fare riferimento nella configurazione con la consueta notazione %projectId%.

Parametri Dinamici

Possiamo aggiungere al container anche parametri dinamici, i cui valori diversi, a differenza dei parametri statici, non causano la generazione di nuovi container DI.

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

In questo modo possiamo aggiungere semplicemente, ad esempio, variabili d'ambiente, a cui si può fare riferimento nella configurazione con la notazione %env.variable%.

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

Parametri Predefiniti

Nei file di configurazione è possibile utilizzare questi parametri statici:

  • %appDir% è il percorso assoluto alla directory contenente il file Bootstrap.php
  • %wwwDir% è il percorso assoluto alla directory contenente il file di input index.php
  • %tempDir% è il percorso assoluto alla directory per i file temporanei
  • %vendorDir% è il percorso assoluto alla directory in cui Composer installa le librerie
  • %rootDir% è il percorso assoluto alla directory principale del progetto
  • %debugMode% indica se l'applicazione è in modalità debug
  • %consoleMode% indica se la richiesta è arrivata tramite la riga di comando

Servizi Importati

Ora andiamo più a fondo. Sebbene lo scopo del container DI sia quello di creare oggetti, eccezionalmente potrebbe sorgere la necessità di inserire un oggetto esistente nel container. Lo facciamo definendo il servizio con il flag imported: true.

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

E nel bootstrap inseriamo l'oggetto nel container:

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

Ambienti Diversi

Non aver paura di modificare la classe Bootstrap secondo le tue esigenze. Puoi aggiungere parametri al metodo bootWebApplication() per distinguere i progetti web. Oppure possiamo aggiungere altri metodi, ad esempio bootTestEnvironment(), che inizializza l'ambiente per i test unitari, bootConsoleApplication() per gli script chiamati dalla riga di comando, ecc.

public function bootTestEnvironment(): Nette\DI\Container
{
	Tester\Environment::setup(); // inizializzazione di 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();
}
versione: 4.0