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 behaves differently depending on whether it is running on a development or production server:

🛠️ Development Mode
Displays the Tracy debug bar with useful information (e.g., SQL queries, execution time, memory usage).
Shows a detailed error page with function call traces and variable contents when an error occurs.
Automatically refreshes the cache when Latte templates, configuration files, etc., are modified.
🚀 Production Mode
Does not display any debugging information; all errors are logged.
Shows an ErrorPresenter or a generic “Server Error” page when an error occurs.
Cache is never automatically refreshed!
Optimized for speed and security.

The mode is determined automatically, so in most cases, there’s no need to configure or switch it manually:

  • Development mode: Active on localhost (IP address 127.0.0.1 or ::1) unless a proxy is in use (i.e., based on its HTTP headers).
  • Production mode: Active everywhere else.

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 of Bootstrap.php file
  • %wwwDir% is the absolute path to the directory containing the index.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();
}