Nette Documentation Preview

syntax
Bootstrap
*********

<div class=perex>

Bootstrap to kod startowy, który inicjalizuje środowisko, tworzy kontener wtrysku zależności (DI) i uruchamia aplikację. Powiedzmy:

- jak skonfigurować przy użyciu plików NEON
- jak odróżnić tryb produkcyjny od deweloperskiego
- jak stworzyć kontener DI

</div>


Aplikacje, niezależnie od tego, czy są to aplikacje internetowe, czy skrypty wiersza poleceń, rozpoczynają swój czas działania od pewnej formy inicjalizacji środowiska. W dawnych czasach robił to plik o nazwie na przykład `include.inc.php`, który inkubował początkowy plik.
W nowoczesnych aplikacjach Nette zostało to zastąpione klasą `Bootstrap`, która jako część aplikacji znajduje się w pliku `app/Bootstrap.php` Może to wyglądać tak:

```php
use Nette\Bootstrap\Configurator;

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

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// Konfigurator jest odpowiedzialny za konfigurację środowiska aplikacji i usług.
		$this->configurator = new Configurator;
		// Ustawienie katalogu dla plików tymczasowych generowanych przez Nette (np. skompilowanych szablonów).
		$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 jest inteligentny i tryb deweloperski włącza się automatycznie,
		// lub można go włączyć dla określonego adresu IP, odkomentowując następującą linię:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Włącza Tracy: najlepsze narzędzie do debugowania.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: automatycznie ładuje wszystkie klasy w podanym katalogu
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Ładowanie plików konfiguracyjnych
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}
```


index.php .[#toc-index-php]
===========================

Początkowym plikiem dla aplikacji internetowych jest `index.php`, znajdujący się w publicznym katalogu `www/`. Używa on klasy `Bootstrap` do zainicjowania środowiska i utworzenia kontenera DI. Następnie uzyskuje usługę `Application` z kontenera, który uruchamia aplikację internetową:

```php
$bootstrap = new App\Bootstrap;
// Inicjalizacja środowiska + utworzenie kontenera DI
$container = $bootstrap->bootWebApplication();
// Kontener DI tworzy obiekt Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Uruchom aplikację Nette i obsłuż przychodzące żądanie
$application->run();
```

Jak widać, klasa [api:Nette\Bootstrap\Configurator] pomaga w konfiguracji środowiska i tworzeniu kontenera dependency injection (DI), któremu teraz przyjrzymy się bliżej.


Tryb deweloperski a produkcyjny .[#toc-development-vs-production-mode]
======================================================================

Nette zachowuje się różnie w zależności od tego, czy działa na serwerze deweloperskim czy produkcyjnym:

🛠️ Tryb deweloperski:
	- Wyświetla pasek debugowania Tracy z przydatnymi informacjami (np. zapytania SQL, czas wykonania, użycie pamięci).
	- Wyświetla szczegółową stronę błędu ze śladami wywołań funkcji i zawartością zmiennych w przypadku wystąpienia błędu.
	- Automatycznie odświeża pamięć podręczną, gdy modyfikowane są szablony Latte, pliki konfiguracyjne itp.


Tryb produkcyjny:
	- Nie wyświetla żadnych informacji debugowania; wszystkie błędy są rejestrowane.
	- Wyświetla stronę `ErrorPresenter` lub ogólną stronę "Błąd serwera", gdy wystąpi błąd.
	- Pamięć podręczna nigdy nie jest automatycznie odświeżana!
	- Zoptymalizowany pod kątem szybkości i bezpieczeństwa.


Tryb jest określany automatycznie, więc w większości przypadków nie ma potrzeby konfigurowania lub przełączania go ręcznie:

- Tryb deweloperski: Aktywny na localhost (adres IP `127.0.0.1` lub `::1`), chyba że używany jest serwer proxy (tj. na podstawie jego nagłówków HTTP).
- Tryb produkcyjny: Aktywny wszędzie indziej.

Jeśli chcemy włączyć tryb deweloperski w innych przypadkach, takich jak programiści uzyskujący dostęp z określonego adresu IP, używamy `setDebugMode()`:

```php
$this->configurator->setDebugMode('23.75.345.200'); // można również określić pole adresu IP
```

Zdecydowanie zalecamy połączenie adresu IP z plikiem cookie. W pliku cookie `nette-debug` przechowujemy tajny token, np. `secret1234`, i w ten sposób umożliwiamy tryb deweloperski dla programistów uzyskujących dostęp z określonego adresu IP i posiadających token w pliku cookie:

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

Możemy również całkowicie wyłączyć tryb deweloperski, nawet dla localhost:

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

Uwaga, wartość `true` domyślnie włącza tryb deweloperski, co nigdy nie może mieć miejsca na serwerze produkcyjnym.


Narzędzie do debugowania Tracy .[#toc-debugging-tool-tracy]
===========================================================

Aby ułatwić debugowanie, włączmy wspaniałe narzędzie [Tracy |tracy:]. Wizualizuje błędy w trybie deweloperskim i loguje błędy w trybie produkcyjnym do podanego katalogu:

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


Pliki tymczasowe .[#toc-temporary-files]
========================================

Nette używa buforowania dla kontenera DI, RobotLoader, szablonów itp. Dlatego musisz ustawić ścieżkę do katalogu, w którym będzie przechowywany cache:

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

W systemach Linux lub macOS ustaw katalogi `log/` i `temp/` na uprawnienia do [zapisu |nette:troubleshooting#Setting-Directory-Permissions].


RobotLoader .[#toc-robotloader]
===============================

Zazwyczaj będziemy chcieli automatycznie załadować klasy za pomocą [RobotLoader |robot-loader:], więc musimy go uruchomić i kazać mu załadować klasy z katalogu, w którym znajduje się `Bootstrap.php` (czyli `__DIR__`), oraz z wszelkich podkatalogów:

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

Alternatywnym podejściem jest pozwolić mu załadować klasy tylko poprzez [Composer |best-practices:composer], jednocześnie podążając za PSR-4.


Strefa czasowa .[#toc-timezone]
===============================

Domyślną strefę czasową można ustawić za pośrednictwem konfiguratora.

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


Konfiguracja kontenera DI .[#toc-di-container-configuration]
============================================================

Częścią procesu uruchamiania jest stworzenie kontenera DI, czyli fabryki obiektów, która jest sercem aplikacji. Jest to właściwie klasa PHP, która jest generowana przez Nette i przechowywana w katalogu cache. Fabryka produkuje kluczowe obiekty aplikacji, a my za pomocą plików konfiguracyjnych instruujemy ją, jak ma je tworzyć i ustawiać, wpływając tym samym na zachowanie całej aplikacji.

Pliki konfiguracyjne są zazwyczaj zapisane w formacie [NEON |neon:format]. Zobacz osobny rozdział, aby dowiedzieć się, co [można skonfigurować |nette:configuring].

.[tip]
W trybie deweloperskim kontener jest automatycznie aktualizowany przy każdej zmianie kodu lub plików konfiguracyjnych. W trybie produkcyjnym jest on generowany tylko raz, a zmiany nie są sprawdzane w celu uzyskania maksymalnej wydajności.

Pliki konfiguracyjne są ładowane za pomocą `addConfig()`:

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

Jeśli chcemy dodać więcej plików konfiguracyjnych, możemy wywołać funkcję `addConfig()` wielokrotnie.

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

Nazwa `cli.php` nie jest literówką, konfiguracja może być również zapisana w pliku PHP, który zwraca ją jako tablicę.

Możemy również dodać inne pliki konfiguracyjne w [sekcji `includes` |dependency-injection:configuration#including-files].

Jeśli w plikach konfiguracyjnych pojawią się elementy o takich samych kluczach, zostaną one nadpisane, lub [scalone |dependency-injection:configuration#merging] w przypadku pól. Plik dodany później ma wyższy priorytet niż poprzedni. Plik, w którym wymieniona jest sekcja `includes` ma wyższy priorytet niż pliki w niej zawarte.


Parametry statyczne .[#toc-static-parameters]
---------------------------------------------

Parametry wykorzystywane w plikach konfiguracyjnych można zdefiniować [w sekcji `parameters` |dependency-injection:configuration#parameters], a także przekazać (lub nadpisać) za pomocą metody `addStaticParameters()` (posiada ona alias `addParameters()`). Co ważne, różne wartości parametrów spowodują wygenerowanie dodatkowych kontenerów DI, czyli dodatkowych klas.

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

W plikach konfiguracyjnych możemy zapisać zwykłą notację `%projectId%` aby uzyskać dostęp do parametru o nazwie `projectId`.


Parametry dynamiczne .[#toc-dynamic-parameters]
-----------------------------------------------

Do kontenera możemy również dodać parametry dynamiczne, których różne wartości, w przeciwieństwie do parametrów statycznych, nie będą powodowały generowania nowych kontenerów DI.

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

Możemy po prostu dodać np. zmienne środowiskowe, do których następnie możemy się odwołać w konfiguracji pisząc `%env.variable%`.

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


Parametry domyślne .[#toc-default-parameters]
---------------------------------------------

W plikach konfiguracyjnych można używać następujących parametrów statycznych:

- `%appDir%` jest bezwzględną ścieżką do katalogu zawierającego plik `Bootstrap.php`
- `%wwwDir%` jest bezwzględną ścieżką do katalogu zawierającego plik wejściowy `index.php`
- `%tempDir%` jest bezwzględną ścieżką do katalogu plików tymczasowych
- `%vendorDir%` to bezwzględna ścieżka do katalogu, w którym Composer instaluje biblioteki
- `%rootDir%` to bezwzględna ścieżka do katalogu głównego projektu
- `%debugMode%` wskazuje, czy aplikacja jest w trybie debugowania
- `%consoleMode%` wskazuje, czy żądanie przyszło z linii poleceń


Usługi importowane .[#toc-imported-services]
--------------------------------------------

Teraz wchodzimy głębiej. Chociaż punktem kontenera DI jest wytwarzanie obiektów, w rzadkich przypadkach może zaistnieć potrzeba wstawienia do kontenera istniejącego obiektu. Robimy to poprzez zdefiniowanie usługi z flagą `imported: true`.

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

A w bootstrapie wstawiamy obiekt do kontenera:

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


Różne środowiska .[#toc-different-environments]
===============================================

Nie wahaj się dostosować klasy `Bootstrap` do swoich potrzeb. Możesz dodać parametry do metody `bootWebApplication()`, aby rozróżnić projekty internetowe. Alternatywnie można dodać inne metody, takie jak `bootTestEnvironment()` do inicjalizacji środowiska dla testów jednostkowych, `bootConsoleApplication()` dla skryptów wywoływanych z wiersza poleceń itp.

```php
public function bootTestEnvironment(): Nette\DI\Container
{
	Tester\Environment::setup(); // Inicjalizacja testera sieci
	$this->setupContainer();
	return $this->configurator->createContainer();
}

public function bootConsoleApplication(): Nette\DI\Container
{
	$this->configurator->setDebugMode(false);
	$this->initializeEnvironment();
	$this->setupContainer();
	return $this->configurator->createContainer();
}
```

Bootstrap

Bootstrap to kod startowy, który inicjalizuje środowisko, tworzy kontener wtrysku zależności (DI) i uruchamia aplikację. Powiedzmy:

  • jak skonfigurować przy użyciu plików NEON
  • jak odróżnić tryb produkcyjny od deweloperskiego
  • jak stworzyć kontener DI

Aplikacje, niezależnie od tego, czy są to aplikacje internetowe, czy skrypty wiersza poleceń, rozpoczynają swój czas działania od pewnej formy inicjalizacji środowiska. W dawnych czasach robił to plik o nazwie na przykład include.inc.php, który inkubował początkowy plik. W nowoczesnych aplikacjach Nette zostało to zastąpione klasą Bootstrap, która jako część aplikacji znajduje się w pliku app/Bootstrap.php Może to wyglądać tak:

use Nette\Bootstrap\Configurator;

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

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// Konfigurator jest odpowiedzialny za konfigurację środowiska aplikacji i usług.
		$this->configurator = new Configurator;
		// Ustawienie katalogu dla plików tymczasowych generowanych przez Nette (np. skompilowanych szablonów).
		$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 jest inteligentny i tryb deweloperski włącza się automatycznie,
		// lub można go włączyć dla określonego adresu IP, odkomentowując następującą linię:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Włącza Tracy: najlepsze narzędzie do debugowania.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: automatycznie ładuje wszystkie klasy w podanym katalogu
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Ładowanie plików konfiguracyjnych
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}

index.php

Początkowym plikiem dla aplikacji internetowych jest index.php, znajdujący się w publicznym katalogu www/. Używa on klasy Bootstrap do zainicjowania środowiska i utworzenia kontenera DI. Następnie uzyskuje usługę Application z kontenera, który uruchamia aplikację internetową:

$bootstrap = new App\Bootstrap;
// Inicjalizacja środowiska + utworzenie kontenera DI
$container = $bootstrap->bootWebApplication();
// Kontener DI tworzy obiekt Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Uruchom aplikację Nette i obsłuż przychodzące żądanie
$application->run();

Jak widać, klasa Nette\Bootstrap\Configurator pomaga w konfiguracji środowiska i tworzeniu kontenera dependency injection (DI), któremu teraz przyjrzymy się bliżej.

Tryb deweloperski a produkcyjny

Nette zachowuje się różnie w zależności od tego, czy działa na serwerze deweloperskim czy produkcyjnym:

🛠️ Tryb deweloperski
Wyświetla pasek debugowania Tracy z przydatnymi informacjami (np. zapytania SQL, czas wykonania, użycie pamięci).
Wyświetla szczegółową stronę błędu ze śladami wywołań funkcji i zawartością zmiennych w przypadku wystąpienia błędu.
Automatycznie odświeża pamięć podręczną, gdy modyfikowane są szablony Latte, pliki konfiguracyjne itp.
Tryb produkcyjny
Nie wyświetla żadnych informacji debugowania; wszystkie błędy są rejestrowane.
Wyświetla stronę ErrorPresenter lub ogólną stronę „Błąd serwera“, gdy wystąpi błąd.
Pamięć podręczna nigdy nie jest automatycznie odświeżana!
Zoptymalizowany pod kątem szybkości i bezpieczeństwa.

Tryb jest określany automatycznie, więc w większości przypadków nie ma potrzeby konfigurowania lub przełączania go ręcznie:

  • Tryb deweloperski: Aktywny na localhost (adres IP 127.0.0.1 lub ::1), chyba że używany jest serwer proxy (tj. na podstawie jego nagłówków HTTP).
  • Tryb produkcyjny: Aktywny wszędzie indziej.

Jeśli chcemy włączyć tryb deweloperski w innych przypadkach, takich jak programiści uzyskujący dostęp z określonego adresu IP, używamy setDebugMode():

$this->configurator->setDebugMode('23.75.345.200'); // można również określić pole adresu IP

Zdecydowanie zalecamy połączenie adresu IP z plikiem cookie. W pliku cookie nette-debug przechowujemy tajny token, np. secret1234, i w ten sposób umożliwiamy tryb deweloperski dla programistów uzyskujących dostęp z określonego adresu IP i posiadających token w pliku cookie:

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

Możemy również całkowicie wyłączyć tryb deweloperski, nawet dla localhost:

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

Uwaga, wartość true domyślnie włącza tryb deweloperski, co nigdy nie może mieć miejsca na serwerze produkcyjnym.

Narzędzie do debugowania Tracy

Aby ułatwić debugowanie, włączmy wspaniałe narzędzie Tracy. Wizualizuje błędy w trybie deweloperskim i loguje błędy w trybie produkcyjnym do podanego katalogu:

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

Pliki tymczasowe

Nette używa buforowania dla kontenera DI, RobotLoader, szablonów itp. Dlatego musisz ustawić ścieżkę do katalogu, w którym będzie przechowywany cache:

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

W systemach Linux lub macOS ustaw katalogi log/ i temp/ na uprawnienia do zapisu.

RobotLoader

Zazwyczaj będziemy chcieli automatycznie załadować klasy za pomocą RobotLoader, więc musimy go uruchomić i kazać mu załadować klasy z katalogu, w którym znajduje się Bootstrap.php (czyli __DIR__), oraz z wszelkich podkatalogów:

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

Alternatywnym podejściem jest pozwolić mu załadować klasy tylko poprzez Composer, jednocześnie podążając za PSR-4.

Strefa czasowa

Domyślną strefę czasową można ustawić za pośrednictwem konfiguratora.

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

Konfiguracja kontenera DI

Częścią procesu uruchamiania jest stworzenie kontenera DI, czyli fabryki obiektów, która jest sercem aplikacji. Jest to właściwie klasa PHP, która jest generowana przez Nette i przechowywana w katalogu cache. Fabryka produkuje kluczowe obiekty aplikacji, a my za pomocą plików konfiguracyjnych instruujemy ją, jak ma je tworzyć i ustawiać, wpływając tym samym na zachowanie całej aplikacji.

Pliki konfiguracyjne są zazwyczaj zapisane w formacie NEON. Zobacz osobny rozdział, aby dowiedzieć się, co można skonfigurować.

W trybie deweloperskim kontener jest automatycznie aktualizowany przy każdej zmianie kodu lub plików konfiguracyjnych. W trybie produkcyjnym jest on generowany tylko raz, a zmiany nie są sprawdzane w celu uzyskania maksymalnej wydajności.

Pliki konfiguracyjne są ładowane za pomocą addConfig():

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

Jeśli chcemy dodać więcej plików konfiguracyjnych, możemy wywołać funkcję addConfig() wielokrotnie.

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

Nazwa cli.php nie jest literówką, konfiguracja może być również zapisana w pliku PHP, który zwraca ją jako tablicę.

Możemy również dodać inne pliki konfiguracyjne w sekcji includes.

Jeśli w plikach konfiguracyjnych pojawią się elementy o takich samych kluczach, zostaną one nadpisane, lub scalone w przypadku pól. Plik dodany później ma wyższy priorytet niż poprzedni. Plik, w którym wymieniona jest sekcja includes ma wyższy priorytet niż pliki w niej zawarte.

Parametry statyczne

Parametry wykorzystywane w plikach konfiguracyjnych można zdefiniować w sekcji parameters, a także przekazać (lub nadpisać) za pomocą metody addStaticParameters() (posiada ona alias addParameters()). Co ważne, różne wartości parametrów spowodują wygenerowanie dodatkowych kontenerów DI, czyli dodatkowych klas.

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

W plikach konfiguracyjnych możemy zapisać zwykłą notację %projectId% aby uzyskać dostęp do parametru o nazwie projectId.

Parametry dynamiczne

Do kontenera możemy również dodać parametry dynamiczne, których różne wartości, w przeciwieństwie do parametrów statycznych, nie będą powodowały generowania nowych kontenerów DI.

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

Możemy po prostu dodać np. zmienne środowiskowe, do których następnie możemy się odwołać w konfiguracji pisząc %env.variable%.

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

Parametry domyślne

W plikach konfiguracyjnych można używać następujących parametrów statycznych:

  • %appDir% jest bezwzględną ścieżką do katalogu zawierającego plik Bootstrap.php
  • %wwwDir% jest bezwzględną ścieżką do katalogu zawierającego plik wejściowy index.php
  • %tempDir% jest bezwzględną ścieżką do katalogu plików tymczasowych
  • %vendorDir% to bezwzględna ścieżka do katalogu, w którym Composer instaluje biblioteki
  • %rootDir% to bezwzględna ścieżka do katalogu głównego projektu
  • %debugMode% wskazuje, czy aplikacja jest w trybie debugowania
  • %consoleMode% wskazuje, czy żądanie przyszło z linii poleceń

Usługi importowane

Teraz wchodzimy głębiej. Chociaż punktem kontenera DI jest wytwarzanie obiektów, w rzadkich przypadkach może zaistnieć potrzeba wstawienia do kontenera istniejącego obiektu. Robimy to poprzez zdefiniowanie usługi z flagą imported: true.

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

A w bootstrapie wstawiamy obiekt do kontenera:

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

Różne środowiska

Nie wahaj się dostosować klasy Bootstrap do swoich potrzeb. Możesz dodać parametry do metody bootWebApplication(), aby rozróżnić projekty internetowe. Alternatywnie można dodać inne metody, takie jak bootTestEnvironment() do inicjalizacji środowiska dla testów jednostkowych, bootConsoleApplication() dla skryptów wywoływanych z wiersza poleceń itp.

public function bootTestEnvironment(): Nette\DI\Container
{
	Tester\Environment::setup(); // Inicjalizacja testera sieci
	$this->setupContainer();
	return $this->configurator->createContainer();
}

public function bootConsoleApplication(): Nette\DI\Container
{
	$this->configurator->setDebugMode(false);
	$this->initializeEnvironment();
	$this->setupContainer();
	return $this->configurator->createContainer();
}