Search code examples
phpcomposer-phpphp-extension

Why does Composer (PHP) allow me to specify "ext-json" but not other extensions like "ext-gmp" during "composer require"?


When I use Composer to run composer require ext-gmp, without a composer.json file or composer.lock file in the current directory, I get the following error:

Could not find a matching version of package ext-gmp. Check the package spelling,
your version constraint and that the package is available in a stability which
matches your minimum-stability (stable).

This makes sense to me based on my understanding of PHP extensions and Composer so far. You wouldn't use Composer to install extensions. They're installed globally on the system. Composer is only for PHP packages.

However, I noticed that if I repeat the previous steps and instead run composer require ext-json, I get the following output:

./composer.json has been created
Running composer update ext-json
Loading composer repositories with package information
Updating dependencies
Nothing to modify in lock file
Writing lock file
Installing dependencies from lock file (including require-dev)
Nothing to install, update or remove
Generating autoload files
No installed packages - skipping audit.

And at that point, I have a composer.json file that looks like this:

{
    "require": {
        "ext-json": "*"
    }
}

And I have a composer.lock file that looks like this:

{
    "_readme": [
        "This file locks the dependencies of your project to a known state",
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
        "This file is @generated automatically"
    ],
    "content-hash": "93d2b466348eca908189088d43f9fb1a",
    "packages": [],
    "packages-dev": [],
    "aliases": [],
    "minimum-stability": "stable",
    "stability-flags": [],
    "prefer-stable": false,
    "prefer-lowest": false,
    "platform": {
        "ext-json": "*"
    },
    "platform-dev": [],
    "plugin-api-version": "2.3.0"
}

I find this strange. It looks like Composer created entries in composer.json and composer.lock that are correct for when you want to indicate that your project requires the ext-json installed on the system. So far, I haven't experimented with other extensions besides ext-json and ext-gmp to uncover more extensions that Composer will allow you to notate in composer.json and composer.lock as system requirements, like for ext-json.

What's special about ext-json that makes it work this way with Composer?

When I searched for whether the question was already answered, I found this question, where the asker wanted to know why Composer could not install an extension for them. This is different from my situation. Here, I understand that Composer is not the tool one would use to install a PHP extension, and therefore I understand what the error message from composer require ext-gmp is telling me. But I want to know why Composer makes an exception for ext-json and behaves differently for it.

System info:

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.2 LTS
Release:        22.04
Codename:       jammy
$ php --version
PHP 8.1.2-1ubuntu2.11 (cli) (built: Feb 22 2023 22:56:18) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.2-1ubuntu2.11, Copyright (c), by Zend Technologies
$ composer --version
Composer version 2.5.1 2022-12-22 15:33:54

Solution

  • It's because ext-json is already installed.

    Composer supports things it calls "platform packages".

    These are things for which Composer can check for the thing, but not actually run the install if the thing isn't already available.

    There are four main things in this:

    • a particular build of PHP itself (which includes version constraints, but also architecture complaints so you could, if you chose to, require php-64bit)
    • hhvm
    • extensions, which are specified by requiring a package of the format "ext-name"
    • libraries that aren't packages, which is specified by requiring a package of the format "lib-libname"... where the valid entries for libname are curl, iconv, icu, libxml, openssl, pcre, uuid, xsl.