Skip to main content

Container Introduction

DomainFlow provides a lightweight but feature-rich dependency injection container. This container can be the core of how services and dependencies are managed throughout your application.

Below is a detailed guide to help you understand and use it effectively.


Purpose and Features

  • PSR-11 Compliant: You can use standard methods get() and has() to interact with container entries.
  • ArrayAccess Compatibility: Interact with the container via ['key'] syntax if preferred.
  • Automatic Binding and Resolution: Use bind(), singleton(), instance(), or alias() to register dependencies, and make() or get() to resolve them.
  • Reflection-Based Autowiring: The container inspects constructors and named class types. Union types resolve only when exactly one candidate is resolvable or a priority is configured. An intersection requires a concrete class whose instance satisfies every interface; an interface-only intersection cannot be constructed automatically.
  • Property Injection: The container's #[Inject] attribute applies to properties only. The property must have one non-built-in named type and must not be readonly; union and intersection property types are rejected.
  • Circular Dependency Detection: A dependency cycle is detected during resolution and raises a ContainerException naming the full chain, instead of recursing forever.
  • Contextual Binding: Override which concrete class is injected into specific classes (or contexts).
  • Tagging: Group services under tags for bulk retrieval or iteration.
  • Scopes: Create sub-containers (scopes) that fall back to a parent container but maintain their own local overrides, with resetScope() and disposeScope() to manage their lifecycle.
  • Resolution Definition Cache: Optionally persist declarative class bindings and aliases via ContainerCacheInterface so a fresh container in a new process can restore its configuration before resolving anything.
  • Resolve/Before Hooks: Run custom logic before and after every resolution via addBeforeResolve() and addAfterResolve().

Requirements

  • PHP 8.4 or 8.5

Installation

If you're using the container in a standalone project, you can install it via Composer:

composer require domainflow/container

Basic Usage

When you start working with the container, the typical steps are:

  1. Instantiate the Container.
  2. Bind your classes or closures to the container.
  3. Resolve them as needed via make() or get().

The following sections demonstrate a few real-world usage patterns.

Example: container_basic_binding_a.php

<?php declare(strict_types=1);

use DomainFlow\Container;

// 1. Instantiate the container
$container = new Container();

// 2. Bind using a closure
$container->bind('logger', function () {
// Suppose Logger is a custom class
return new Logger('/var/log/app.log');
});

// 3. Resolve the logger via `make` or `get`
$logger = $container->make('logger');
$logger->info("Hello from the container!");

Note: You need to create a class named Logger for this example to work or use an existing class from your project.


Summary

The Container is a foundational building block, letting you register and retrieve services in a straightforward manner. It supports a range of patterns — from the simplest closure factory to advanced reflection-based autowiring, contextual overrides, tagging, and scoping.

As you continue through the documentation, you’ll learn how to leverage these features to cleanly manage dependencies in your application code.