nette/bootstrap
Nette Bootstrap is the bootstrap layer for Nette Framework applications. It loads configuration, initializes the DI container and environment, and wires up the application so requests can be handled with minimal setup.
Treat nette/bootstrap as the equivalent of Laravel's bootstrap/app.php plus the framework's service-provider registration phase — compressed into one Configurator object that produces a compiled DI container. It is not a library you add to a Laravel app; it is the front door of a Nette application (or of any standalone PHP tool where you want real DI instead of new chains).
Minimal setup:
composer require nette/bootstrap tracy/tracy
<?php
// www/index.php
require __DIR__ . '/../vendor/autoload.php';
$configurator = new Nette\Bootstrap\Configurator;
$configurator->setDebugMode(getenv('APP_DEBUG') === '1');
$configurator->enableTracy(__DIR__ . '/../log');
$configurator->setTimeZone('Europe/Prague');
$configurator->setTempDirectory(__DIR__ . '/../temp'); // required for caching
$configurator->addConfig(__DIR__ . '/../config/common.neon');
$configurator->addConfig(__DIR__ . '/../config/local.neon'); // last wins
$container = $configurator->createContainer();
$container->getByType(App\Router::class)->run();
Where to look first, in this order:
config/ directory — common.neon is the base, local.neon is the per-machine override (git-ignored, the closest thing to .env).vendor/nette/bootstrap/src/Bootstrap/Configurator.php — it is ~300 lines and reading it once removes all the magic. Every method is a knob you can see.temp/ (e.g. temp/cache/Nette.Configurator/*Container.php). When autowiring fails, the generated PHP tells you exactly what the compiler decided. This is the single most valuable debugging surface and the thing Laravel devs never think to open.First real use case: replacing a pile of require + manual wiring in a CLI script or legacy endpoint with a shared bootstrap.php that returns the container. One file, one container, and every script gets the same services and config.
| Laravel concept | Nette equivalent |
|---|---|
bootstrap/app.php, Application |
Configurator |
config/*.php |
config/*.neon |
.env |
local.neon + dynamic parameters |
| Service providers | DI config in NEON, or a compiler extension |
App::make() / app() |
$container->getByType() |
Runtime bind() / singleton() |
Compile-time only — cannot change after createContainer() |
One bootstrap, many entry points. Put the shared setup in app/bootstrap.php returning the container, then have thin entry scripts for web, CLI, and cron. This is the pattern that pays off daily:
// app/bootstrap.php
return (function (): Nette\DI\Container {
$c = new Nette\Bootstrap\Configurator;
$c->setDebugMode(PHP_SAPI === 'cli' ? true : getenv('APP_DEBUG') === '1');
$c->setTempDirectory(__DIR__ . '/../temp');
$c->addConfig(__DIR__ . '/../config/common.neon');
if (is_file($local = __DIR__ . '/../config/local.neon')) {
$c->addConfig($local);
}
return $c->createContainer();
})();
Environment-specific config via includes and parameters, not via branching PHP. common.neon holds structure; local.neon holds values; a production.neon can be added on top in the deploy script. Parameters interpolate with %name%, service references use @name, and getenv() can be read at config time when you genuinely need container-level env vars:
parameters:
appUrl: https://example.test
debug: false
services:
- App\Http\Router(%appUrl%)
- App\Mailer
mailer.interface: App\Mailer # autowire interface -> implementation
- App\Listener\LogListener(tag: events)
For secrets, prefer $configurator->addDynamicParameters(['dbPassword' => getenv('DB_PASSWORD')]) over committing values. Dynamic parameters are resolved later and keep credentials out of the compiled config that ends up in temp/.
Service definitions over manual wiring. Every day-to-day addition is a NEON line, not a provider class:
services:
- App\Repository\UserRepository
- App\Service\ReportGenerator
factory: App\Service\ReportGenerator # rename a service
setup:
- setLogger(@logger)
Use decorator: for cross-cutting concerns instead of a Laravel-style middleware stack, and tags plus a compiler extension only when NEON stops being expressive enough. For a Laravel dev, the natural first compiler extension is anything you'd have written as a service provider with $this->app->tag(...) plus a resolver.
Constructor injection everywhere. There is no facade, no app() helper, and no runtime container access in idiomatic Nette code. Services take their dependencies as constructor arguments and the container injects them. When you are porting a Laravel habit, the translation is almost always "add a constructor parameter," not "call the container."
Testing. Build a container per test from the same NEON plus a test-specific override, then pull the subject out with getByType(). Because everything is compile-time, a test with a deliberately bad config fails at container creation — which is exactly the failure you want, and much earlier than a Laravel runtime binding error.
temp/cache/Nette.Configurator/. Symptom: "Service of type X not found" or a constructor receiving wrong arguments even though your config looks correct. Wire your dev script to clear it; do not rely on memory.setTempDirectory() and setDebugMode() must be called before createContainer(). Order matters and failures are silent-ish. Debug mode also changes the cache key, so debug-on and debug-off containers do not collide — but only if you set it consistently.bind(), no conditional registration, no deferred provider. If a service's dependencies depend on a request value, that value belongs in a method argument or a factory service, not in the container. Every Laravel dev hits this in week one.addConfig() calls overwrite earlier ones. This is the mechanism for environments, but it also means a stray local.neon can silently clobber production values. Log or assert which files were loaded in non-debug environments.% must be escaped in literal values, :: denotes class constants, and unquoted on/off/yes are booleans. Most "config not applied" reports are indentation or a missing - in a list.required at runtime produces confusing reflection errors, not a clean "class not found."- line under services: deletes an inherited definition; a named definition with the same name overrides it. Handy for swapping a mail transport in tests without touching common.neon.$container->getByType() deep inside business logic is a smell — restrict those calls to entry points and factories.NOASSERTION in the package metadata (Nette is historically BSD-3-Clause with a GPL dual option). Confirm the actual LICENSE file before shipping in a commercial product; do not rely on the repository badge.^3.4) instead of using *.Configurator::addConfig, addDynamicParameters, addServices, onCompile (last-chance container mutation, closest to a Laravel booted hook), and custom Nette\DI\CompilerExtension classes for anything repeated across three or more services.Overall: for a Laravel developer joining a Nette codebase, nette/bootstrap is a small, readable, fast-to-grok package whose entire learning curve is concentrated in one mental shift — configuration and wiring are compiled, not resolved at runtime. Read Configurator.php, read the generated container, and the rest follows.
How can I help you explore Laravel packages today?