-
-
Notifications
You must be signed in to change notification settings - Fork 1
Configuration reference
Полное описание декларативной конфигурации CloudCastle DI: все ключи массива конфигурации, способы загрузки и слияния файлов, примеры для каждого формата.
Краткий обзор и диаграммы — в Конфигурация из файлов. Здесь — справочник по параметрам и пошаговые примеры.
| Способ | Когда подходит |
|---|---|
PHP API (set, bind, scan, …) |
Полный контроль, callable-фабрики, условная логика в коде |
| Файлы конфигурации | Один composition root, несколько окружений (dev/prod), общие defaults |
| Смешанный |
loadMany() + правки массива + apply(), или PHP-файл с return и callable в services
|
Конфигурация необязательна — контейнер работает без ContainerConfigurator.
flowchart LR
S[Источники] --> R[ConfigurationSourceResolver]
R --> L[Слои ConfigurationLayer]
L --> M[ConfigurationMerger]
M --> A[ConfigurationApplicator]
A --> C[Container]
- Resolver разворачивает строки, каталоги и объекты-источники в список файлов (слои).
- Merger объединяет секции с учётом priority и порядка загрузки.
- Applicator вызывает методы контейнера в фиксированном порядке (см. ниже).
| Метод | Назначение |
|---|---|
configure($container, $sources) |
loadMany() + apply()
|
loadMany($sources) |
Загрузить и слить, без применения к контейнеру |
load($path) |
Один файл → массив |
apply($container, $config) |
Применить уже готовый массив |
| Тип | Пример | Поведение |
|---|---|---|
string (файл) |
'config/base.php' |
Один файл |
string (каталог) |
'config/layers/' |
Все поддерживаемые файлы каталога, без вложенных подкаталогов |
ConfigurationSource |
new ConfigurationSource($path, priority: 50) |
Один файл + приоритет слоя |
ConfigurationDirectorySource |
new ConfigurationDirectorySource($dir, priority: 10, scan: …) |
Каталог + опции |
ConfigurationFilesSource |
new ConfigurationFilesSource([$a, $b], priority: 5) |
Явный список файлов |
Поддерживаемые расширения: .php, .json, .yaml, .yml, .xml.
<?php
use CloudCastle\DI\Configuration\ContainerConfigurator;
use CloudCastle\DI\Container;
$container = new Container();
(new ContainerConfigurator())->configure($container, [
__DIR__ . '/config/container.php',
]);$configurator->configure($container, [
__DIR__ . '/config/base.php',
__DIR__ . '/config/override.json',
__DIR__ . '/config/prod.xml',
]);Без явного priority значение app.label из prod.xml перекроет override.json.
Все файлы с поддерживаемым расширением в каталоге загружаются в лексикографическом порядке имени:
$configurator->configure($container, [
__DIR__ . '/config/layers', // 01-base.php, 02-overlay.json, …
]);Неподдерживаемые расширения (.ini, .env) пропускаются.
use CloudCastle\DI\Configuration\ConfigurationSource;
$configurator->configure($container, [
__DIR__ . '/config/override.json',
new ConfigurationSource(__DIR__ . '/config/base.php', priority: 100),
]);Файл base.php с priority: 100 перебьёт более поздний JSON без приоритета.
use CloudCastle\DI\Configuration\ConfigurationDirectoryScan;
use CloudCastle\DI\Configuration\ConfigurationDirectorySource;
$configurator->configure($container, [
new ConfigurationDirectorySource(__DIR__ . '/config/layers'),
new ConfigurationDirectorySource(
__DIR__ . '/config/nested',
priority: 50,
scan: ConfigurationDirectoryScan::Recursive,
),
]);| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
$directory |
string |
— | Путь к каталогу |
$priority |
?int |
null |
Приоритет каждого файла каталога; null — порядок в общем списке источников |
$scan |
ConfigurationDirectoryScan |
Flat |
Flat — только файлы каталога; Recursive — включая подкаталоги |
use CloudCastle\DI\Configuration\ConfigurationFilesSource;
$configurator->configure($container, [
new ConfigurationFilesSource([
__DIR__ . '/config/base.php',
__DIR__ . '/config/override.json',
], priority: 10),
]);Порядок в массиве $paths = порядок слияния внутри этого источника.
$configurator = new ContainerConfigurator();
$config = $configurator->loadMany([
__DIR__ . '/config/base.php',
__DIR__ . '/config/local.php',
]);
$config['services']['app.debug'] = true;
$configurator->apply($container, $config);$configurator->apply($container, [
'autowiring' => ['enabled' => true],
'services' => [
'app.env' => 'prod',
],
'bind' => [
LoggerInterface::class => FileLogger::class,
],
]);| Расширение | Загрузчик | PHP-расширение |
|---|---|---|
.php |
PhpConfigurationLoader |
— (формат по умолчанию) |
.json |
JsonConfigurationLoader |
ext-json |
.yaml, .yml
|
YamlConfigurationLoader |
ext-yaml |
.xml |
XmlConfigurationLoader |
ext-simplexml |
-
PHP — единственный формат, где в
servicesдопустимы callable (фабрики). - JSON / YAML / XML — только декларативные данные (строки, числа, bool, массивы).
Без ext-yaml загрузка .yaml бросает ContainerException с подсказкой установить расширение.
Итоговый массив после слияния может содержать секции:
| Ключ | Тип | Применение в контейнере |
|---|---|---|
priority |
int |
Приоритет всех параметров файла без собственного priority (метаданные слоя, не вызывает API) |
register_attributes |
list<string> |
registerAttribute() для каждого FQCN |
autowiring |
array<string, bool> |
enableAutowiring() и флаги по имени |
scan |
list<array> |
scan($directory, $namespace?) |
services |
map<string, mixed> |
set(), bind(), autowire(), lazy()
|
autowire |
list<string> |
autowire($class) |
bind |
map<string, string> |
bind($abstract, $concrete) |
aliases |
map<string, string> |
alias($alias, $target) |
tags |
map<string, list<string>> |
tag($id, $tag) |
register_attributes → autowiring → scan → services → autowire → bind → aliases → tags
Сначала регистрируются attributes и флаги autowiring, затем scan добавляет классы, потом явные services, и т.д.
Задаёт дефолтный приоритет слоя для всех ключей файла, у которых нет своего priority.
PHP:
return [
'priority' => 50,
'services' => [
'app.mode' => 'runtime',
],
];XML — атрибут корня <container priority="50">.
При слиянии побеждает запись с большим effectivePriority; при равенстве — более поздний слой (больший order).
Список FQCN классов PHP attributes, реализующих ServiceIdAttribute (пользовательские @Inject-аналоги).
'register_attributes' => [
App\Attribute\InjectConfig::class,
App\Attribute\InjectLogger::class,
],JSON:
{
"register_attributes": [
"App\\Attribute\\InjectConfig"
]
}YAML:
register_attributes:
- App\Attribute\InjectConfigXML:
<register_attributes>
<attribute class="App\Attribute\InjectConfig"/>
</register_attributes>Включает режимы autowiring контейнера. Учитываются только ключи со значением true (явно).
| Ключ | Метод контейнера |
|---|---|
enabled |
enableAutowiring() |
parameter_name |
enableParameterNameAutowiring() |
property |
enablePropertyAutowiring() |
method |
enableMethodAutowiring() |
PHP / JSON / YAML:
autowiring:
enabled: true
parameter_name: true
property: true
method: trueXML:
<autowiring enabled="true" parameter_name="true" property="true" method="true"/>Атрибуты false, 0, no в XML не экспортируются в массив (флаг просто отсутствует).
Список каталогов для автоматической регистрации классов через Container::scan().
PHP:
'scan' => [
[
'directory' => __DIR__ . '/../src',
'namespace' => 'App\\',
],
[
'directory' => __DIR__ . '/../modules/Billing',
// namespace опционален
],
],YAML:
scan:
- directory: /var/www/app/src
namespace: App\XML:
<scan>
<directory path="/var/www/app/src" namespace="App\"/>
</scan>При слиянии дубликаты по ключу directory разрешаются через priority/order (как list-секция).
Карта id → значение. Id — произвольная строка (app.logger, config.dsn, FQCN как id).
'services' => [
// скаляр → set($id, $value)
'app.env' => 'prod',
'app.debug' => false,
'app.timeout' => 30,
// произвольный массив → set как есть
'config.mail' => [
'host' => 'smtp.example.com',
'port' => 587,
],
// callable — только в PHP-файлах
'logger' => static fn (Container $c): LoggerInterface => new FileLogger(
$c->get('config.log_path'),
),
],'services' => [
// id === class → autowire(FQCN)
FileLogger::class => [
'class' => FileLogger::class,
],
// id !== class → bind($id, FQCN)
'logger' => [
'class' => FileLogger::class,
],
// lazy-сервис
'lazy.logger' => [
'class' => FileLogger::class,
'lazy' => true,
],
],| Поле | Тип | Эффект |
|---|---|---|
class |
string FQCN |
Класс для autowire / bind / lazy
|
lazy |
bool |
true → set($id, $container->lazy($class))
|
JSON (без callable):
{
"services": {
"app.label": "from-json",
"lazy.logger": {
"class": "App\\FileLogger",
"lazy": true
}
}
}XML:
<services>
<service id="app.label">from-xml</service>
<service id="logger" class="App\FileLogger" lazy="true"/>
</services>Явная обёртка (JSON, YAML, XML, PHP):
'app.label' => [
'value' => 'from-low-priority-file',
'priority' => 10,
],Inline priority в определении класса (PHP, после слияния priority снимается с массива):
'app.label' => [
'class' => SomeClass::class,
'priority' => 100,
],XML:
<service id="app.label" priority="100">winner</service>Список FQCN для autowire($class) (регистрация по имени класса).
PHP:
'autowire' => [
App\Services\Clock::class,
App\Services\Mailer::class,
],JSON:
{
"autowire": [
"App\\Services\\Clock"
]
}XML — элемент <class> или <class name="FQCN"/>:
<autowire>
<class>App\Services\Clock</class>
<class name="App\Services\Mailer"/>
</autowire>Сопоставление абстракции (интерфейс / id) → реализация.
PHP:
'bind' => [
LoggerInterface::class => FileLogger::class,
'mailer' => App\Mail\SmtpMailer::class,
],YAML:
bind:
Psr\Log\LoggerInterface: App\FileLoggerXML:
<bind>
<binding abstract="Psr\Log\LoggerInterface" concrete="App\FileLogger"/>
</bind>С priority:
LoggerInterface::class => [
'value' => FileLogger::class,
'priority' => 100,
],Короткие имена для существующих id.
PHP:
'aliases' => [
'env' => 'app.env',
'log' => 'logger',
],JSON:
{
"aliases": {
"timeout": "app.timeout"
}
}Группировка id сервисов для TaggedServiceIterator / TaggedServiceLocator.
PHP:
'tags' => [
'event.handler' => [
'handler.user_created',
'handler.order_placed',
],
'middleware' => [
'middleware.auth',
'middleware.cors',
],
],XML:
<tags>
<tag name="event.handler">
<id>handler.user_created</id>
<id>handler.order_placed</id>
</tag>
</tags>При конфликте одного ключа в map-секциях (services, bind, aliases, tags):
-
priorityу параметра —['value' => …, 'priority' => N]или XML-атрибутpriorityна элементе -
priorityфайла — ключpriorityв корне илиConfigurationSource/ConfigurationDirectorySource/ConfigurationFilesSource - Порядок загрузки — без явных priority побеждает последний файл / слой
Пример: JSON задаёт app.label, XML с priority="100" на <service> выигрывает даже если JSON загружен позже.
List-секции (autowire, register_attributes, scan): элементы с одинаковым ключом (FQCN, directory) сливаются по тем же правилам; итоговый список сортируется по order слоя.
config/container.php:
<?php
declare(strict_types=1);
use App\Attribute\InjectConfig;
use App\FileLogger;
use App\Services\Clock;
use CloudCastle\DI\Container;
use Psr\Log\LoggerInterface;
return [
'priority' => 10,
'register_attributes' => [
InjectConfig::class,
],
'autowiring' => [
'enabled' => true,
'parameter_name' => true,
],
'scan' => [
[
'directory' => __DIR__ . '/../src',
'namespace' => 'App\\',
],
],
'services' => [
'app.env' => $_ENV['APP_ENV'] ?? 'dev',
'logger' => static fn (Container $c): LoggerInterface => new FileLogger(
__DIR__ . '/../var/log/app.log',
),
Clock::class => [
'class' => Clock::class,
],
'lazy.heavy' => [
'class' => App\Services\HeavyService::class,
'lazy' => true,
],
],
'autowire' => [
App\Services\Mailer::class,
],
'bind' => [
LoggerInterface::class => FileLogger::class,
],
'aliases' => [
'env' => 'app.env',
],
'tags' => [
'console.command' => [
'command.cache_clear',
'command.migrate',
],
],
];Composition root:
$container = new Container();
(new ContainerConfigurator())->configure($container, [
__DIR__ . '/config/container.php',
__DIR__ . '/config/local.php', // переопределения окружения
]);
$container->freeze();config/10-base.json:
{
"services": {
"app.env": "staging",
"app.timeout": 30
},
"aliases": {
"timeout": "app.timeout"
}
}config/20-overlay.yaml:
services:
app.label: from-yaml
app.region: eu
aliases:
region: app.region
autowiring:
enabled: trueconfig/30-prod.xml:
<?xml version="1.0" encoding="UTF-8"?>
<container priority="100">
<services>
<service id="app.label" priority="200">production</service>
<service id="app.env">prod</service>
</services>
<autowiring enabled="true" parameter_name="true"/>
<bind>
<binding abstract="Psr\Log\LoggerInterface" concrete="App\FileLogger"/>
</bind>
</container>Загрузка каталогом:
$configurator->configure($container, [
__DIR__ . '/config', // 10-base.json → 20-overlay.yaml → 30-prod.xml
]);Итог: app.label = production (priority 200), app.env = prod, autowiring включён.
Структура:
config/
layers/
01-base.php
02-overlay.json
nested/
root.php
sub/
child.json
use CloudCastle\DI\Configuration\ConfigurationDirectoryScan;
use CloudCastle\DI\Configuration\ConfigurationDirectorySource;
$configurator->configure($container, [
new ConfigurationDirectorySource(__DIR__ . '/config/layers'),
new ConfigurationDirectorySource(
__DIR__ . '/config/nested',
scan: ConfigurationDirectoryScan::Recursive,
),
]);Корень: <container priority="…"> (атрибут priority опционален).
| Секция | Дочерние элементы |
|---|---|
<services> |
<service id="…" class="…" lazy="true|false" priority="…">текст</service> |
<aliases> |
<alias name="…" target="…" priority="…"/> |
<bind> |
<binding abstract="…" concrete="…" priority="…"/> |
<autowire> |
<class>FQCN</class> или <class name="FQCN" priority="…"/>
|
<tags> |
<tag name="…"><id>…</id></tag> |
<scan> |
<directory path="…" namespace="…"/> |
<register_attributes> |
<attribute class="FQCN" priority="…"/> |
<autowiring> |
атрибуты enabled, parameter_name, property, method
|
use CloudCastle\DI\Configuration\ConfigurationLoaderRegistry;
use CloudCastle\DI\Configuration\ContainerConfigurator;
use CloudCastle\DI\Configuration\Loader\JsonConfigurationLoader;
use CloudCastle\DI\Configuration\Loader\PhpConfigurationLoader;
$configurator = new ContainerConfigurator(
loaderRegistry: new ConfigurationLoaderRegistry([
new PhpConfigurationLoader(),
new JsonConfigurationLoader(),
new MyTomlConfigurationLoader(), // ConfigurationLoaderInterface
]),
);Реализуйте CloudCastle\DI\Contract\ConfigurationLoaderInterface:
supports(string $path): boolload(string $path): array
После $container->freeze() вызов configure() / apply() приведёт к ContainerException при попытке изменить определения (как и прямые set() / bind()).
Рекомендуемый порядок:
$configurator->configure($container, $sources);
$container->freeze();| Ситуация | Исключение |
|---|---|
| Файл не найден / не читается |
ContainerException «не найден или недоступен» |
| Неподдерживаемое расширение | «формат … не поддерживается» |
Пустой ConfigurationFilesSource
|
«список файлов … не может быть пустым» |
| Каталог не существует | «каталог … не найден» |
| YAML без ext-yaml | подсказка установить расширение |
| Невалидный JSON/XML | сообщение парсера |
Следующее настраивается только PHP API (не секции файлов):
-
decorator()/ цепочки декораторов -
afterResolving()/bind()с closure -
make()/ прототипы (только через явныйsetс фабрикой) ContainerRegistry::set()
См. Теги и декораторы, call(), bind(), afterResolving.
- Конфигурация из файлов — обзор и диаграммы
-
Примеры bootstrap — composition root, prod +
freeze() -
Autowiring —
register_attributes, inject attributes -
Сканирование классов — детали
scan() -
Справочник API —
ContainerConfigurator,registerAttribute()
CloudCastle DI · Репозиторий · Packagist · Releases · Discussions · Issues
Исходники Wiki — wiki/ в репозитории (синхронизация через Actions).
- Архитектура
- Быстрый старт
- Сравнение с PHP-DI, Symfony, Pimple — пошагово
- Примеры bootstrap
- Autowiring
- Конфигурация из файлов
- Справочник параметров конфигурации
- Сканирование классов
- Глобальный реестр
- Теги и декораторы
- call(), bind(), afterResolving
- Прототипы, alias и lazy
- Справочник API
- Фабрики и singleton
- Тестирование
- Тесты безопасности
- Нагрузка и производительность
- Анти-паттерны