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

Vanio Di Extra Bundle Laravel Package

vanio/vanio-di-extra-bundle

Symfony bundle that extends the framework's dependency injection container with extra configuration options and conveniences, helping PHP developers define, wire, and organize services with less boilerplate in their applications.

View on GitHub
Deep Wiki
Context7

Getting Started

This bundle is a dependency-injection add-on for Symfony: expect it to reduce boilerplate around service discovery, interface-based tagging, and compiler passes rather than to replace Symfony's DI container. Treat it as sugar on top of services.yaml, not as a framework of its own.

Minimal setup

composer require vanio/di-extra-bundle

If there is no Flex recipe (likely, given the package's size), register it manually:

// config/bundles.php
return [
    // ...
    Vanio\DIExtraBundle\VanioDIExtraBundle::class => ['all' => true],
];

Then clear the cache and confirm the extension loads:

bin/console cache:clear
bin/console debug:container --parameters | grep -i vanio
bin/console debug:config vanio_di_extra   # if the bundle exposes a config tree

Where to look first

  1. src/DependencyInjection/Configuration.php and .../VanioDIExtraExtension.php — the actual config keys and defaults. Do not trust README examples over this file.
  2. src/DependencyInjection/Compiler/ — this is where the value usually is. Read every pass and note whether it runs beforeOptimization or beforeRemoval.
  3. src/Resources/config/ — the service definitions the bundle registers for itself; these tell you what tags and aliases are expected.
  4. composer.json — check the Symfony version constraint before adopting. A 0-star, 0-dependent package is unlikely to track Symfony 7 deprecations.

First use case

The realistic first win is eliminating hand-written service definitions for an application namespace while auto-tagging services by implemented interface:

# config/services.yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true

    App\:
        resource: '../src/'
        exclude: '../src/{Entity,Kernel.php}'

If the bundle provides namespace-based registration or interface-to-tag mapping, wire one namespace and one marker interface first, run debug:container --tag=<your_tag>, and only then expand. Start with a single bounded context (e.g. App\Payment\) rather than App\ so a misconfiguration does not silently register entities or DTOs as services.

Implementation Patterns

Convention-based registration

The dominant pattern is: define the namespace once, let the bundle (or _defaults) handle the rest, and rely on constructor autowiring. Keep exclusions explicit and narrow.

# config/services.yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false

    App\Domain\:
        resource: '../src/Domain/'
        exclude: '../src/Domain/{Entity,ValueObject}/'

Prefer this over per-class attributes when you have many small services; prefer attributes when you need per-service control (lazy, decorates, tags) on a minority of classes.

Interface-driven tagging

Marker interfaces plus a single tag mapping scale much better than tagging each class:

interface PaymentMethodInterface
{
    public function supports(Order $order): bool;
}

Configure the mapping once (key names depend on the bundle's Configuration.php — verify before committing):

vanio_di_extra:
    tags:
        App\Payment\PaymentMethodInterface: app.payment_method

Consume it with a tagged iterator/locator rather than injecting the container:

final class PaymentMethodRegistry
{
    public function __construct(
        #[TaggedIterator('app.payment_method')]
        private readonly iterable $methods,
    ) {}
}

For lookups by key, use a tagged locator and index services with #[AsTaggedItem(index: 'stripe')] or an explicit priority/index in the tag attributes.

Compiler passes as the extension point

Anything the bundle does not cover, do in your own pass. This is also how you customize what the bundle's passes produce:

final class HandlerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container): void
    {
        foreach ($container->findTaggedServiceIds('app.handler') as $id => $tags) {
            $definition = $container->getDefinition($id);
            $definition->setArgument('$bus', new Reference('app.command_bus'));
            $definition->addTag('monolog.logger', ['channel' => 'handler']);
        }
    }
}

Register it in your kernel's build():

protected function build(ContainerBuilder $container): void
{
    $container->addCompilerPass(new HandlerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 10);
}

If the bundle exposes a base pass class or a pass-priority convention, use it so your pass runs in a predictable order relative to theirs.

Integration with autoconfigure

Use _instanceof for global behavior and let the bundle handle discovery:

services:
    _instanceof:
        App\Shared\Domain\EventHandlerInterface:
            tags: ['app.event_handler']

Watch for double-tagging: if both _instanceof and the bundle's interface mapping add the same tag, you can end up with duplicated entries in !tagged_iterator or unexpected priorities. Pick one mechanism per interface.

Testing

Use the test container rather than the real one:

final class PaymentMethodRegistryTest extends KernelTestCase
{
    public function testMethodsAreDiscovered(): void
    {
        self::bootKernel();
        $registry = self::getContainer()->get(PaymentMethodRegistry::class);
        self::assertNotEmpty(iterator_to_array($registry->all()));
    }
}

Because the bundle's value is container shape, a cheap smoke test that boots the kernel and asserts a few tagged collections are non-empty catches most misconfigurations.

Gotchas and Tips

  • Adoption risk is the real assessment. Zero stars and zero dependents means no ecosystem pressure to keep it compatible. Pin the exact version in composer.json, and be prepared to vendor/fork it. MIT license makes that easy; read the passes before you rely on them.
  • Compiler pass ordering bites. Passes that add References after RemoveUnusedDefinitionsPass may be ignored or leave services private. Use TYPE_BEFORE_OPTIMIZATION for wiring and TYPE_BEFORE_REMOVAL only when you intentionally react to the final graph. When debugging, re-run with bin/console debug:container --tag=<tag> and check the compiled container in var/cache/dev/App_KernelDevDebugContainer.php.
  • Duplicate tags from _instanceof + bundle mapping. This is the most common silent failure: the service is discovered twice so it appears twice in a tagged iterator. Audit with debug:container --tag.
  • public: false and test access. In tests, use self::getContainer() (test service container), not $kernel->getContainer()->get() for private services. If you must fetch directly, alias it in config/services_test.yaml.
  • Resource globs. resource: '../src/' will pick up entities, migrations, and DTOs unless excluded. Always exclude Entity, Migrations, Kernel.php, and data-transfer/value objects. A mis-registered entity surfaces later as odd Doctrine errors, not as a DI error.
  • Annotation vs attribute support. Older bundles still use doctrine/annotations, which is abandoned and removed in newer Doctrine versions. Check whether the bundle uses ReflectionAttribute (PHP 8) or the annotation reader; if the former, annotations in docblocks will be silently ignored.
  • Config key names are not the README. Confirm the root alias (likely vanio_di_extra) and every key in Configuration.php. Symfony silently ignores unknown keys only if the extension is not strict; if it is strict you get a clear error, if not you get silent no-ops.
  • Cache invalidation. Compiler passes and config changes require bin/console cache:clear in dev and a container rebuild in prod. debug:container reads the compiled container, so a stale cache will show stale tags and send you down the wrong path.
  • Debugging workflow. debug:autowiring <type> for missing aliases, debug:container --show-private --tag=<tag> for discovery, lint:container for broken references, and debug:config <alias> for config shape. Set container.dumper.inline_factories: false locally if you need to read the dumped container.
  • Prefer native Symfony where equivalent. #[AutoconfigureTag], _instanceof, !tagged_iterator, #[AsTaggedItem], and #[AutowireLocator] cover most of what DI extra bundles historically provided. Use the bundle only for the specific convenience it adds; otherwise you take on maintenance risk for behavior already in core.
  • Extension points. If you need to change discovery behavior, prefer subclassing the bundle's compiler pass or registering your own pass at a higher priority over editing vendor code — the latter breaks on the next composer update and this package will not give you a deprecation window.
  • Version compatibility check before adoption. Run composer why-not symfony/framework-bundle 7.* against the installed package to see whether it constrains you. A DI bundle that pins Symfony 5/6 will block an upgrade of the whole app.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package