The Container class is ForkBB’s dependency injection container, providing service resolution, shared instances, and parameter management.
Overview
Based on artoodetoo/container , the Container manages:
Shared services - Singleton instances created once
Multiple services - New instances created on each access
Parameters - Configuration values and data
Factory methods - Services created via factory patterns
The Container is available throughout ForkBB via constructor injection, typically as $c or $this->c.
Constructor
public function __construct ( array $config = [])
Container configuration with optional keys:
shared - Array of shared service definitions
multiple - Array of multiple instance service definitions
Other keys become instance parameters
Example:
$container = new Container ([
'shared' => [
'db' => [ DB :: class , $dsn , $username , $password ],
'router' => [ Router :: class , '%base.url%' , '@csrf' ]
],
'multiple' => [
'validator' => Validator :: class
],
'base.url' => 'https://example.com' ,
'debug' => true
]);
Service Resolution
__get
public function __get ( string $key ) : mixed
Retrieves a service or parameter from the container.
Service name or parameter path. Supports dot notation for nested values (e.g., config.base.url).
The resolved service instance or parameter value.
Service Resolution Rules:
Existing instances - Returns cached value immediately
Dot notation - Traverses arrays/objects (e.g., config.db.host)
Shared services - Creates and caches singleton
Multiple services - Creates new instance each time
Parameter substitution - Resolves %param% pattern (e.g., %base.url%)
Examples:
// Access shared service (singleton)
$db = $this -> c -> DB ;
$router = $this -> c -> Router ;
// Access nested configuration
$dbHost = $this -> c -> { 'config.db.host' };
$debugMode = $this -> c -> { 'config.debug' };
// Create new validator instance
$validator = $this -> c -> Validator ;
Factory Method Pattern:
// Service definition using factory
'shared' => [
'cache' => '@factory:createCache'
]
// Factory class
class Factory {
public function createCache ( $container ) {
return new Cache ( $container -> config );
}
}
// Usage
$cache = $this -> c -> cache ;
__set
public function __set ( string $key , mixed $service ) : void
Sets a service or parameter value.
Service name. Cannot contain dots.
The service instance or parameter value to store.
Example:
// Store a service instance
$this -> c -> myService = new MyService ();
// Store a parameter
$this -> c -> apiKey = 'secret-key-123' ;
Configuration
config
public function config ( array $config ) : void
Merges additional configuration into the container.
Configuration array with same structure as constructor.
Example:
$this -> c -> config ([
'shared' => [
'mailer' => [ Mailer :: class , '%smtp.host%' ]
],
'smtp.host' => 'smtp.example.com'
]);
setParameter
public function setParameter ( string $name , mixed $value ) : Container
Sets a nested parameter using dot notation.
Parameter path with dot notation (e.g., db.host).
Returns self for method chaining.
Example:
$this -> c
-> setParameter ( 'db.host' , 'localhost' )
-> setParameter ( 'db.port' , 3306 )
-> setParameter ( 'db.charset' , 'utf8mb4' );
// Access parameters
$host = $this -> c -> { 'db.host' }; // 'localhost'
Utility Methods
fromArray
public function fromArray ( array $array , array $tree ) : mixed
Retrieves a value from a nested array.
Array of keys representing the path.
The value at the path, or null if not found.
Example:
$data = [
'user' => [
'profile' => [
'name' => 'John Doe'
]
]
];
$name = $this -> c -> fromArray ( $data , [ 'user' , 'profile' , 'name' ]);
// Returns: 'John Doe'
isInit
public function isInit ( string $name ) : bool
Checks if a service has been initialized.
True if the service instance exists in cache.
Example:
if ( $this -> c -> isInit ( 'DB' )) {
// Database connection already established
$queries = $this -> c -> DB -> getQueries ();
}
Parameter Substitution
The Container supports automatic parameter substitution:
Full Substitution
Replaces entire string and returns any type:
'shared' => [
'router' => [ Router :: class , '%base.url%' ] // Returns actual value type
]
Partial Substitution
Replaces within string, always returns string:
'dsn' => 'mysql:host=%db.host%;dbname=%db.name%'
// Result: 'mysql:host=localhost;dbname=forkbb'
Service Reference
References another service using @ prefix:
'shared' => [
'validator' => [ Validator :: class , '@secury' , '@db' ]
]
// Access nested service properties
'config.version' => '@app.version'
Real-World Examples
Accessing Core Services
~/workspace/source/app/Controllers/Routing.php
class Routing
{
public function __construct ( protected Container $c )
{
}
public function routing () : Page
{
// Access shared services
$user = $this -> c -> user ;
$config = $this -> c -> config ;
$router = $this -> c -> Router ;
// Use services
$router -> add (
$router :: GET ,
'/forum/{id|i:[1-9]\d*}/{name}' ,
'Forum:view' ,
'Forum'
);
return $page ;
}
}
Service Configuration
// Define services
$container -> config ([
'shared' => [
'db' => [
DB :: class ,
'dsn' => '%db.dsn%' ,
'username' => '%db.user%' ,
'password' => '%db.pass%' ,
'prefix' => '%db.prefix%'
],
'router' => [ Router :: class , '%base.url%' , '@csrf' ],
'validator' => [ Validator :: class , '@container' ]
],
'db.dsn' => 'mysql:host=localhost;dbname=forkbb' ,
'db.user' => 'root' ,
'db.pass' => 'secret' ,
'db.prefix' => 'fork_' ,
'base.url' => 'https://example.com/forum'
]);
Creating Multiple Instances
// Each access creates a new instance
$container -> config ([
'multiple' => [
'validator' => Validator :: class
]
]);
$validator1 = $this -> c -> Validator ; // New instance
$validator2 = $this -> c -> Validator ; // Another new instance
Error Handling
The Container throws InvalidArgumentException for:
Accessing undefined services
Using dots in __set() keys
Setting parameters on scalar values in path
Example:
try {
$service = $this -> c -> nonExistentService ;
} catch ( InvalidArgumentException $e ) {
// Handle: "Wrong property name: nonExistentService"
}
Best Practices
Use Type Hints public function __construct ( protected Container $c )
Always inject the Container via constructor with type hints.
Prefer Shared Services Use shared for services that maintain state or are expensive to create (database, router, etc.).
Check Initialization if ( $this -> c -> isInit ( 'DB' )) {
// Use existing connection
}
Check if expensive services are initialized before accessing.
Use Parameter Substitution '%base.url%/api' // Good
$container -> { 'base.url' } . '/api' // Works but verbose
Leverage parameter substitution for cleaner configuration.
See Also