symfony/config
The Symfony Config component helps you find, load, combine, autofill, and validate configuration values from any source—YAML, XML, INI files, or even a database—giving your PHP applications flexible, reliable configuration handling.
Architecture fit
symfony/config is a framework-agnostic library that solves a problem Laravel applications consistently handle badly: validating, merging, and documenting structured configuration from heterogeneous sources. It is not a Laravel replacement for config/ — it is a schema layer you place around a bounded configuration domain.
Two namespaces are relevant to us:
Config\Definition — TreeBuilder, NodeBuilder, ArrayNodeDefinition, Processor. This is the high-value half: declarative schema, defaults, required/enum/numeric constraints, canBeEnabled()/canBeDisabled() semantics, deep-merge rules, and YamlReferenceDumper/ArrayReferenceDumper for auto-generated config reference docs.Config\Loader + ConfigCache — FileLocator, YamlFileLoader/PhpFileLoader/XmlFileLoader, DelegatingLoader, and resource-tracked caches (FileResource, GlobResource, ReflectionClassResource, ComposerResource). The resource-tracking cache is genuinely differentiated: Laravel's config:cache is all-or-nothing, whereas ConfigCache invalidates on specific file/class/composer changes.Where it fits for us: package/module configuration with a public contract, a multi-source settings layer (file + env + DB), and enterprise deployments that need a published, validated config reference. It does not fit as a general-purpose application config store — Laravel's Repository plus env() is the right tool at the app layer.
Integration feasibility
Composer-installable, MIT, no service provider required, no container coupling. Constraints to verify before commit:
symfony/filesystem and symfony/deprecation-contracts; YAML/XML support is opt-in via symfony/yaml/ext-dom. If we only need PHP-array or DB-sourced config, the footprint stays small.#60639). Laravel 11/12 projects routinely run 8.2/8.3. We must target ^7.4, which is the LTS line — see Operational Impact.EnvConfigurator/EnvPlaceholderParameterBag live in symfony/dependency-injection. If we want env placeholders resolved, we wire that ourselves or accept literal values.#62092, #62135). Neither matters for our use case, but it signals the PHP-config API is churning — another reason to stay on 7.4.Technical risk
Dependents: 0 and Stars: 4276 are artifacts of the read-only split-repo model: real usage and stars accrue to symfony/symfony, and downstream dependents are counted against symfony/framework-bundle, not this repo. The 54.7 composite score under-weights a component with effectively universal PHP-framework adoption. Do not let this number drive a reject decision.#61808 ("incorrect serialized data mangling") and #61727 (__sleep → __(un)serialize) mean cached node graphs are not upgrade-safe — deploys must flush the config cache.TreeBuilder closure DSL is terse and unlike anything in the Laravel ecosystem; review quality degrades without a convention document.Key questions
config/)?config/reference.php-style output) for customer-facing docs? That is often the real ROI.nette/schema is lighter but the Symfony component's resource tracking and dumpers have no close equivalent.Stack fit
Adopt as a library inside a bounded module, layered alongside Laravel's config:
config/*.php and Config::get() untouched for framework and app wiring.symfony/config for one domain — recommended first candidate: a first-party package's configuration, or a tenant/settings module where the schema is the contract.TreeBuilder.Migration path
TreeBuilder schema; load existing values through the loader; diff processed output against current runtime values. Success criterion: byte-identical effective config plus better error messages on invalid input.php artisan config:validate-module) against real configs without changing runtime behaviour. This surfaces merge-semantics surprises cheaply.ConfigCache under storage/framework/cache/, and gate the old path behind a config flag for one release.Compatibility
symfony/config ^7.4 with an explicit comment that 8.x is blocked on PHP 8.4. Add a Composer constraint check in CI.symfony/filesystem and symfony/deprecation-contracts do not conflict with Laravel's existing Symfony constraints (typically ^7.x — compatible).symfony/yaml is only added if YAML loading is in scope.ConfigCache writes on first boot; under Octane the cache must be warmed at deploy, not lazily per worker.Sequencing
Schema definition → loader wiring → cache layer with explicit invalidation key → CI validation command → provider binding → docs/dumper output. Do the cache layer before the provider binding; retrofitting invalidation after consumers depend on the values is materially more expensive.
Maintenance
Upstream is the Symfony core team, with a six-month release cadence and a published support policy. The 7.4 LTS line is the anchor: ~3 years of bug fixes and ~4 years of security fixes from release. That is a stronger support horizon than most packages we carry, and it aligns with the PHP 8.2/8.3 reality of our fleet. Cost to us is version-bump cadence plus a mandatory config-cache flush on every deploy.
Support
No commercial support, no vendor escalation path — community issues route to symfony/symfony. Acceptable for infrastructure-grade libraries with LTS windows, but it means we own the integration layer and the DSL conventions. Budget roughly 0.1–0.2 FTE of ongoing ownership, concentrated on schema review.
Scaling
Tree processing is CPU-bound and happens once per boot; with ConfigCache the steady-state cost is a filemtime/hash check. Warm the cache at deploy time and the runtime cost is negligible. The risk vector is not throughput — it is cache-coherence across horizontally scaled workers when config sources change outside the tracked resources.
Failure modes
| Failure | Symptom | Mitigation |
|---|---|---|
Stale ConfigCache |
Changed config not applied | Warm cache in deploy pipeline; version the cache key per release |
| Untracked source (env/DB) | Silent wrong values | Explicit invalidation key in the cache namespace |
| Serialized-node incompatibility across upgrade | Boot failure or mangled config | Flush config cache as a deploy step; test upgrades in staging |
| Invalid config in production | InvalidConfigurationException at boot |
Fail fast is correct; ensure the exception renders without dumping secret values to logs |
| Deep-merge surprise | Unexpected override precedence | Lock merge behaviour with fixture-based tests before cutover |
Ramp-up
Two developers need fluency in TreeBuilder/NodeBuilder. Realistic curve: 1–2 days to read, ~1 week to author a non-trivial schema confidently, 2–3 weeks to review others' schemas well. Mitigate with a short internal convention doc (node naming, info() usage, when to use canBeEnabled(), required-vs-default rules — note 8.x now forbids a node having both isRequired() and defaultValue(), so write the convention to be forward-compatible) and one reference schema checked into the repo.
Verdict framing for the decision record: adopt, scoped to one domain, pinned to ^7.4 LTS, with a pre-committed review after one release cycle. The 54.7 composite score reflects split-repo measurement artifacts, not package quality; the binding constraints are the PHP 8.4 floor on 8.x and the absence of env-placeholder handling outside DependencyInjection.
How can I help you explore Laravel packages today?