Skip to content

Dependency Injection #6550

@Cyperghost

Description

@Cyperghost

Dependency injection enables classes to obtain their required dependencies from outside, rather than creating or accessing them directly (e.g., via singletons or static calls).
Object creation and linking are managed centrally, while individual classes focus exclusively on their own tasks.

Goal

The goal of introducing dependency injection is to gradually reduce dependence on singletons and global/static states, provide a clear and extensible service architecture, and create a solid foundation for unit testing and long-term maintainability.

Advantages

  • First step toward proper unit testing
    By eliminating hard dependencies (e.g., singletons, static access), services can be easily simulated or replaced, enabling isolated unit testing.
  • Clear and explicit structure
    Dependencies become visible through constructors/members instead of remaining hidden behind static calls.
  • Loose coupling
    The core code depends on interfaces, not concrete implementations, which improves maintainability and safety during refactoring.
  • Better extensibility for plugins
    Plugins can provide their own service implementations without modifying the core code, as long as the extension points are clearly defined.

Service registration

New services (provided by both the core and plugins) should be registered during the bootstrap phase(Bootstrap Scripts).

Required changes

To introduce Dependency Injection the following steps are required:

  • Introduce a ServiceRegister API
    The ServiceRegister is responsible for:

    • Management of the services
      • Handling overwrites of an already registered service
      • Multiple registration of a service, e.g., different payment providers
    • Management of the containers
    • Creation of new instances of the service, if necessary
  • Extend the bootstrap script
    The bootstrap mechanism must be extended so that a central ServiceRegister (or equivalent) is passed to bootstrap scripts.
    Core and plugin bootstrap scripts must register their services explicitly via this registry.

  • Deprecate existing singletons
    All existing singleton-based services should be marked as deprecated and exposed as services via the DI container.
    The direct use of singletons outside of containers is therefore deprecated and must be revised.

  • Gradual removal of singletons
    After a transition period, deprecated singletons should be removed entirely and replaced by proper service injection.

Code example

Use php attributes to mark a service that can then be injected.

interface IFooService {
   public function fooBar(): int;
}

#[Service]
final class MyFooService implements IFooService
{
   public function fooBar(): int {
      // do something
   }
   // ...
}

Bootstrap-Script

return static function (ServiceRegister $register): void {
   $register->register(MyFooService::class, IFooService::class);
   // …
}

Usage

#[Container]
class MyContainer {
    #[Inject]
    private IFooService $service;

    public function doSomething(): void {
        $i = $service->fooBar();
        // …
    }
}

PHP attributes should only be used to mark that this is a service and, if necessary, to offer configuration options. Not to find them using reflection scanning.

Possible libraries

The following libraries could be used to implement this feature:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions