ThePHPF / pie-design

108 stars 4 forks source link

šŸ„§ PIE (PHP Installer for Extensions) Design

A work in progress design documentation for the "new pecl" iteration.

Basis:

Naming

The current favourite name is pie, the PHP Installer for E xtensions.

Assumptions

Out of initial scope

Whilst we are not going to rule out EVER implementing the following features, we just don't plan to include them in the initial version of this new tool. We will consider these items for future inclusion and improvements:

PIE itself

CLI commands

install {package}{?:version-constraint}{?@stability}

Installs the requested package. The {package} is a Composer package name, and not the extension name. For example, pie install xdebug/xdebug is a valid request. At the time of writing, PIE-compatible packages are listed on https://packagist.org/extensions. Note that previous iterations of this design draft suggested using the extension name as the pie install parameter, but after some discussion it was agreed to use the Composer package name instead.

If version-constraint is given, try to install that version if it matches the allowed versions. Version constraints are resolved using the same format as Composer, along with the minimum stability.

If no version-constraint is given, try to install any compatible latest and stable version.

On Windows this downloads the correct DLL (attached as file to the release tag), if available. Otherwise, it will download the source and compile the extension.

It will then attempt to create a 20-{extension_name}.ini file with extension={extension_name} in the directory returned by php-config --ini-dir, the name that is used is the one from the "name" element in the metadata file.

If the extension is a Zend extension (such as Xdebug), then it will write the line zend_extension={extension_name} instead.

If the extension has a composer.json defined priority, the 20 in the ini file filename will be replaced by that priority.

Each step should run with as few privileges as possible.

build {package}{?:version-constraint}{?@stability}

Same behaviour as install, but only compiles (or downloads if it's Windows).

changelog {?version}

Shows the release notes of the version it was going to install, or from the specific version, if given.

download {package}{?:version-constraint}{?@stability}

Same behaviour as build, but puts the files in a local directory for manual building and installation.

info

Shows the description from the metadata file

list

Shows all available commands

show

Shows all installed extensions available with the PHP version in the path, including their versions. This includes all loaded PHP extensions, and not just PIE-sourced ones.

upgrade {?{package}{?:version-constraint}{?@stability}}

Attempts to upgrade all installed versions to the latest available ones on GitHub (unless their major version has changed). If a specific package is specified, only attempt to upgrade that specific one.

CLI Options

--dry-run

Shows what it is about to do, but doesn't actually install

--force

To attempt to install a version that doesn't match the version constraints from the meta-data, for instance to install an older version than recommended, or when the signature is not available.

--with-php-config=/path/to/php-config

Allows installation of extensions with PHP versions that are not in the path

--{option}{?=value}

All options specified in the configure-options section of the composer.json file can be given, including a value if they take them. For example, for Xdebug you could run pie install xdebug/xdebug --without-xdebug-compression.

Extension maintainer: register a PIE package

Create a composer.json in your repository, and commit it. The following is ONLY AN EXAMPLE, and is not necessarily the real composer.json in Xdebug.

{
    "name": "xdebug/xdebug",
    "type": "php-ext-zend",
    "license": "Xdebug-1.03",
    "description": "Xdebug is an amazing tool...",
    "require": {
        "ext-something": "*",
        "php": ">=7.4.21,<8.4"
    },
    "conflict": {
        "ext-a-conflicting-extension": "*"
    },
    "replace": {
        "ext-xdebug": "*"
    },
    "php-ext": {
        "extension-name": "ext-xdebug",
        "priority": 80,
        "support-zts": false,
        "configure-options": [
            {
                "name": "enable-xdebug-dev",
                "description": "Enable developer flags"
            },
            {
                "name": "without-xdebug-compression",
                "description": "Disable compression through zlib"
            },
            {
                "name": "some-path-to-something",
                "needs-value": true,
                "description": "This should be the path to the thing that is needed."
            }
        ]
    }
}

Submit your repo URL into https://packagist.org/packages/submit. Packagist will read the composer.json as usual.

When releases are made, Packagist will do the usual thing of producing metadata for the release

Notes on the composer.json

Windows binaries

Windows needs pre-built binary DLLs. The expected workflow is that the release is made, then some kind of build takes place (for example, in a GitHub Action, or manually on a compatible build environment), and the .zip file is added to the GitHub release.

The name for the ZIP must follow the following pattern:

The descriptions of these items:

Contents of the Windows ZIP

The pre-built ZIP should contain at minimum a DLL named in the same way as the ZIP itself, for example php_{extension-name}-{tag}-{php-maj/min}-{ts|nts}-{compiler}-{arch}.dll. The .dll will be moved into the PHP extensions path, and renamed, e.g. to C:\path\to\php\ext\php_{extension-name}.dll. The ZIP file may include additional resources, such as:

End user: installing a PIE package

Run:

$ pie install xdebug/xdebug

PIE uses composer/composer library to help resolve dependencies

sequenceDiagram
    participant pie
    participant composer as composer library
    participant packagist as packagist.org

    pie ->> composer : request to install "xdebug/xdebug"
    composer ->> packagist : read metadata to resolve dependencies
    packagist ->> composer : metadata
    composer ->> pie : release information

Once we have the release information, for Linux:

non-Windows installation

The <options> for ./configure come from the $.php-ext.configure-options section in composer.json. These would be specified as part of the build or install commands. Here are some examples:

$ pie install xdebug/xdebug
$ pie install xdebug/xdebug --enable-xdebug-dev
$ pie install xdebug/xdebug --enable-xdebug-dev --without-xdebug-compression
$ pie install xdebug/xdebug --some-path-to-something=/usr/local/lib/something
$ pie install xdebug/xdebug --not-a-defined-configuration-option # this would fail

Windows installation

When installing the extension on Windows, the PHP version invoking PIE would be used for determining the PHP version, library install path and so on, unless --with-php-path=<php-path> is provided. Example:

# Assuming C:\usr\php8.3.4\php.exe is in the $PATH
$ pie install xdebug/xdebug                                     # uses C:\usr\php8.3.4\php.exe (i.e. the version that invoked PIE)
$ pie install xdebug/xdebug --with-php-path="C:\usr\php7.4.33"  # uses C:\usr\php7.4.33\php.exe

In the follow examples, the $PHP_PATH is whichever the path is given above, e.g. C:\usr\php8.3.4 or C:\usr\php7.4.33

Determine the expected name for the Windows ZIP:

Because arch is optional, we have to try therefore, the following file formats, in order:

If the release is found:

Remaining steps:

High level overview

This graph is a high level overview of the key processes of downloading, building and installing both Windows and Linux based PHP extensions:

flowchart LR
    subgraph composer
        dependency-resolver
        release-notes-processing
        composer-json-php-ext-config
    end
    subgraph packagist
        metadata
    end
    subgraph LinuxDownloader
        direction LR
        DownloadZipFromGitHub-->ExtractZip
    end
    subgraph WindowsDownloader
        direction LR
        DetermineCompiler-->DetermineExpectedDllNames
        DetermineZendThreadSafe-->DetermineExpectedDllNames
        DeterminePlatformArch-->DetermineExpectedDllNames
        DetermineExpectedDllNames-->DownloadDllFromGitHub
    end
    subgraph LinuxBuilder
        direction LR
        Phpize-->Configure
        Configure-->Make
    end
    subgraph WindowsBuilder
        direction LR
        no-op
    end
    style InstallWithSuperuser fill:#ffa15e
    subgraph InstallWithSuperuser
        direction LR
        InstallLibrary--windows-->CopyDllToPhp
        InstallLibrary--"linux"-->MakeInstall
        CopyDllToPhp-->ConfigurePhpIni
        MakeInstall-->ConfigurePhpIni
    end

    entrypoint(bin/pie)

    entrypoint--pie changelog-->ChangelogCommand
    ChangelogCommand-->DependencyResolver

    entrypoint--pie info-->InfoCommand
    InfoCommand-->DependencyResolver

    entrypoint--pie download-->DownloadCommand
    DownloadCommand-->Downloader
    Downloader--resolve ext-name-->DependencyResolver
    DependencyResolver-->composer
    composer--request metadata-->packagist
    Downloader--"linux"-->LinuxDownloader
    Downloader--windows-->WindowsDownloader

    entrypoint--pie build-->BuildCommand
    BuildCommand--1-->Downloader
    BuildCommand--2-->Builder
    Builder--"linux"-->LinuxBuilder
    Builder--windows-->WindowsBuilder

    entrypoint--pie upgrade-->InstallCommand
    entrypoint--pie install-->InstallCommand
    InstallCommand--1-->Downloader
    InstallCommand--2-->Builder
    InstallCommand--3-->Installer
    Installer-->InstallWithSuperuser
    InstallWithSuperuser-->ShowReleaseNotes
    ShowReleaseNotes-->Cleanup

    entrypoint--pie show-->ShowCommand
    ShowCommand-->ListInstalledModulesAndVersions

    composer-->ShowReleaseNotes