Bootstrap
Bootstrap is boot code that initializes the environment, creates a dependency injection (DI) container, and starts the application. We will discuss:
- how to configure your application using NEON files
- how to handle production and development modes
- how to create the DI container
Applications, whether web-based or command-line scripts, begin by some form of environment initialization. In ancient times, it
could be a file named eg include.inc.php
that was in charge of this, and was included in the initial file. In modern
Nette applications, it has been replaced by the file bootstrap.php
, which as part of the application can be found in
the app/bootstrap.php
. It might look for example like this:
$configurator = new Nette\Configurator;
//$configurator->setDebugMode('secret@23.75.345.200');
$configurator->enableTracy(__DIR__ . '/../log');
$configurator->setTempDirectory(__DIR__ . '/../temp');
$configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
$configurator->addConfig(__DIR__ . '/config/config.neon');
// create a DI container
$container = $configurator->createContainer();
return $container;
index.php
In the case of web applications, the initial file is index.php
, which is located in the public directory
www/
. It lets the bootstrap.php
to initialize the environment and return the DI container
$container
. Then it obtains the Application
service, that runs the web application:
// initialize the environment + get DI container
$container = require __DIR__ . '/../app/bootstrap.php';
// DI container creates a Nette\Application\Application object
$application = $container->getByType(Nette\Application\Application::class);
// start Nette application
$application->run();
As you can see, the Nette\Configurator class, which we will now introduce in more detail, helps with setting up the environment and creating a dependency injection (DI) container.
Development vs Production Mode
Nette distinguishes between two basic modes in which a request is executed: development and production. The development mode is focused on the maximum comfort of the programmer, Tracy is displayed, the cache is automatically updated when changing templates or DI container configuration, etc. Production mode is focused on performance, Tracy only logs errors and changes of templates and other files are not checked.
Mode selection is done by autodetection, so there is usually no need to configure or switch anything manually. The mode is
development if the application is running on localhost (ie IP address 127.0.0.1
or ::1
) and no proxy is
present (ie its HTTP header). Otherwise, it runs in production mode.
If you want to enable development mode in other cases, for example, for programmers accessing from a specific IP address, you
can use setDebugMode()
:
$configurator->setDebugMode('23.75.345.200'); // one or more IP addresses
We definitely recommend combining an IP address with a cookie. We will store a secret token into the nette-debug
cookie, e.g. secret1234
, and the development mode will be activated for programmers with this combination of IP and
cookie.
$configurator->setDebugMode('secret1234@23.75.345.200');
We can also turn off developer mode completely, even for localhost:
$configurator->setDebugMode(false);
Debugging Tool Tracy
For easy debugging, we will turn on the great tool Tracy. In developer mode it visualizes errors and in production mode it logs errors to the specified directory:
$configurator->enableTracy(__DIR__ . '/../log');
Temporary Files
Nette uses the cache for DI container, RobotLoader, templates, etc. Therefore it is necessary to set the path to the directory where the cache will be stored:
$configurator->setTempDirectory(__DIR__ . '/../temp');
On Linux or macOS, set the write
permissions for directories log/
and temp/
.
RobotLoader
Usually, we will want to automatically load the classes using RobotLoader, so we have to start it up and let it load classes from the
directory where bootstrap.php
is located (i.e. __DIR__
) and all its subdirectories:
$configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
An alternative way is to only use Composer PSR-4 autoloading.
Timezone
Configurator allows you to specify a timezone for your application.
$configurator->setTimeZone('Europe/Prague');
DI Container Configuration
Part of the boot process is the creation of a DI container, ie a factory for objects, which is the heart of the whole application. It is actually a PHP class generated by Nette and stored in a cache directory. The factory produces key application objects and configuration files instruct it how to create and configure them, and thus we influence the behavior of the whole application.
Configuration files are usually written in the format NEON. You can read what can be configured here.
In the development mode, the container is automatically updated each time you change the code or configuration files. In production mode, it is generated only once and file changes are not checked to maximize performance.
Configuration files are loaded using addConfig()
:
$configurator->addConfig(__DIR__ . '/config/config.neon');
The method addConfig()
can be called multiple times to add multiple files.
$configurator->addConfig(__DIR__ . '/config/config.neon');
$configurator->addConfig(__DIR__ . '/config/config.local.neon');
if (PHP_SAPI === 'cli') {
$configurator->addConfig(__DIR__ . '/config/cli.php');
}
The name cli.php
is not a typo, the configuration can also be written in a PHP file, which returns it as
an array.
Alternatively, we can use the includes
section to
load more configuration files.
If items with the same keys appear within configuration files, they will be overwritten or merged in the case of
arrays. Later included file has a higher priority than the previous one. The file in which the includes
section is
listed has a higher priority than the files included in it.
Static Parameters
Parameters used in configuration files can be defined in the section parameters
and also passed (or overwritten) by the addParameters()
method. It is important that different parameter values cause
the generation of additional DI containers, i.e. additional classes.
$configurator->addParameters([
'projectId' => 23,
]);
In configuration files, we can write usual notation %projectId%
to access the parameter named
projectId
. By default, the Configurator populates the following parameters: appDir
, wwwDir
,
tempDir
, debugMode
and consoleMode
.
Dynamic Parameters
We can also add dynamic parameters to the container, their different values, unlike static parameters, will not cause the generation of new DI containers.
$configurator->addDynamicParameters([
'remoteIp' => $_SERVER['REMOTE_ADDR'],
]);
Environment variables could be easily made available using dynamic parameters. We can access them via
%env.variable%
in configuration files.
$configurator->addDynamicParameters([
'env' => $_ENV,
]);
Imported Services
We're going deeper now. Although the purpose of a DI container is to create objects, exceptionally there may be a need to
insert an existing object into the container. We do this by defining the service with the dynamic: true
attribute.
services:
myservice:
class: App\Model\MyCustomService
dynamic: true
Create a new instance and insert it in bootstrap:
$configurator->addServices([
'myservice' => new App\Model\MyCustomService('foobar'),
]);