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'),
]);