PHP

1. What's in the catalogue

Four published lines — 8.2.33, 8.3.33, 8.4.24, 8.5.9 — pin with @8.2@8.5 (or the full version). As of catalogue 2026.35.22 (2026-08-25):

ToolCapsRequiresNotes
phprwthe CLI; no composer tool in the catalogue — the composer provider brings composer into the environment build itself

No declared-not-published PHP entries. No php-cs-fixer, no phpstan, no pest line in the catalogue either — see Tooling.

2. The five-minute version

kapsl php --version
PHP 8.5.9 (cli) (built: Aug 17 2026 13:28:18) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.5.9, Copyright (c), by Zend Technologies
    with Zend OPcache v8.5.9, Copyright (c), by Zend Technologies

Run a script:

kapsl php app.php

The Composer-based form — composer install against your composer.json, into a scanned environment image, then the script:

kapsl -e @composer:composer.json php app.php

The composer provider is auto-detected by filename: in a directory with a composer.json, kapsl -e . php app.php picks it up. composer.lock is the companion file that makes the build reproducible.

3. The environment story: the vendor-directory redirect

Composer's install model is per-project by convention: composer install writes to ./vendor, autoloaded via __DIR__ . "/vendor/autoload.php" relative to the project root. But the environment build happens in an isolated build context, disconnected from the real working tree mounted at run time — a plain ./vendor from the build would not be where the run container's script looks.

So the provider redirects the install to a fixed, image-wide path: COMPOSER_VENDOR_DIR=/opt/composer/vendor. That mirrors gem's system gem directory and npm's NODE_PATH approach — the dependency set lives in the scanned image at a known absolute path, and the working tree stays clean. The practical consequence is one line in your bootstrap:

require '/opt/composer/vendor/autoload.php';

rather than the conventional __DIR__ . '/vendor/autoload.php'. The php-composer example (Monolog) does exactly this, and its README walks through the reasoning.

PECL extensions (native C) work too: composer install runs with g++/make apt-installed live against the composed PHP image, so ext-… packages that compile at install time succeed inside the environment build.

4. Tooling

No PHP linter or test runner is in the catalogue: no php-cs-fixer, no phpstan, no pest line. Until one publishes, linting is the IDE use case's territory, and test suites run through the environment itself — note the vendor path from the environment build: kapsl -e @composer:composer.json php /opt/composer/vendor/bin/pest — or through a project overlay that grants what the suite needs.

5. Project patterns

  • php-simple — the no-dependency shape: php app.php under kapsl, at the basic levels.
  • php-composer — the full dependency story: composer install into the environment, the /opt/composer/vendor redirect, and PECL/native-extension support.

6. Known limitations

  • The fixed vendor path is a convention break. Your autoloader has to require /opt/composer/vendor/autoload.php; the __DIR__-relative idiom PHP developers reach for by default will not find the dependency. One-line fix, but it shows up in every Composer project.
  • The catalogue side is thin. One tool, one provider, no linters — same honest state as Ruby. Projects that need php-cs-fixer today run it through a local [tools] mapping (see Tool mappings) or wait for a published line.