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 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();
}