Dependency Injection
Before continuing, review the PSR-11 Container documentation and the byjg/config Dependency Injection documentation.
Overview
Dependency Injection (DI) decouples your code from specific implementations, making it easier to swap dependencies based on environment or requirements.
Example: Environment-Specific Cache
You might want caching enabled in production but disabled in development for easier debugging.
Development - config/dev/01-infrastructure.php:
<?php
use ByJG\Cache\Psr16\BaseCacheEngine;
use ByJG\Cache\Psr16\NoCacheEngine;
use ByJG\Config\DependencyInjection as DI;
return [
BaseCacheEngine::class => DI::bind(NoCacheEngine::class)
->toSingleton(),
];
Production - config/prod/01-infrastructure.php:
<?php
use ByJG\Cache\Psr16\BaseCacheEngine;
use ByJG\Cache\Psr16\FileSystemCacheEngine;
use ByJG\Config\DependencyInjection as DI;
return [
BaseCacheEngine::class => DI::bind(FileSystemCacheEngine::class)
->toSingleton(),
];
Usage in Code:
<?php
use ByJG\Config\Config;
use ByJG\Cache\Psr16\BaseCacheEngine;
// Get the cache instance (implementation depends on APP_ENV)
$cache = Config::get(BaseCacheEngine::class);
// Use it the same way regardless of environment
$cache->set('key', 'value', 3600);
$value = $cache->get('key');
The application automatically returns the correct implementation based on the APP_ENV environment variable.
Common DI Patterns
Constructor Injection
DummyService::class => DI::bind(DummyService::class)
->withInjectedConstructor()
->toSingleton(),
The container automatically injects dependencies defined in the constructor based on their type hints.
Constructor with Parameters
JwtWrapper::class => DI::bind(JwtWrapper::class)
->withConstructorArgs([
Param::get('API_SERVER'),
Param::get(JwtKeyInterface::class)
])
->toSingleton(),
Mix environment parameters and other dependencies.
Factory Method
DbDriverInterface::class => DI::bind(Factory::class)
->withFactoryMethod("getDbRelationalInstance", [
Param::get('DBDRIVER_CONNECTION')
])
->toSingleton(),
Use a factory method instead of a constructor.
Singleton vs Transient
// Singleton - Same instance every time
MyService::class => DI::bind(MyService::class)->toSingleton(),
// Transient - New instance every time
MyService::class => DI::bind(MyService::class),
Configuration Organization
Dependencies are organized by layer in numbered files:
01-infrastructure.php- Database, Cache, Logging02-security.php- JWT, Authentication, User Management03-api.php- OpenAPI, Routes, Middleware04-repositories.php- Data access layer05-services.php- Business logic layer06-external.php- Email, SMS, external APIs
This organization makes it easy to find and modify related configurations.