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().
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);
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');
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
- Register in Bootstrap — register services during the bootstrap phase.
- Prefer constructor injection — let the plugin container auto-wire dependencies (the theme container needs explicit construction inside the closure).
- Singletons for stateless services — use
singleton(). - Factories for stateful objects — use the plugin's
bind()(non-shared). - Bind concretes today — the codebase resolves services by concrete class;
there is no
RepositoryInterfacelayer 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);
});