Why Dependency Injection?
Dependency injection provides several benefits over traditional singleton and global access patterns:- Testability: Easy to mock dependencies in unit tests
- Maintainability: Clear dependencies make code easier to understand
- Flexibility: Swap implementations without changing dependent code
- Single Responsibility: Classes focus on their core purpose, not dependency management
The Container
WooCommerce’s container is available globally via thewc_get_container() function.
Container Setup
The container is initialized in the main plugin file:plugins/woocommerce/woocommerce.php
Container Implementation
The container is a PSR-11 compliant implementation:src/Container.php
Getting Services
Retrieve services from the container using theget() method.
From Legacy Code
Inincludes/ directory, use wc_get_container():
includes/class-woocommerce.php
From Modern Code
Insrc/ directory, inject dependencies via the init() method:
src/Internal/MyClass.php
The container automatically resolves dependencies when you type-hint them in the
init() method.The init() Method Pattern
All classes in src/ that require initialization should implement an init() method.
Basic Pattern
With Dependencies
With Container Access
If you need the container itself:Container Patterns
Common patterns for using the container effectively.Automatic Resolution
The container uses reflection to automatically resolve dependencies:src/Internal/DependencyManagement/RuntimeContainer.php
Singleton Behavior
The container provides singleton behavior automatically:Allowed Classes
Only classes in theAutomattic\WooCommerce namespace can be resolved:
src/Internal/DependencyManagement/RuntimeContainer.php
Circular Dependency Detection
The container detects and prevents circular dependencies:Dependency Requirements
Follow these rules when declaring dependencies ininit() methods.
Valid Dependency Types
- Class Dependencies
- Invalid: Scalar Types
- Invalid: Arrays
- Invalid: References
Type-hint class dependencies:
Constructor Requirements
Classes resolved by the container must have either:- No constructor, or
- A public constructor with only optional parameters
Real-World Example
Here’s a complete example from WooCommerce core:src/Internal/AssignDefaultCategory.php
Common Patterns
Proxies for WordPress Functions
Use proxies to interact with WordPress and legacy code:Multiple Dependencies
Inject multiple services:Optional Container Access
Some classes may need the container for lazy loading:Testing with the Container
The container makes testing easier by allowing dependency mocking.Unit Test Example
tests/php/src/Internal/MyClassTest.php
Best Practices
1
Use init() Method
Always inject dependencies via the
init() method:2
Mark init() as Final
Prevent overriding the init method:
3
Type-Hint Dependencies
Always use type hints for clarity and automatic resolution:
4
Store Dependencies as Properties
Save injected dependencies for later use:
5
Use Proxies for WordPress
Don’t call WordPress functions directly; use proxies:
6
Register Hooks in init()
Add all hooks inside the init method:
Error Handling
The container throwsContainerException for various errors:
Class Not Found
Outside Namespace
Circular Dependency
Next Steps
Modern vs Legacy
Understand when to use dependency injection
Architecture
Learn about WooCommerce’s overall architecture