Configuration and Dependency Injection
Configuration and dependency injection in one PSR-11 container, with environments that inherit from each other and autowiring only where you choose it.
Why
Every application has a dev, a test, a staging and a production configuration that are mostly the same. Copying the files means a change has to be made in four places, and the container that builds your services usually decides for you how much is wired by magic.
Config builds the environments as a hierarchy:
$dev = new Environment('dev');
$test = new Environment('test', [$dev]); // dev, plus what test changes
$base = new Environment('base', [$dev], abstract: true); // shared, never loaded directly
$prod = new Environment('prod', [$base], final: true); // nothing may inherit from prod
- Write a value once. An environment inherits everything from its parents and only
states what is different.
abstractandfinalkeep the hierarchy honest. - Explicit bindings, autowiring when you want it.
DI::bind()declares a service and its decisions (implementation, singleton, constructor arguments); anAutowire::rule()resolves a whole namespace by type hints. You choose per namespace, trading convenience for a plain lookup.
Features
- PSR-11 Compatible: Implements PSR-11 Container Interface for standardized dependency injection
- Auto-Initialization: Automatically loads configuration from a bootstrap file with zero setup
- Static Facade: Clean, Laravel-style static API for accessing configuration anywhere
- Environment-Based Configuration: Easily switch between development, production, and custom environments
- Multiple Configuration Formats: Support for both PHP arrays and .env files
- Dependency Injection: Simple API for defining and resolving dependencies
- Type Conversion: Built-in parsers for converting configuration values to specific types
- Caching Support: Optional caching of configuration values for improved performance
- Environment Inheritance: Environments can inherit from one another to reduce configuration duplication
- Abstract & Final Environments: Design flexible environment hierarchies with constraints
- Performance Optimizations: Configure caching modes for different use cases
Quick Start with Auto-Initialization
The fastest way to get started is using the auto-initialization feature:
<?php
// 1. Create config/ConfigBootstrap.php
use ByJG\Config\ConfigInitializeInterface;
use ByJG\Config\Definition;
use ByJG\Config\Environment;
return new class implements ConfigInitializeInterface {
public function loadDefinition(?string $env = null): Definition {
$dev = new Environment('dev');
$prod = new Environment('prod', [$dev]);
return (new Definition())
->addEnvironment($dev)
->addEnvironment($prod);
}
};
<?php
// 2. Create your config file (config/config-dev.php)
return [
'database' => [
'host' => 'localhost',
'port' => 3306
],
'app_name' => 'My Application',
'debug' => true
];
<?php
// 3. Use Config anywhere in your app - no initialization needed!
use ByJG\Config\Config;
$appName = Config::get('app_name'); // "My Application"
$dbHost = Config::get('database')['host']; // "localhost"
Traditional Example
You can also use the traditional approach without auto-initialization:
<?php
// 1. Create a config file (config/config-dev.php)
return [
'database' => [
'host' => 'localhost',
'port' => 3306
],
'app_name' => 'My Application',
'debug' => true
];
// 2. Set up and load the configuration
use ByJG\Config\Definition;
use ByJG\Config\Environment;
// Define the environments
$dev = new Environment('dev');
$prod = new Environment('prod', [$dev]); // prod inherits from dev
// Create and build the definition
$definition = (new Definition())
->addEnvironment($dev)
->addEnvironment($prod);
// Build with the current environment (using APP_ENV environment variable)
$container = $definition->build();
// 3. Use the configuration values
$appName = $container->get('app_name'); // "My Application"
$dbHost = $container->get('database')['host']; // "localhost"
Dependency Injection Example
<?php
// 1. Define your classes
namespace MyApp;
class DatabaseService
{
private $host;
private $port;
public function __construct(array $database)
{
$this->host = $database['host'];
$this->port = $database['port'];
}
}
class UserRepository
{
private $db;
public function __construct(DatabaseService $db)
{
$this->db = $db;
}
}
// 2. Register them in your config file
use ByJG\Config\DependencyInjection as DI;
use ByJG\Config\Param;
return [
'database' => [
'host' => 'localhost',
'port' => 3306
],
// Register the database service. A Param refers to the whole entry, so it receives the array
\MyApp\DatabaseService::class => DI::bind(\MyApp\DatabaseService::class)
->withConstructorArgs([Param::get('database')])
->toSingleton(),
// Register the repository with auto-injection
\MyApp\UserRepository::class => DI::bind(\MyApp\UserRepository::class)
->withInjectedConstructor()
->toInstance(),
// For mixed injection (auto-inject classes, manually provide scalars)
\MyApp\UserService::class => DI::bind(\MyApp\UserService::class)
->withInjectedConstructorOverrides([
'apiKey' => 'my-secret-key' // Manually provide scalar values
])
->toInstance()
];
// 3. Use the services from the container
$userRepo = $container->get(\MyApp\UserRepository::class);
Documentation
Start with the fundamentals and move toward the advanced features following this order:
- Set up the configuration files – Define environments, file naming, and directory structure;
- Load the configuration – Build
Definitioninstances and work with the PSR-11 container; - Using the Config Facade – Access container values through the static API;
- Auto-Initialization – Let
Configbootstrap itself fromconfig/ConfigBootstrap.php; - Define dependency injection – Bind services, constructor parameters, and factories;
- Work with special types – Parse
.envvalues through built-in or custom parsers; - Configure your webserver – Expose environment variables in different runtimes;
- Follow good practices – Apply conventions that keep configuration manageable;
- Explore advanced features – Use caching, inheritance constraints, and container helpers;
Installation
composer require "byjg/config"
Running Tests
./vendor/bin/phpunit