Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Di Laravel Package

nette/di

Nette DI is a fast, flexible dependency injection container for PHP with smart autowiring and compile-time configuration. It helps structure applications cleanly, reduce boilerplate, and catch wiring errors early. Ideal for Nette Framework and standalone PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and bootstrap. nette/di is a standalone, compile-time DI container. It is not a Laravel replacement; it is best used as the wiring layer for a self-contained module, package, CLI tool, or long-running worker.

composer require nette/di nette/neon

A minimal bootstrap is three files: a NEON config, a cache directory, and a ContainerLoader call.

// bootstrap.php
require __DIR__ . '/vendor/autoload.php';

$loader = new Nette\DI\ContainerLoader(
    __DIR__ . '/temp',      // compiled container cache
    autoRebuild: true       // dev only; false in production
);

$container = $loader->load(function (Nette\DI\Compiler $compiler): void {
    $compiler->addConfig(__DIR__ . '/config/services.neon');
    $compiler->addConfig([
        'parameters' => [
            'appEnv' => getenv('APP_ENV') ?: 'prod',
        ],
    ]);
});
# config/services.neon
services:
    logger: App\Logger
    repository: App\UserRepository
    userService: App\UserService
$service = $container->getByType(App\UserService::class);
// or by name:
$service = $container->getService('userService');

Where to look first.

  1. Nette\DI\ContainerLoader — the entry point; controls compilation, cache, and rebuild behavior.
  2. Nette\DI\Compiler and Nette\DI\CompilerExtension — the extension system is the real API surface.
  3. Nette\DI\Definitions\ServiceDefinition, FactoryDefinition, AccessorDefinition, LocatorDefinition — how NEON entries become PHP.
  4. The generated temp/Container_*.php file — the fastest way to understand what your config actually produces.
  5. getServiceDescriptors() (3.2.7+) — inspect services without instantiating them.

First useful use case. Wire a small CLI application: one service per class, constructor autowiring, and a generated factory for short-lived objects. You get Laravel-style constructor injection without runtime reflection, and the whole graph is validated at compile time.

Implementation Patterns

Constructor autowiring. Give every type exactly one definition and Nette resolves constructor arguments by type.

final class UserService
{
    public function __construct(
        private UserRepository $repository,
        private Logger $logger,
    ) {}
}
services:
    - App\Logger
    - App\UserRepository
    - App\UserService

This is the day-to-day default. It is fast because the generated container passes objects directly; there is no runtime reflection.

Interfaces generate factories and accessors. This is nette/di's most distinctive feature. Define an interface with a typed return and register the interface as a service.

interface ArticleFactory
{
    public function create(string $title): Article;
}

interface ArticleAccessor
{
    public function get(): Article;
}
services:
    - App\ArticleFactory
    - App\ArticleAccessor
    - App\Article

Nette generates implementations. The factory creates a new Article and autowires its constructor; the accessor lazily returns the shared Article service. Use factories for per-request/per-job objects and accessors for lazy singletons. This replaces most manual new calls and most Laravel app()->make() usage.

Multiple services of the same type. Autowiring becomes ambiguous when two services share a type. Resolve it with explicit references or by disabling autowiring on all but one.

services:
    primaryLogger: App\Logger
    backupLogger:
        type: App\Logger
        autowired: false
    userService:
        type: App\UserService
        arguments:
            - @primaryLogger

Prefer named arguments where possible; positional arguments are brittle when constructors change.

Tags and collections. Use tags to collect related services, e.g. handlers, commands, or subscribers. Modern versions support list<Type> autowiring for collections, so a registry can receive all tagged services instead of using a locator.

services:
    - App\Handler\FooHandler
        tags: [app.handler]
    - App\Handler\BarHandler
        tags: [app.handler]

Then inject the collection into a registry or iterate tagged services in an extension. Tags are also the clean way to build a command list for a CLI kernel.

Compiler extensions are your service providers. Anything reusable — package integration, convention-based registration, configuration schema — belongs in an extension rather than a bootstrap closure.

final class ApiExtension extends Nette\DI\CompilerExtension
{
    public function loadConfiguration(): void
    {
        $builder = $this->getContainerBuilder();

        $builder->addDefinition($this->prefix('client'))
            ->setFactory(ApiClient::class, [
                'baseUrl' => $this->config->baseUrl,
            ]);

        $builder->addDefinition($this->prefix('cache'))
            ->setFactory(ApiCache::class);
    }
}
extensions:
    api: App\DI\ApiExtension(%api.baseUrl%)

Extensions run at compile time. Use loadConfiguration() to add definitions, beforeCompile() to validate or adjust, and afterCompile() sparingly. This is where you put logic that in Laravel would live in register() and boot().

Decorators for cross-cutting behavior. The built-in decorator extension lets you apply setup calls to every service of a given type. Use it for logging, metrics, or wrapping.

decorator:
    App\Mailer:
        setup:
            - setLogger(@logger)

Keep decorators narrow; broad type-based decorators can unexpectedly touch generated factories and test doubles.

Testing pattern. Build a fresh container per test from a minimal NEON file, or compile directly with Compiler.

$compiler = new Nette\DI\Compiler;
$compiler->addConfig(__DIR__ . '/fixtures/test.neon');
$container = $compiler->compile();

$service = $container->getByType(App\UserService::class);

This is more predictable than reusing an application container. For unit tests, instantiate classes directly and reserve the container for integration tests.

Laravel integration. Do not make Laravel and nette/di compete for ownership of the same graph. The clean pattern is a Laravel service provider that boots a nette/di container for one isolated module and exposes its entry points.

final class ModuleServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(ModuleContainer::class, function () {
            $loader = new Nette\DI\ContainerLoader(
                storage_path('framework/nette-di'),
                autoRebuild: $this->app->environment('local')
            );

            return new ModuleContainer($loader->load(function ($compiler) {
                $compiler->addConfig(base_path('modules/billing/config.neon'));
            }));
        });
    }
}

Bridge the result back to Laravel deliberately: expose a small number of facade services, not the entire nette/di container. If a third-party library wants PSR-11, wrap it with Nette\Bridges\DIPsr\PsrContainer.

Introspection and tooling. Use getServiceDescriptors() for admin panels, debug commands, and documentation generation.

foreach ($container->getServiceDescriptors() as $descriptor) {
    // name, type, autowiring, tags, aliases, optional instance
}

This avoids the old Closure::bind() hacks and reflection over createService* methods.

Gotchas and Tips

Compiled means compile-time. Parameters, service definitions, and most validation happen when the container is built. You cannot add a service or change a constructor argument at runtime. If you need runtime configuration, pass values as constructor arguments via parameters or create a runtime factory service.

Long-running processes were historically dangerous. Before 3.2.7, a queue worker or dev server could keep serving a stale container forever because PHP cannot redeclare an already-loaded class. Containers now use fresh class names and content-based rebuild detection. Still: keep autoRebuild: true in development only. In production, compile once during deploy and never rebuild.

Cache directories must be unique and writable. Two applications sharing one temp directory can collide. Use a per-application, per-environment path, especially in tests, CI, and parallel workers.

Autowiring ambiguity is a compile error, not a runtime guess. If two services share a type, explicitly configure arguments or set autowired: false. Do not rely on registration order.

@self and factories. In a factory's own setup section, @self and self-typed arguments now work as expected. This matters when a produced object needs to register itself with a registry or needs a back-reference.

Factory return types are mandatory. Since 3.2, generated accessors and factories must declare return types. An untyped factory method will fail or behave unexpectedly. This is a common migration issue.

Watch NEON escaping. @ starts a service reference. Write @@ for a literal @. %foo% expands parameters; deprecations around %parameters% and legacy class/dynamic options mean you should prefer type, explicit arguments, and setup.

Optional parameters are only autowired when they have defaults. Named arguments are supported, but combining named and positional arguments in the same call is an error. Keep constructor signatures stable and prefer named arguments in configuration.

getByType() is for a single service; use getByName() or tags for collections. getByType() throws when the type is ambiguous. Modern versions support list<Type> autowiring, but tagged collections remain the most explicit approach.

addService() and removeService() now treat factories as first-class. A name occupied by a factory cannot be reused by addService(). removeService() removes factories too, so dynamically reconfiguring a module is safer than it used to be.

Decorator ordering matters. The compiler runs search extensions before decorators, so decorators see resolved definitions. If a decorator silently does nothing, check that the target type was already defined and autoloadable. Since 3.0.7 the decorator extension throws when the class does not exist.

Prefer constructor injection over property injection. The inject extension warns about non-public, static, and readonly properties. Constructor injection is clearer, works with readonly classes, and keeps the compile-time graph honest.

Debug by reading the generated container. When behavior is surprising, open the newest Container_*.php under the cache directory. Search for createService methods to see exactly which arguments and setup calls were generated. Then use getServiceDescriptors() to confirm tags, aliases, and autowiring flags.

Use Tracy in development. The DI panel shows service descriptors, creation order, and generated factories. It is the fastest way to spot a mis-tagged service or an unexpectedly eager dependency.

Static analysis pays off. Recent releases ship much better PHPDoc generics. Use PHPStan with the Nette ruleset if available; the container's generated types are precise, but only if your service annotations and return types are precise too.

Performance is no longer a reason to avoid large graphs. Service registration is now constant-time for name clashes, and compiled containers avoid runtime reflection. Thousands of services are fine; the real cost is rebuild time during development, not request time.

Escape hatches are intentional but should stay rare. A locator or the PSR-11 bridge is appropriate at an application boundary, but injecting Container into domain code turns nette/di into a service locator and loses the compile-time guarantees that make it valuable.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console