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 Bootstrap
class, which as part of the application can be found in the
app/Bootstrap.php
. It might look for example like this:
use Nette\Bootstrap\Configurator;
class Bootstrap
{
private Configurator $configurator;
private string $rootDir;
public function __construct()
{
$this->rootDir = dirname(__DIR__);
// The configurator is responsible for setting up the application environment and services.
$this->configurator = new Configurator;
// Set the directory for temporary files generated by Nette (e.g. compiled 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 is smart, and the development mode turns on automatically,
// or you can enable for a specific IP address it by uncommenting the following line:
// $this->configurator->setDebugMode('secret@23.75.345.200');
// Enables Tracy: the ultimate "swiss army knife" debugging tool.
$this->configurator->enableTracy($this->rootDir . '/log');
// RobotLoader: autoloads all classes in the given directory
$this->configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
}
private function setupContainer(): void
{
// Load configuration files
$this->configurator->addConfig($this->rootDir . '/config/common.neon');
}
}
index.php
The initial file for web applications is index.php
, located in the public directory www/
. It uses the
Bootstrap
class to initialize the environment and create a DI container. Then, it obtains the
Application
service from the container, which launches the web application:
$bootstrap = new App\Bootstrap;
// Initialize the environment + create a DI container
$container = $bootstrap->bootWebApplication();
// DI container creates a Nette\Application\Application object
$application = $container->getByType(Nette\Application\Application::class);
// Start the Nette application and handle the incoming request
$application->run();
As you can see, the Nette\Bootstrap\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()
:
$this->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.
$this->configurator->setDebugMode('secret1234@23.75.345.200');
We can also turn off developer mode completely, even for localhost:
$this->configurator->setDebugMode(false);
Note that the value true
turns on developer mode by hard, which should never happen on a production server.
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:
$this->configurator->enableTracy($this->rootDir . '/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:
$this->configurator->setTempDirectory($this->rootDir . '/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:
$this->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.
$this->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 NEON format. 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()
:
$this->configurator->addConfig($this->rootDir . '/config/common.neon');
The method addConfig()
can be called multiple times to add multiple files.
$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');
}
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 addStaticParameters()
method (it has alias addParameters()
). It is
important that different parameter values cause the generation of additional DI containers, i.e. additional classes.
$this->configurator->addStaticParameters([
'projectId' => 23,
]);
In configuration files, we can write usual notation %projectId%
to access the parameter named
projectId
.
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.
$this->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.
$this->configurator->addDynamicParameters([
'env' => getenv(),
]);
Default Parameters
You can use the following static parameters in the configuration files:
%appDir%
is the absolute path to the directory ofBootstrap.php
file%wwwDir%
is the absolute path to the directory containing theindex.php
entry file%tempDir%
is the absolute path to the directory for temporary files%vendorDir%
is the absolute path to the directory where Composer installs libraries%rootDir%
is the absolute path to the root directory of the project%debugMode%
indicates whether the application is in debug mode%consoleMode%
indicates whether the request came through the command line
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 imported: true
attribute.
services:
myservice:
type: App\Model\MyCustomService
imported: true
Create a new instance and insert it in bootstrap:
$this->configurator->addServices([
'myservice' => new App\Model\MyCustomService('foobar'),
]);
Different Environments
Don't hesitate to customize the Bootstrap
class according to your needs. You can add parameters to the
bootWebApplication()
method to differentiate between web projects. Alternatively, you can add other methods, such as
bootTestEnvironment()
to initialize the environment for unit tests, bootConsoleApplication()
for scripts
called from the command line, and so on.
public function bootTestEnvironment(): Nette\DI\Container
{
Tester\Environment::setup(); // Nette Tester initialization
$this->setupContainer();
return $this->configurator->createContainer();
}
public function bootConsoleApplication(): Nette\DI\Container
{
$this->configurator->setDebugMode(false);
$this->initializeEnvironment();
$this->setupContainer();
return $this->configurator->createContainer();
}