Parte 5 - Configurazione

I meccanismi relativi alla configurazione... cosa c'è sotto? Vediamolo insieme in questa serie di Christopher Pitt tradotta in italiano!
francesco
Francesco Malatesta
14/10/2014 in

In questa serie, traduzione autorizzata di quella pubblicata da Christopher Pitt su Rebuilding Laravel, esploreremo in profondità il framework in tutti i suoi aspetti. Dall'autoloading alla configurazione, passando per le operazioni di pulizia ed il routing.

A volte la configurazione dell'ambiente di lavoro può inquinare facilmente il codice dell'applicazione. Per fortuna, Laravel mantiene questi due aspetti separati. Vediamo qui come.

Configurazione "Sensibile"

Le prossime linee di codice, presenti in vendor/.../Foundation/start.php, isolano alcuni dei dati di configurazione.

with($envVariables = new EnvironmentVariables($app->getEnvironmentVariablesLoader()))->load($env);

Il commento precedente a tale linea di codice specifica che serve a tenere le variabili _$ENV e _$SERVER lontane dal resto del codice dell'applicazione. Guardando indietro, al codice della classe Application possiamo fare caso al seguente metodo:

public function getEnvironmentVariablesLoader()
{
  return new FileEnvironmentVariablesLoader(
    new Filesystem,
    $this['path.base']
  );
}

Siamo in vendor/laravel/framework/src/Illuminate/Foundation/Application.php.

Siamo ad un livello "basso" ed il codice serve a leggere i file .env.php. In questo modo possiamo escludere alcune variabili più importanti dai file presenti in app/config. Il metodo load() si presenta così:

public function load($environment = null)
{
  foreach ($this->loader->load($environment) as $key => $value)
  {
    $_ENV[$key] = $value;

    $_SERVER[$key] = $value;

    putenv("{$key}={$value}");
  }
}

Siamo in vendor/laravel/framework/src/Illuminate/Configuration/EnvironmentVariables.php.

La variabile di ambiente dell'applicazione viene usata per determinare quali variabili di configurazione vengono caricate. Ad esempio, nel caso in cui l'ambiente dovesse essere impostato su "local", le variabili nei file .env.local.php verrebbero caricate di conseguenza.

Se l'ambiente è quello di produzione, la classe FileEnvironmentVariablesLoader cercherà di caricare i file .env.php. Guarderemo questo processo meglio dopo, quando dissezioneremo il package illuminate/config.

Questi file di configurazione definiscono essenzialmente array associativi. Gli array multidimensionali, invece, non sono supportati. La cosa è ragionevole, se consideri che neanche $_ENV e $_SERVER li supportano.

Puoi lavorare con queste variabili grazie ai metodi putenv() e getenv().

La Cara e Vecchia Configurazione

Tornando in vendor/.../Foundation/start.php possiamo notare meglio il processo relativo alla configurazione:

$app->instance('config', $config = new Config(

    $app->getConfigLoader(), $env

));

Siamo in vendor/laravel/framework/src/Illuminate/Foundation/start.php.

L'obiettivo è facilitare il caricamento di file di configurazione regolari (quelli in app/config, per capirci) con un loader ottenuto dall'istanza di Application. Eccolo:

public function getConfigLoader()
{
  return new FileLoader(
    new Filesystem,
    $this['path'].'/config'
  );
}

Siamo in vendor/laravel/framework/src/Illuminate/Foundation/Application.php.

Il codice è molto simile a quello visto per il caricamento dei file *.env.php. In parte penso che il meccanismo possa essere astratto in un modo più utile, evitando di ripetere il codice nei vari package.

Vuoi saperne di più? Dai uno sguardo qui.

Gestione delle Eccezioni

Le linee di codice successive sistemano la gestione degli errori (e relativo reporting):

$app->startExceptionHandling();

if ($env != 'testing') ini_set('display_errors', 'Off');

Siamo in vendor/laravel/framework/src/Illuminate/Foundation/start.php.

Vediamo questo metodo nella classe Application.

public function startExceptionHandling()
{
  $this['exception']->register($this->environment());

  $this['exception']->setDebug($this['config']['app.debug']);
}

Siamo in vendor/laravel/framework/src/Illuminate/Foundation/Application.php.

Il metodo register() si occupa di registrare l'eccezione, error e successivo shutdown (anche se quest'ultimo è escluso in caso di ambiente di testing). Il metodo setDebug() aiuta la classe di exception a determinare quale messaggio deve essere visualizzato.

Per capirci meglio: se il debug è impostato su true in app/config/app.php vengono mostrate svariate informazioni per il debugging. In caso contrario, invece, verrà generato un messaggio di errore generico ("Whoops, looks like something went wrong").

Sembra scontato, ma non dovresti tenere su true il valore di debug se sei in produzione. In una qualsiasi schermata di errore, infatti, vengono mostrate svariate informazioni potenzialmente sensibili che sarebbe meglio tenere nascoste.

Cureremo meglio tutta la dinamica relativa agli errori in un altro capitolo di questa serie, comunque.

Se vuoi saperne di più dai un'occhiata, nel frattempo, alla pagina http://laravel-italia.it/documentazione/errori-e-logging#p2.

Timezone

Dopo la gestione degli errori viene gestita e configurata la Timezone dell'applicazione.

$config = $app['config']['app'];

date_default_timezone_set($config['timezone']);

Siamo in vendor/laravel/framework/src/Illuminate/Foundation/start.php.

La timezone influenza la differenza tra l'orario del tuo computer e quello del tuo server. Non è un problema importantissimo, ma da PHP 5.3 ti verranno comunque segnalate eventuali incongruenze o problemi con un warning.

Laravel normalmente sopprime questo tipo di errori: non vuol dire però che debbano riempire i tuoi file di log, vero?

Alias delle Classi

Nelle prossime linee di codice c'è un po' di "magia":

$aliases = $config['aliases'];

AliasLoader::getInstance($aliases)->register();

Siamo in vendor/laravel/framework/src/Illuminate/Foundation/start.php.

La chiave aliases fa riferimento a quello che viene trovato in app/config/app.php. Di base usa la funzione class_alias().

public function load($alias)
{
  if (isset($this->aliases[$alias]))
  {
    return class_alias($this->aliases[$alias], $alias);
  }
}

Siamo in vendor/laravel/framework/src/Illuminate/Foundation/AliasLoader.php.

Ammetto di aver speso un bel po' di tempo per capire bene perché alcune operazioni in queste classe sono state fatte in un certo modo. Ecco i miei pensieri a riguardo:

  • La classe AliasLoader viene concepita come una Singleton class, ma viene usata solo dopo che il Container è stato inizializzato. Presumo per evitare overhead quando tali Alias vengono usati, specialmente considerando che...
  • La classe AliasLoader fa uso di spl_class_autoloader. Questo perché la funzione di class_alias() cercherà di caricare la classe alla quale associare l'alias prima di applicare l'alias. Ad esempio, volendo collegare l'alias App a Illuminate\Support\Facades\App, il metodo class_alias() cercherà innanzitutto di caricare la classe specificata. Spostare in avanti nel tempo la chiamata a tale classe evita operazioni, da parte delle classi stesse, non previste o comunque fuori tempo.

Il flusso dell'applicazione diventa quindi:

  • Un alias viene caricato con App::make().
  • AliasLoader, anticipando la lisat degli autoload, chiama load().
  • Viene usato il metodo class_alias() per collegare App ad Illuminate\Support\Facades\App.
  • La classe è quindi caricata, usando uno degli autoloader rimanenti.

Interessante, vero?