How Symfony Flex Works
What is it? Symfony Flex is a Composer plugin from Symfony that handles the configuration of installed packages. It adds configuration files, updates .gitignore and .env, registers bundles, and more. For example, when installing PHPUnit, Flex will: Add a default configuration file phpunit.dist.xml Create tests/bootstrap.php Add /phpunit.xml and /.phpunit.cache/ to .gitignore Create .env.test with environment variables for tests Where does Flex get its instructions? In Flex terminology, these instructions are called recipes. They are stored in a GitHub repository. You can check out the recipes for the current version of PHPUnit here. Is this a complete list of recipes? No. The symfony/recipes repository is maintained by Symfony itself, while community-contributed recipes are stored in a separate repository. Anyone can contribute there. To allow Flex to apply community recipes, you need to add the following to composer.json: "extra": { "symfony": { "allow-contrib": true } } How does it work under the hood? When initialized, Flex loads links to all current recipes in Downloader::index. By default, it fetches them from two repositories described above: private const DEFAULT_ENDPOINTS = [ 'https://raw.githubusercontent.com/symfony/recipes/flex/main/index.json', 'https://raw.githubusercontent.com/symfony/recipes-contrib/flex/main/index.json', ]; However, you can add your own repositories by specifying them in composer.json under extra.symfony.endpoint. Since Flex is a Composer plugin, it listens for various events: $events = [ PackageEvents::POST_PACKAGE_UPDATE => 'enableThanksReminder', PackageEvents::POST_PACKAGE_INSTALL => 'recordFlexInstall', PackageEvents::POST_PACKAGE_UNINSTALL => 'record', InstallerEvents::PRE_OPERATIONS_EXEC => 'recordOperations', PluginEvents::PRE_POOL_CREATE => 'truncatePackages', ScriptEvents::POST_CREATE_PROJECT_CMD => 'configureProject', ScriptEvents::POST_INSTALL_CMD => 'install', ScriptEvents::PRE_UPDATE_CMD => 'configureInstaller', ScriptEvents::POST_UPDATE_CMD => 'update', 'auto-scripts' => 'executeAutoScripts', ]; During install and update commands, the Flex::install() method runs. It checks whether there are recipes for the current package by looking at both the package name and its version. Recipe versioning is handled using subdirectories inside the package directory. For example, looking at PHPUnit’s recipes, you’ll see folders for versions 10.0, 9.6, 9.3, and 4.7. Flex selects the highest available recipe version that is less than or equal to the installed package version. For example: PHPUnit 12 will use the 10.0 recipe PHPUnit 9.9 will use the 9.6 recipe If a recipe is found, the instructions in its manifest are executed using various configurators. The console will display a message like: Symfony operations: 1 recipe (13c8b05b36f03f13c596b688cb97d0da) - Configuring phpunit/phpunit (>=10.0): From github.com/symfony/recipes:main Some files have been created and/or updated to configure your new packages. Useful commands To check the current state of recipes: composer symfony:recipes If a package has an updated recipe (marked as update available), you can update it with: composer recipes:update vendor/package

What is it?
Symfony Flex is a Composer plugin from Symfony that handles the configuration of installed packages. It adds configuration files, updates .gitignore and .env, registers bundles, and more.
For example, when installing PHPUnit, Flex will:
- Add a default configuration file phpunit.dist.xml
- Create tests/bootstrap.php
- Add /phpunit.xml and /.phpunit.cache/ to .gitignore
- Create .env.test with environment variables for tests
Where does Flex get its instructions?
In Flex terminology, these instructions are called recipes. They are stored in a GitHub repository. You can check out the recipes for the current version of PHPUnit here.
Is this a complete list of recipes?
No. The symfony/recipes repository is maintained by Symfony itself, while community-contributed recipes are stored in a separate repository. Anyone can contribute there.
To allow Flex to apply community recipes, you need to add the following to composer.json:
"extra": {
"symfony": {
"allow-contrib": true
}
}
How does it work under the hood?
When initialized, Flex loads links to all current recipes in Downloader::index. By default, it fetches them from two repositories described above:
private const DEFAULT_ENDPOINTS = [
'https://raw.githubusercontent.com/symfony/recipes/flex/main/index.json',
'https://raw.githubusercontent.com/symfony/recipes-contrib/flex/main/index.json',
];
However, you can add your own repositories by specifying them in composer.json under extra.symfony.endpoint.
Since Flex is a Composer plugin, it listens for various events:
$events = [
PackageEvents::POST_PACKAGE_UPDATE => 'enableThanksReminder',
PackageEvents::POST_PACKAGE_INSTALL => 'recordFlexInstall',
PackageEvents::POST_PACKAGE_UNINSTALL => 'record',
InstallerEvents::PRE_OPERATIONS_EXEC => 'recordOperations',
PluginEvents::PRE_POOL_CREATE => 'truncatePackages',
ScriptEvents::POST_CREATE_PROJECT_CMD => 'configureProject',
ScriptEvents::POST_INSTALL_CMD => 'install',
ScriptEvents::PRE_UPDATE_CMD => 'configureInstaller',
ScriptEvents::POST_UPDATE_CMD => 'update',
'auto-scripts' => 'executeAutoScripts',
];
During install and update commands, the Flex::install() method runs. It checks whether there are recipes for the current package by looking at both the package name and its version.
Recipe versioning is handled using subdirectories inside the package directory. For example, looking at PHPUnit’s recipes, you’ll see folders for versions 10.0, 9.6, 9.3, and 4.7. Flex selects the highest available recipe version that is less than or equal to the installed package version.
For example:
- PHPUnit 12 will use the 10.0 recipe
- PHPUnit 9.9 will use the 9.6 recipe
If a recipe is found, the instructions in its manifest are executed using various configurators. The console will display a message like:
Symfony operations: 1 recipe (13c8b05b36f03f13c596b688cb97d0da)
- Configuring phpunit/phpunit (>=10.0): From github.com/symfony/recipes:main
Some files have been created and/or updated to configure your new packages.
Useful commands
To check the current state of recipes:
composer symfony:recipes
If a package has an updated recipe (marked as update available), you can update it with:
composer recipes:update vendor/package