0073: PHP version and extensions from Composer
On this page
Status: accepted
Context
PHP projects say what they need in composer.json: the PHP versions in require.php, and the extensions as ext-* packages in composer.json and composer.lock. Short Leash ignored both. A site ran on Short Leash's default PHP unless someone picked a version for it, and a missing extension showed up only as an error from the app. Short Leash's PHP builds don't all carry the same extensions: a version installed before Short Leash built its own (0017) can be a static-php-cli build (0006) with fewer, and some extensions don't exist on one system or left PHP in a later version.
Decision
The PHP version. Engine::php_choice picks a site's version in this order:
- The version picked for the site (its pin) wins.
- Otherwise Short Leash's default, when
composer.jsonhas no readablerequire.phporrequire.phpallows the default. - Otherwise the newest installed, released version that
require.phpallows (runtimes::php::version_from_composer). - Otherwise the default.
- A version from the project's
leash.tomlorleash.local.tomlcomes between 1 and 2 (0078). composer.jsonmoves a site only when the default doesn't fit it, so installing a newer PHP or upgrading Short Leash doesn't move every^8.2site off the version it ran on. We chose this over "the newest installed version that fits", which would.- Only installed versions are candidates. Released ones (
runtimes::php::SUPPORTED) come first; an installed preview is picked only when no installed released version fits (0083).composer.jsonnever makes Short Leash download anything. - The site's Needs a look covers the three ways the version can miss
require.php. A pinned site whose pin doesn't fit keeps its pin, and the check says so and names the newest version Short Leash offers that fits, to pick in the site's settings. An unpinned site that no installed version fits stays on the default, and the check names the version to install on the Runtimes screen. When no version Short Leash offers fits, the check says Short Leash doesn't offer one. - Constraints are read as Composer reads them, with Short Leash's own small parser and no new dependency:
||alternatives,,or spaces for conditions that all hold, hyphen ranges,^,~, wildcards and comparison operators. A bare version means exactly that version. A missing or brokencomposer.json, or a constraint that can't be read ("latest"), has no opinion: the site keeps the default and no check is raised. - The choice runs on every PHP request, so the composer pick is cached per site folder (
composer_php), reused while it's younger thandefaults::COMPOSER_PHP_CACHEandcomposer.json's modified time hasn't changed. The cache is cleared when Short Leash's settings change, such as the default PHP, and when a PHP version is installed. - The site's Overview says where the version comes from: picked for this site, from
leash.local.tomlorleash.toml(0078), fromcomposer.json, or Short Leash's default.
The extensions. sites::extensions::required reads the ext-* names a project needs from composer.json (require, require-dev) and composer.lock (the require of each package in packages and packages-dev, and the lock's top-level platform and platform-dev). It drops any name that composer.json's config.platform, provide or replace, the lock's platform-overrides, or a locked package's provide or replace covers, so a polyfill that provides ext-mbstring counts as having it.
- They're checked against what the installed PHP loads:
php -mwith Short Leash's ini files (PHPRC,PHP_INI_SCAN_DIR), so extensions you load from your ownconf.dcount. The result is cached per version. When that version isn't installed, or the run fails, Short Leash falls back to the list in code (runtimes::php::available_extensions). - Each missing extension gets one fix (
ExtensionFix):- turn it on (Xdebug, when it's off for that version);
- it doesn't exist for PHP on this system (such as
pcntlon Windows); - update this PHP version on the Runtimes screen, when the installed build lacks it but Short Leash's current build of the same version has it (an install from before Short Leash built its own, which an update or Short Leash's rebuild check replaces);
- it isn't in Short Leash's build of this version: then, and only then, Short Leash looks for another version, naming the newest released one other than the site's own that has it (by what an installed one loads, else the list in code), or saying none does yet.
- The same report appears in the site's Needs a look, at
GET /v1/sites/{name}/extensionsand in the MCP toolcheck_extensions, which chat may use as a read (0062). None of them has a fix button: Short Leash doesn't change the project or install extensions for it. - Octane sites on FrankenPHP skip the check, because FrankenPHP runs the PHP it was built with.
Consequences
- Each PHP request costs one
statofcomposer.jsonfor the cached lookup. A change torequire.phpapplies on the next request; processes Short Leash runs for the site pick it up when they restart. - A site can change PHP version without anyone touching Short Leash, when its
composer.jsonchanges. The Overview's source wording says why. - Constraints behave exactly as they do for
composer install, surprises included:<=8.3means<=8.3.0, so it excludes 8.3.x. php -mstarts PHP once per version, and only for the Overview and the extension check, never on the request path.- The list in code has to follow ShortLeash/runtimes'
scripts/php-extensions.txt; its doc comment says so, and a test checks it against a real build where one is installed. Its Windows half grows with Short Leash's Windows extensions. - Short Leash doesn't install a PHP version or an extension because
composer.jsonasks for one. The person decides, from what Needs a look says.