Property Injection
Overview
Property injection enables the automatic injection of dependencies directly into class properties. By marking a property with the #[Inject]
attribute, the container identifies it as requiring dependency injection during the autowiring process. This reduces boilerplate code and simplifies dependency initialization, particularly when constructor injection is not feasible.
Key Concepts
Attribute-Based Injection:
Properties marked with the PHP attribute #[Inject]
are automatically flagged for dependency injection.
Automatic Resolution:
During object construction, the container scans for properties with the Inject
attribute, uses reflection to resolve the type-hinted dependency, and assigns the resolved instance to the property.
Reflection Integration:
The container’s autowiring mechanism leverages PHP’s Reflection API (using methods like injectProperties()
) to perform the injection, including adjusting accessibility for non-public properties.
Implementation Details
The Injection Process
During the build process, the container calls the injectProperties()
method to inspect each property of the constructed object. For every property marked with the Inject
attribute:
- The method checks the property’s type.
- If the type is a non-builtin class, it resolves the dependency using the container’s
make()
method. - The property’s value is then set to the resolved instance, with reflection used to adjust the property’s accessibility if necessary.
Example Usage
Below is an example demonstrating how property injection works in practice:
use DomainFlow\Container;
use DomainFlow\Container\Attribute\Inject;
class DatabaseConnection {
public function connect() {
echo "Database connected.";
}
}
class UserRepository {
#[Inject] // This attribute marks the property for automatic dependency injection.
private DatabaseConnection $dbConnection;
public function fetchAll() {
// Use the injected dependency.
$this->dbConnection->connect();
echo "Fetching all users...";
}
}
// Create the container and bind the dependency.
$container = new Container();
$container->bind(DatabaseConnection::class, DatabaseConnection::class);
// Resolve the UserRepository.
// During instantiation, the container automatically injects an instance of DatabaseConnection.
$userRepo = $container->get(UserRepository::class);
$userRepo->fetchAll();
In this example:
- The
UserRepository
class has a private property$dbConnection
marked with the#[Inject]
attribute. - When
UserRepository
is resolved by the container, theinjectProperties()
method automatically sets$dbConnection
to an instance ofDatabaseConnection
. - The
fetchAll()
method then uses this injected dependency without any manual wiring.
Benefits
-
Reduced Boilerplate:
Eliminates the need to manually pass dependencies via the constructor or setters. -
Cleaner Code:
Keeps class constructors uncluttered, particularly for classes with many dependencies. -
Flexibility:
Enables injection even in scenarios where constructor injection is impractical, such as in legacy code or with immutable objects.
Summary
Property injection, powered by the #[Inject]
attribute and supported by the container’s reflection-based autowiring, offers an elegant solution for dependency management. It streamlines the injection process by automatically resolving and assigning dependencies to marked properties, reducing manual configuration and simplifying code maintenance.