Managing PHP Extensions
Last updated November 29, 2024
This article is a work in progress, or documents a feature that is not yet released to all users. This article is unlisted. Only those with the link can access it.
Heroku offers a variety of built-in and third-party extensions that you can use in your PHP applications. You can also use optional extensions for your app. This article shows how you can declare optional extensions.
Using Optional Extensions
You can declare any optional extensions you want to use via composer.json
using Composer Platform Packages; simply prefix any of the identifiers in the list of extensions above with “ext-
” in the package name.
For example, to enable the optional bundled extensions bcmath
and GMP
, together with the third-party Memcached, and a version of the third-party MongoDB:
{
"require": {
"ext-bcmath": "*",
"ext-gmp": "*",
"ext-memcached": "*",
"ext-mongodb": "^1.19.0"
}
}
It’s recommended that you use “*
” as the version selector when specifying extensions that are bundled with PHP, as their version numbers can be highly inconsistent (they often report their version as “0”).
Next, ensure that your new requirements are “frozen” to composer.lock
by running:
$ composer update
Finally, don’t forget to git add
and git commit
both files!
If you don’t have the desired extension available locally on your computer, the composer update
step fails because the requirements in composer.json
can’t be satisfied.
If you can’t install the missing extension on your computer using pecl
, brew
, or similar methods (something you absolutely should do in the interest of maintaining dev/prod parity), you can instruct composer to ignore the missing ( “platform”) requirements:
$ composer update --ignore-platform-reqs
You can use the same --ignore-platform-reqs
flag when running composer install
on subsequent dependency installations in new environments, for example on other developers’ computers, where the extension is similarly unavailable.
Upon the next push, Heroku installs and enables the corresponding PHP extensions:
-----> Installing platform packages...
- php (8.2.13)
- ext-bcmath (bundled with php)
- ext-mcrypt (bundled with php)
- ext-mongodb (1.17.0)
- ext-memcached (3.2.0)
- apache (2.4.58)
- nginx (1.24.0)
Any PHP extension required by a dependency of a project pushed to Heroku is installed automatically, as the list of extensions to be installed is read from composer.lock
.
For example, if a project depends on the stripe/stripe-php
PHP SDK for Stripe, the mbstring
extension required by the Stripe SDK will automatically be installed upon deploy, without the ext-mbstring
package having to be listed explicitly in the require
section of your main composer.json
.
Treatment of Extensions “Provided” by Userland Packages
Composer packages like Symfony Polyfills declare a native PHP extension as provide
in their package metadata, and implement the functionality of that extension, partially or completely, purely using PHP code. This causes Composer to consider them a possible replacement of the real, native PHP extension during the resolution of dependencies.
During a build, these “polyfill” declarations are honored by Heroku when installing platform packages, using the exact same rules that Composer applies to the installation of packages.
This means that a requirement of composer.json
, or any dependency, such as ext-mbstring
doesn’t lead to the installation of the native ext-mbstring
extension if the symfony/polyfill-mbstring
package is also present in composer.lock
, as symfony/polyfill-mbstring
declares ext-mbstring
as provide
d:
-----> Installing platform packages...
- php (8.1.2)
- apache (2.4.52)
- composer (2.2.5)
- nginx (1.20.2)
For maximum performance and compatibility, after the initial resolving of platform package dependencies have finished, Heroku’s PHP support then attempts to install native versions of all extensions that userland packages have declared as provide
:
-----> Installing platform packages...
- php (8.1.2)
- apache (2.4.52)
- composer (2.2.5)
- nginx (1.20.2)
NOTICE: detected userland polyfill packages for PHP extensions
NOTICE: now attempting to install native extension packages
Installing extensions provided by symfony/polyfill-mbstring:
- ext-mbstring (bundled with php)
These installation attempts aren’t always successful, like if the extension isn’t available on Heroku, or for the selected PHP version:
-----> Installing platform packages...
- php (8.1.2)
- apache (2.4.52)
- composer (2.2.5)
- nginx (1.20.2)
NOTICE: detected userland polyfill packages for PHP extensions
NOTICE: now attempting to install native extension packages
Installing extensions provided by phpseclib/mcrypt_compat:
NOTICE: no suitable native version of ext-mcrypt available
No changes are made to platform packages that have already resolved. If a native variant isn’t available for the installed PHP version, no downgrade to a PHP version is performed, even if that lower PHP version is permitted by all other dependencies.
This behavior ensures that userland polyfill packages can correctly serve their purpose where necessary, like when acting as a replacement for an extension that is no longer bundled with PHP (for example phpseclib/mcrypt_compat
does for ext-mcrypt
in the example above), while simultaneously ensuring that the native PHP extension in question is installed whenever possible for maximum performance and compatibility.