Skip to main content

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, the injectProperties() method automatically sets $dbConnection to an instance of DatabaseConnection.
  • 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.