symfony/config
Symfony Config component helps you find, load, merge, auto-fill, and validate configuration from many sources (YAML, XML, INI, database). Designed for building robust, flexible config systems in PHP applications.
Installation:
composer require symfony/config
Laravel already bundles this component, so no additional installation is needed.
First Use Case: Load and merge configuration from multiple sources (e.g., YAML files, environment variables, or databases) into a unified structure. Example:
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\DelegatingLoader;
// Define a loader for YAML files
$locator = new FileLocator(__DIR__.'/config');
$yamlLoader = new YamlFileLoader($locator, 'yaml');
// Resolve and delegate loaders
$resolver = new LoaderResolver([$yamlLoader]);
$loader = new DelegatingLoader($resolver);
// Load configuration
$config = $loader->load('app.yaml');
Where to Look First:
config/ directory and config/app.php for built-in usage patterns.vendor/symfony/config/Resources/doc/index.md for advanced features.Loading Configuration:
Use Laravel’s built-in Config facade or Symfony’s LoaderInterface to load configurations from files (YAML, XML, PHP arrays) or external sources.
// Laravel example (uses Symfony Config under the hood)
$config = config('app.timezone');
Merging Configurations:
Combine configurations from multiple environments (e.g., config/app.php, config/env.php, or environment variables).
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class AppConfig implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('app');
$rootNode = $treeBuilder->getRootNode();
$rootNode
->children()
->scalarNode('timezone')->defaultValue('UTC')->end()
->arrayNode('services')
->useAttributeAsKey('name')
->arrayPrototype()
->children()
->scalarNode('class')->isRequired()->end()
->end()
->end()
->end()
->end();
return $treeBuilder;
}
}
Validation:
Define strict validation rules for configuration nodes using TreeBuilder and NodeDefinition.
$rootNode
->children()
->scalarNode('debug')
->validate()
->ifTrue(function ($value) { return $value !== true && $value !== false; })
->thenInvalid('The debug setting must be a boolean.')
->end()
->end()
->end();
Environment-Specific Configs:
Load environment-specific configurations (e.g., .env variables or config/local.php).
$loader->load('env.php'); // Load environment-specific overrides
Caching Configurations: Cache compiled configurations to avoid reloading on every request.
use Symfony\Component\Config\ConfigCache;
$cache = new ConfigCache(__DIR__.'/var/config/appConfig.php', debug: false);
$config = $loader->load('app.yaml', 'yaml', $cache);
Integration with Laravel:
config() helper or Config facade for accessing configurations.ConfigRepository to add custom loaders or validation logic.config/caching.php or environment variables.Custom Loaders: Create custom loaders for non-standard sources (e.g., databases, APIs).
use Symfony\Component\Config\Loader\Loader;
class DatabaseLoader extends Loader
{
public function load($resource, $type = null)
{
// Fetch config from database and return as array
return $this->configFromDatabase();
}
public function supports($resource, $type = null)
{
return 'database' === $type;
}
}
Dynamic Configuration:
Use ConfigCache to dynamically reload configurations without restarting the app.
$cache->write($config, $timestamp);
Dependency Injection: Bind configuration objects to Laravel’s service container for type-hinted access.
$this->app->bind('config.app', function () {
return new AppConfig();
});
Testing: Mock or stub configuration loaders in tests to isolate logic.
$this->app->instance(LoaderInterface::class, $mockLoader);
Circular References:
Avoid circular references in configuration files (e.g., app.yaml including services.yaml, which includes app.yaml). Use LoaderResolver to manage dependencies explicitly.
Case Sensitivity:
Configuration keys are case-sensitive. Ensure consistency in YAML/array keys (e.g., app.timezone vs. app.Timezone).
Default Values vs. Required Fields:
Symfony 8+ enforces that nodes cannot have both defaultValue() and isRequired(). Choose one or the other.
// Wrong (throws error in Symfony 8+)
$node->defaultValue('UTC')->isRequired();
// Correct
$node->defaultValue('UTC'); // Optional
// OR
$node->isRequired(); // No default
Deprecated Features:
ConfigBuilder) is deprecated in Symfony 8+. Use TreeBuilder instead.ExtensionInterface::getXsdValidationBasePath() and getNamespace() are deprecated. Use getNamespace() in TreeBuilder for XML validation.Resource Tracking:
Ensure custom loaders implement supports() and load() correctly. Missing resource tracking can cause issues with caching or validation.
Enum Support:
Symfony 7.3+ supports enums in configuration. Use EnumNode for type safety:
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
$rootNode->enumNode('status')
->values(['active', 'inactive', 'pending'])
->defaultValue('active');
Glob Patterns:
Be cautious with GlobResource patterns. Trailing slashes in paths may not work as expected (fixed in Symfony 7.3+).
Enable Debug Mode:
Set debug: true in ConfigCache to see raw configuration data:
$cache = new ConfigCache(__DIR__.'/var/config/appConfig.php', debug: true);
Inspect Loaded Config: Dump the loaded configuration to identify issues:
dd($loader->load('app.yaml'));
Validation Errors:
Symfony throws InvalidConfigurationException for invalid configs. Check the error message for the exact issue (e.g., missing keys, invalid types).
Cache Issues: Clear cached configurations when making changes:
php artisan config:clear
Or manually delete bootstrap/cache/config.php.
Loader Order:
Use LoaderResolver to control the order of loaders. Later loaders can override earlier ones.
Custom Node Types:
Extend NodeDefinition to create custom validation logic or data transformation:
class CustomNodeDefinition extends NodeDefinition
{
public function __construct(string $name, callable $normalizer = null)
{
parent::__construct($name);
$this->normalizer = $normalizer;
}
}
Post-Processing:
Use NodeDefinition::beforeNormalization() or afterNormalization() to modify values:
$node->beforeNormalization()
->ifString()
->then(function ($value) { return strtoupper($value); });
Environment-Aware Configs: Dynamically load configurations based on the environment:
$loader->load(sprintf('config/%s.yaml', app()->environment()));
Schema Validation:
Validate configurations against a schema (e.g., JSON Schema) using Symfony\Component\Config\Definition\Validator\ValidatorBuilder.
Integration with Laravel Packages:
Publish custom configurations using Laravel’s publishes in service-provider.php:
$this->publishes([
__DIR__.'/config/app.php' => config_path('packages/my-package/app.php'),
]);
How can I help you explore Laravel packages today?