skip to content

In Composer, what does the generated vendor/composer/platform_check.php do, and how does the platform-check config setting change it?

level: seniorimportance: nice to knowfreq 18%

answer

  1. runs when autoload.php is included
  2. default php-only
  3. true adds ext-* presence checks
  4. HTTP 500 and RuntimeException
  5. dev packages are skipped

basics

~20 s

platform_check.php runs when vendor/autoload.php is included and stops the request if the running PHP is older than the installed packages require. platform-check defaults to php-only; true also checks that required extensions are loaded; false skips generating the file.

solid answer

~40 s

Since Composer 2.0, generating the autoloader can also write `vendor/composer/platform_check.php`, which `vendor/autoload.php` includes before your code runs. It compares the **running** PHP with the highest minimum PHP version that the non-dev installed packages require. If the check fails, it sends an HTTP 500 header when headers are not yet sent, prints `Composer detected issues in your platform` when `display_errors` is off, and throws a `RuntimeException`. The `platform-check` config key controls it: the default `"php-only"` checks only the PHP version (and 64-bit when required); `true` also checks that each required `ext-*` is loaded, presence only, not version; `false` does not generate the file. It checks lower bounds, not upper bounds, and never `lib-*`. It is a last line of defence: the real guard is `composer check-platform-reqs` in the build.

code

bash · 4 lines
bash
composer config platform-check true
composer dump-autoload -o
# in the deploy, against the real runtime:
composer check-platform-reqs --no-dev || exit 1

go deeper

for a junior

Know that Composer can stop an app at startup when the PHP version is too old for the installed packages.

for a middle

Explain the php-only default, what true adds, what false does, and that only lower bounds are checked.

for a senior

Recognize the 'Composer detected issues in your platform' 500 in production, and put check-platform-reqs in the deploy so it never reaches traffic.

for a principal

Decide where platform guarantees are enforced across build, deploy and runtime, so a mismatch fails in the pipeline rather than in front of users.

## Why a runtime check exists Composer checks platform requirements (`php`, `ext-*`) when it **resolves or installs** packages, on the machine running Composer. But code is often installed in one place and **run** in another: a build container on PHP 8.5, a production host still on 8.3; a CLI with an extension that the FPM pool lacks. Without a runtime check, the first sign is an obscure parse error or an undefined function deep inside a dependency. Composer 2.0 added a cheap guard that runs where the code runs. ## What the generated file does When the autoloader is dumped (by `install`, `update` or `dump-autoload`), Composer computes, from the **non-dev** installed packages: - the **highest lower bound** of all `php` requirements, for example 8.2.0 if one package needs `>=8.1` and another `^8.2`; - whether any package requires `php-64bit`; - with `platform-check` set to `true`, the list of required `ext-*` extensions, skipping any that another package provides or replaces. It writes these as plain PHP comparisons into `vendor/composer/platform_check.php`, which the autoloader bootstrap requires before registering anything else. When a check fails, the file: 1. sends `HTTP/1.1 500 Internal Server Error` if headers have not been sent; 2. if `display_errors` is off, writes `Composer detected issues in your platform:` and the list of issues to STDERR on the CLI, or to the output on the web when headers are not yet sent; 3. throws a `RuntimeException` carrying the same message. Composer's runtime documentation describes the failure as exiting with code 104; the generated code in Composer 2.10 throws the exception instead, so the process ends with an uncaught exception. ## The `platform-check` setting | Value | What is checked | File generated | |---|---|---| | `"php-only"` (default) | PHP version lower bound, 64-bit if required | yes, if there is a requirement | | `true` | the above plus presence of each required `ext-*` | yes | | `false` | nothing | no | ```json { "config": { "platform-check": true } } ``` With `true`, extensions are checked for **presence only**: `extension_loaded()`, not version, for performance. `pcntl` and `readline` are checked only on the CLI, where they belong. ## What it does not check - **Upper bounds.** A package that declares `php <8.4` is not stopped on 8.5 by this file; only the lowest required version is enforced. - **`lib-*` requirements**, never. - **Dev packages**, skipped on purpose: the check is a production safeguard. - Requirements you told Composer to ignore: `dump-autoload --ignore-platform-req=ext-foo` also leaves that requirement out of the check. ## Reading a failure A typical production message, taken from the generated code, reads: `Composer detected issues in your platform: Your Composer dependencies require a PHP version ">= 8.3.0". You are running 8.2.20.` With `platform-check` set to `true`, a missing extension adds a line saying the dependencies require the following PHP extensions to be installed, followed by their names. Both mean the same thing: the code was installed for a different runtime than the one executing it. Typical causes are a CLI and a web server on different PHP versions on the same host, a container image built from one base and run on another, or a PHP downgrade after a rollback. ## Where it fits in a deploy The runtime check turns a confusing failure into a clear one, but a clear HTTP 500 in production is still an outage. Composer's own recommendation is to run `composer check-platform-reqs` against the real runtime during the build or deploy, and abort on a non-zero exit. That command checks the real PHP and extensions, ignores `config.platform`, and fails before traffic reaches the new release. Disable the runtime check (`false`) only with a reason: for example, when the CLI that dumps the autoloader and the web SAPI legitimately differ in a way the check misreads, and the build already verifies the real runtime.

  • Production returns HTTP 500 right after a server's PHP was downgraded, and the log says 'Composer detected issues in your platform'. What happened, and what is the proper fix?
    The runtime platform check found the running PHP older than the highest minimum required by an installed non-dev package, and threw before any application code ran. The fix is to run PHP at or above that version, or to run `composer update` with `config.platform.php` set to the target version so compatible package versions are locked, then verify with `composer check-platform-reqs` before deploying.
  • Why doesn't platform-check true catch a wrong extension version?
    For performance, the generated file only calls `extension_loaded()` for each required extension, so it verifies presence, not version. Version constraints on extensions are enforced when Composer resolves or installs, and by `composer check-platform-reqs`.

saying these in an interview costs you the question

  • platform_check.php verifies extensions by default
  • The runtime platform check also enforces upper bounds such as php <8.4
  • platform-check false makes Composer skip platform checks during install
  • The runtime check replaces the need for check-platform-reqs in the deploy
  • Dev packages' PHP requirements are included in the runtime check