laminas/laminas-config-aggregator
Aggregate and merge configuration from multiple providers in Laminas/Mezzio apps. Supports ordered loading, caching, PHP/array and glob-based config files, and environment-specific overrides for fast, predictable configuration builds.
Begin by installing the package via Composer: composer require laminas/laminas-config-aggregator. The core entrypoint is the ConfigAggregator class — instantiate it, pass in an array of config providers, and call getMergedConfig() to get your final configuration array.
A typical minimal first use case: aggregate config from your application’s main config.php, a local.php (for environment-specific overrides), and a third-party module config file. For example:
use Laminas\ConfigAggregator\ConfigAggregator;
use Laminas\ConfigAggregator\PhpFileProvider;
$aggregator = new ConfigAggregator([
new PhpFileProvider('config/config.php'),
new PhpFileProvider('config/local.php'),
new PhpFileProvider('vendor/package/config/config.php'),
]);
$config = $aggregator->getMergedConfig();
Check the ConfigAggregator constructor: it accepts a second boolean argument $cacheEnabled (or a PSR-6/PSR-16 cache item pool) to enable caching — crucial for production performance.
Modular config extraction: Each module or package provides a dedicated config provider (e.g., class implementing Laminas\ConfigAggregatorProviderInterface, or returning an array from a callable). This decouples config from bootstrap logic and improves testability.
Layered environments: Use provider priority or separate provider arrays per environment (dev/prod) to override defaults. Config is merged in the order providers appear — later entries override earlier ones.
Caching in production: Wrap providers in CachedConfigProvider or pass a cache instance to ConfigAggregator. This avoids re-parsing config files on every request:
$aggregator = new ConfigAggregator(
$providers,
true,
new SimpleCacheAdapter(new ArrayAdapter()) // or real PSR-16 cache
);
Conditional providers: Use closures or factories as providers to dynamically include config based on runtime conditions (e.g., enable debug-only config only in dev):
fn () => $isDev ? require 'config/debug.php' : [],
Extension with custom providers: Extend CallbackConfigProvider or implement ProviderInterface for complex sources (e.g., YAML via symfony/yaml, database-backed config).
Merge strategy matters: Providers are merged recursively, but array keys are overwritten (not merged by default like array_merge_recursive). Use ConfigAggregator::mergeConfig() if you need fine-grained control — its default is a shallow merge with later values replacing earlier ones.
Order = priority: Providers listed first have lower precedence. A common anti-pattern is placing environment overrides before defaults — ensure local.php appears after config.php.
Cache invalidation: Cached configs won’t auto-refresh on file changes. Use cache key namespaces per environment or bust cache on deploy (e.g., append filemtime() or Git commit hash to cache key).
Provider side effects: Avoid heavy computation or I/O in providers — they run on config aggregation every time unless cached. Prefer lazy loading via CallbackConfigProvider only where necessary.
Debugging merge conflicts: Use getCacheChecksum() or manually inspect intermediate arrays with var_dump($aggregator->getMergedConfig()) — or enable Xdebug trace for mergeConfig() to see where overrides originate.
No autowiring assumptions: This package is only for aggregation — it doesn’t integrate with DI containers directly. Use the final $config array to configure services in your DI container (e.g., Laravel’s config() helper, Symfony’s config component, or PSR-11).
How can I help you explore Laravel packages today?