Ayesh Karunaratne: Full-time traveler, freelance software architect and security researcher
Thu, 2019-11-14 18:37
A Polyfill is a package that provides functionality that could be missing in a given system. They seamlessly add the necessary functionality if it is not available already in your server environment.
One of the most popular polyfills we have in PHP/Composer world is
symfony/polyfill-mbstring. Without any surprises, this package provides the functionality provided by the
mbstring extension if it not already available on the server. At the time of writing, there are over 200 dependents for this package, and over whopping 177 million installations of it.
mbstring extension is a popular extension that is available in pretty much every sane PHP setup, but package that require this polyfill makes all projects that have this polyfill require some minimal PHP code, even if you already have
mbstring extension already enabled in its PHP setup. While PHP's opcache should help minimize the impact, this is still code that does not help if you have a fully compliant PHP environment.
Composer does not yet provide functionality that makes it possible for polyfill providers to specify if the package can be skipped if the PHP environment already has the necessary functionality.
This post is about a workaround that you can override and prevent the installation of polyfills if you are certain that the target server that the application runs on already has all the necessary functionality the polyfills are about to provide.
This has been discussed quite extensively in Composer, Symfony Polyfills, and even at Debian dependencies:
- Composer#751: Support for alternative requirement
- Composer#6041: Include a package based upon the version of PHP running?
- Composer#6525: Require one package OR another
- Symfony/polyfill#54: Add proper provides rules for mbstring and iconv polyfills
This is complicated because not every PHP application uses Composer at deployment, and the target server may need the polyfill although the dev environment fulfilled the polyfill functionality natively.
While working on a private project that I'm planning to deploy on a server that I have full control over, I was a bit annoyed to have Symfony polyfills automatically installed because another package that I have added as a dependency required the polyfill.
Composer has a configuration directive called
replace, that a package can declare a list of other packages it replaces. Composer will happily skip/remove the packages that are being replaced when you require the package. You can specify a list of polyfills that you wish to not install, in either the root
composer.json file, or as a local package that in turn replaces the said package.
The snippet above, when added to your root
composer.json file will prevent the three mentioned packages from being installed. This will not check if the underline functionality is already available, which you can ensure by requiring the necessary server environment from the same
After adding the
replace directives, make sure to run
composer update, so composer can remove the packages if they are already installed.
If you would like to check the extensions installed in your PHP setup, run
composer show --platform, and it will show all extensions and other information such as underline libraries, thread-safety, etc that you can use in your
composer.json file as replacements.
Replacements as a separate package
If putting multiple
replace directives in your root
composer.json file is not your thing, you can create a separate package with its own
composer.json file that
requires the extensions/versions and their counterpart
replace packages. You do not have to publish this package to Packagist where composer local packages come handy:
Above snippet, when used in a root
composer.json file, will tell composer to look for a package in the
app/packages directory, where you can create a separate
composer.json file that declares a new package with its own (and likely more strict) requirements for the platform and packages you'd like to skip polyfills for.