Bootstrap
Το Bootstrap είναι ο κώδικας εκκίνησης που αρχικοποιεί το περιβάλλον, δημιουργεί το dependency injection (DI) container και εκκινεί την εφαρμογή. Θα συζητήσουμε:
- πώς διαμορφώνεται χρησιμοποιώντας αρχεία NEON
- πώς να διακρίνουμε μεταξύ λειτουργίας παραγωγής και ανάπτυξης
- πώς να δημιουργήσετε ένα DI container
Οι εφαρμογές, είτε πρόκειται για διαδικτυακές εφαρμογές είτε για
σενάρια που εκτελούνται από τη γραμμή εντολών, ξεκινούν την εκτέλεσή
τους με κάποια μορφή αρχικοποίησης περιβάλλοντος. Στο παρελθόν, αυτό
γινόταν συνήθως από ένα αρχείο με όνομα όπως include.inc.php
, το οποίο
το αρχικό αρχείο συμπεριλάμβανε. Στις σύγχρονες εφαρμογές Nette, αυτό
έχει αντικατασταθεί από την κλάση Bootstrap
, την οποία, ως μέρος της
εφαρμογής, θα βρείτε στο αρχείο app/Bootstrap.php
. Μπορεί να μοιάζει
κάπως έτσι:
use Nette\Bootstrap\Configurator;
class Bootstrap
{
private Configurator $configurator;
private string $rootDir;
public function __construct()
{
$this->rootDir = dirname(__DIR__);
// Ο Configurator είναι υπεύθυνος για τη ρύθμιση του περιβάλλοντος της εφαρμογής και των υπηρεσιών.
$this->configurator = new Configurator;
// Ορίζει τον κατάλογο για προσωρινά αρχεία που δημιουργούνται από το Nette (π.χ. μεταγλωττισμένα 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 είναι έξυπνο και η λειτουργία ανάπτυξης ενεργοποιείται αυτόματα,
// ή μπορείτε να την ενεργοποιήσετε για μια συγκεκριμένη διεύθυνση IP αποσχολιάζοντας την ακόλουθη γραμμή:
// $this->configurator->setDebugMode('secret@23.75.345.200');
// Ενεργοποιεί το Tracy: το απόλυτο "ελβετικό μαχαίρι" για αποσφαλμάτωση.
$this->configurator->enableTracy($this->rootDir . '/log');
// RobotLoader: φορτώνει αυτόματα όλες τις κλάσεις στον επιλεγμένο κατάλογο
$this->configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
}
private function setupContainer(): void
{
// Φορτώνει αρχεία διαμόρφωσης
$this->configurator->addConfig($this->rootDir . '/config/common.neon');
}
}
index.php
Το αρχικό αρχείο στην περίπτωση των διαδικτυακών εφαρμογών είναι το
index.php
, το οποίο βρίσκεται στον δημόσιο κατάλογο
www/
. Αυτό ζητά από την κλάση Bootstrap να αρχικοποιήσει το περιβάλλον
και να δημιουργήσει το DI container. Στη συνέχεια, λαμβάνει την υπηρεσία
Application
από αυτό, η οποία εκκινεί την διαδικτυακή εφαρμογή:
$bootstrap = new App\Bootstrap;
// Αρχικοποίηση περιβάλλοντος + δημιουργία DI container
$container = $bootstrap->bootWebApplication();
// Το DI container δημιουργεί ένα αντικείμενο Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Εκκίνηση της εφαρμογής Nette και επεξεργασία της εισερχόμενης αίτησης
$application->run();
Όπως μπορείτε να δείτε, η κλάση Nette\Bootstrap\Configurator βοηθά στη ρύθμιση του περιβάλλοντος και στη δημιουργία του dependency injection (DI) container, την οποία θα παρουσιάσουμε τώρα λεπτομερέστερα.
Λειτουργία Ανάπτυξης vs Παραγωγής
Το Nette συμπεριφέρεται διαφορετικά ανάλογα με το αν εκτελείται σε διακομιστή ανάπτυξης ή παραγωγής:
- 🛠️ Λειτουργία Ανάπτυξης (Development)
- Εμφανίζει τη γραμμή αποσφαλμάτωσης Tracy με χρήσιμες πληροφορίες (ερωτήματα SQL, χρόνος εκτέλεσης, χρησιμοποιούμενη μνήμη)
- Σε περίπτωση σφάλματος, εμφανίζει μια λεπτομερή σελίδα σφάλματος με κλήσεις συναρτήσεων και περιεχόμενο μεταβλητών
- Ανανεώνει αυτόματα την cache κατά την αλλαγή templates Latte, την τροποποίηση αρχείων διαμόρφωσης κ.λπ.
- 🚀 Λειτουργία Παραγωγής (Production)
- Δεν εμφανίζει καμία πληροφορία αποσφαλμάτωσης, όλα τα σφάλματα καταγράφονται στο αρχείο καταγραφής
- Σε περίπτωση σφάλματος, εμφανίζει τον ErrorPresenter ή μια γενική σελίδα “Server Error”
- Η cache δεν ανανεώνεται ποτέ αυτόματα!
- Βελτιστοποιημένο για ταχύτητα και ασφάλεια
Η επιλογή της λειτουργίας γίνεται με αυτόματη ανίχνευση, οπότε συνήθως δεν χρειάζεται να διαμορφώσετε ή να αλλάξετε τίποτα χειροκίνητα:
- λειτουργία ανάπτυξης: στο localhost (διεύθυνση IP
127.0.0.1
ή::1
) εάν δεν υπάρχει proxy (δηλαδή η κεφαλίδα HTTP του) - λειτουργία παραγωγής: παντού αλλού
Αν θέλουμε να ενεργοποιήσουμε τη λειτουργία ανάπτυξης και σε άλλες
περιπτώσεις, για παράδειγμα για προγραμματιστές που έχουν πρόσβαση
από μια συγκεκριμένη διεύθυνση IP, χρησιμοποιούμε το setDebugMode()
:
$this->configurator->setDebugMode('23.75.345.200'); // μπορείτε επίσης να καθορίσετε έναν πίνακα διευθύνσεων IP
Συνιστούμε οπωσδήποτε να συνδυάσετε τη διεύθυνση IP με ένα cookie.
Αποθηκεύουμε ένα μυστικό token, π.χ. secret1234
, στο cookie nette-debug
και με αυτόν τον τρόπο ενεργοποιούμε τη λειτουργία ανάπτυξης για
προγραμματιστές που έχουν πρόσβαση από μια συγκεκριμένη διεύθυνση IP
και ταυτόχρονα έχουν το αναφερόμενο token στο cookie:
$this->configurator->setDebugMode('secret1234@23.75.345.200');
Μπορούμε επίσης να απενεργοποιήσουμε εντελώς τη λειτουργία ανάπτυξης, ακόμη και για το localhost:
$this->configurator->setDebugMode(false);
Προσοχή, η τιμή true
ενεργοποιεί τη λειτουργία ανάπτυξης μόνιμα,
κάτι που δεν πρέπει ποτέ να συμβεί σε διακομιστή παραγωγής.
Εργαλείο Αποσφαλμάτωσης Tracy
Για εύκολη αποσφαλμάτωση, ενεργοποιούμε επίσης το εξαιρετικό εργαλείο Tracy. Στη λειτουργία ανάπτυξης, οπτικοποιεί τα σφάλματα και στη λειτουργία παραγωγής, καταγράφει τα σφάλματα στον καθορισμένο κατάλογο:
$this->configurator->enableTracy($this->rootDir . '/log');
Προσωρινά Αρχεία
Το Nette χρησιμοποιεί cache για το DI container, το RobotLoader, τα templates κ.λπ. Επομένως, είναι απαραίτητο να ορίσετε τη διαδρομή προς τον κατάλογο όπου θα αποθηκεύεται η cache:
$this->configurator->setTempDirectory($this->rootDir . '/temp');
Σε Linux ή macOS, ορίστε δικαιώματα εγγραφής για
τους καταλόγους log/
και temp/
.
RobotLoader
Συνήθως, θα θέλουμε να φορτώνουμε αυτόματα κλάσεις χρησιμοποιώντας
το RobotLoader, οπότε πρέπει να το ξεκινήσουμε
και να το αφήσουμε να φορτώνει κλάσεις από τον κατάλογο όπου βρίσκεται
το Bootstrap.php
(δηλαδή __DIR__
), και όλους τους υποκαταλόγους:
$this->configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
Μια εναλλακτική προσέγγιση είναι να αφήσετε τις κλάσεις να φορτώνονται μόνο μέσω του Composer τηρώντας το PSR-4.
Ζώνη Ώρας
Μέσω του configurator, μπορείτε να ορίσετε την προεπιλεγμένη ζώνη ώρας.
$this->configurator->setTimeZone('Europe/Prague');
Διαμόρφωση του DI Container
Μέρος της διαδικασίας εκκίνησης είναι η δημιουργία του DI container ή factory αντικειμένων, το οποίο είναι η καρδιά ολόκληρης της εφαρμογής. Πρόκειται στην πραγματικότητα για μια κλάση PHP που δημιουργείται από το Nette και αποθηκεύεται στον κατάλογο cache. Το factory παράγει τα βασικά αντικείμενα της εφαρμογής και, χρησιμοποιώντας αρχεία διαμόρφωσης, το καθοδηγούμε πώς να τα δημιουργεί και να τα ρυθμίζει, επηρεάζοντας έτσι τη συμπεριφορά ολόκληρης της εφαρμογής.
Τα αρχεία διαμόρφωσης συνήθως γράφονται σε μορφή NEON. Σε ένα ξεχωριστό κεφάλαιο, θα μάθετε τι μπορεί να διαμορφωθεί.
Στη λειτουργία ανάπτυξης, το container ενημερώνεται αυτόματα κάθε φορά που αλλάζει ο κώδικας ή τα αρχεία διαμόρφωσης. Στη λειτουργία παραγωγής, δημιουργείται μόνο μία φορά και οι αλλαγές δεν ελέγχονται για μεγιστοποίηση της απόδοσης.
Φορτώνουμε τα αρχεία διαμόρφωσης χρησιμοποιώντας το addConfig()
:
$this->configurator->addConfig($this->rootDir . '/config/common.neon');
Αν θέλουμε να προσθέσουμε περισσότερα αρχεία διαμόρφωσης, μπορούμε
να καλέσουμε τη συνάρτηση addConfig()
πολλές φορές.
$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');
}
Το όνομα cli.php
δεν είναι τυπογραφικό λάθος, η διαμόρφωση μπορεί
επίσης να γραφτεί σε ένα αρχείο PHP που την επιστρέφει ως array.
Μπορούμε επίσης να προσθέσουμε άλλα αρχεία διαμόρφωσης στην ενότητα
includes
.
Αν εμφανιστούν στοιχεία με τα ίδια κλειδιά στα αρχεία διαμόρφωσης, θα
αντικατασταθούν ή, στην περίπτωση arrays, θα συγχωνευθούν. Το
αρχείο που εισάγεται αργότερα έχει υψηλότερη προτεραιότητα από το
προηγούμενο. Το αρχείο στο οποίο αναφέρεται η ενότητα includes
έχει
υψηλότερη προτεραιότητα από τα αρχεία που περιλαμβάνονται σε αυτό.
Στατικές Παράμετροι
Μπορούμε να ορίσουμε παραμέτρους που χρησιμοποιούνται στα αρχεία
διαμόρφωσης στην ενότητα
parameters
και επίσης να τις μεταβιβάσουμε (ή να τις
αντικαταστήσουμε) με τη μέθοδο addStaticParameters()
(έχει το ψευδώνυμο
addParameters()
). Είναι σημαντικό ότι διαφορετικές τιμές παραμέτρων
προκαλούν τη δημιουργία πρόσθετων DI containers, δηλαδή πρόσθετων κλάσεων.
$this->configurator->addStaticParameters([
'projectId' => 23,
]);
Στην παράμετρο projectId
μπορείτε να αναφερθείτε στη διαμόρφωση
με τη συνηθισμένη σύνταξη %projectId%
.
Δυναμικές Παράμετροι
Μπορούμε επίσης να προσθέσουμε δυναμικές παραμέτρους στο container, των οποίων οι διαφορετικές τιμές, σε αντίθεση με τις στατικές παραμέτρους, δεν προκαλούν τη δημιουργία νέων DI containers.
$this->configurator->addDynamicParameters([
'remoteIp' => $_SERVER['REMOTE_ADDR'],
]);
Με αυτόν τον τρόπο, μπορούμε εύκολα να προσθέσουμε, για παράδειγμα,
μεταβλητές περιβάλλοντος, στις οποίες μπορείτε στη συνέχεια να
αναφερθείτε στη διαμόρφωση χρησιμοποιώντας τη σύνταξη
%env.variable%
.
$this->configurator->addDynamicParameters([
'env' => getenv(),
]);
Προεπιλεγμένες Παράμετροι
Στα αρχεία διαμόρφωσης, μπορείτε να χρησιμοποιήσετε αυτές τις στατικές παραμέτρους:
%appDir%
είναι η απόλυτη διαδρομή προς τον κατάλογο με το αρχείοBootstrap.php
%wwwDir%
είναι η απόλυτη διαδρομή προς τον κατάλογο με το αρχείο εισόδουindex.php
%tempDir%
είναι η απόλυτη διαδρομή προς τον κατάλογο για προσωρινά αρχεία%vendorDir%
είναι η απόλυτη διαδρομή προς τον κατάλογο όπου ο Composer εγκαθιστά βιβλιοθήκες%rootDir%
είναι η απόλυτη διαδρομή προς τον ριζικό κατάλογο του έργου%debugMode%
υποδεικνύει εάν η εφαρμογή βρίσκεται σε λειτουργία debugging%consoleMode%
υποδεικνύει εάν η request προήλθε από τη γραμμή εντολών
Εισαγόμενες Υπηρεσίες
Τώρα πηγαίνουμε βαθύτερα. Αν και ο σκοπός του DI container είναι να παράγει
αντικείμενα, εξαιρετικά μπορεί να προκύψει η ανάγκη να εισαγάγουμε ένα
υπάρχον αντικείμενο στο container. Αυτό το κάνουμε ορίζοντας την υπηρεσία
με τη σημαία imported: true
.
services:
myservice:
type: App\Model\MyCustomService
imported: true
Και στο bootstrap, εισάγουμε το αντικείμενο στο container:
$this->configurator->addServices([
'myservice' => new App\Model\MyCustomService('foobar'),
]);
Διαφορετικό Περιβάλλον
Μη διστάσετε να τροποποιήσετε την κλάση Bootstrap σύμφωνα με τις ανάγκες
σας. Μπορείτε να προσθέσετε παραμέτρους στη μέθοδο bootWebApplication()
για να διακρίνετε τα διαδικτυακά έργα. Ή μπορούμε να προσθέσουμε άλλες
μεθόδους, όπως bootTestEnvironment()
, που αρχικοποιεί το περιβάλλον για unit
tests, bootConsoleApplication()
για σενάρια που καλούνται από τη γραμμή
εντολών κ.λπ.
public function bootTestEnvironment(): Nette\DI\Container
{
Tester\Environment::setup(); // αρχικοποίηση του 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();
}