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 rozróżnia dwa podstawowe tryby, w których realizowane jest żądanie: deweloperski i produkcyjny. Tryb deweloperski ma na celu maksymalną wygodę dla programisty, wyświetlana jest Tracy, pamięć podręczna jest automatycznie aktualizowana, gdy zmieniają się szablony lub konfiguracje kontenerów DI itp. Produkcja skupia się na wydajności i rześkim wdrożeniu, Tracy tylko loguje błędy, a zmiany w szablonach i innych plikach nie są testowane.
Wybór trybu odbywa się poprzez autodetekcję, więc zazwyczaj nie ma potrzeby konfigurowania czy ręcznego przełączania
czegokolwiek. Trybem deweloperskim jest sytuacja, kiedy aplikacja jest uruchomiona na localhoście (czyli na adresie IP
127.0.0.1
lub ::1
) i nie ma proxy (czyli jego nagłówka HTTP). W przeciwnym razie działa w trybie
produkcyjnym.
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 plikBootstrap.php
%wwwDir%
jest bezwzględną ścieżką do katalogu zawierającego plik wejściowyindex.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();
}