Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Zend Config Aggregator Parameters Laravel Package

zendframework/zend-config-aggregator-parameters

A Zend Config Aggregator post-processor that resolves %parameter% placeholders in configuration, letting you centralize reusable values and keep aggregated config DRY across environments and modules.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a single-purpose post-processor for zendframework/zend-config-aggregator: it takes a flat parameters array and substitutes %placeholder% tokens inside your aggregated config, in the same spirit as Symfony's container parameters.

  1. Require it: composer require zendframework/zend-config-aggregator-parameters. As of 1.2.0 the dependency chain accepts symfony/dependency-injection ^3.4 || ^4.0 || ^5.0, so it slots into both older and current Symfony-based stacks. Confirm what you actually got with composer why symfony/dependency-injection — you may already have a ParameterBag on the classpath for other reasons.
  2. Create config/parameters.php returning a flat key/value map:
    return [
        'db.host' => getenv('DB_HOST') ?: 'localhost',
        'db.name' => 'app',
        'db.dsn'  => 'mysql:host=%db.host%;dbname=%db.name%', // nested references work
    ];
    
  3. Register the post-processor in your aggregator, after the file providers and before processors that need resolved values:
    use Zend\ConfigAggregator\ConfigAggregator;
    use Zend\ConfigAggregator\PhpFileProvider;
    use Zend\ConfigAggregatorParameters\ParameterPostProcessor;
    
    $aggregator = new ConfigAggregator([
        new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
        new ParameterPostProcessor(require 'config/parameters.php'),
    ], 'data/cache/config-cache.php');
    
  4. First real use case: a single parameters.php per environment, with module configs referencing %db.dsn%, %mail.from%, %feature.flag% instead of scattering getenv() calls through every config file. Grep your config tree for % to see what you can lift.

Where to look first: the ParameterPostProcessor class itself (it is thin — it delegates the actual substitution to Symfony's ParameterBag) and the README, which shows the exact aggregator wiring. Since the interesting logic lives in ParameterBag, read its resolveValue()/resolveString() semantics if placeholder behaviour surprises you.

Implementation Patterns

Layer parameters over autoloaded config. Keep parameters.php free of structure — just scalars and strings that reference other parameters. All shaping (arrays, service manager config, middleware pipelines) stays in the aggregator files. This makes environment diffs readable and reviewable.

Compose parameter files per environment. Point the aggregator at a file selected by env rather than shipping secrets in the repo:

$env = getenv('APP_ENV') ?: 'development';
$aggregator = new ConfigAggregator([
    new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
    new ParameterPostProcessor(require "config/parameters/{$env}.php"),
], 'data/cache/config-cache.php');

Because nested references resolve recursively, parameters/development.php can override just db.host on top of a shared base.php merged with array_replace_recursive.

Order processors deliberately. The post-processor only rewrites strings that are already in the aggregate. Anything a later processor injects (e.g. a processor that appends values) will not be parameterised, and any processor that consumes %...% must run after this one. Keep ParameterPostProcessor immediately after the PhpFileProvider block and treat it as the boundary between "raw config" and "resolved config".

Use it to kill duplicated env plumbing. Classic day-to-day use: config/autoload/db.global.php declares 'dsn' => '%db.dsn%', and only parameters.php knows about DB_HOST/DB_NAME. Swapping to an RDS endpoint or a CI database becomes a one-line parameter change instead of a hunt through module configs.

Test the parameter file in isolation. The processor is a plain callable, so unit tests need no bootstrapped app:

$processor = new ParameterPostProcessor(['db.host' => 'replica.internal']);
$this->assertSame(
    ['dsn' => 'mysql:host=replica.internal'],
    $processor(['dsn' => 'mysql:host=%db.host%'])
);

Add a smoke assertion that config/parameters/{$env}.php resolves cleanly — this catches missing keys at test time rather than at container boot.

Cache-aware deploys. Since the aggregator writes data/cache/config-cache.php, your deploy step must clear or warm that cache after parameters change (and the cache key/path must differ per environment, otherwise a local build poisons a production cache).

Gotchas and Tips

  • Cache staleness is the #1 footgun. Editing parameters.php without clearing data/cache/config-cache.php leaves the old resolved values in place. If a change "doesn't take", clear the config cache before debugging anything else.
  • Unknown parameters throw. Resolution goes through ParameterBag::resolveValue(), which raises ParameterNotFoundException for a placeholder with no matching key — typically at first request, not at deploy. Guard this with the smoke test above, or pre-validate the parameter file against a grep -rho '%[a-z0-9_.-]*%' config/ list in CI.
  • Circular references throw too. $a => '%b%', $b => '%a%' produces a ParameterCircularReferenceException from Symfony, not a silent loop. Nested parameters are a feature, but keep the graph shallow and one-directional (base → derived → consumed).
  • Escape literal percent signs as %%. Anything that looks like %word% in a string value — URLs with percent-encoding, printf format strings, SQL LIKE '%foo%', cron snippets — will otherwise be treated as a parameter reference. This is the most common cause of "why is my DSN mangled".
  • Only string values are substituted, recursively through arrays. Array keys, integers, booleans and objects are left alone, so you cannot build env-specific keys or flip a boolean via a placeholder — use a ternary in the aggregator file instead.
  • Upgrading to 1.2.0 widens the Symfony range but does not add Symfony 6. If your app is on symfony/dependency-injection ^6.0, this package will still conflict; you would need to pin the Symfony line down or fork the constraint. Conversely, if you were previously stuck on Symfony 4 because of this package, composer update zendframework/zend-config-aggregator-parameters can now pull in Symfony 5 transitively — re-run the full test suite, since other packages sharing the ParameterBag may see behavioural differences.
  • Version 1.2.0 is a compat-only release. No API, config or deprecation changes: ParameterPostProcessor's constructor signature and __invoke() contract are unchanged, so upgrading is a composer update with no code edits. There is no new extension point to learn.
  • Extension point is the aggregator, not this package. If you need different placeholder syntax (e.g. ${foo}), extra validation, or typed parameter coercion, don't subclass the post-processor — write your own ConfigAggregator post-processor callable and place it in the same slot. It only needs __invoke(array $config): array.
  • Watch the Zend→Laminas namespace split. The class lives under Zend\ConfigAggregatorParameters. In a project that has already migrated aggregator and framework packages to Laminas\*, verify which namespace your installed version registers (and whether a Laminas-namespaced equivalent exists) before copy-pasting README snippets — a mismatched use statement yields a confusing "class not found" at bootstrap.
  • Debugging tip: temporarily dump the post-processor's output (var_dump((new ParameterPostProcessor($params))($config))) before the aggregator's cache file is written, or pass null as the cache path in a scratch script. Trying to inspect a cached aggregate is how you end up chasing a bug that no longer exists on disk.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource