yiisoft/definitions
Lightweight definitions library for Yii and PHP DI containers. Provides a consistent way to describe services, factories, references, and parameters for building dependency graphs, resolving callables, and configuring containers with minimal overhead.
Installation Add the package via Composer:
composer require yiisoft/definitions
No additional configuration is required for basic usage.
First Use Case: Defining a Service
Define a service (e.g., a logger) using the Definition class:
use Yiisoft\Definitions\Definition;
use Yiisoft\Definitions\DefinitionInterface;
$loggerDefinition = Definition::create()
->className(\Psr\Log\LoggerInterface::class)
->constructor([
Definition::create()->className(\Psr\Log\Handler\StreamHandler::class)
->constructor(['/path/to/logs/app.log'])
->method('setFormatter', Definition::create()->className(\Psr\Log\Formatter\LineFormatter::class))
]);
Where to Look First
Definition, DefinitionInterface, and DefinitionContainer.Service Definition
Use Definition to configure services with dependencies, constructors, and methods:
$repositoryDefinition = Definition::create()
->className(\App\Repositories\UserRepository::class)
->constructor([
Definition::create()->className(\App\Services\Database::class)
]);
Dynamic Configuration
Leverage Definition to inject configuration dynamically:
$configurableService = Definition::create()
->className(\App\Services\ConfigurableService::class)
->method('setConfig', Definition::create()->value(['key' => 'value']));
Integration with Laravel's Service Container
Use the DefinitionContainer to resolve definitions in Laravel's IoC container:
$container = new \Yiisoft\Definitions\DefinitionContainer();
$container->setDefinitions([
'logger' => $loggerDefinition,
]);
$logger = $container->get('logger'); // Resolves the logger service
Grouping Definitions Organize definitions hierarchically for complex applications:
$definitions = [
'database' => Definition::create()->className(\App\Services\Database::class),
'userRepository' => Definition::create()
->className(\App\Repositories\UserRepository::class)
->constructor([Definition::create()->reference('database')]),
];
$container->setDefinitions($definitions);
Lazy Loading
Use Definition::lazy() to defer instantiation until first use:
$lazyService = Definition::create()
->lazy()
->className(\App\Services\HeavyService::class);
Laravel Service Providers Register definitions in a Laravel service provider:
public function register()
{
$container = new \Yiisoft\Definitions\DefinitionContainer();
$this->app->singleton('definitions', fn() => $container);
}
Configuration Files
Store definitions in config/definitions.php and load them in a service provider:
$definitions = require config_path('definitions.php');
$container->setDefinitions($definitions);
Testing Mock definitions in tests for isolated service resolution:
$mockDefinition = Definition::create()->value(new \App\Mocks\MockService());
$container->setDefinitions(['mockService' => $mockDefinition]);
Circular Dependencies Avoid circular references between definitions. The container will throw an exception if detected:
// ❌ Avoid this:
$definitions = [
'a' => Definition::create()->constructor([Definition::create()->reference('b')]),
'b' => Definition::create()->constructor([Definition::create()->reference('a')]),
];
Immutable Definitions Definitions are immutable after creation. Use method chaining to build them:
// ❌ Won't work:
$definition->constructor([...]); // Throws error if called after creation.
// ✅ Correct:
Definition::create()->constructor([...]);
Non-Existent References Always ensure referenced definitions exist when resolving:
// ❌ Throws exception if 'nonexistent' isn't defined:
Definition::create()->constructor([Definition::create()->reference('nonexistent')]);
Type Safety The package doesn’t enforce type hints for constructor arguments. Validate manually or use PHP 8+ return types:
// Ensure the constructor matches the class's type hints.
Inspect Definitions
Use DefinitionContainer::getDefinition() to debug unresolved definitions:
$definition = $container->getDefinition('serviceName');
dump($definition->toArray()); // Inspect raw definition structure.
Enable Strict Mode
Set DefinitionContainer::setStrictMode(true) to catch issues early (e.g., missing definitions).
Logging Resolution
Override DefinitionContainer to log resolution steps:
$container = new class extends \Yiisoft\Definitions\DefinitionContainer {
protected function resolveDefinition(DefinitionInterface $definition): mixed {
\Log::debug('Resolving: ' . $definition->className);
return parent::resolveDefinition($definition);
}
};
Custom Resolvers
Extend DefinitionContainer to add custom resolution logic:
class CustomContainer extends \Yiisoft\Definitions\DefinitionContainer {
protected function resolveDefinition(DefinitionInterface $definition): mixed {
if ($definition->className === \App\Services\CustomService::class) {
return new \App\Services\CustomService($this->get('config'));
}
return parent::resolveDefinition($definition);
}
}
Definition Factories Create reusable factories for common definitions:
class LoggerFactory {
public static function create(string $path): DefinitionInterface {
return Definition::create()
->className(\Psr\Log\LoggerInterface::class)
->constructor([
Definition::create()
->className(\Psr\Log\Handler\StreamHandler::class)
->constructor([$path])
]);
}
}
Integration with Laravel Bindings
Combine with Laravel’s bind() for hybrid resolution:
$this->app->bind(\App\Contracts\UserRepository::class, function ($app) {
$container = $app->make('definitions');
return $container->get('userRepository');
});
Environment-Aware Definitions Dynamically adjust definitions based on environment (e.g., staging vs. production):
$definition = Definition::create()
->className(\App\Services\Cache::class)
->constructor([
Definition::create()->value(config('cache.driver') === 'redis' ? 'redis' : 'file')
]);
How can I help you explore Laravel packages today?