DEV Community

Andrew Markhai
Andrew Markhai

Posted on

4 1

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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',
];
Enter fullscreen mode Exit fullscreen mode

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',
];
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

Useful commands

To check the current state of recipes:

composer symfony:recipes
Enter fullscreen mode Exit fullscreen mode

If a package has an updated recipe (marked as update available), you can update it with:

composer recipes:update vendor/package
Enter fullscreen mode Exit fullscreen mode

Heroku

Amplify your impact where it matters most — building exceptional apps.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

If you found this post useful, consider leaving a ❤️ or a nice comment!

Got it