Skip to main content

Dependency Injection Container

Both the plugin and theme ship a custom DI container. They are not identical: the plugin container is the fuller one (binding + reflection auto-wiring); the theme container is deliberately lighter. Both resolve services with resolve().

caution

The resolution method is resolve(), not make(). There is no make() method on either container.

Plugin Container

Class WPECommerce\Core\Container — located at wp-ecommerce-core/includes/Container.php. Supports bind(), singleton(), instance(), resolve() and reflection-based auto-wiring (build()).

Registering Services

// Singleton — same instance every time (concrete is a Closure)
$container->singleton(ProductService::class, function ($c) {
return new ProductService(
$c->resolve(ProductRepository::class)
);
});

// bind() — non-shared by default (new instance per resolve unless $shared = true)
$container->bind(Cart::class, function ($c) {
return new Cart($c->resolve(CartStorage::class));
});

// Register a pre-built object
$container->instance(SomeService::class, $alreadyBuilt);

Resolving Services

// Explicit resolution
$productService = $container->resolve(ProductService::class);

// Auto-wiring — the container reflects on the constructor and resolves
// type-hinted dependencies automatically
$service = $container->resolve(OrderService::class);
// If OrderService type-hints ProductService in its constructor,
// the container resolves it for you.

Accessing the Container

// From anywhere in the plugin
$container = \WPECommerce\Core\Bootstrap::instance()->container();
$service = $container->resolve(ProductService::class);
note

The Bootstrap accessor is Bootstrap::instance() (not getInstance()), and the container accessor is ->container() (not getContainer()).

Theme Container

Class Flavor_Container — located at flavor-starter/inc/core/Container.php. It is lighter than the plugin container: it supports singleton() (closure, cached), instance() (pre-built), resolve() and has(). It has no bind() factory and no reflection auto-wiring — construct dependencies explicitly inside the closure.

$container->singleton(MyThemeService::class, function ($c) {
return new MyThemeService(
$c->resolve(SomeDependency::class)
);
});

$service = $container->resolve(MyThemeService::class);

Accessing via Helpers

$bootstrap = flavor_app(); // → Flavor_Bootstrap::instance()
$container = flavor_container(); // → flavor_app()->container()
$service = flavor_resolve(MyThemeService::class); // shorthand resolve

Accessing the Module Manager

The Module Manager is its own singleton, not a method on the Bootstrap. Reach it directly:

$manager = \Flavor\Modules\ModuleManager::getInstance();
$labels = $manager->getModuleInstance('product-labels');
caution

flavor_app()->getModuleManager() does not exist. Use \Flavor\Modules\ModuleManager::getInstance() instead.

What registers in the container

Beyond the commerce core, the plugin container also holds ERP services (invoicing/myDATA, stock, purchasing, accounting, CRM, HR) and the AI / abilities foundation services — the same registration pattern applies to all of them.

Best Practices

  1. Register in Bootstrap — register services during the bootstrap phase.
  2. Prefer constructor injection — let the plugin container auto-wire dependencies (the theme container needs explicit construction inside the closure).
  3. Singletons for stateless services — use singleton().
  4. Factories for stateful objects — use the plugin's bind() (non-shared).
  5. Bind concretes today — the codebase resolves services by concrete class; there is no RepositoryInterface layer to bind against yet. If you introduce an interface, bind it with a closure that returns the concrete:
// Interface → concrete, via a closure (both containers accept closures)
$container->singleton(MyContract::class, function ($c) {
return $c->resolve(MyConcrete::class);
});