Bootstrap

Bootstrap es el código de arranque que inicializa el entorno, crea el contenedor de inyección de dependencias (DI) y ejecuta la aplicación. Hablaremos de:

  • cómo se configura usando archivos NEON
  • cómo distinguir entre modo de producción y desarrollo
  • cómo crear un contenedor DI

Las aplicaciones, ya sean web o scripts ejecutados desde la línea de comandos, comienzan su ejecución con alguna forma de inicialización del entorno. En tiempos pasados, esto solía estar a cargo de un archivo llamado, por ejemplo, include.inc.php, que el archivo inicial incluía. En las aplicaciones Nette modernas, ha sido reemplazado por la clase Bootstrap, que como parte de la aplicación se encuentra en el archivo app/Bootstrap.php. Puede verse, por ejemplo, así:

use Nette\Bootstrap\Configurator;

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

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// El configurador es responsable de configurar el entorno de la aplicación y los servicios.
		$this->configurator = new Configurator;
		// Establece el directorio para los archivos temporales generados por Nette (p. ej., plantillas compiladas)
		$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 es inteligente y el modo de desarrollo se activa automáticamente,
		// o puedes habilitarlo para una dirección IP específica descomentando la siguiente línea:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Activa Tracy: la "navaja suiza" definitiva para la depuración.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: carga automáticamente todas las clases en el directorio seleccionado
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Carga los archivos de configuración
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}

index.php

El archivo inicial en el caso de las aplicaciones web es index.php, que se encuentra en el directorio público www/. Este solicita a la clase Bootstrap que inicialice el entorno y cree el contenedor DI. Luego, obtiene de él el servicio Application, que ejecuta la aplicación web:

$bootstrap = new App\Bootstrap;
// Inicialización del entorno + creación del contenedor DI
$container = $bootstrap->bootWebApplication();
// El contenedor DI crea un objeto Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Ejecución de la aplicación Nette y procesamiento de la petición entrante
$application->run();

Como se puede ver, la clase Nette\Bootstrap\Configurator ayuda con la configuración del entorno y la creación del contenedor de inyección de dependencias (DI), que ahora presentaremos con más detalle.

Modo de desarrollo vs producción

Nette se comporta de manera diferente dependiendo de si se ejecuta en un servidor de desarrollo o de producción:

🛠️ Modo de desarrollo (Development)
Muestra la barra de depuración de Tracy con información útil (consultas SQL, tiempo de ejecución, memoria utilizada)
En caso de error, muestra una página de error detallada con llamadas a funciones y contenido de variables
Actualiza automáticamente la caché al cambiar las plantillas Latte, modificar archivos de configuración, etc.
🚀 Modo de producción (Production)
No muestra ninguna información de depuración, todos los errores se registran en el log
En caso de error, muestra ErrorPresenter o una página genérica “Server Error”
¡La caché nunca se actualiza automáticamente!
Optimizado para velocidad y seguridad

La elección del modo se realiza por autodetección, por lo que generalmente no es necesario configurar nada ni cambiar manualmente:

  • modo de desarrollo: en localhost (dirección IP 127.0.0.1 o ::1) si no hay proxy presente (es decir, su cabecera HTTP)
  • modo de producción: en cualquier otro lugar

Si queremos habilitar el modo de desarrollo también en otros casos, por ejemplo, para programadores que acceden desde una dirección IP específica, usamos setDebugMode():

$this->configurator->setDebugMode('23.75.345.200'); // también se puede indicar un array de direcciones IP

Definitivamente recomendamos combinar la dirección IP con una cookie. Guardamos un token secreto en la cookie nette-debug, por ejemplo, secret1234, y de esta manera activamos el modo de desarrollo para los programadores que acceden desde una dirección IP específica y que además tienen el token mencionado en la cookie:

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

También podemos desactivar completamente el modo de desarrollo, incluso para localhost:

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

Atención, el valor true activa el modo de desarrollo de forma permanente, lo cual nunca debe ocurrir en un servidor de producción.

Herramienta de depuración Tracy

Para facilitar la depuración, también activaremos la excelente herramienta Tracy. En el modo de desarrollo, visualiza los errores y en el modo de producción, registra los errores en el directorio especificado:

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

Archivos temporales

Nette utiliza caché para el contenedor DI, RobotLoader, plantillas, etc. Por lo tanto, es necesario establecer la ruta al directorio donde se almacenará la caché:

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

En Linux o macOS, establezca permisos de escritura para los directorios log/ y temp/.

RobotLoader

Generalmente, querremos cargar clases automáticamente usando RobotLoader, por lo que debemos iniciarlo y hacer que cargue clases desde el directorio donde se encuentra Bootstrap.php (es decir, __DIR__), y todos los subdirectorios:

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

Un enfoque alternativo es dejar que las clases se carguen solo a través de Composer cumpliendo con PSR-4.

Zona horaria

A través del configurador, puede establecer la zona horaria predeterminada.

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

Configuración del contenedor DI

Parte del proceso de arranque es la creación del contenedor DI o fábrica de objetos, que es el corazón de toda la aplicación. En realidad, es una clase PHP que Nette genera y guarda en el directorio de caché. La fábrica produce los objetos clave de la aplicación y, mediante archivos de configuración, le instruimos cómo debe crearlos y configurarlos, influyendo así en el comportamiento de toda la aplicación.

Los archivos de configuración generalmente se escriben en formato NEON. En un capítulo aparte, aprenderá qué se puede configurar.

En el modo de desarrollo, el contenedor se actualiza automáticamente cada vez que se cambia el código o los archivos de configuración. En el modo de producción, se genera solo una vez y los cambios no se verifican para maximizar el rendimiento.

Cargamos los archivos de configuración usando addConfig():

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

Si queremos agregar más archivos de configuración, podemos llamar a la función addConfig() varias veces.

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

El nombre cli.php no es un error tipográfico, la configuración también puede estar escrita en un archivo PHP que la devuelve como un array.

También podemos agregar otros archivos de configuración en la sección includes.

Si aparecen elementos con las mismas claves en los archivos de configuración, se sobrescribirán o, en el caso de arrays, se fusionarán. El archivo cargado posteriormente tiene mayor prioridad que el anterior. El archivo en el que se indica la sección includes tiene mayor prioridad que los archivos incluidos en él.

Parámetros estáticos

Los parámetros utilizados en los archivos de configuración se pueden definir en la sección parameters y también se pueden pasar (o sobrescribir) con el método addStaticParameters() (tiene el alias addParameters()). Es importante que diferentes valores de parámetros provoquen la generación de contenedores DI adicionales, es decir, clases adicionales.

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

Al parámetro projectId se puede hacer referencia en la configuración con la notación habitual %projectId%.

Parámetros dinámicos

También podemos agregar parámetros dinámicos al contenedor, cuyos diferentes valores, a diferencia de los parámetros estáticos, no provocan la generación de nuevos contenedores DI.

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

De esta manera, podemos agregar fácilmente, por ejemplo, variables de entorno, a las que luego se puede hacer referencia en la configuración con la notación %env.variable%.

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

Parámetros predeterminados

En los archivos de configuración, puede utilizar estos parámetros estáticos:

  • %appDir% es la ruta absoluta al directorio con el archivo Bootstrap.php
  • %wwwDir% es la ruta absoluta al directorio con el archivo de entrada index.php
  • %tempDir% es la ruta absoluta al directorio para archivos temporales
  • %vendorDir% es la ruta absoluta al directorio donde Composer instala las librerías
  • %rootDir% es la ruta absoluta al directorio raíz del proyecto
  • %debugMode% indica si la aplicación está en modo de depuración
  • %consoleMode% indica si la petición llegó a través de la línea de comandos

Servicios importados

Ahora vamos más profundo. Aunque el propósito del contenedor DI es fabricar objetos, excepcionalmente puede surgir la necesidad de insertar un objeto existente en el contenedor. Hacemos esto definiendo el servicio con el indicador imported: true.

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

Y en bootstrap insertamos el objeto en el contenedor:

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

Entorno diferente

No dude en modificar la clase Bootstrap según sus necesidades. Puede agregar parámetros al método bootWebApplication() para distinguir proyectos web. O podemos agregar otros métodos, por ejemplo, bootTestEnvironment(), que inicializa el entorno para pruebas unitarias, bootConsoleApplication() para scripts llamados desde la línea de comandos, etc.

public function bootTestEnvironment(): Nette\DI\Container
{
	Tester\Environment::setup(); // inicialización de 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();
}
versión: 4.0