Dependency Injection Container (PSR-11)

Info: PSR-11 Compliance: The container follows Psr\Container\ContainerInterface standard for maximum interoperability.

What is a Container?

The dependency injection container is the heart of Larafony. It manages class dependencies and performs automatic dependency injection through constructor parameters. The container can:

Basic Usage

Accessing the Container

The container is available throughout your application. You can access it in several ways:

use Larafony\Framework\Web\Application;
use Psr\Container\ContainerInterface;

// Method 1: Through Application instance
$app = Application::instance();
$container = $app; // Application implements ContainerInterface

// Method 2: Dependency injection (preferred)
class UserController
{
public function __construct(
private readonly ContainerInterface $container
) {}
}

Retrieving Services

The get() method retrieves services from the container. If the service doesn't exist, the container will attempt to autowire it:

use App\Services\EmailService;

// Get a service - will autowire if not registered
$emailService = $container->get(EmailService::class);

// Get with dot notation
$dbHost = $container->get('database.host');

Checking Service Existence

if ($container->has(EmailService::class)) {
$service = $container->get(EmailService::class);
}

Binding Values

Simple Value Bindings

Use bind() to store scalar values (strings, numbers, booleans, null):

// Bind configuration values
$container->bind('base_path', '/var/www/app');
$container->bind('debug', true);
$container->bind('timeout', 30);

// Retrieve bindings
$basePath = $container->getBinding('base_path'); // '/var/www/app'
$debug = $container->getBinding('debug'); // true

Warning: Note: bind() only accepts scalar values. For objects or arrays, use set().

Registering Services

Using set()

The set() method registers services, instances, or any values:

use App\Services\EmailService;
use Larafony\Framework\Config\Contracts\ConfigContract;

// Set an instance
$config = new ConfigBase($container);
$container->set(ConfigContract::class, $config);

// Set a class name (will be autowired when retrieved)
$container->set('email', EmailService::class);

// Set with dot notation for nested values
$container->set('database.host', 'localhost');
$container->set('database.port', 3306);

Automatic Autowiring

How Autowiring Works

The container automatically resolves class dependencies by analyzing constructor parameters:

namespace App\Services;

use App\Repositories\UserRepository;
use Psr\Log\LoggerInterface;

class UserService
{
public function __construct(
private readonly UserRepository $repository,
private readonly LoggerInterface $logger
) {}
}

// Container automatically resolves dependencies
$userService = $container->get(UserService::class);
// Container will:
// 1. Resolve UserRepository (and its dependencies)
// 2. Resolve LoggerInterface (must be registered)
// 3. Instantiate UserService with both dependencies

Requirements for Autowiring

Service Providers

Registering Services in Providers

Service providers are the recommended way to register services. They keep your bootstrap code organized:

namespace App\Providers;

use Larafony\Framework\Container\ServiceProvider;
use Psr\Log\LoggerInterface;
use App\Services\FileLogger;

class LoggerServiceProvider extends ServiceProvider
{
public function register(): void
{
// Bind interface to implementation
$this->container->set(
LoggerInterface::class,
new FileLogger('/var/log/app.log')
);
}
}

Register the provider in your bootstrap/app.php:

$app->registerProviders([
App\Providers\LoggerServiceProvider::class,
// ... other providers
]);

Practical Examples

Example: Repository Pattern

// Interface
interface UserRepositoryInterface
{
public function find(int $id): ?User;
}

// Implementation
class DatabaseUserRepository implements UserRepositoryInterface
{
public function __construct(
private readonly ConnectionContract $db
) {}

public function find(int $id): ?User
{
// Database query logic
}
}

// Register in provider
class RepositoryServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->container->set(
UserRepositoryInterface::class,
DatabaseUserRepository::class // Will be autowired
);
}
}

// Use in service
class UserService
{
public function __construct(
private readonly UserRepositoryInterface $repository
) {}
}

Best Practices

Do

Don't

API Reference

Method Description Returns
`get(string $id)` Retrieve a service or autowire it `mixed`
`has(string $id)` Check if service is registered `bool`
`set(string $key, mixed $value)` Register a service or value `self`
`bind(string $key, scalar $value)` Bind a scalar value `void`
`getBinding(string $key)` Retrieve a bound scalar value `scalar`

Next Steps

Configuration

Learn how to manage configuration files and environment variables.

Read Guide

Database

Explore the Query Builder and Schema Builder for database operations.

Read Guide