Bootstrap

Bootstrap ist der Startcode, der die Umgebung initialisiert, den Dependency Injection (DI) Container erstellt und die Anwendung startet. Wir werden erklären:

  • wie sie mithilfe von NEON-Dateien konfiguriert wird
  • wie man zwischen Produktions- und Entwicklungsmodus unterscheidet
  • wie man den DI-Container erstellt

Anwendungen, egal ob Webanwendungen oder von der Kommandozeile gestartete Skripte, beginnen ihre Ausführung mit einer Form der Initialisierung der Umgebung. Früher war dafür eine Datei wie include.inc.php verantwortlich, die von der initialen Datei eingebunden wurde. In modernen Nette-Anwendungen wurde dies durch die Klasse Bootstrap ersetzt, die Sie als Teil der Anwendung in der Datei app/Bootstrap.php finden. Sie könnte zum Beispiel so aussehen:

use Nette\Bootstrap\Configurator;

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

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// Der Configurator ist für die Einstellung der Anwendungsumgebung und der Dienste verantwortlich.
		$this->configurator = new Configurator;
		// Legt das Verzeichnis für temporäre Dateien fest, die von Nette generiert werden (z. B. kompilierte Templates)
		$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 ist schlau und der Entwicklungsmodus wird automatisch aktiviert,
		// oder Sie können ihn für eine bestimmte IP-Adresse aktivieren, indem Sie die folgende Zeile auskommentieren:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Aktiviert Tracy: das ultimative "Schweizer Taschenmesser" für das Debugging.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: lädt automatisch alle Klassen im ausgewählten Verzeichnis
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Lädt Konfigurationsdateien
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}

index.php

Die initiale Datei für Webanwendungen ist index.php, die sich im öffentlichen Verzeichnis www/ befindet. Diese lässt die Umgebung von der Bootstrap-Klasse initialisieren und den DI-Container erstellen. Danach holt sie sich den Dienst Application daraus, der die Webanwendung startet:

$bootstrap = new App\Bootstrap;
// Initialisierung der Umgebung + Erstellung des DI-Containers
$container = $bootstrap->bootWebApplication();
// Der DI-Container erstellt das Objekt Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Start der Nette-Anwendung und Verarbeitung der eingehenden Anfrage
$application->run();

Wie man sieht, hilft die Klasse Nette\Bootstrap\Configurator bei der Einstellung der Umgebung und der Erstellung des Dependency Injection (DI) Containers, die wir uns nun genauer ansehen werden.

Entwicklungs- vs. Produktionsmodus

Nette verhält sich unterschiedlich, je nachdem, ob es auf einem Entwicklungs- oder Produktionsserver läuft:

🛠️ Entwicklungsmodus (Development)
Zeigt die Tracy Debugbar mit nützlichen Informationen an (SQL-Abfragen, Ausführungszeit, verwendeter Speicher)
Zeigt im Fehlerfall eine detaillierte Fehlerseite mit Funktionsaufrufen und Variableninhalten an
Erneuert automatisch den Cache bei Änderungen an Latte-Templates, Konfigurationsdateien usw.
🚀 Produktionsmodus (Production)
Zeigt keine Debugging-Informationen an, alle Fehler werden im Log protokolliert
Zeigt im Fehlerfall den ErrorPresenter oder eine allgemeine “Server Error”-Seite an
Der Cache wird niemals automatisch erneuert!
Optimiert für Geschwindigkeit und Sicherheit

Die Moduswahl erfolgt durch Auto-Detektion, sodass normalerweise nichts konfiguriert oder manuell umgeschaltet werden muss:

  • Entwicklungsmodus: auf Localhost (IP-Adresse 127.0.0.1 oder ::1), wenn kein Proxy vorhanden ist (d. h. dessen HTTP-Header)
  • Produktionsmodus: überall sonst

Wenn wir den Entwicklungsmodus auch in anderen Fällen aktivieren möchten, z. B. für Programmierer, die von einer bestimmten IP-Adresse zugreifen, verwenden wir setDebugMode():

$this->configurator->setDebugMode('23.75.345.200'); // es kann auch ein Array von IP-Adressen angegeben werden

Wir empfehlen dringend, die IP-Adresse mit einem Cookie zu kombinieren. Wir speichern einen geheimen Token, z. B. secret1234, im Cookie nette-debug und aktivieren auf diese Weise den Entwicklungsmodus für Programmierer, die von einer bestimmten IP-Adresse zugreifen und gleichzeitig den erwähnten Token im Cookie haben:

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

Wir können den Entwicklungsmodus auch vollständig deaktivieren, sogar für Localhost:

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

Achtung, der Wert true schaltet den Entwicklungsmodus fest ein, was auf einem Produktionsserver niemals passieren darf.

Debugging-Tool Tracy

Für einfaches Debugging aktivieren wir noch das großartige Werkzeug Tracy. Im Entwicklungsmodus visualisiert es Fehler und im Produktionsmodus protokolliert es Fehler in das angegebene Verzeichnis:

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

Temporäre Dateien

Nette verwendet einen Cache für den DI-Container, RobotLoader, Templates usw. Daher ist es notwendig, den Pfad zum Verzeichnis festzulegen, in dem der Cache gespeichert wird:

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

Unter Linux oder macOS setzen Sie für die Verzeichnisse log/ und temp/ Schreibrechte.

RobotLoader

In der Regel möchten wir Klassen automatisch mit dem RobotLoader laden, also müssen wir ihn starten und ihn Klassen aus dem Verzeichnis laden lassen, in dem sich Bootstrap.php befindet (d. h. __DIR__), sowie aus allen Unterverzeichnissen:

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

Ein alternativer Ansatz besteht darin, Klassen nur über Composer unter Einhaltung von PSR-4 laden zu lassen.

Zeitzone

Über den Konfigurator können Sie die Standard-Zeitzone einstellen.

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

Konfiguration des DI-Containers

Ein Teil des Boot-Prozesses ist die Erstellung des DI-Containers, auch Objektfabrik genannt, der das Herz der gesamten Anwendung ist. Es handelt sich tatsächlich um eine PHP-Klasse, die von Nette generiert und im Cache-Verzeichnis gespeichert wird. Die Fabrik erstellt die Schlüsselobjekte der Anwendung, und mithilfe von Konfigurationsdateien weisen wir sie an, wie sie diese erstellen und einstellen soll, wodurch wir das Verhalten der gesamten Anwendung beeinflussen.

Konfigurationsdateien werden normalerweise im NEON-Format geschrieben. In einem separaten Kapitel erfahren Sie, was alles konfiguriert werden kann.

Im Entwicklungsmodus wird der Container bei jeder Änderung des Codes oder der Konfigurationsdateien automatisch aktualisiert. Im Produktionsmodus wird er nur einmal generiert, und Änderungen werden zur Maximierung der Leistung nicht überprüft.

Konfigurationsdateien laden wir mit addConfig():

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

Wenn wir mehrere Konfigurationsdateien hinzufügen möchten, können wir die Funktion addConfig() mehrmals aufrufen.

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

Der Name cli.php ist kein Tippfehler; die Konfiguration kann auch in einer PHP-Datei geschrieben sein, die sie als Array zurückgibt.

Wir können auch weitere Konfigurationsdateien im Abschnitt includes hinzufügen.

Wenn in den Konfigurationsdateien Elemente mit denselben Schlüsseln erscheinen, werden sie überschrieben oder im Falle von Arrays zusammengeführt. Die später eingebundene Datei hat eine höhere Priorität als die vorherige. Die Datei, in der der Abschnitt includes aufgeführt ist, hat eine höhere Priorität als die darin eingebundenen Dateien.

Statische Parameter

Parameter, die in Konfigurationsdateien verwendet werden, können im Abschnitt parameters definiert und auch über die Methode addStaticParameters() (hat den Alias addParameters()) übergeben (oder überschrieben) werden. Wichtig ist, dass unterschiedliche Werte der Parameter die Generierung zusätzlicher DI-Container, also zusätzlicher Klassen, verursachen.

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

Auf den Parameter projectId kann in der Konfiguration mit der üblichen Schreibweise %projectId% verwiesen werden.

Dynamische Parameter

Wir können dem Container auch dynamische Parameter hinzufügen, deren unterschiedliche Werte im Gegensatz zu statischen Parametern nicht die Generierung neuer DI-Container verursachen.

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

So können wir einfach z. B. Umgebungsvariablen hinzufügen, auf die dann in der Konfiguration mit der Schreibweise %env.variable% verwiesen werden kann.

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

Standardparameter

In Konfigurationsdateien können Sie diese statischen Parameter verwenden:

  • %appDir% ist der absolute Pfad zum Verzeichnis mit der Datei Bootstrap.php
  • %wwwDir% ist der absolute Pfad zum Verzeichnis mit der Eingabedatei index.php
  • %tempDir% ist der absolute Pfad zum Verzeichnis für temporäre Dateien
  • %vendorDir% ist der absolute Pfad zum Verzeichnis, in dem Composer Bibliotheken installiert
  • %rootDir% ist der absolute Pfad zum Stammverzeichnis des Projekts
  • %debugMode% gibt an, ob sich die Anwendung im Debugging-Modus befindet
  • %consoleMode% gibt an, ob die Anfrage über die Kommandozeile kam

Importierte Dienste

Jetzt gehen wir tiefer. Obwohl der Zweck des DI-Containers darin besteht, Objekte zu erstellen, kann es ausnahmsweise notwendig sein, ein vorhandenes Objekt in den Container einzufügen. Dies tun wir, indem wir den Dienst mit dem Flag imported: true definieren.

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

Und im Bootstrap fügen wir das Objekt in den Container ein:

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

Unterschiedliche Umgebungen

Scheuen Sie sich nicht, die Bootstrap-Klasse an Ihre Bedürfnisse anzupassen. Sie können der Methode bootWebApplication() Parameter hinzufügen, um Webprojekte zu unterscheiden. Oder wir können weitere Methoden hinzufügen, zum Beispiel bootTestEnvironment(), die die Umgebung für Unit-Tests initialisiert, bootConsoleApplication() für Skripte, die von der Kommandozeile aufgerufen werden, usw.

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