diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index 91ccbe6..fb1d4a1 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -7,3 +7,26 @@ on:
jobs:
ci:
uses: laminas/workflow-continuous-integration/.github/workflows/continuous-integration.yml@1.x
+
+ windows-tests:
+ runs-on: ${{ matrix.operating-system }}
+ strategy:
+ matrix:
+ operating-system:
+ - windows-latest
+ php-versions:
+ - '8.1'
+ - '8.2'
+ - '8.3'
+ steps:
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-versions }}
+ extensions: intl, sodium, zip
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - uses: actions/checkout@v4
+ - uses: ramsey/composer-install@v3
+ - name: Run PHPUnit
+ run: vendor/bin/phpunit
diff --git a/.gitignore b/.gitignore
index 1d5a365..21c8f73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
/vendor/
/psalm.xml
/.phpunit.cache/
+/.phpunit.result.cache
/.phpcs-cache
diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php
new file mode 100644
index 0000000..05552cf
--- /dev/null
+++ b/.phpstorm.meta.php
@@ -0,0 +1,9 @@
+ '@',
+ ])
+ );
+}
diff --git a/README.md b/README.md
index 19b1baa..e9c4a00 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,51 @@
# 🥧 PIE (PHP Installer for Extensions)
-(to be completed...)
+You will need PHP 8.1 or newer to run PIE, but PIE can install an extension to any installed PHP version.
## Installing
## Usage
+You can download an extension ready to be built or installed using the `download` command. For example, to download the
+`example_pie_extension` extension, you would run:
+
+```shell
+$ bin/pie download asgrim/example-pie-extension
+You are running PHP 8.3.7
+Target PHP installation: 8.3.7 nts, on Linux/OSX/etc x86_64 (from /usr/bin/php8.3)
+Found package: asgrim/example-pie-extension:1.0.1 which provides ext-example_pie_extension
+Extracted asgrim/example-pie-extension:1.0.1 source to: /tmp/pie_downloader_6645f07a28bec9.66045489/asgrim-example-pie-extension-769f906
+$
+```
+
+If you are trying to install an extension for a different version of PHP, you may specify this on non-Windows systems
+with the `--with-php-config` option like:
+
+```shell
+bin/pie download --with-php-config=/usr/bin/php-config7.2 my/extension
+```
+
+On all platforms, you may provide a path to the `php` executable itself using the `--with-php-path` option. This is an
+example on Windows where PHP 8.1 is used to run PIE, but we want to download the extension for PHP 8.3:
+
+```shell
+> C:\php-8.1.7\php.exe bin/pie download --with-php-path=C:\php-8.3.6\php.exe asgrim/example-pie-extension
+You are running PHP 8.1.7
+Target PHP installation: 8.3.6 ts, vs16, on Windows x86_64 (from C:\php-8.3.6\php.exe)
+Found package: asgrim/example-pie-extension:1.0.1 which provides ext-example_pie_extension
+Extracted asgrim/example-pie-extension:1.0.1 source to: C:\path\to\temp\pie_downloader_66547faa7db3d7.06129230
+```
+
+And this is a very similar example (using PHP 8.1 to run PIE to download a PHP 8.3 extension) on a non-Windows platform:
+
+```shell
+$ php8.1 bin/pie download --with-php-path=/usr/bin/php8.3 asgrim/example-pie-extension
+You are running PHP 8.1.28
+Target PHP installation: 8.3.7 nts, on Linux/OSX/etc x86_64 (from /usr/bin/php8.3)
+Found package: asgrim/example-pie-extension:1.0.1 which provides ext-example_pie_extension
+Extracted asgrim/example-pie-extension:1.0.1 source to: /tmp/pie_downloader_66547da1e6c685.25242810/asgrim-example-pie-extension-769f906
+```
+
## Developing
### Testing
diff --git a/bin/pie b/bin/pie
index 6d3eb3e..cbff1ac 100755
--- a/bin/pie
+++ b/bin/pie
@@ -3,14 +3,24 @@
declare(strict_types=1);
-use Php\Pie\Command;
+namespace Php\Pie;
+
+use Php\Pie\Command\DownloadCommand;
use Symfony\Component\Console\Application;
+use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
/** @psalm-suppress UnresolvableInclude */
include $_composer_autoload_path ?? __DIR__ . '/../vendor/autoload.php';
+$container = Container::factory();
+
$application = new Application('pie', 'dev-main');
-$application->addCommands([
- new Command\DownloadCommand(),
-]);
-$application->run();
+$application->setCommandLoader(new ContainerCommandLoader(
+ $container,
+ [
+ 'download' => DownloadCommand::class,
+ ]
+));
+$application->run($container->get(InputInterface::class), $container->get(OutputInterface::class));
diff --git a/composer.json b/composer.json
index 9603cee..7f50029 100644
--- a/composer.json
+++ b/composer.json
@@ -11,6 +11,7 @@
},
"autoload-dev": {
"psr-4": {
+ "Php\\PieIntegrationTest\\": "test/integration/",
"Php\\PieUnitTest\\": "test/unit/"
}
},
@@ -26,11 +27,22 @@
],
"require": {
"php": "8.1.*||8.2.*||8.3.*",
- "symfony/console": "^6.4"
+ "ext-zip": "*",
+ "azjezz/psl": "^2.9",
+ "composer/composer": "dev-main@dev",
+ "guzzlehttp/guzzle": "^7.8",
+ "guzzlehttp/psr7": "^2.6",
+ "illuminate/container": "^10.47",
+ "psr/http-message": "^2.0",
+ "symfony/console": "^6.4",
+ "symfony/process": "^6.4",
+ "webmozart/assert": "^1.11"
},
"require-dev": {
"doctrine/coding-standard": "^12.0",
+ "php-standard-library/psalm-plugin": "^2.3",
"phpunit/phpunit": "^10.5",
+ "psalm/plugin-phpunit": "^0.18.4",
"vimeo/psalm": "^5.22"
},
"config": {
diff --git a/composer.lock b/composer.lock
index 3d9e038..3437487 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,34 +4,2283 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "84a9164241781879ce2a3156ecb3fd35",
+ "content-hash": "527f1f5b3881b9c5f1def2bc223a385d",
"packages": [
+ {
+ "name": "azjezz/psl",
+ "version": "2.9.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/azjezz/psl.git",
+ "reference": "1ade4f1a99fe07a8e06f8dee596609aa07585422"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/azjezz/psl/zipball/1ade4f1a99fe07a8e06f8dee596609aa07585422",
+ "reference": "1ade4f1a99fe07a8e06f8dee596609aa07585422",
+ "shasum": ""
+ },
+ "require": {
+ "ext-bcmath": "*",
+ "ext-intl": "*",
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "ext-sodium": "*",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0",
+ "revolt/event-loop": "^1.0.1"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.22.0",
+ "php-coveralls/php-coveralls": "^2.6.0",
+ "php-standard-library/psalm-plugin": "^2.2.1",
+ "phpbench/phpbench": "^1.2.14",
+ "phpunit/phpunit": "^9.6.10",
+ "roave/infection-static-analysis-plugin": "^1.32.0",
+ "squizlabs/php_codesniffer": "^3.7.2",
+ "vimeo/psalm": "^5.13.1"
+ },
+ "suggest": {
+ "php-standard-library/psalm-plugin": "Psalm integration"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "hhvm/hsl",
+ "url": "https://github.com/hhvm/hsl"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/bootstrap.php"
+ ],
+ "psr-4": {
+ "Psl\\": "src/Psl"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "azjezz",
+ "email": "azjezz@protonmail.com"
+ }
+ ],
+ "description": "PHP Standard Library",
+ "support": {
+ "issues": "https://github.com/azjezz/psl/issues",
+ "source": "https://github.com/azjezz/psl/tree/2.9.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/azjezz",
+ "type": "github"
+ }
+ ],
+ "time": "2024-04-05T05:18:37+00:00"
+ },
+ {
+ "name": "composer/ca-bundle",
+ "version": "1.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/ca-bundle.git",
+ "reference": "0c5ccfcfea312b5c5a190a21ac5cef93f74baf99"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/ca-bundle/zipball/0c5ccfcfea312b5c5a190a21ac5cef93f74baf99",
+ "reference": "0c5ccfcfea312b5c5a190a21ac5cef93f74baf99",
+ "shasum": ""
+ },
+ "require": {
+ "ext-openssl": "*",
+ "ext-pcre": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.10",
+ "psr/log": "^1.0",
+ "symfony/phpunit-bridge": "^4.2 || ^5",
+ "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\CaBundle\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
+ "keywords": [
+ "cabundle",
+ "cacert",
+ "certificate",
+ "ssl",
+ "tls"
+ ],
+ "support": {
+ "irc": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/ca-bundle/issues",
+ "source": "https://github.com/composer/ca-bundle/tree/1.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-03-15T14:00:32+00:00"
+ },
+ {
+ "name": "composer/class-map-generator",
+ "version": "1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/class-map-generator.git",
+ "reference": "8286a62d243312ed99b3eee20d5005c961adb311"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/class-map-generator/zipball/8286a62d243312ed99b3eee20d5005c961adb311",
+ "reference": "8286a62d243312ed99b3eee20d5005c961adb311",
+ "shasum": ""
+ },
+ "require": {
+ "composer/pcre": "^2.1 || ^3.1",
+ "php": "^7.2 || ^8.0",
+ "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.6",
+ "phpstan/phpstan-deprecation-rules": "^1",
+ "phpstan/phpstan-phpunit": "^1",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "symfony/filesystem": "^5.4 || ^6",
+ "symfony/phpunit-bridge": "^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\ClassMapGenerator\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be"
+ }
+ ],
+ "description": "Utilities to scan PHP code and generate class maps.",
+ "keywords": [
+ "classmap"
+ ],
+ "support": {
+ "issues": "https://github.com/composer/class-map-generator/issues",
+ "source": "https://github.com/composer/class-map-generator/tree/1.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-03-15T12:53:41+00:00"
+ },
+ {
+ "name": "composer/composer",
+ "version": "dev-main",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/composer.git",
+ "reference": "877f1b150ffdc7b894e392ec05aa4a130fbb40ae"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/composer/zipball/877f1b150ffdc7b894e392ec05aa4a130fbb40ae",
+ "reference": "877f1b150ffdc7b894e392ec05aa4a130fbb40ae",
+ "shasum": ""
+ },
+ "require": {
+ "composer/ca-bundle": "^1.0",
+ "composer/class-map-generator": "^1.0",
+ "composer/metadata-minifier": "^1.0",
+ "composer/pcre": "^2.1 || ^3.1",
+ "composer/semver": "^3.2.5",
+ "composer/spdx-licenses": "^1.5.7",
+ "composer/xdebug-handler": "^2.0.2 || ^3.0.3",
+ "justinrainbow/json-schema": "^5.2.11",
+ "php": "^7.2.5 || ^8.0",
+ "psr/log": "^1.0 || ^2.0 || ^3.0",
+ "react/promise": "^2.8 || ^3",
+ "seld/jsonlint": "^1.4",
+ "seld/phar-utils": "^1.2",
+ "seld/signal-handler": "^2.0",
+ "symfony/console": "^5.4.11 || ^6.0.11 || ^7",
+ "symfony/filesystem": "^5.4 || ^6.0 || ^7",
+ "symfony/finder": "^5.4 || ^6.0 || ^7",
+ "symfony/polyfill-php73": "^1.24",
+ "symfony/polyfill-php80": "^1.24",
+ "symfony/polyfill-php81": "^1.24",
+ "symfony/process": "^5.4 || ^6.0 || ^7"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.9.3",
+ "phpstan/phpstan-deprecation-rules": "^1",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1",
+ "phpstan/phpstan-symfony": "^1.2.10",
+ "symfony/phpunit-bridge": "^6.4.1 || ^7.0.1"
+ },
+ "suggest": {
+ "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
+ "ext-zip": "Enabling the zip extension allows you to unzip archives",
+ "ext-zlib": "Allow gzip compression of HTTP requests"
+ },
+ "default-branch": true,
+ "bin": [
+ "bin/composer"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.7-dev"
+ },
+ "phpstan": {
+ "includes": [
+ "phpstan/rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\": "src/Composer/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "https://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be"
+ }
+ ],
+ "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.",
+ "homepage": "https://getcomposer.org/",
+ "keywords": [
+ "autoload",
+ "dependency",
+ "package"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/composer/issues",
+ "security": "https://github.com/composer/composer/security/policy",
+ "source": "https://github.com/composer/composer/tree/main"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-04-29T09:03:27+00:00"
+ },
+ {
+ "name": "composer/metadata-minifier",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/metadata-minifier.git",
+ "reference": "c549d23829536f0d0e984aaabbf02af91f443207"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207",
+ "reference": "c549d23829536f0d0e984aaabbf02af91f443207",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2",
+ "phpstan/phpstan": "^0.12.55",
+ "symfony/phpunit-bridge": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\MetadataMinifier\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "Small utility library that handles metadata minification and expansion.",
+ "keywords": [
+ "composer",
+ "compression"
+ ],
+ "support": {
+ "issues": "https://github.com/composer/metadata-minifier/issues",
+ "source": "https://github.com/composer/metadata-minifier/tree/1.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-04-07T13:37:33+00:00"
+ },
+ {
+ "name": "composer/pcre",
+ "version": "3.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/pcre.git",
+ "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
+ "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.3",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "symfony/phpunit-bridge": "^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Pcre\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "PCRE wrapping library that offers type-safe preg_* replacements.",
+ "keywords": [
+ "PCRE",
+ "preg",
+ "regex",
+ "regular expression"
+ ],
+ "support": {
+ "issues": "https://github.com/composer/pcre/issues",
+ "source": "https://github.com/composer/pcre/tree/3.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-03-19T10:26:25+00:00"
+ },
+ {
+ "name": "composer/semver",
+ "version": "3.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/semver.git",
+ "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32",
+ "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.4",
+ "symfony/phpunit-bridge": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Semver\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ },
+ {
+ "name": "Rob Bast",
+ "email": "rob.bast@gmail.com",
+ "homepage": "http://robbast.nl"
+ }
+ ],
+ "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "keywords": [
+ "semantic",
+ "semver",
+ "validation",
+ "versioning"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/semver/issues",
+ "source": "https://github.com/composer/semver/tree/3.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-08-31T09:50:34+00:00"
+ },
+ {
+ "name": "composer/spdx-licenses",
+ "version": "1.5.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/spdx-licenses.git",
+ "reference": "560bdcf8deb88ae5d611c80a2de8ea9d0358cc0a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/560bdcf8deb88ae5d611c80a2de8ea9d0358cc0a",
+ "reference": "560bdcf8deb88ae5d611c80a2de8ea9d0358cc0a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^0.12.55",
+ "symfony/phpunit-bridge": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Spdx\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ },
+ {
+ "name": "Rob Bast",
+ "email": "rob.bast@gmail.com",
+ "homepage": "http://robbast.nl"
+ }
+ ],
+ "description": "SPDX licenses list and validation library.",
+ "keywords": [
+ "license",
+ "spdx",
+ "validator"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/spdx-licenses/issues",
+ "source": "https://github.com/composer/spdx-licenses/tree/1.5.8"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-11-20T07:44:33+00:00"
+ },
+ {
+ "name": "composer/xdebug-handler",
+ "version": "3.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/xdebug-handler.git",
+ "reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/4f988f8fdf580d53bdb2d1278fe93d1ed5462255",
+ "reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255",
+ "shasum": ""
+ },
+ "require": {
+ "composer/pcre": "^1 || ^2 || ^3",
+ "php": "^7.2.5 || ^8.0",
+ "psr/log": "^1 || ^2 || ^3"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Composer\\XdebugHandler\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "John Stevenson",
+ "email": "john-stevenson@blueyonder.co.uk"
+ }
+ ],
+ "description": "Restarts a process without Xdebug.",
+ "keywords": [
+ "Xdebug",
+ "performance"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/xdebug-handler/issues",
+ "source": "https://github.com/composer/xdebug-handler/tree/3.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-03-26T18:29:49+00:00"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "7.8.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "41042bc7ab002487b876a0683fc8dce04ddce104"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104",
+ "reference": "41042bc7ab002487b876a0683fc8dce04ddce104",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/promises": "^1.5.3 || ^2.0.1",
+ "guzzlehttp/psr7": "^1.9.1 || ^2.5.1",
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-client": "^1.0",
+ "symfony/deprecation-contracts": "^2.2 || ^3.0"
+ },
+ "provide": {
+ "psr/http-client-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "ext-curl": "*",
+ "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
+ "php-http/message-factory": "^1.1",
+ "phpunit/phpunit": "^8.5.36 || ^9.6.15",
+ "psr/log": "^1.1 || ^2.0 || ^3.0"
+ },
+ "suggest": {
+ "ext-curl": "Required for CURL handler support",
+ "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Jeremy Lindblom",
+ "email": "jeremeamia@gmail.com",
+ "homepage": "https://github.com/jeremeamia"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "psr-18",
+ "psr-7",
+ "rest",
+ "web service"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.8.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-12-03T20:35:24+00:00"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223",
+ "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.36 || ^9.6.15"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-12-03T20:19:20+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "2.6.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221",
+ "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "ralouphie/getallheaders": "^3.0"
+ },
+ "provide": {
+ "psr/http-factory-implementation": "1.0",
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "http-interop/http-factory-tests": "^0.9",
+ "phpunit/phpunit": "^8.5.36 || ^9.6.15"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/2.6.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-12-03T20:05:35+00:00"
+ },
+ {
+ "name": "illuminate/container",
+ "version": "v10.48.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/container.git",
+ "reference": "ddc26273085fad3c471b2602ad820e0097ff7939"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/container/zipball/ddc26273085fad3c471b2602ad820e0097ff7939",
+ "reference": "ddc26273085fad3c471b2602ad820e0097ff7939",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/contracts": "^10.0",
+ "php": "^8.1",
+ "psr/container": "^1.1.1|^2.0.1"
+ },
+ "provide": {
+ "psr/container-implementation": "1.1|2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "10.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Container\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Container package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2023-06-18T09:12:03+00:00"
+ },
+ {
+ "name": "illuminate/contracts",
+ "version": "v10.48.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/contracts.git",
+ "reference": "8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/contracts/zipball/8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac",
+ "reference": "8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1",
+ "psr/container": "^1.1.1|^2.0.1",
+ "psr/simple-cache": "^1.0|^2.0|^3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "10.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Contracts\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Contracts package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2024-01-15T18:52:32+00:00"
+ },
+ {
+ "name": "justinrainbow/json-schema",
+ "version": "v5.2.13",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/justinrainbow/json-schema.git",
+ "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793",
+ "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1",
+ "json-schema/json-schema-test-suite": "1.2.0",
+ "phpunit/phpunit": "^4.8.35"
+ },
+ "bin": [
+ "bin/validate-json"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "JsonSchema\\": "src/JsonSchema/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bruno Prieto Reis",
+ "email": "bruno.p.reis@gmail.com"
+ },
+ {
+ "name": "Justin Rainbow",
+ "email": "justin.rainbow@gmail.com"
+ },
+ {
+ "name": "Igor Wiedler",
+ "email": "igor@wiedler.ch"
+ },
+ {
+ "name": "Robert Schönthal",
+ "email": "seroscho@googlemail.com"
+ }
+ ],
+ "description": "A library to validate a json schema.",
+ "homepage": "https://github.com/justinrainbow/json-schema",
+ "keywords": [
+ "json",
+ "schema"
+ ],
+ "support": {
+ "issues": "https://github.com/justinrainbow/json-schema/issues",
+ "source": "https://github.com/justinrainbow/json-schema/tree/v5.2.13"
+ },
+ "time": "2023-09-26T02:20:38+00:00"
+ },
{
"name": "psr/container",
"version": "2.0.2",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/container.git",
- "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
+ },
+ {
+ "name": "psr/http-client",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client"
+ },
+ "time": "2023-09-23T14:17:50+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "e616d01114759c4c489f93b099585439f795fe35"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
+ "reference": "e616d01114759c4c489f93b099585439f795fe35",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
+ },
+ "time": "2023-04-10T20:10:41+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/2.0"
+ },
+ "time": "2023-04-04T09:54:51+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
+ "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/3.0.0"
+ },
+ "time": "2021-07-14T16:46:02+00:00"
+ },
+ {
+ "name": "psr/simple-cache",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
+ "keywords": [
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
+ },
+ "time": "2021-10-29T13:26:27+00:00"
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
+ "name": "react/promise",
+ "version": "v3.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise.git",
+ "reference": "e563d55d1641de1dea9f5e84f3cccc66d2bfe02c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise/zipball/e563d55d1641de1dea9f5e84f3cccc66d2bfe02c",
+ "reference": "e563d55d1641de1dea9f5e84f3cccc66d2bfe02c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "1.10.39 || 1.4.10",
+ "phpunit/phpunit": "^9.6 || ^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+ "keywords": [
+ "promise",
+ "promises"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/promise/issues",
+ "source": "https://github.com/reactphp/promise/tree/v3.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2023-11-16T16:21:57+00:00"
+ },
+ {
+ "name": "revolt/event-loop",
+ "version": "v1.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/revoltphp/event-loop.git",
+ "reference": "25de49af7223ba039f64da4ae9a28ec2d10d0254"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/25de49af7223ba039f64da4ae9a28ec2d10d0254",
+ "reference": "25de49af7223ba039f64da4ae9a28ec2d10d0254",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "jetbrains/phpstorm-stubs": "^2019.3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.15"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Revolt\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "ceesjank@gmail.com"
+ },
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Rock-solid event loop for concurrent PHP applications.",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "concurrency",
+ "event",
+ "event-loop",
+ "non-blocking",
+ "scheduler"
+ ],
+ "support": {
+ "issues": "https://github.com/revoltphp/event-loop/issues",
+ "source": "https://github.com/revoltphp/event-loop/tree/v1.0.6"
+ },
+ "time": "2023-11-30T05:34:44+00:00"
+ },
+ {
+ "name": "seld/jsonlint",
+ "version": "1.10.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/jsonlint.git",
+ "reference": "9bb7db07b5d66d90f6ebf542f09fc67d800e5259"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/9bb7db07b5d66d90f6ebf542f09fc67d800e5259",
+ "reference": "9bb7db07b5d66d90f6ebf542f09fc67d800e5259",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.5",
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13"
+ },
+ "bin": [
+ "bin/jsonlint"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Seld\\JsonLint\\": "src/Seld/JsonLint/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be"
+ }
+ ],
+ "description": "JSON Linter",
+ "keywords": [
+ "json",
+ "linter",
+ "parser",
+ "validator"
+ ],
+ "support": {
+ "issues": "https://github.com/Seldaek/jsonlint/issues",
+ "source": "https://github.com/Seldaek/jsonlint/tree/1.10.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/Seldaek",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-07T12:57:50+00:00"
+ },
+ {
+ "name": "seld/phar-utils",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/phar-utils.git",
+ "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/ea2f4014f163c1be4c601b9b7bd6af81ba8d701c",
+ "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Seld\\PharUtils\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be"
+ }
+ ],
+ "description": "PHAR file format utilities, for when PHP phars you up",
+ "keywords": [
+ "phar"
+ ],
+ "support": {
+ "issues": "https://github.com/Seldaek/phar-utils/issues",
+ "source": "https://github.com/Seldaek/phar-utils/tree/1.2.1"
+ },
+ "time": "2022-08-31T10:31:18+00:00"
+ },
+ {
+ "name": "seld/signal-handler",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/signal-handler.git",
+ "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/signal-handler/zipball/04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98",
+ "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1",
+ "phpstan/phpstan-deprecation-rules": "^1.0",
+ "phpstan/phpstan-phpunit": "^1",
+ "phpstan/phpstan-strict-rules": "^1.3",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23",
+ "psr/log": "^1 || ^2 || ^3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Seld\\Signal\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "Simple unix signal handler that silently fails where signals are not supported for easy cross-platform development",
+ "keywords": [
+ "posix",
+ "sigint",
+ "signal",
+ "sigterm",
+ "unix"
+ ],
+ "support": {
+ "issues": "https://github.com/Seldaek/signal-handler/issues",
+ "source": "https://github.com/Seldaek/signal-handler/tree/2.0.2"
+ },
+ "time": "2023-09-03T09:24:00+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v6.4.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "a2708a5da5c87d1d0d52937bdeac625df659e11f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/a2708a5da5c87d1d0d52937bdeac625df659e11f",
+ "reference": "a2708a5da5c87d1d0d52937bdeac625df659e11f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/string": "^5.4|^6.0|^7.0"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<5.4",
+ "symfony/dotenv": "<5.4",
+ "symfony/event-dispatcher": "<5.4",
+ "symfony/lock": "<5.4",
+ "symfony/process": "<5.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^5.4|^6.0|^7.0",
+ "symfony/dependency-injection": "^5.4|^6.0|^7.0",
+ "symfony/event-dispatcher": "^5.4|^6.0|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/lock": "^5.4|^6.0|^7.0",
+ "symfony/messenger": "^5.4|^6.0|^7.0",
+ "symfony/process": "^5.4|^6.0|^7.0",
+ "symfony/stopwatch": "^5.4|^6.0|^7.0",
+ "symfony/var-dumper": "^5.4|^6.0|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v6.4.6"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-03-29T19:07:53+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
+ "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.4-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-05-23T14:45:45+00:00"
+ },
+ {
+ "name": "symfony/filesystem",
+ "version": "v6.4.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "9919b5509ada52cc7f66f9a35c86a4a29955c9d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/9919b5509ada52cc7f66f9a35c86a4a29955c9d3",
+ "reference": "9919b5509ada52cc7f66f9a35c86a4a29955c9d3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Filesystem\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides basic utilities for the filesystem",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/filesystem/tree/v6.4.6"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-03-21T19:36:20+00:00"
+ },
+ {
+ "name": "symfony/finder",
+ "version": "v6.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "11d736e97f116ac375a81f96e662911a34cd50ce"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/11d736e97f116ac375a81f96e662911a34cd50ce",
+ "reference": "11d736e97f116ac375a81f96e662911a34cd50ce",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "symfony/filesystem": "^6.0|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Finder\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Finds files and directories via an intuitive fluent interface",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/finder/tree/v6.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-10-31T17:30:12+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.29.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
+ "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-01-29T20:11:03+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.29.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
- "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f",
+ "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f",
"shasum": ""
},
"require": {
- "php": ">=7.4.0"
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-master": "2.0.x-dev"
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
"psr-4": {
- "Psr\\Container\\": "src/"
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -40,76 +2289,79 @@
],
"authors": [
{
- "name": "PHP-FIG",
- "homepage": "https://www.php-fig.org/"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
}
],
- "description": "Common Container Interface (PHP FIG PSR-11)",
- "homepage": "https://github.com/php-fig/container",
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
"keywords": [
- "PSR-11",
- "container",
- "container-interface",
- "container-interop",
- "psr"
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
],
"support": {
- "issues": "https://github.com/php-fig/container/issues",
- "source": "https://github.com/php-fig/container/tree/2.0.2"
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0"
},
- "time": "2021-11-05T16:47:00+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-01-29T20:11:03+00:00"
},
{
- "name": "symfony/console",
- "version": "v6.4.4",
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.29.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/console.git",
- "reference": "0d9e4eb5ad413075624378f474c4167ea202de78"
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "bc45c394692b948b4d383a08d7753968bed9a83d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/0d9e4eb5ad413075624378f474c4167ea202de78",
- "reference": "0d9e4eb5ad413075624378f474c4167ea202de78",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d",
+ "reference": "bc45c394692b948b4d383a08d7753968bed9a83d",
"shasum": ""
},
"require": {
- "php": ">=8.1",
- "symfony/deprecation-contracts": "^2.5|^3",
- "symfony/polyfill-mbstring": "~1.0",
- "symfony/service-contracts": "^2.5|^3",
- "symfony/string": "^5.4|^6.0|^7.0"
- },
- "conflict": {
- "symfony/dependency-injection": "<5.4",
- "symfony/dotenv": "<5.4",
- "symfony/event-dispatcher": "<5.4",
- "symfony/lock": "<5.4",
- "symfony/process": "<5.4"
- },
- "provide": {
- "psr/log-implementation": "1.0|2.0|3.0"
+ "php": ">=7.1"
},
- "require-dev": {
- "psr/log": "^1|^2|^3",
- "symfony/config": "^5.4|^6.0|^7.0",
- "symfony/dependency-injection": "^5.4|^6.0|^7.0",
- "symfony/event-dispatcher": "^5.4|^6.0|^7.0",
- "symfony/http-foundation": "^6.4|^7.0",
- "symfony/http-kernel": "^6.4|^7.0",
- "symfony/lock": "^5.4|^6.0|^7.0",
- "symfony/messenger": "^5.4|^6.0|^7.0",
- "symfony/process": "^5.4|^6.0|^7.0",
- "symfony/stopwatch": "^5.4|^6.0|^7.0",
- "symfony/var-dumper": "^5.4|^6.0|^7.0"
+ "suggest": {
+ "ext-intl": "For best performance"
},
"type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
"autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
"psr-4": {
- "Symfony\\Component\\Console\\": ""
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
- "exclude-from-classmap": [
- "/Tests/"
+ "classmap": [
+ "Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
@@ -118,24 +2370,26 @@
],
"authors": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Eases the creation of beautiful and testable command line interfaces",
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
"homepage": "https://symfony.com",
"keywords": [
- "cli",
- "command-line",
- "console",
- "terminal"
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v6.4.4"
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0"
},
"funding": [
{
@@ -151,39 +2405,45 @@
"type": "tidelift"
}
],
- "time": "2024-02-22T20:27:10+00:00"
+ "time": "2024-01-29T20:11:03+00:00"
},
{
- "name": "symfony/deprecation-contracts",
- "version": "v3.4.0",
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.29.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
- "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
+ "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "3.4-dev"
- },
"thanks": {
- "name": "symfony/contracts",
- "url": "https://github.com/symfony/contracts"
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
- "function.php"
- ]
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -199,10 +2459,17 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "A generic function and convention to trigger deprecation notices",
+ "description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
"support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
},
"funding": [
{
@@ -218,31 +2485,25 @@
"type": "tidelift"
}
],
- "time": "2023-05-23T14:45:45+00:00"
+ "time": "2024-01-29T20:11:03+00:00"
},
{
- "name": "symfony/polyfill-ctype",
+ "name": "symfony/polyfill-php73",
"version": "v1.29.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
+ "url": "https://github.com/symfony/polyfill-php73.git",
+ "reference": "21bd091060673a1177ae842c0ef8fe30893114d2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
- "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/21bd091060673a1177ae842c0ef8fe30893114d2",
+ "reference": "21bd091060673a1177ae842c0ef8fe30893114d2",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
- "provide": {
- "ext-ctype": "*"
- },
- "suggest": {
- "ext-ctype": "For best performance"
- },
"type": "library",
"extra": {
"thanks": {
@@ -255,8 +2516,11 @@
"bootstrap.php"
],
"psr-4": {
- "Symfony\\Polyfill\\Ctype\\": ""
- }
+ "Symfony\\Polyfill\\Php73\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -264,24 +2528,24 @@
],
"authors": [
{
- "name": "Gert de Pagter",
- "email": "BackEndTea@gmail.com"
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill for ctype functions",
+ "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
- "ctype",
"polyfill",
- "portable"
+ "portable",
+ "shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
+ "source": "https://github.com/symfony/polyfill-php73/tree/v1.29.0"
},
"funding": [
{
@@ -300,25 +2564,22 @@
"time": "2024-01-29T20:11:03+00:00"
},
{
- "name": "symfony/polyfill-intl-grapheme",
+ "name": "symfony/polyfill-php80",
"version": "v1.29.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
- "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f"
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f",
- "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
+ "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
- "suggest": {
- "ext-intl": "For best performance"
- },
"type": "library",
"extra": {
"thanks": {
@@ -331,14 +2592,21 @@
"bootstrap.php"
],
"psr-4": {
- "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
- }
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
@@ -348,18 +2616,16 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill for intl's grapheme_* functions",
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
- "grapheme",
- "intl",
"polyfill",
"portable",
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0"
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0"
},
"funding": [
{
@@ -378,25 +2644,22 @@
"time": "2024-01-29T20:11:03+00:00"
},
{
- "name": "symfony/polyfill-intl-normalizer",
+ "name": "symfony/polyfill-php81",
"version": "v1.29.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "bc45c394692b948b4d383a08d7753968bed9a83d"
+ "url": "https://github.com/symfony/polyfill-php81.git",
+ "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d",
- "reference": "bc45c394692b948b4d383a08d7753968bed9a83d",
+ "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/c565ad1e63f30e7477fc40738343c62b40bc672d",
+ "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
- "suggest": {
- "ext-intl": "For best performance"
- },
"type": "library",
"extra": {
"thanks": {
@@ -409,7 +2672,7 @@
"bootstrap.php"
],
"psr-4": {
- "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ "Symfony\\Polyfill\\Php81\\": ""
},
"classmap": [
"Resources/stubs"
@@ -429,18 +2692,16 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
- "intl",
- "normalizer",
"polyfill",
"portable",
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0"
+ "source": "https://github.com/symfony/polyfill-php81/tree/v1.29.0"
},
"funding": [
{
@@ -459,42 +2720,30 @@
"time": "2024-01-29T20:11:03+00:00"
},
{
- "name": "symfony/polyfill-mbstring",
- "version": "v1.29.0",
+ "name": "symfony/process",
+ "version": "v6.4.4",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
+ "url": "https://github.com/symfony/process.git",
+ "reference": "710e27879e9be3395de2b98da3f52a946039f297"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
- "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
+ "url": "https://api.github.com/repos/symfony/process/zipball/710e27879e9be3395de2b98da3f52a946039f297",
+ "reference": "710e27879e9be3395de2b98da3f52a946039f297",
"shasum": ""
},
"require": {
- "php": ">=7.1"
- },
- "provide": {
- "ext-mbstring": "*"
- },
- "suggest": {
- "ext-mbstring": "For best performance"
+ "php": ">=8.1"
},
"type": "library",
- "extra": {
- "thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
- }
- },
"autoload": {
- "files": [
- "bootstrap.php"
- ],
"psr-4": {
- "Symfony\\Polyfill\\Mbstring\\": ""
- }
+ "Symfony\\Component\\Process\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -502,25 +2751,18 @@
],
"authors": [
{
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill for the Mbstring extension",
+ "description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "mbstring",
- "polyfill",
- "portable",
- "shim"
- ],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
+ "source": "https://github.com/symfony/process/tree/v6.4.4"
},
"funding": [
{
@@ -536,20 +2778,20 @@
"type": "tidelift"
}
],
- "time": "2024-01-29T20:11:03+00:00"
+ "time": "2024-02-20T12:31:00+00:00"
},
{
"name": "symfony/service-contracts",
- "version": "v3.4.1",
+ "version": "v3.4.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0"
+ "reference": "11bbf19a0fb7b36345861e85c5768844c552906e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0",
- "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/11bbf19a0fb7b36345861e85c5768844c552906e",
+ "reference": "11bbf19a0fb7b36345861e85c5768844c552906e",
"shasum": ""
},
"require": {
@@ -602,7 +2844,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/service-contracts/tree/v3.4.1"
+ "source": "https://github.com/symfony/service-contracts/tree/v3.4.2"
},
"funding": [
{
@@ -618,7 +2860,7 @@
"type": "tidelift"
}
],
- "time": "2023-12-26T14:02:43+00:00"
+ "time": "2023-12-19T21:51:00+00:00"
},
{
"name": "symfony/string",
@@ -705,21 +2947,79 @@
}
],
"time": "2024-02-01T13:16:41+00:00"
+ },
+ {
+ "name": "webmozart/assert",
+ "version": "1.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/webmozarts/assert.git",
+ "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
+ "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<0.12.20",
+ "vimeo/psalm": "<4.6.1 || 4.6.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.13"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.10-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Webmozart\\Assert\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Assertions to validate method input/output with nice error messages.",
+ "keywords": [
+ "assert",
+ "check",
+ "validate"
+ ],
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/1.11.0"
+ },
+ "time": "2022-06-03T18:03:27+00:00"
}
],
"packages-dev": [
{
"name": "amphp/amp",
- "version": "v2.6.2",
+ "version": "v2.6.4",
"source": {
"type": "git",
"url": "https://github.com/amphp/amp.git",
- "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb"
+ "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb",
- "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb",
+ "url": "https://api.github.com/repos/amphp/amp/zipball/ded3d9be08f526089eb7ee8d9f16a9768f9dec2d",
+ "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d",
"shasum": ""
},
"require": {
@@ -731,8 +3031,8 @@
"ext-json": "*",
"jetbrains/phpstorm-stubs": "^2019.3",
"phpunit/phpunit": "^7 | ^8 | ^9",
- "psalm/phar": "^3.11@dev",
- "react/promise": "^2"
+ "react/promise": "^2",
+ "vimeo/psalm": "^3.12"
},
"type": "library",
"extra": {
@@ -787,7 +3087,7 @@
"support": {
"irc": "irc://irc.freenode.org/amphp",
"issues": "https://github.com/amphp/amp/issues",
- "source": "https://github.com/amphp/amp/tree/v2.6.2"
+ "source": "https://github.com/amphp/amp/tree/v2.6.4"
},
"funding": [
{
@@ -795,20 +3095,20 @@
"type": "github"
}
],
- "time": "2022-02-20T17:52:18+00:00"
+ "time": "2024-03-21T18:52:26+00:00"
},
{
"name": "amphp/byte-stream",
- "version": "v1.8.1",
+ "version": "v1.8.2",
"source": {
"type": "git",
"url": "https://github.com/amphp/byte-stream.git",
- "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd"
+ "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd",
- "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd",
+ "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc",
+ "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc",
"shasum": ""
},
"require": {
@@ -824,11 +3124,6 @@
"psalm/phar": "^3.11.4"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.x-dev"
- }
- },
"autoload": {
"files": [
"lib/functions.php"
@@ -847,213 +3142,68 @@
"email": "aaron@trowski.com"
},
{
- "name": "Niklas Keller",
- "email": "me@kelunik.com"
- }
- ],
- "description": "A stream abstraction to make working with non-blocking I/O simple.",
- "homepage": "http://amphp.org/byte-stream",
- "keywords": [
- "amp",
- "amphp",
- "async",
- "io",
- "non-blocking",
- "stream"
- ],
- "support": {
- "irc": "irc://irc.freenode.org/amphp",
- "issues": "https://github.com/amphp/byte-stream/issues",
- "source": "https://github.com/amphp/byte-stream/tree/v1.8.1"
- },
- "funding": [
- {
- "url": "https://github.com/amphp",
- "type": "github"
- }
- ],
- "time": "2021-03-30T17:13:30+00:00"
- },
- {
- "name": "composer/pcre",
- "version": "3.1.1",
- "source": {
- "type": "git",
- "url": "https://github.com/composer/pcre.git",
- "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9",
- "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9",
- "shasum": ""
- },
- "require": {
- "php": "^7.4 || ^8.0"
- },
- "require-dev": {
- "phpstan/phpstan": "^1.3",
- "phpstan/phpstan-strict-rules": "^1.1",
- "symfony/phpunit-bridge": "^5"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "3.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Composer\\Pcre\\": "src"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Jordi Boggiano",
- "email": "j.boggiano@seld.be",
- "homepage": "http://seld.be"
- }
- ],
- "description": "PCRE wrapping library that offers type-safe preg_* replacements.",
- "keywords": [
- "PCRE",
- "preg",
- "regex",
- "regular expression"
- ],
- "support": {
- "issues": "https://github.com/composer/pcre/issues",
- "source": "https://github.com/composer/pcre/tree/3.1.1"
- },
- "funding": [
- {
- "url": "https://packagist.com",
- "type": "custom"
- },
- {
- "url": "https://github.com/composer",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/composer/composer",
- "type": "tidelift"
- }
- ],
- "time": "2023-10-11T07:11:09+00:00"
- },
- {
- "name": "composer/semver",
- "version": "3.4.0",
- "source": {
- "type": "git",
- "url": "https://github.com/composer/semver.git",
- "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32",
- "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32",
- "shasum": ""
- },
- "require": {
- "php": "^5.3.2 || ^7.0 || ^8.0"
- },
- "require-dev": {
- "phpstan/phpstan": "^1.4",
- "symfony/phpunit-bridge": "^4.2 || ^5"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "3.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Composer\\Semver\\": "src"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nils Adermann",
- "email": "naderman@naderman.de",
- "homepage": "http://www.naderman.de"
- },
- {
- "name": "Jordi Boggiano",
- "email": "j.boggiano@seld.be",
- "homepage": "http://seld.be"
- },
- {
- "name": "Rob Bast",
- "email": "rob.bast@gmail.com",
- "homepage": "http://robbast.nl"
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
}
],
- "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "description": "A stream abstraction to make working with non-blocking I/O simple.",
+ "homepage": "https://amphp.org/byte-stream",
"keywords": [
- "semantic",
- "semver",
- "validation",
- "versioning"
+ "amp",
+ "amphp",
+ "async",
+ "io",
+ "non-blocking",
+ "stream"
],
"support": {
- "irc": "ircs://irc.libera.chat:6697/composer",
- "issues": "https://github.com/composer/semver/issues",
- "source": "https://github.com/composer/semver/tree/3.4.0"
+ "issues": "https://github.com/amphp/byte-stream/issues",
+ "source": "https://github.com/amphp/byte-stream/tree/v1.8.2"
},
"funding": [
{
- "url": "https://packagist.com",
- "type": "custom"
- },
- {
- "url": "https://github.com/composer",
+ "url": "https://github.com/amphp",
"type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/composer/composer",
- "type": "tidelift"
}
],
- "time": "2023-08-31T09:50:34+00:00"
+ "time": "2024-04-13T18:00:56+00:00"
},
{
- "name": "composer/xdebug-handler",
- "version": "3.0.3",
+ "name": "composer/package-versions-deprecated",
+ "version": "1.11.99.5",
"source": {
"type": "git",
- "url": "https://github.com/composer/xdebug-handler.git",
- "reference": "ced299686f41dce890debac69273b47ffe98a40c"
+ "url": "https://github.com/composer/package-versions-deprecated.git",
+ "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c",
- "reference": "ced299686f41dce890debac69273b47ffe98a40c",
+ "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d",
+ "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d",
"shasum": ""
},
"require": {
- "composer/pcre": "^1 || ^2 || ^3",
- "php": "^7.2.5 || ^8.0",
- "psr/log": "^1 || ^2 || ^3"
+ "composer-plugin-api": "^1.1.0 || ^2.0",
+ "php": "^7 || ^8"
+ },
+ "replace": {
+ "ocramius/package-versions": "1.11.99"
},
"require-dev": {
- "phpstan/phpstan": "^1.0",
- "phpstan/phpstan-strict-rules": "^1.1",
- "symfony/phpunit-bridge": "^6.0"
+ "composer/composer": "^1.9.3 || ^2.0@dev",
+ "ext-zip": "^1.13",
+ "phpunit/phpunit": "^6.5 || ^7"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PackageVersions\\Installer",
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
},
- "type": "library",
"autoload": {
"psr-4": {
- "Composer\\XdebugHandler\\": "src"
+ "PackageVersions\\": "src/PackageVersions"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1062,19 +3212,18 @@
],
"authors": [
{
- "name": "John Stevenson",
- "email": "john-stevenson@blueyonder.co.uk"
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be"
}
],
- "description": "Restarts a process without Xdebug.",
- "keywords": [
- "Xdebug",
- "performance"
- ],
+ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"support": {
- "irc": "irc://irc.freenode.org/composer",
- "issues": "https://github.com/composer/xdebug-handler/issues",
- "source": "https://github.com/composer/xdebug-handler/tree/3.0.3"
+ "issues": "https://github.com/composer/package-versions-deprecated/issues",
+ "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5"
},
"funding": [
{
@@ -1090,7 +3239,7 @@
"type": "tidelift"
}
],
- "time": "2022-02-25T21:32:43+00:00"
+ "time": "2022-01-17T14:14:24+00:00"
},
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
@@ -1584,21 +3733,21 @@
},
{
"name": "nikic/php-parser",
- "version": "v4.18.0",
+ "version": "v4.19.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999"
+ "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999",
- "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b",
+ "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
- "php": ">=7.0"
+ "php": ">=7.1"
},
"require-dev": {
"ircmaxell/php-yacc": "^0.0.7",
@@ -1634,26 +3783,27 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1"
},
- "time": "2023-12-10T21:03:43+00:00"
+ "time": "2024-03-17T08:10:35+00:00"
},
{
"name": "phar-io/manifest",
- "version": "2.0.3",
+ "version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phar-io/manifest.git",
- "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
- "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
"shasum": ""
},
"require": {
"ext-dom": "*",
+ "ext-libxml": "*",
"ext-phar": "*",
"ext-xmlwriter": "*",
"phar-io/version": "^3.0.1",
@@ -1694,9 +3844,15 @@
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
"support": {
"issues": "https://github.com/phar-io/manifest/issues",
- "source": "https://github.com/phar-io/manifest/tree/2.0.3"
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
},
- "time": "2021-07-20T11:28:43+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
},
{
"name": "phar-io/version",
@@ -1749,6 +3905,60 @@
},
"time": "2022-02-21T01:04:05+00:00"
},
+ {
+ "name": "php-standard-library/psalm-plugin",
+ "version": "2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-standard-library/psalm-plugin.git",
+ "reference": "bf6d560ae498966150bc66a42e02744b0ee242c5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-standard-library/psalm-plugin/zipball/bf6d560ae498966150bc66a42e02744b0ee242c5",
+ "reference": "bf6d560ae498966150bc66a42e02744b0ee242c5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1",
+ "vimeo/psalm": ">=5.16"
+ },
+ "conflict": {
+ "azjezz/psl": "<2.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^2.18",
+ "roave/security-advisories": "dev-master",
+ "squizlabs/php_codesniffer": "^3.5"
+ },
+ "type": "psalm-plugin",
+ "extra": {
+ "psalm": {
+ "pluginClass": "Psl\\Psalm\\Plugin"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psl\\Psalm\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "azjezz",
+ "email": "azjezz@protonmail.com"
+ }
+ ],
+ "description": "Psalm plugin for the PHP Standard Library",
+ "support": {
+ "issues": "https://github.com/php-standard-library/psalm-plugin/issues",
+ "source": "https://github.com/php-standard-library/psalm-plugin/tree/2.3.0"
+ },
+ "time": "2023-11-28T12:22:48+00:00"
+ },
{
"name": "phpdocumentor/reflection-common",
"version": "2.2.0",
@@ -1804,28 +4014,35 @@
},
{
"name": "phpdocumentor/reflection-docblock",
- "version": "5.3.0",
+ "version": "5.4.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
- "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
+ "reference": "298d2febfe79d03fe714eb871d5538da55205b1a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
- "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/298d2febfe79d03fe714eb871d5538da55205b1a",
+ "reference": "298d2febfe79d03fe714eb871d5538da55205b1a",
"shasum": ""
},
"require": {
+ "doctrine/deprecations": "^1.1",
"ext-filter": "*",
- "php": "^7.2 || ^8.0",
+ "php": "^7.4 || ^8.0",
"phpdocumentor/reflection-common": "^2.2",
- "phpdocumentor/type-resolver": "^1.3",
+ "phpdocumentor/type-resolver": "^1.7",
+ "phpstan/phpdoc-parser": "^1.7",
"webmozart/assert": "^1.9.1"
},
"require-dev": {
- "mockery/mockery": "~1.3.2",
- "psalm/phar": "^4.8"
+ "mockery/mockery": "~1.3.5",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-webmozart-assert": "^1.2",
+ "phpunit/phpunit": "^9.5",
+ "vimeo/psalm": "^5.13"
},
"type": "library",
"extra": {
@@ -1849,15 +4066,15 @@
},
{
"name": "Jaap van Otterdijk",
- "email": "account@ijaap.nl"
+ "email": "opensource@ijaap.nl"
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"support": {
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
- "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.0"
},
- "time": "2021-10-19T17:43:47+00:00"
+ "time": "2024-04-09T21:13:58+00:00"
},
{
"name": "phpdocumentor/type-resolver",
@@ -1919,16 +4136,16 @@
},
{
"name": "phpstan/phpdoc-parser",
- "version": "1.26.0",
+ "version": "1.28.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
- "reference": "231e3186624c03d7e7c890ec662b81e6b0405227"
+ "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/231e3186624c03d7e7c890ec662b81e6b0405227",
- "reference": "231e3186624c03d7e7c890ec662b81e6b0405227",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb",
+ "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb",
"shasum": ""
},
"require": {
@@ -1960,22 +4177,22 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
- "source": "https://github.com/phpstan/phpdoc-parser/tree/1.26.0"
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/1.28.0"
},
- "time": "2024-02-23T16:05:55+00:00"
+ "time": "2024-04-03T18:51:33+00:00"
},
{
"name": "phpunit/php-code-coverage",
- "version": "10.1.11",
+ "version": "10.1.14",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "78c3b7625965c2513ee96569a4dbb62601784145"
+ "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145",
- "reference": "78c3b7625965c2513ee96569a4dbb62601784145",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
+ "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
"shasum": ""
},
"require": {
@@ -2032,7 +4249,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
- "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11"
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14"
},
"funding": [
{
@@ -2040,7 +4257,7 @@
"type": "github"
}
],
- "time": "2023-12-21T15:38:30+00:00"
+ "time": "2024-03-12T15:33:41+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -2287,16 +4504,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "10.5.11",
+ "version": "10.5.20",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "0d968f6323deb3dbfeba5bfd4929b9415eb7a9a4"
+ "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0d968f6323deb3dbfeba5bfd4929b9415eb7a9a4",
- "reference": "0d968f6323deb3dbfeba5bfd4929b9415eb7a9a4",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/547d314dc24ec1e177720d45c6263fb226cc2ae3",
+ "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3",
"shasum": ""
},
"require": {
@@ -2368,7 +4585,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.11"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.20"
},
"funding": [
{
@@ -2384,34 +4601,49 @@
"type": "tidelift"
}
],
- "time": "2024-02-25T14:05:00+00:00"
+ "time": "2024-04-24T06:32:35+00:00"
},
{
- "name": "psr/log",
- "version": "3.0.0",
+ "name": "psalm/plugin-phpunit",
+ "version": "0.18.4",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/log.git",
- "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
+ "url": "https://github.com/psalm/psalm-plugin-phpunit.git",
+ "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
- "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
+ "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc",
+ "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc",
"shasum": ""
},
"require": {
- "php": ">=8.0.0"
+ "composer/package-versions-deprecated": "^1.10",
+ "composer/semver": "^1.4 || ^2.0 || ^3.0",
+ "ext-simplexml": "*",
+ "php": "^7.1 || ^8.0",
+ "vimeo/psalm": "dev-master || dev-4.x || ^4.7.1 || ^5@beta || ^5.0"
},
- "type": "library",
+ "conflict": {
+ "phpunit/phpunit": "<7.5"
+ },
+ "require-dev": {
+ "codeception/codeception": "^4.0.3",
+ "php": "^7.3 || ^8.0",
+ "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.3.1",
+ "weirdan/codeception-psalm-module": "^0.11.0",
+ "weirdan/prophecy-shim": "^1.0 || ^2.0"
+ },
+ "type": "psalm-plugin",
"extra": {
- "branch-alias": {
- "dev-master": "3.x-dev"
+ "psalm": {
+ "pluginClass": "Psalm\\PhpUnitPlugin\\Plugin"
}
},
"autoload": {
"psr-4": {
- "Psr\\Log\\": "src"
+ "Psalm\\PhpUnitPlugin\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -2420,34 +4652,29 @@
],
"authors": [
{
- "name": "PHP-FIG",
- "homepage": "https://www.php-fig.org/"
+ "name": "Matt Brown",
+ "email": "github@muglug.com"
}
],
- "description": "Common interface for logging libraries",
- "homepage": "https://github.com/php-fig/log",
- "keywords": [
- "log",
- "psr",
- "psr-3"
- ],
+ "description": "Psalm plugin for PHPUnit",
"support": {
- "source": "https://github.com/php-fig/log/tree/3.0.0"
+ "issues": "https://github.com/psalm/psalm-plugin-phpunit/issues",
+ "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.18.4"
},
- "time": "2021-07-14T16:46:02+00:00"
+ "time": "2022-12-03T07:47:07+00:00"
},
{
"name": "sebastian/cli-parser",
- "version": "2.0.0",
+ "version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/cli-parser.git",
- "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae"
+ "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae",
- "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084",
+ "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084",
"shasum": ""
},
"require": {
@@ -2482,7 +4709,8 @@
"homepage": "https://github.com/sebastianbergmann/cli-parser",
"support": {
"issues": "https://github.com/sebastianbergmann/cli-parser/issues",
- "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0"
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1"
},
"funding": [
{
@@ -2490,7 +4718,7 @@
"type": "github"
}
],
- "time": "2023-02-03T06:58:15+00:00"
+ "time": "2024-03-02T07:12:49+00:00"
},
{
"name": "sebastian/code-unit",
@@ -2740,16 +4968,16 @@
},
{
"name": "sebastian/diff",
- "version": "5.1.0",
+ "version": "5.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f"
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
- "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
"shasum": ""
},
"require": {
@@ -2757,7 +4985,7 @@
},
"require-dev": {
"phpunit/phpunit": "^10.0",
- "symfony/process": "^4.2 || ^5"
+ "symfony/process": "^6.4"
},
"type": "library",
"extra": {
@@ -2795,7 +5023,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"security": "https://github.com/sebastianbergmann/diff/security/policy",
- "source": "https://github.com/sebastianbergmann/diff/tree/5.1.0"
+ "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1"
},
"funding": [
{
@@ -2803,20 +5031,20 @@
"type": "github"
}
],
- "time": "2023-12-22T10:55:06+00:00"
+ "time": "2024-03-02T07:15:17+00:00"
},
{
"name": "sebastian/environment",
- "version": "6.0.1",
+ "version": "6.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951"
+ "reference": "8074dbcd93529b357029f5cc5058fd3e43666984"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951",
- "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984",
+ "reference": "8074dbcd93529b357029f5cc5058fd3e43666984",
"shasum": ""
},
"require": {
@@ -2831,7 +5059,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "6.0-dev"
+ "dev-main": "6.1-dev"
}
},
"autoload": {
@@ -2859,7 +5087,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/environment/issues",
"security": "https://github.com/sebastianbergmann/environment/security/policy",
- "source": "https://github.com/sebastianbergmann/environment/tree/6.0.1"
+ "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0"
},
"funding": [
{
@@ -2867,20 +5095,20 @@
"type": "github"
}
],
- "time": "2023-04-11T05:39:26+00:00"
+ "time": "2024-03-23T08:47:14+00:00"
},
{
"name": "sebastian/exporter",
- "version": "5.1.1",
+ "version": "5.1.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc"
+ "reference": "955288482d97c19a372d3f31006ab3f37da47adf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc",
- "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf",
+ "reference": "955288482d97c19a372d3f31006ab3f37da47adf",
"shasum": ""
},
"require": {
@@ -2937,7 +5165,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"security": "https://github.com/sebastianbergmann/exporter/security/policy",
- "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1"
+ "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2"
},
"funding": [
{
@@ -2945,20 +5173,20 @@
"type": "github"
}
],
- "time": "2023-09-24T13:22:09+00:00"
+ "time": "2024-03-02T07:17:12+00:00"
},
{
"name": "sebastian/global-state",
- "version": "6.0.1",
+ "version": "6.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4"
+ "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4",
- "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
+ "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
"shasum": ""
},
"require": {
@@ -2992,14 +5220,14 @@
}
],
"description": "Snapshotting of global state",
- "homepage": "http://www.github.com/sebastianbergmann/global-state",
+ "homepage": "https://www.github.com/sebastianbergmann/global-state",
"keywords": [
"global state"
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"security": "https://github.com/sebastianbergmann/global-state/security/policy",
- "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1"
+ "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2"
},
"funding": [
{
@@ -3007,7 +5235,7 @@
"type": "github"
}
],
- "time": "2023-07-19T07:19:23+00:00"
+ "time": "2024-03-02T07:19:19+00:00"
},
{
"name": "sebastian/lines-of-code",
@@ -3353,32 +5581,32 @@
},
{
"name": "slevomat/coding-standard",
- "version": "8.14.1",
+ "version": "8.15.0",
"source": {
"type": "git",
"url": "https://github.com/slevomat/coding-standard.git",
- "reference": "fea1fd6f137cc84f9cba0ae30d549615dbc6a926"
+ "reference": "7d1d957421618a3803b593ec31ace470177d7817"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/fea1fd6f137cc84f9cba0ae30d549615dbc6a926",
- "reference": "fea1fd6f137cc84f9cba0ae30d549615dbc6a926",
+ "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/7d1d957421618a3803b593ec31ace470177d7817",
+ "reference": "7d1d957421618a3803b593ec31ace470177d7817",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
"php": "^7.2 || ^8.0",
"phpstan/phpdoc-parser": "^1.23.1",
- "squizlabs/php_codesniffer": "^3.7.1"
+ "squizlabs/php_codesniffer": "^3.9.0"
},
"require-dev": {
"phing/phing": "2.17.4",
"php-parallel-lint/php-parallel-lint": "1.3.2",
- "phpstan/phpstan": "1.10.37",
+ "phpstan/phpstan": "1.10.60",
"phpstan/phpstan-deprecation-rules": "1.1.4",
- "phpstan/phpstan-phpunit": "1.3.14",
- "phpstan/phpstan-strict-rules": "1.5.1",
- "phpunit/phpunit": "8.5.21|9.6.8|10.3.5"
+ "phpstan/phpstan-phpunit": "1.3.16",
+ "phpstan/phpstan-strict-rules": "1.5.2",
+ "phpunit/phpunit": "8.5.21|9.6.8|10.5.11"
},
"type": "phpcodesniffer-standard",
"extra": {
@@ -3402,7 +5630,7 @@
],
"support": {
"issues": "https://github.com/slevomat/coding-standard/issues",
- "source": "https://github.com/slevomat/coding-standard/tree/8.14.1"
+ "source": "https://github.com/slevomat/coding-standard/tree/8.15.0"
},
"funding": [
{
@@ -3414,7 +5642,7 @@
"type": "tidelift"
}
],
- "time": "2023-10-08T07:28:08+00:00"
+ "time": "2024-03-09T15:20:58+00:00"
},
{
"name": "spatie/array-to-xml",
@@ -3481,16 +5709,16 @@
},
{
"name": "squizlabs/php_codesniffer",
- "version": "3.9.0",
+ "version": "3.9.2",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
- "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b"
+ "reference": "aac1f6f347a5c5ac6bc98ad395007df00990f480"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/d63cee4890a8afaf86a22e51ad4d97c91dd4579b",
- "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/aac1f6f347a5c5ac6bc98ad395007df00990f480",
+ "reference": "aac1f6f347a5c5ac6bc98ad395007df00990f480",
"shasum": ""
},
"require": {
@@ -3557,83 +5785,20 @@
"type": "open_collective"
}
],
- "time": "2024-02-16T15:06:51+00:00"
- },
- {
- "name": "symfony/filesystem",
- "version": "v6.4.3",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/filesystem.git",
- "reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
- "reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
- "shasum": ""
- },
- "require": {
- "php": ">=8.1",
- "symfony/polyfill-ctype": "~1.8",
- "symfony/polyfill-mbstring": "~1.8"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\Filesystem\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Provides basic utilities for the filesystem",
- "homepage": "https://symfony.com",
- "support": {
- "source": "https://github.com/symfony/filesystem/tree/v6.4.3"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2024-01-23T14:51:35+00:00"
+ "time": "2024-04-23T20:25:34+00:00"
},
{
"name": "theseer/tokenizer",
- "version": "1.2.2",
+ "version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
- "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
- "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
"shasum": ""
},
"require": {
@@ -3662,7 +5827,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
- "source": "https://github.com/theseer/tokenizer/tree/1.2.2"
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
},
"funding": [
{
@@ -3670,20 +5835,20 @@
"type": "github"
}
],
- "time": "2023-11-20T00:12:19+00:00"
+ "time": "2024-03-03T12:36:25+00:00"
},
{
"name": "vimeo/psalm",
- "version": "5.22.2",
+ "version": "5.23.1",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
- "reference": "d768d914152dbbf3486c36398802f74e80cfde48"
+ "reference": "8471a896ccea3526b26d082f4461eeea467f10a4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/vimeo/psalm/zipball/d768d914152dbbf3486c36398802f74e80cfde48",
- "reference": "d768d914152dbbf3486c36398802f74e80cfde48",
+ "url": "https://api.github.com/repos/vimeo/psalm/zipball/8471a896ccea3526b26d082f4461eeea467f10a4",
+ "reference": "8471a896ccea3526b26d082f4461eeea467f10a4",
"shasum": ""
},
"require": {
@@ -3780,74 +5945,19 @@
"issues": "https://github.com/vimeo/psalm/issues",
"source": "https://github.com/vimeo/psalm"
},
- "time": "2024-02-22T23:39:07+00:00"
- },
- {
- "name": "webmozart/assert",
- "version": "1.11.0",
- "source": {
- "type": "git",
- "url": "https://github.com/webmozarts/assert.git",
- "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
- "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
- "shasum": ""
- },
- "require": {
- "ext-ctype": "*",
- "php": "^7.2 || ^8.0"
- },
- "conflict": {
- "phpstan/phpstan": "<0.12.20",
- "vimeo/psalm": "<4.6.1 || 4.6.2"
- },
- "require-dev": {
- "phpunit/phpunit": "^8.5.13"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.10-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Webmozart\\Assert\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Bernhard Schussek",
- "email": "bschussek@gmail.com"
- }
- ],
- "description": "Assertions to validate method input/output with nice error messages.",
- "keywords": [
- "assert",
- "check",
- "validate"
- ],
- "support": {
- "issues": "https://github.com/webmozarts/assert/issues",
- "source": "https://github.com/webmozarts/assert/tree/1.11.0"
- },
- "time": "2022-06-03T18:03:27+00:00"
+ "time": "2024-03-11T20:33:46+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {
+ "composer/composer": 20
+ },
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": "8.1.*||8.2.*||8.3.*"
+ "php": "8.1.*||8.2.*||8.3.*",
+ "ext-zip": "*"
},
"platform-dev": [],
"plugin-api-version": "2.6.0"
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index 3ee6c03..b9e5857 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -9,6 +9,7 @@
bin/pie
src
test/unit
+ test/integration
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 675d305..2808b46 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -6,14 +6,17 @@
cacheDirectory=".phpunit.cache"
executionOrder="depends,defects"
requireCoverageMetadata="true"
- beStrictAboutCoverageMetadata="true"
beStrictAboutOutputDuringTests="true"
+ displayDetailsOnSkippedTests="true"
failOnRisky="true"
failOnWarning="true">
test/unit
+
+ test/integration
+
diff --git a/psalm.xml.dist b/psalm.xml.dist
index aa2ce96..cc9b3b2 100644
--- a/psalm.xml.dist
+++ b/psalm.xml.dist
@@ -9,10 +9,23 @@
findUnusedCode="true"
>
-
-
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Command/DownloadCommand.php b/src/Command/DownloadCommand.php
index 97876df..9611d6a 100644
--- a/src/Command/DownloadCommand.php
+++ b/src/Command/DownloadCommand.php
@@ -4,10 +4,29 @@
namespace Php\Pie\Command;
+use Composer\Package\Version\VersionParser;
+use InvalidArgumentException;
+use Php\Pie\DependencyResolver\DependencyResolver;
+use Php\Pie\Downloading\DownloadAndExtract;
+use Php\Pie\Platform\OperatingSystem;
+use Php\Pie\Platform\TargetPhp\PhpBinaryPath;
+use Php\Pie\Platform\TargetPlatform;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
+use Webmozart\Assert\Assert;
+
+use function array_key_exists;
+use function is_array;
+use function is_string;
+use function reset;
+use function sprintf;
+use function strtolower;
+
+use const PHP_VERSION;
#[AsCommand(
name: 'download',
@@ -15,10 +34,114 @@
)]
final class DownloadCommand extends Command
{
+ private const ARG_REQUESTED_PACKAGE_AND_VERSION = 'requested-package-and-version';
+ private const OPTION_WITH_PHP_CONFIG = 'with-php-config';
+ private const OPTION_WITH_PHP_PATH = 'with-php-path';
+
+ public function __construct(
+ private readonly DependencyResolver $dependencyResolver,
+ private readonly DownloadAndExtract $downloadAndExtract,
+ ) {
+ parent::__construct();
+ }
+
+ public function configure(): void
+ {
+ parent::configure();
+
+ $this->addArgument(
+ self::ARG_REQUESTED_PACKAGE_AND_VERSION,
+ InputArgument::REQUIRED,
+ 'The extension name and version constraint to use, in the format {ext-name}{?:{?version-constraint}{?@stability}}, for example `xdebug/xdebug:^3.4@alpha`, `xdebug/xdebug:@alpha`, `xdebug/xdebug:^3.4`, etc.',
+ );
+ $this->addOption(
+ self::OPTION_WITH_PHP_CONFIG,
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'The path to the `php-config` binary to find the target PHP platform on ' . OperatingSystem::NonWindows->asFriendlyName() . ', e.g. --' . self::OPTION_WITH_PHP_CONFIG . '=/usr/bin/php-config7.4',
+ );
+ $this->addOption(
+ self::OPTION_WITH_PHP_PATH,
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'The path to the `php` binary to use as the target PHP platform on ' . OperatingSystem::Windows->asFriendlyName() . ', e.g. --' . self::OPTION_WITH_PHP_PATH . '=C:\usr\php7.4.33\php.exe',
+ );
+ }
+
public function execute(InputInterface $input, OutputInterface $output): int
{
- $output->writeln('to do'); // @todo
+ $phpBinaryPath = PhpBinaryPath::fromCurrentProcess();
+
+ /** @var mixed $withPhpConfig */
+ $withPhpConfig = $input->getOption(self::OPTION_WITH_PHP_CONFIG);
+ if (is_string($withPhpConfig) && $withPhpConfig !== '') {
+ $phpBinaryPath = PhpBinaryPath::fromPhpConfigExecutable($withPhpConfig);
+ }
+
+ /** @var mixed $withPhpPath */
+ $withPhpPath = $input->getOption(self::OPTION_WITH_PHP_PATH);
+ if (is_string($withPhpPath) && $withPhpPath !== '') {
+ $phpBinaryPath = PhpBinaryPath::fromPhpBinaryPath($withPhpPath);
+ }
+
+ $targetPlatform = TargetPlatform::fromPhpBinaryPath($phpBinaryPath);
+
+ $output->writeln(sprintf('You are running PHP %s', PHP_VERSION));
+ $output->writeln(sprintf(
+ 'Target PHP installation: %s %s%s, on %s %s (from %s)',
+ $phpBinaryPath->version(),
+ $targetPlatform->threadSafety->asShort(),
+ strtolower($targetPlatform->windowsCompiler !== null ? ', ' . $targetPlatform->windowsCompiler->name : ''),
+ $targetPlatform->operatingSystem->asFriendlyName(),
+ $targetPlatform->architecture->name,
+ $phpBinaryPath->phpBinaryPath,
+ ));
+
+ $requestedNameAndVersionPair = $this->requestedNameAndVersionPair($input);
+
+ $package = ($this->dependencyResolver)(
+ $targetPlatform,
+ $requestedNameAndVersionPair['name'],
+ $requestedNameAndVersionPair['version'],
+ );
+
+ $output->writeln(sprintf('Found package: %s which provides %s', $package->prettyNameAndVersion(), $package->extensionName->nameWithExtPrefix()));
+
+ $downloadedPackage = ($this->downloadAndExtract)($targetPlatform, $package);
+
+ $output->writeln(sprintf(
+ 'Extracted %s source to: %s',
+ $downloadedPackage->package->prettyNameAndVersion(),
+ $downloadedPackage->extractedSourcePath,
+ ));
return Command::SUCCESS;
}
+
+ /** @return array{name: non-empty-string, version: non-empty-string|null} */
+ private function requestedNameAndVersionPair(InputInterface $input): array
+ {
+ $requestedPackageString = $input->getArgument(self::ARG_REQUESTED_PACKAGE_AND_VERSION);
+
+ if (! is_string($requestedPackageString) || $requestedPackageString === '') {
+ throw new InvalidArgumentException('No package was requested for installation');
+ }
+
+ $nameAndVersionPairs = (new VersionParser())
+ ->parseNameVersionPairs([$requestedPackageString]);
+ $requestedNameAndVersionPair = reset($nameAndVersionPairs);
+
+ if (! is_array($requestedNameAndVersionPair)) {
+ throw new InvalidArgumentException('Failed to parse the name/version pair');
+ }
+
+ if (! array_key_exists('version', $requestedNameAndVersionPair)) {
+ $requestedNameAndVersionPair['version'] = null;
+ }
+
+ Assert::stringNotEmpty($requestedNameAndVersionPair['name']);
+ Assert::nullOrStringNotEmpty($requestedNameAndVersionPair['version']);
+
+ return $requestedNameAndVersionPair;
+ }
}
diff --git a/src/Container.php b/src/Container.php
new file mode 100644
index 0000000..c8e8c8f
--- /dev/null
+++ b/src/Container.php
@@ -0,0 +1,115 @@
+instance(InputInterface::class, new ArgvInput());
+ $container->instance(OutputInterface::class, new ConsoleOutput());
+
+ $container->singleton(DownloadCommand::class);
+
+ $container->singleton(IOInterface::class, static function (ContainerInterface $container): IOInterface {
+ return new ConsoleIO(
+ $container->get(InputInterface::class),
+ $container->get(OutputInterface::class),
+ new HelperSet([]),
+ );
+ });
+ $container->singleton(Composer::class, static function (ContainerInterface $container): Composer {
+ $io = $container->get(IOInterface::class);
+ $composer = (new ComposerFactory())->createComposer(
+ $io,
+ [
+ 'config' => ['lock' => false],
+ ],
+ true,
+ );
+ $io->loadConfiguration($composer->getConfig());
+
+ return $composer;
+ });
+
+ $container->singleton(
+ DependencyResolver::class,
+ static function (ContainerInterface $container): DependencyResolver {
+ $composer = $container->get(Composer::class);
+ $repositorySet = new RepositorySet();
+ $repositorySet->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories()));
+
+ return new ResolveDependencyWithComposer(
+ $repositorySet,
+ new ResolveTargetPhpToPlatformRepository(),
+ );
+ },
+ );
+ $container->bind(
+ ClientInterface::class,
+ static function (): ClientInterface {
+ return new Client([RequestOptions::HTTP_ERRORS => false]);
+ },
+ );
+ $container->singleton(
+ AuthHelper::class,
+ static function (ContainerInterface $container): AuthHelper {
+ return new AuthHelper(
+ $container->get(IOInterface::class),
+ $container->get(Composer::class)->getConfig(),
+ );
+ },
+ );
+ $container->alias(DownloadZipWithGuzzle::class, DownloadZip::class);
+ $container->alias(GithubPackageReleaseAssets::class, PackageReleaseAssets::class);
+ $container->when(GithubPackageReleaseAssets::class)
+ ->needs('$githubApiBaseUrl')
+ ->give('https://api.github.com');
+ $container->singleton(
+ DownloadAndExtract::class,
+ static function (ContainerInterface $container): DownloadAndExtract {
+ if (Platform::isWindows()) {
+ return $container->get(WindowsDownloadAndExtract::class);
+ }
+
+ return $container->get(UnixDownloadAndExtract::class);
+ },
+ );
+
+ return $container;
+ }
+}
diff --git a/src/DependencyResolver/DependencyResolver.php b/src/DependencyResolver/DependencyResolver.php
new file mode 100644
index 0000000..e316091
--- /dev/null
+++ b/src/DependencyResolver/DependencyResolver.php
@@ -0,0 +1,14 @@
+getPrettyName(),
+ $completePackage->getPrettyVersion(),
+ $completePackage->getDistUrl(),
+ );
+ }
+
+ public function prettyNameAndVersion(): string
+ {
+ return $this->name . ':' . $this->version;
+ }
+}
diff --git a/src/DependencyResolver/ResolveDependencyWithComposer.php b/src/DependencyResolver/ResolveDependencyWithComposer.php
new file mode 100644
index 0000000..23c199a
--- /dev/null
+++ b/src/DependencyResolver/ResolveDependencyWithComposer.php
@@ -0,0 +1,53 @@
+repositorySet,
+ ($this->resolveTargetPhpToPlatformRepository)($targetPlatform->phpBinaryPath),
+ ))
+ ->findBestCandidate($packageName, $requestedVersion, $preferredStability, null, $repoSetFlags);
+
+ if (! $package instanceof CompletePackageInterface) {
+ throw UnableToResolveRequirement::fromRequirement($packageName, $requestedVersion);
+ }
+
+ $type = $package->getType();
+ if (! in_array($type, [Package::TYPE_PHP_MODULE, Package::TYPE_ZEND_EXTENSION])) {
+ throw UnableToResolveRequirement::toPhpOrZendExtension($package, $packageName, $requestedVersion);
+ }
+
+ return Package::fromComposerCompletePackage($package);
+ }
+}
diff --git a/src/DependencyResolver/UnableToResolveRequirement.php b/src/DependencyResolver/UnableToResolveRequirement.php
new file mode 100644
index 0000000..1815eef
--- /dev/null
+++ b/src/DependencyResolver/UnableToResolveRequirement.php
@@ -0,0 +1,32 @@
+getName(),
+ $requiredPackageName,
+ $requiredVersion !== null ? sprintf(' for version %s', $requiredVersion) : '',
+ ));
+ }
+}
diff --git a/src/Downloading/AddAuthenticationHeader.php b/src/Downloading/AddAuthenticationHeader.php
new file mode 100644
index 0000000..1e54478
--- /dev/null
+++ b/src/Downloading/AddAuthenticationHeader.php
@@ -0,0 +1,38 @@
+downloadUrl === null) {
+ throw new RuntimeException(sprintf('The package %s does not have a download URL', $package->name));
+ }
+
+ $authHeaders = $authHelper->addAuthenticationHeader([], 'github.com', $package->downloadUrl);
+ array_walk(
+ $authHeaders,
+ static function (string $v) use (&$request): void {
+ // @todo probably process this better
+ $headerParts = explode(':', $v);
+ $request = $request->withHeader(trim($headerParts[0]), trim($headerParts[1]));
+ },
+ );
+
+ return $request;
+ }
+}
diff --git a/src/Downloading/AssertHttp.php b/src/Downloading/AssertHttp.php
new file mode 100644
index 0000000..d4639d0
--- /dev/null
+++ b/src/Downloading/AssertHttp.php
@@ -0,0 +1,28 @@
+getStatusCode();
+ if ($actualStatusCode !== $expectedStatusCode) {
+ throw new InvalidArgumentException(sprintf(
+ 'Expected HTTP %d response, got %d - response: %s',
+ $expectedStatusCode,
+ $actualStatusCode,
+ $response->getBody()->__toString(),
+ ));
+ }
+ }
+}
diff --git a/src/Downloading/DownloadAndExtract.php b/src/Downloading/DownloadAndExtract.php
new file mode 100644
index 0000000..f2f2091
--- /dev/null
+++ b/src/Downloading/DownloadAndExtract.php
@@ -0,0 +1,14 @@
+client
+ ->sendAsync(
+ $request,
+ [
+ RequestOptions::ALLOW_REDIRECTS => true,
+ RequestOptions::HTTP_ERRORS => false,
+ RequestOptions::SYNCHRONOUS => true,
+ ],
+ )
+ ->wait();
+ assert($response instanceof ResponseInterface);
+
+ AssertHttp::responseStatusCode(200, $response);
+
+ $tmpZipFile = $localPath . '/downloaded.zip';
+ file_put_contents($tmpZipFile, $response->getBody()->__toString());
+
+ return $tmpZipFile;
+ }
+}
diff --git a/src/Downloading/DownloadedPackage.php b/src/Downloading/DownloadedPackage.php
new file mode 100644
index 0000000..0e21461
--- /dev/null
+++ b/src/Downloading/DownloadedPackage.php
@@ -0,0 +1,26 @@
+ $expectedAssetNames */
+ public static function forPackage(Package $package, array $expectedAssetNames): self
+ {
+ return new self(sprintf(
+ 'Could not find release asset for %s named one of "%s"',
+ $package->prettyNameAndVersion(),
+ implode(', ', $expectedAssetNames),
+ ));
+ }
+
+ public static function forPackageWithMissingTag(Package $package): self
+ {
+ return new self(sprintf(
+ 'Could not find release by tag name for %s',
+ $package->prettyNameAndVersion(),
+ ));
+ }
+
+ public static function forMissingWindowsCompiler(TargetPlatform $targetPlatform): self
+ {
+ return new self(sprintf(
+ 'Could not determine Windows Compiler for PHP %s on %s',
+ $targetPlatform->phpBinaryPath->version(),
+ $targetPlatform->operatingSystem->name,
+ ));
+ }
+}
diff --git a/src/Downloading/ExtractZip.php b/src/Downloading/ExtractZip.php
new file mode 100644
index 0000000..e5f0cec
--- /dev/null
+++ b/src/Downloading/ExtractZip.php
@@ -0,0 +1,47 @@
+performExtractionUsingZipArchive($zipFile, $destination);
+ }
+
+ private function performExtractionUsingZipArchive(string $zipFile, string $destination): string
+ {
+ $zip = new ZipArchive();
+
+ $openError = $zip->open($zipFile);
+ if ($openError !== true) {
+ throw new RuntimeException(sprintf(
+ 'Could not open ZIP [%s]: %s',
+ $openError === false ? '(false)' : $openError,
+ $zipFile,
+ ));
+ }
+
+ if (! $zip->extractTo($destination)) {
+ throw new RuntimeException(sprintf('Could not extract ZIP "%s" to path: %s', $zipFile, $destination));
+ }
+
+ // @todo maybe improve this; GH wraps archives in a top level directory based on the repo name
+ // and commit, but does anyone else? :s
+ $extractedPath = explode('/', $zip->getNameIndex(0))[0];
+
+ $zip->close();
+
+ return $destination . '/' . $extractedPath;
+ }
+}
diff --git a/src/Downloading/GithubPackageReleaseAssets.php b/src/Downloading/GithubPackageReleaseAssets.php
new file mode 100644
index 0000000..0d9bd70
--- /dev/null
+++ b/src/Downloading/GithubPackageReleaseAssets.php
@@ -0,0 +1,148 @@
+selectMatchingReleaseAsset(
+ $targetPlatform,
+ $package,
+ $this->getReleaseAssetsForPackage($package),
+ );
+
+ return $releaseAsset['browser_download_url'];
+ }
+
+ /** @return non-empty-list */
+ private function expectedWindowsAssetNames(TargetPlatform $targetPlatform, Package $package): array
+ {
+ if ($targetPlatform->operatingSystem !== OperatingSystem::Windows || $targetPlatform->windowsCompiler === null) {
+ throw CouldNotFindReleaseAsset::forMissingWindowsCompiler($targetPlatform);
+ }
+
+ /**
+ * During development, we swapped compiler/ts around. It is fairly trivial to support both, so we can check
+ * both formats pretty easily, just to avoid confusion for package maintainers...
+ */
+ return [
+ strtolower(sprintf(
+ 'php_%s-%s-%s-%s-%s-%s.zip',
+ $package->extensionName->name(),
+ $package->version,
+ $targetPlatform->phpBinaryPath->majorMinorVersion(),
+ $targetPlatform->threadSafety->asShort(),
+ strtolower($targetPlatform->windowsCompiler->name),
+ $targetPlatform->architecture->name,
+ )),
+ strtolower(sprintf(
+ 'php_%s-%s-%s-%s-%s-%s.zip',
+ $package->extensionName->name(),
+ $package->version,
+ $targetPlatform->phpBinaryPath->majorMinorVersion(),
+ strtolower($targetPlatform->windowsCompiler->name),
+ $targetPlatform->threadSafety->asShort(),
+ $targetPlatform->architecture->name,
+ )),
+ ];
+ }
+
+ /** @link https://github.com/squizlabs/PHP_CodeSniffer/issues/3734 */
+ // phpcs:disable Squiz.Commenting.FunctionComment.MissingParamName
+ /**
+ * @param list $releaseAssets
+ *
+ * @return array{name: non-empty-string, browser_download_url: non-empty-string, ...}
+ */
+ // phpcs:enable
+ private function selectMatchingReleaseAsset(TargetPlatform $targetPlatform, Package $package, array $releaseAssets): array
+ {
+ $expectedAssetNames = $this->expectedWindowsAssetNames($targetPlatform, $package);
+
+ foreach ($releaseAssets as $releaseAsset) {
+ if (in_array(strtolower($releaseAsset['name']), $expectedAssetNames, true)) {
+ return $releaseAsset;
+ }
+ }
+
+ throw Exception\CouldNotFindReleaseAsset::forPackage($package, $expectedAssetNames);
+ }
+
+ /** @return list */
+ private function getReleaseAssetsForPackage(Package $package): array
+ {
+ // @todo confirm prettyName will always match the repo name - it might not
+ $request = AddAuthenticationHeader::withAuthHeaderFromComposer(
+ new Request('GET', $this->githubApiBaseUrl . '/repos/' . $package->name . '/releases/tags/' . $package->version),
+ $package,
+ $this->authHelper,
+ );
+
+ $response = $this->client
+ ->sendAsync(
+ $request,
+ [
+ RequestOptions::ALLOW_REDIRECTS => true,
+ RequestOptions::HTTP_ERRORS => false,
+ RequestOptions::SYNCHRONOUS => true,
+ ],
+ )
+ ->wait();
+ assert($response instanceof ResponseInterface);
+
+ /** @link https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#get-a-release-by-tag-name */
+ if ($response->getStatusCode() === 404) {
+ throw Exception\CouldNotFindReleaseAsset::forPackageWithMissingTag($package);
+ }
+
+ AssertHttp::responseStatusCode(200, $response);
+
+ $releaseAssets = Json\typed(
+ (string) $response->getBody(),
+ Type\shape(
+ [
+ 'assets' => Type\vec(Type\shape(
+ [
+ 'name' => Type\non_empty_string(),
+ 'browser_download_url' => Type\non_empty_string(),
+ ],
+ true,
+ )),
+ ],
+ true,
+ ),
+ );
+
+ return $releaseAssets['assets'];
+ }
+}
diff --git a/src/Downloading/PackageReleaseAssets.php b/src/Downloading/PackageReleaseAssets.php
new file mode 100644
index 0000000..d39902d
--- /dev/null
+++ b/src/Downloading/PackageReleaseAssets.php
@@ -0,0 +1,15 @@
+downloadZip->downloadZipAndReturnLocalPath(
+ $this->createRequestForUnixDownloadUrl($package),
+ $localTempPath,
+ );
+
+ $extractedPath = $this->extractZip->to($tmpZipFile, $localTempPath);
+
+ return DownloadedPackage::fromPackageAndExtractedPath($package, $extractedPath);
+ }
+
+ private function createRequestForUnixDownloadUrl(Package $package): RequestInterface
+ {
+ if ($package->downloadUrl === null) {
+ throw new RuntimeException(sprintf('The package %s does not have a download URL', $package->name));
+ }
+
+ $request = new Request('GET', $package->downloadUrl);
+
+ return AddAuthenticationHeader::withAuthHeaderFromComposer($request, $package, $this->authHelper);
+ }
+}
diff --git a/src/Downloading/WindowsDownloadAndExtract.php b/src/Downloading/WindowsDownloadAndExtract.php
new file mode 100644
index 0000000..fd317e9
--- /dev/null
+++ b/src/Downloading/WindowsDownloadAndExtract.php
@@ -0,0 +1,43 @@
+packageReleaseAssets->findWindowsDownloadUrlForPackage($targetPlatform, $package);
+
+ $localTempPath = Path::vaguelyRandomTempPath();
+
+ $tmpZipFile = $this->downloadZip->downloadZipAndReturnLocalPath(
+ AddAuthenticationHeader::withAuthHeaderFromComposer(
+ new Request('GET', $windowsDownloadUrl),
+ $package,
+ $this->authHelper,
+ ),
+ $localTempPath,
+ );
+
+ $this->extractZip->to($tmpZipFile, $localTempPath);
+
+ return DownloadedPackage::fromPackageAndExtractedPath($package, $localTempPath);
+ }
+}
diff --git a/src/ExtensionName.php b/src/ExtensionName.php
new file mode 100644
index 0000000..afe876b
--- /dev/null
+++ b/src/ExtensionName.php
@@ -0,0 +1,92 @@
+normalisedExtensionName = $normalisedExtensionName;
+ }
+
+ public static function determineFromComposerPackage(PackageInterface $package): self
+ {
+ $phpExt = $package->getPhpExt();
+
+ /** @psalm-suppress DocblockTypeContradiction just in case runtime type is not correct */
+ if (
+ $phpExt === null
+ || ! array_key_exists('extension-name', $phpExt)
+ || ! is_string($phpExt['extension-name'])
+ || $phpExt['extension-name'] === ''
+ ) {
+ $packageNameParts = explode('/', $package->getPrettyName());
+ Assert::count($packageNameParts, 2, 'Expected a package name like vendor/package for ' . $package->getPrettyName());
+ Assert::keyExists($packageNameParts, 1);
+
+ return self::normaliseFromString($packageNameParts[1]);
+ }
+
+ return self::normaliseFromString($phpExt['extension-name']);
+ }
+
+ public static function normaliseFromString(string $extensionName): self
+ {
+ if (str_starts_with($extensionName, 'ext-')) {
+ return new self(substr($extensionName, strlen('ext-')));
+ }
+
+ return new self($extensionName);
+ }
+
+ /** @return non-empty-string */
+ public function name(): string
+ {
+ return $this->normalisedExtensionName;
+ }
+
+ /** @return non-empty-string */
+ public function nameWithExtPrefix(): string
+ {
+ return 'ext-' . $this->normalisedExtensionName;
+ }
+}
diff --git a/src/Platform/Architecture.php b/src/Platform/Architecture.php
new file mode 100644
index 0000000..99bcb92
--- /dev/null
+++ b/src/Platform/Architecture.php
@@ -0,0 +1,27 @@
+ self::x86_64,
+ 'arm64' => self::arm64,
+ default => self::x86,
+ };
+ }
+}
diff --git a/src/Platform/OperatingSystem.php b/src/Platform/OperatingSystem.php
new file mode 100644
index 0000000..3b66b81
--- /dev/null
+++ b/src/Platform/OperatingSystem.php
@@ -0,0 +1,20 @@
+ 'Windows',
+ self::NonWindows => 'Linux/OSX/etc',
+ };
+ }
+}
diff --git a/src/Platform/TargetPhp/PhpBinaryPath.php b/src/Platform/TargetPhp/PhpBinaryPath.php
new file mode 100644
index 0000000..0c28d06
--- /dev/null
+++ b/src/Platform/TargetPhp/PhpBinaryPath.php
@@ -0,0 +1,186 @@
+
+ */
+ public function extensions(): array
+ {
+ $extVersionsRawJson = trim((new Process([
+ $this->phpBinaryPath,
+ '-r',
+ <<<'PHP'
+$exts = get_loaded_extensions();
+$extVersions = array_map(
+ static function ($extension) {
+ $extVersion = phpversion($extension);
+ if ($extVersion === false) {
+ return '0';
+ }
+ return $extVersion;
+ },
+ $exts
+);
+echo json_encode(array_combine($exts, $extVersions));
+PHP,
+ ]))
+ ->mustRun()
+ ->getOutput());
+
+ return Json\typed(
+ $extVersionsRawJson,
+ Type\dict(
+ Type\string(),
+ Type\string(),
+ ),
+ );
+ }
+
+ public function operatingSystem(): OperatingSystem
+ {
+ $winOrNot = trim((new Process([
+ $this->phpBinaryPath,
+ '-r',
+ 'echo \\defined(\'PHP_WINDOWS_VERSION_BUILD\') ? \'win\' : \'not\';',
+ ]))
+ ->mustRun()
+ ->getOutput());
+ Assert::stringNotEmpty($winOrNot, 'Could not determine PHP version');
+
+ return $winOrNot === 'win' ? OperatingSystem::Windows : OperatingSystem::NonWindows;
+ }
+
+ /** @return non-empty-string */
+ public function version(): string
+ {
+ $phpVersion = trim((new Process([
+ $this->phpBinaryPath,
+ '-r',
+ 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "." . PHP_RELEASE_VERSION;',
+ ]))
+ ->mustRun()
+ ->getOutput());
+ Assert::stringNotEmpty($phpVersion, 'Could not determine PHP version');
+
+ // normalizing the version will throw an exception if it is not a valid version
+ (new VersionParser())->normalize($phpVersion);
+
+ return $phpVersion;
+ }
+
+ /** @return non-empty-string */
+ public function majorMinorVersion(): string
+ {
+ $phpVersion = trim((new Process([
+ $this->phpBinaryPath,
+ '-r',
+ 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;',
+ ]))
+ ->mustRun()
+ ->getOutput());
+ Assert::stringNotEmpty($phpVersion, 'Could not determine PHP version');
+
+ // normalizing the version will throw an exception if it is not a valid version
+ (new VersionParser())->normalize($phpVersion);
+
+ return $phpVersion;
+ }
+
+ public function machineType(): Architecture
+ {
+ $phpMachineType = trim((new Process([
+ $this->phpBinaryPath,
+ '-r',
+ 'echo php_uname("m");',
+ ]))
+ ->mustRun()
+ ->getOutput());
+ Assert::stringNotEmpty($phpMachineType, 'Could not determine PHP machine type');
+
+ return Architecture::parseArchitecture($phpMachineType);
+ }
+
+ public function phpIntSize(): int
+ {
+ $phpIntSize = trim((new Process([
+ $this->phpBinaryPath,
+ '-r',
+ 'echo PHP_INT_SIZE;',
+ ]))
+ ->mustRun()
+ ->getOutput());
+ Assert::stringNotEmpty($phpIntSize, 'Could not fetch PHP_INT_SIZE');
+ Assert::same($phpIntSize, (string) (int) $phpIntSize, 'PHP_INT_SIZE was not an integer processed %2$s from %s');
+
+ return (int) $phpIntSize;
+ }
+
+ /** @return non-empty-string */
+ public function phpinfo(): string
+ {
+ $phpInfo = trim((new Process([
+ $this->phpBinaryPath,
+ '-i',
+ ]))
+ ->mustRun()
+ ->getOutput());
+
+ Assert::stringNotEmpty($phpInfo, sprintf('Could not run phpinfo using %s', $this->phpBinaryPath));
+
+ return $phpInfo;
+ }
+
+ public static function fromPhpConfigExecutable(string $phpConfig): self
+ {
+ $phpExecutable = trim((new Process([$phpConfig, '--php-binary']))
+ ->mustRun()
+ ->getOutput());
+ Assert::stringNotEmpty($phpExecutable, 'Could not find path to PHP executable.');
+
+ return new self($phpExecutable);
+ }
+
+ /** @param non-empty-string $phpBinary */
+ public static function fromPhpBinaryPath(string $phpBinary): self
+ {
+ return new self($phpBinary);
+ }
+
+ public static function fromCurrentProcess(): self
+ {
+ $phpExecutable = trim((string) (new PhpExecutableFinder())->find());
+ Assert::stringNotEmpty($phpExecutable, 'Could not find path to PHP executable.');
+
+ return new self($phpExecutable);
+ }
+}
diff --git a/src/Platform/TargetPhp/PhpBinaryPathBasedPlatformRepository.php b/src/Platform/TargetPhp/PhpBinaryPathBasedPlatformRepository.php
new file mode 100644
index 0000000..b9e9d65
--- /dev/null
+++ b/src/Platform/TargetPhp/PhpBinaryPathBasedPlatformRepository.php
@@ -0,0 +1,67 @@
+versionParser = new VersionParser();
+ $this->packages = [];
+
+ $phpVersion = $phpBinaryPath->version();
+ $php = new CompletePackage('php', $this->versionParser->normalize($phpVersion), $phpVersion);
+ $php->setDescription('The PHP interpreter');
+ $this->addPackage($php);
+
+ $extVersions = $phpBinaryPath->extensions();
+
+ foreach ($extVersions as $extension => $extensionVersion) {
+ $this->addPackage($this->packageForExtension($extension, $extensionVersion));
+ }
+
+ parent::__construct();
+ }
+
+ private function packageForExtension(string $name, string $prettyVersion): CompletePackage
+ {
+ $extraDescription = '';
+
+ try {
+ $version = $this->versionParser->normalize($prettyVersion);
+ } catch (UnexpectedValueException) {
+ $extraDescription = ' (actual version: ' . $prettyVersion . ')';
+ if (Preg::isMatchStrictGroups('{^(\d+\.\d+\.\d+(?:\.\d+)?)}', $prettyVersion, $match)) {
+ $prettyVersion = $match[1];
+ } else {
+ $prettyVersion = '0';
+ }
+
+ $version = $this->versionParser->normalize($prettyVersion);
+ }
+
+ $package = new CompletePackage(
+ 'ext-' . str_replace(' ', '-', strtolower($name)),
+ $version,
+ $prettyVersion,
+ );
+ $package->setDescription('The ' . $name . ' PHP extension' . $extraDescription);
+ $package->setType('php-ext');
+
+ return $package;
+ }
+}
diff --git a/src/Platform/TargetPhp/ResolveTargetPhpToPlatformRepository.php b/src/Platform/TargetPhp/ResolveTargetPhpToPlatformRepository.php
new file mode 100644
index 0000000..b1abf1e
--- /dev/null
+++ b/src/Platform/TargetPhp/ResolveTargetPhpToPlatformRepository.php
@@ -0,0 +1,16 @@
+operatingSystem();
+
+ $phpinfo = $phpBinaryPath->phpinfo();
+
+ $architecture = $phpBinaryPath->machineType();
+
+ // If we're not on ARM, a more reliable way of determining 32-bit/64-bit is to use PHP_INT_SIZE
+ if ($architecture !== Architecture::arm64) {
+ $architecture = $phpBinaryPath->phpIntSize() === 4 ? Architecture::x86 : Architecture::x86_64;
+ }
+
+ /**
+ * Based on xdebug.org wizard, copyright Derick Rethans, used under MIT licence
+ *
+ * @link https://github.com/xdebug/xdebug.org/blob/aff649f2c3ca303ad471e6ed9dd29c0db16d3e22/src/XdebugVersion.php#L186-L190
+ */
+ if (
+ preg_match('/Architecture([ =>\t]*)(x[0-9]*)/', $phpinfo, $m)
+ && array_key_exists(2, $m)
+ && $m[2] !== ''
+ ) {
+ $architecture = Architecture::parseArchitecture($m[2]);
+ }
+
+ $windowsCompiler = null;
+ $threadSafety = ThreadSafetyMode::ThreadSafe;
+
+ /**
+ * Based on xdebug.org wizard, copyright Derick Rethans, used under MIT licence
+ *
+ * @link https://github.com/xdebug/xdebug.org/blob/aff649f2c3ca303ad471e6ed9dd29c0db16d3e22/src/XdebugVersion.php#L276-L299
+ */
+ if (preg_match('/PHP Extension Build([ =>\t]+)(API.*)/', $phpinfo, $m)) {
+ $parts = explode(',', trim($m[2]));
+ foreach ($parts as $part) {
+ switch ($part) {
+ case 'NTS':
+ $threadSafety = ThreadSafetyMode::NonThreadSafe;
+ break;
+ case 'TS':
+ $threadSafety = ThreadSafetyMode::ThreadSafe;
+ break;
+ case 'VC6':
+ $windowsCompiler = WindowsCompiler::VC6;
+ break;
+ case 'VC8':
+ $windowsCompiler = WindowsCompiler::VC8;
+ break;
+ case 'VC9':
+ $windowsCompiler = WindowsCompiler::VC9;
+ break;
+ case 'VC11':
+ $windowsCompiler = WindowsCompiler::VC11;
+ break;
+ case 'VC14':
+ $windowsCompiler = WindowsCompiler::VC14;
+ break;
+ case 'VC15':
+ $windowsCompiler = WindowsCompiler::VC15;
+ break;
+ case 'VS16':
+ $windowsCompiler = WindowsCompiler::VS16;
+ break;
+ }
+ }
+ }
+
+ return new self(
+ $os,
+ $phpBinaryPath,
+ $architecture,
+ $threadSafety,
+ $windowsCompiler,
+ );
+ }
+}
diff --git a/src/Platform/ThreadSafetyMode.php b/src/Platform/ThreadSafetyMode.php
new file mode 100644
index 0000000..f5fd923
--- /dev/null
+++ b/src/Platform/ThreadSafetyMode.php
@@ -0,0 +1,20 @@
+ 'ts',
+ self::NonThreadSafe => 'nts',
+ };
+ }
+}
diff --git a/src/Platform/WindowsCompiler.php b/src/Platform/WindowsCompiler.php
new file mode 100644
index 0000000..607925b
--- /dev/null
+++ b/src/Platform/WindowsCompiler.php
@@ -0,0 +1,17 @@
+commandTester = new CommandTester(Container::factory()->get(DownloadCommand::class));
+ }
+
+ /**
+ * @return array
+ *
+ * @psalm-suppress PossiblyUnusedMethod https://github.com/psalm/psalm-plugin-phpunit/issues/131
+ */
+ public static function validVersionsList(): array
+ {
+ $versionsAndExpected = [
+ [self::TEST_PACKAGE, self::TEST_PACKAGE . ':1.0.1'],
+ [self::TEST_PACKAGE . ':^1.0', self::TEST_PACKAGE . ':1.0.1'],
+ [self::TEST_PACKAGE . ':1.0.1-alpha.3@alpha', self::TEST_PACKAGE . ':1.0.1-alpha.3'],
+ [self::TEST_PACKAGE . ':*', self::TEST_PACKAGE . ':1.0.1'],
+ [self::TEST_PACKAGE . ':~1.0.0@alpha', self::TEST_PACKAGE . ':1.0.1'],
+ [self::TEST_PACKAGE . ':^1.1.0@alpha', self::TEST_PACKAGE . ':1.1.0-alpha.1'],
+ [self::TEST_PACKAGE . ':~1.0.0', self::TEST_PACKAGE . ':1.0.1'],
+ // @todo https://github.com/php/pie/issues/13 - in theory, these could work, on NonWindows at least
+ // [self::TEST_PACKAGE . ':dev-main', self::TEST_PACKAGE . ':???'],
+ // [self::TEST_PACKAGE . ':dev-main#769f906413d6d1e12152f6d34134cbcd347ca253', self::TEST_PACKAGE . ':???'],
+ ];
+
+ return array_combine(
+ array_map(static fn ($item) => $item[0], $versionsAndExpected),
+ $versionsAndExpected,
+ );
+ }
+
+ #[DataProvider('validVersionsList')]
+ public function testDownloadCommandWillDownloadCompatibleExtension(string $requestedVersion, string $expectedVersion): void
+ {
+ if (PHP_VERSION_ID < 80300 || PHP_VERSION_ID >= 80400) {
+ self::markTestSkipped('This test can only run on PHP 8.3 - you are running ' . PHP_VERSION);
+ }
+
+ $this->commandTester->execute(['requested-package-and-version' => $requestedVersion]);
+
+ $this->commandTester->assertCommandIsSuccessful();
+
+ $outputString = $this->commandTester->getDisplay();
+ self::assertStringContainsString('Found package: ' . $expectedVersion . ' which provides', $outputString);
+ self::assertStringContainsString('Extracted ' . $expectedVersion . ' source to', $outputString);
+ }
+
+ #[DataProvider('validVersionsList')]
+ public function testDownloadingWithPhpConfig(string $requestedVersion, string $expectedVersion): void
+ {
+ // @todo This test makes an assumption you're using `ppa:ondrej/php` to have multiple PHP versions. This allows
+ // us to test scenarios where you run with PHP 8.1 but want to install to a PHP 8.3 instance, for example.
+ // However, this test isn't very portable, and won't run in CI, so we could do with improving this later.
+ $phpConfigPath = '/usr/bin/php-config8.3';
+
+ if (! file_exists($phpConfigPath) || ! is_executable($phpConfigPath)) {
+ self::markTestSkipped('This test can only run where "' . $phpConfigPath . '" exists and is executable, to target PHP 8.3');
+ }
+
+ $this->commandTester->execute([
+ '--with-php-config' => $phpConfigPath,
+ 'requested-package-and-version' => $requestedVersion,
+ ]);
+
+ $this->commandTester->assertCommandIsSuccessful();
+
+ $outputString = $this->commandTester->getDisplay();
+ self::assertStringContainsString('Found package: ' . $expectedVersion . ' which provides', $outputString);
+ self::assertStringContainsString('Extracted ' . $expectedVersion . ' source to', $outputString);
+ }
+
+ #[DataProvider('validVersionsList')]
+ public function testDownloadingWithPhpPath(string $requestedVersion, string $expectedVersion): void
+ {
+ // @todo This test makes an assumption you're using `ppa:ondrej/php` to have multiple PHP versions. This allows
+ // us to test scenarios where you run with PHP 8.1 but want to install to a PHP 8.3 instance, for example.
+ // However, this test isn't very portable, and won't run in CI, so we could do with improving this later.
+ $phpBinaryPath = '/usr/bin/php8.3';
+
+ if (! file_exists($phpBinaryPath) || ! is_executable($phpBinaryPath)) {
+ self::markTestSkipped('This test can only run where "' . $phpBinaryPath . '" exists and is executable, to target PHP 8.3');
+ }
+
+ $this->commandTester->execute([
+ '--with-php-path' => $phpBinaryPath,
+ 'requested-package-and-version' => $requestedVersion,
+ ]);
+
+ $this->commandTester->assertCommandIsSuccessful();
+
+ $outputString = $this->commandTester->getDisplay();
+ self::assertStringContainsString('Found package: ' . $expectedVersion . ' which provides', $outputString);
+ self::assertStringContainsString('Extracted ' . $expectedVersion . ' source to', $outputString);
+ }
+
+ public function testDownloadCommandFailsWhenUsingIncompatiblePhpVersion(): void
+ {
+ if (PHP_VERSION_ID >= 80200) {
+ self::markTestSkipped('This test can only run on older than PHP 8.2 - you are running ' . PHP_VERSION);
+ }
+
+ $this->expectException(UnableToResolveRequirement::class);
+ // 1.0.0 is only compatible with PHP 8.3.0
+ $this->commandTester->execute(['requested-package-and-version' => self::TEST_PACKAGE . ':1.0.0']);
+ }
+}
diff --git a/test/unit/Command/DownloadCommandTest.php b/test/unit/Command/DownloadCommandTest.php
deleted file mode 100644
index 8b68344..0000000
--- a/test/unit/Command/DownloadCommandTest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-createMock(InputInterface::class);
- $output = $this->createMock(OutputInterface::class);
-
- $output->expects(self::once())
- ->method('writeln')
- ->with('to do');
-
- $command = new DownloadCommand();
- self::assertSame(0, $command->execute($input, $output));
- }
-}
diff --git a/test/unit/DependencyResolver/PackageTest.php b/test/unit/DependencyResolver/PackageTest.php
new file mode 100644
index 0000000..1454286
--- /dev/null
+++ b/test/unit/DependencyResolver/PackageTest.php
@@ -0,0 +1,41 @@
+extensionName->name());
+ self::assertSame('vendor/foo', $package->name);
+ self::assertSame('1.2.3', $package->version);
+ self::assertSame('vendor/foo:1.2.3', $package->prettyNameAndVersion());
+ self::assertNull($package->downloadUrl);
+ }
+
+ public function testFromComposerCompletePackageWithExtensionName(): void
+ {
+ $composerCompletePackage = new CompletePackage('vendor/foo', '1.2.3.0', '1.2.3');
+ $composerCompletePackage->setPhpExt(['extension-name' => 'ext-something_else']);
+
+ $package = Package::fromComposerCompletePackage($composerCompletePackage);
+
+ self::assertSame('something_else', $package->extensionName->name());
+ self::assertSame('vendor/foo', $package->name);
+ self::assertSame('1.2.3', $package->version);
+ self::assertSame('vendor/foo:1.2.3', $package->prettyNameAndVersion());
+ self::assertNull($package->downloadUrl);
+ }
+}
diff --git a/test/unit/DependencyResolver/ResolveDependencyWithComposerTest.php b/test/unit/DependencyResolver/ResolveDependencyWithComposerTest.php
new file mode 100644
index 0000000..604911a
--- /dev/null
+++ b/test/unit/DependencyResolver/ResolveDependencyWithComposerTest.php
@@ -0,0 +1,105 @@
+repositorySet = new RepositorySet();
+ $this->repositorySet->addRepository(new CompositeRepository(RepositoryFactory::defaultReposWithDefaultManager(new NullIO())));
+
+ $this->resolveTargetPhpToPlatformRepository = new ResolveTargetPhpToPlatformRepository();
+ }
+
+ public function testPackageThatCanBeResolved(): void
+ {
+ $phpBinaryPath = $this->createMock(PhpBinaryPath::class);
+ $phpBinaryPath->expects(self::any())
+ ->method('version')
+ ->willReturn('8.3.0');
+
+ $targetPlatform = new TargetPlatform(
+ OperatingSystem::NonWindows,
+ $phpBinaryPath,
+ Architecture::x86_64,
+ ThreadSafetyMode::ThreadSafe,
+ null,
+ );
+
+ $package = (new ResolveDependencyWithComposer(
+ $this->repositorySet,
+ $this->resolveTargetPhpToPlatformRepository,
+ ))($targetPlatform, 'asgrim/example-pie-extension', '^1.0');
+
+ self::assertSame('asgrim/example-pie-extension', $package->name);
+ self::assertStringStartsWith('1.', $package->version);
+ }
+
+ /**
+ * @return array, 1: string, 2: string}>
+ *
+ * @psalm-suppress PossiblyUnusedMethod https://github.com/psalm/psalm-plugin-phpunit/issues/131
+ */
+ public static function unresolvableDependencies(): array
+ {
+ return [
+ 'phpVersionTooOld' => [['php' => '8.1.0'], 'asgrim/example-pie-extension', '1.0.0'],
+ 'phpVersionTooNew' => [['php' => '8.4.0'], 'asgrim/example-pie-extension', '1.0.0'],
+ 'notAPhpExtension' => [['php' => '8.3.0'], 'ramsey/uuid', '^4.7'],
+ ];
+ }
+
+ /** @param array $platformOverrides */
+ #[DataProvider('unresolvableDependencies')]
+ public function testPackageThatCannotBeResolvedThrowsException(array $platformOverrides, string $package, string $version): void
+ {
+ $phpBinaryPath = $this->createMock(PhpBinaryPath::class);
+ $phpBinaryPath->expects(self::once())
+ ->method('version')
+ ->willReturn($platformOverrides['php']);
+
+ $targetPlatform = new TargetPlatform(
+ OperatingSystem::NonWindows,
+ $phpBinaryPath,
+ Architecture::x86_64,
+ ThreadSafetyMode::ThreadSafe,
+ null,
+ );
+
+ $this->expectException(UnableToResolveRequirement::class);
+
+ (new ResolveDependencyWithComposer(
+ $this->repositorySet,
+ $this->resolveTargetPhpToPlatformRepository,
+ ))(
+ $targetPlatform,
+ $package,
+ $version,
+ );
+ }
+}
diff --git a/test/unit/DependencyResolver/UnableToResolveRequirementTest.php b/test/unit/DependencyResolver/UnableToResolveRequirementTest.php
new file mode 100644
index 0000000..e79b3ae
--- /dev/null
+++ b/test/unit/DependencyResolver/UnableToResolveRequirementTest.php
@@ -0,0 +1,48 @@
+createMock(PackageInterface::class);
+ $package->method('getName')->willReturn('baz/bat');
+
+ $exception = UnableToResolveRequirement::toPhpOrZendExtension($package, 'foo/bar', '^1.2');
+
+ self::assertSame('Package baz/bat was not of type php-ext or php-ext-zend (requested foo/bar for version ^1.2).', $exception->getMessage());
+ }
+
+ public function testToPhpOrZendExtensionWithoutVersion(): void
+ {
+ $package = $this->createMock(PackageInterface::class);
+ $package->method('getName')->willReturn('baz/bat');
+
+ $exception = UnableToResolveRequirement::toPhpOrZendExtension($package, 'foo/bar', null);
+
+ self::assertSame('Package baz/bat was not of type php-ext or php-ext-zend (requested foo/bar).', $exception->getMessage());
+ }
+
+ public function testFromRequirementWithVersion(): void
+ {
+ $exception = UnableToResolveRequirement::fromRequirement('foo/bar', '^1.2');
+
+ self::assertSame('Unable to find an installable package foo/bar for version ^1.2.', $exception->getMessage());
+ }
+
+ public function testFromRequirementWithoutVersion(): void
+ {
+ $exception = UnableToResolveRequirement::fromRequirement('foo/bar', null);
+
+ self::assertSame('Unable to find an installable package foo/bar.', $exception->getMessage());
+ }
+}
diff --git a/test/unit/Downloading/AddAuthenticationHeaderTest.php b/test/unit/Downloading/AddAuthenticationHeaderTest.php
new file mode 100644
index 0000000..854773c
--- /dev/null
+++ b/test/unit/Downloading/AddAuthenticationHeaderTest.php
@@ -0,0 +1,57 @@
+createMock(AuthHelper::class);
+ $authHelper->expects(self::once())
+ ->method('addAuthenticationHeader')
+ ->with([], 'github.com', $downloadUrl)
+ ->willReturn(['Authorization: whatever ABC123']);
+
+ $requestWithAuthHeader = (new AddAuthenticationHeader())->withAuthHeaderFromComposer(
+ $request,
+ new Package(ExtensionName::normaliseFromString('foo'), 'foo/bar', '1.2.3', $downloadUrl),
+ $authHelper,
+ );
+
+ self::assertSame('whatever ABC123', $requestWithAuthHeader->getHeaderLine('Authorization'));
+ }
+
+ public function testExceptionIsThrownWhenPackageDoesNotHaveDownloadUrl(): void
+ {
+ $downloadUrl = 'http://test-uri/' . uniqid('path', true);
+
+ $request = new Request('GET', $downloadUrl);
+
+ $authHelper = $this->createMock(AuthHelper::class);
+
+ $addAuthenticationHeader = new AddAuthenticationHeader();
+ $package = new Package(ExtensionName::normaliseFromString('foo'), 'foo/bar', '1.2.3', null);
+
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('The package foo/bar does not have a download URL');
+ $addAuthenticationHeader->withAuthHeaderFromComposer($request, $package, $authHelper);
+ }
+}
diff --git a/test/unit/Downloading/AssertHttpTest.php b/test/unit/Downloading/AssertHttpTest.php
new file mode 100644
index 0000000..7568b51
--- /dev/null
+++ b/test/unit/Downloading/AssertHttpTest.php
@@ -0,0 +1,24 @@
+expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('Expected HTTP 201 response, got 404 - response: some body content');
+ AssertHttp::responseStatusCode(201, $response);
+ }
+}
diff --git a/test/unit/Downloading/DownloadZipWithGuzzleTest.php b/test/unit/Downloading/DownloadZipWithGuzzleTest.php
new file mode 100644
index 0000000..ac58dcb
--- /dev/null
+++ b/test/unit/Downloading/DownloadZipWithGuzzleTest.php
@@ -0,0 +1,54 @@
+ 'application/octet-stream',
+ 'Content-length' => (string) (strlen($fakeZipContent)),
+ ],
+ $fakeZipContent,
+ ),
+ ]);
+
+ $guzzleMockClient = new Client(['handler' => HandlerStack::create($mockHandler)]);
+
+ $localPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('pie_test_', true);
+ mkdir($localPath, 0777, true);
+ $downloadedZipFile = (new DownloadZipWithGuzzle($guzzleMockClient))
+ ->downloadZipAndReturnLocalPath(
+ new Request('GET', 'http://test-uri/'),
+ $localPath,
+ );
+
+ self::assertSame($fakeZipContent, file_get_contents($downloadedZipFile));
+ }
+}
diff --git a/test/unit/Downloading/DownloadedPackageTest.php b/test/unit/Downloading/DownloadedPackageTest.php
new file mode 100644
index 0000000..5ead146
--- /dev/null
+++ b/test/unit/Downloading/DownloadedPackageTest.php
@@ -0,0 +1,29 @@
+extractedSourcePath);
+ self::assertSame($package, $downloadedPackage->package);
+ }
+}
diff --git a/test/unit/Downloading/Exception/CouldNotFindReleaseAssetTest.php b/test/unit/Downloading/Exception/CouldNotFindReleaseAssetTest.php
new file mode 100644
index 0000000..b03a335
--- /dev/null
+++ b/test/unit/Downloading/Exception/CouldNotFindReleaseAssetTest.php
@@ -0,0 +1,52 @@
+getMessage());
+ }
+
+ public function testForPackageWithMissingTag(): void
+ {
+ $package = new Package(ExtensionName::normaliseFromString('foo'), 'foo/bar', '1.2.3', null);
+
+ $exception = CouldNotFindReleaseAsset::forPackageWithMissingTag($package);
+
+ self::assertSame('Could not find release by tag name for foo/bar:1.2.3', $exception->getMessage());
+ }
+
+ public function testForMissingWindowsCompiler(): void
+ {
+ $phpBinary = PhpBinaryPath::fromCurrentProcess();
+ $exception = CouldNotFindReleaseAsset::forMissingWindowsCompiler(new TargetPlatform(
+ OperatingSystem::NonWindows,
+ $phpBinary,
+ Architecture::x86,
+ ThreadSafetyMode::NonThreadSafe,
+ null,
+ ));
+
+ self::assertSame('Could not determine Windows Compiler for PHP ' . $phpBinary->version() . ' on NonWindows', $exception->getMessage());
+ }
+}
diff --git a/test/unit/Downloading/ExtractZipTest.php b/test/unit/Downloading/ExtractZipTest.php
new file mode 100644
index 0000000..cbd8a3a
--- /dev/null
+++ b/test/unit/Downloading/ExtractZipTest.php
@@ -0,0 +1,47 @@
+to(__DIR__ . '/../../assets/test-zip.zip', $localPath);
+
+ // The test-zip.zip should contain a deterministic file content for this:
+ self::assertSame('Hello there! Test UUID b925c59b-3e6f-4e45-8029-19431df18de4', file_get_contents($extractedPath . DIRECTORY_SEPARATOR . 'test-file.txt'));
+ }
+
+ public function testFailureToExtractZipWithInvalidZip(): void
+ {
+ $localPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('pie_test_DO_NOT_EXIST_', true);
+
+ $extr = new ExtractZip();
+
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage(sprintf('Could not open ZIP [%d]: %s', ZipArchive::ER_NOZIP, __FILE__));
+ $extr->to(__FILE__, $localPath);
+ }
+}
diff --git a/test/unit/Downloading/GithubPackageReleaseAssetsTest.php b/test/unit/Downloading/GithubPackageReleaseAssetsTest.php
new file mode 100644
index 0000000..7c7efd4
--- /dev/null
+++ b/test/unit/Downloading/GithubPackageReleaseAssetsTest.php
@@ -0,0 +1,152 @@
+createMock(PhpBinaryPath::class);
+ $phpBinaryPath->expects(self::any())
+ ->method('majorMinorVersion')
+ ->willReturn('8.3');
+
+ $targetPlatform = new TargetPlatform(
+ OperatingSystem::Windows,
+ $phpBinaryPath,
+ Architecture::x86,
+ ThreadSafetyMode::ThreadSafe,
+ WindowsCompiler::VC14,
+ );
+
+ $authHelper = $this->createMock(AuthHelper::class);
+
+ $mockHandler = new MockHandler([
+ new Response(
+ 200,
+ [],
+ json_encode([
+ 'assets' => [
+ [
+ 'name' => 'php_foo-1.2.3-8.3-vc14-nts-x86.zip',
+ 'browser_download_url' => 'wrong_download_url',
+ ],
+ [
+ 'name' => 'php_foo-1.2.3-8.3-vc14-ts-x86.zip',
+ 'browser_download_url' => 'actual_download_url',
+ ],
+ ],
+ ]),
+ ),
+ ]);
+
+ $guzzleMockClient = new Client(['handler' => HandlerStack::create($mockHandler)]);
+
+ $package = new Package(ExtensionName::normaliseFromString('foo'), 'asgrim/example-pie-extension', '1.2.3', 'https://test-uri/' . uniqid('downloadUrl', true));
+
+ $releaseAssets = new GithubPackageReleaseAssets($authHelper, $guzzleMockClient, 'https://test-github-api-base-url.thephp.foundation');
+
+ self::assertSame('actual_download_url', $releaseAssets->findWindowsDownloadUrlForPackage($targetPlatform, $package));
+ }
+
+ public function testUrlIsReturnedWhenFindingWindowsDownloadUrlWithCompilerAndThreadSafetySwapped(): void
+ {
+ $phpBinaryPath = $this->createMock(PhpBinaryPath::class);
+ $phpBinaryPath->expects(self::any())
+ ->method('majorMinorVersion')
+ ->willReturn('8.3');
+
+ $targetPlatform = new TargetPlatform(
+ OperatingSystem::Windows,
+ $phpBinaryPath,
+ Architecture::x86,
+ ThreadSafetyMode::ThreadSafe,
+ WindowsCompiler::VC14,
+ );
+
+ $authHelper = $this->createMock(AuthHelper::class);
+
+ $mockHandler = new MockHandler([
+ new Response(
+ 200,
+ [],
+ json_encode([
+ 'assets' => [
+ [
+ 'name' => 'php_foo-1.2.3-8.3-nts-vc14-x86.zip',
+ 'browser_download_url' => 'wrong_download_url',
+ ],
+ [
+ 'name' => 'php_foo-1.2.3-8.3-ts-vc14-x86.zip',
+ 'browser_download_url' => 'actual_download_url',
+ ],
+ ],
+ ]),
+ ),
+ ]);
+
+ $guzzleMockClient = new Client(['handler' => HandlerStack::create($mockHandler)]);
+
+ $package = new Package(ExtensionName::normaliseFromString('foo'), 'asgrim/example-pie-extension', '1.2.3', 'https://test-uri/' . uniqid('downloadUrl', true));
+
+ $releaseAssets = new GithubPackageReleaseAssets($authHelper, $guzzleMockClient, 'https://test-github-api-base-url.thephp.foundation');
+
+ self::assertSame('actual_download_url', $releaseAssets->findWindowsDownloadUrlForPackage($targetPlatform, $package));
+ }
+
+ public function testFindWindowsDownloadUrlForPackageThrowsExceptionWhenAssetNotFound(): void
+ {
+ $targetPlatform = new TargetPlatform(
+ OperatingSystem::Windows,
+ PhpBinaryPath::fromCurrentProcess(),
+ Architecture::x86,
+ ThreadSafetyMode::ThreadSafe,
+ WindowsCompiler::VC14,
+ );
+
+ $authHelper = $this->createMock(AuthHelper::class);
+
+ $mockHandler = new MockHandler([
+ new Response(
+ 200,
+ [],
+ json_encode([
+ 'assets' => [],
+ ]),
+ ),
+ ]);
+
+ $guzzleMockClient = new Client(['handler' => HandlerStack::create($mockHandler)]);
+
+ $package = new Package(ExtensionName::normaliseFromString('foo'), 'asgrim/example-pie-extension', '1.2.3', 'https://test-uri/' . uniqid('downloadUrl', true));
+
+ $releaseAssets = new GithubPackageReleaseAssets($authHelper, $guzzleMockClient, 'https://test-github-api-base-url.thephp.foundation');
+
+ $this->expectException(CouldNotFindReleaseAsset::class);
+ $releaseAssets->findWindowsDownloadUrlForPackage($targetPlatform, $package);
+ }
+}
diff --git a/test/unit/Downloading/PathTest.php b/test/unit/Downloading/PathTest.php
new file mode 100644
index 0000000..8797f86
--- /dev/null
+++ b/test/unit/Downloading/PathTest.php
@@ -0,0 +1,21 @@
+createMock(DownloadZip::class);
+ $extractZip = $this->createMock(ExtractZip::class);
+ $authHelper = $this->createMock(AuthHelper::class);
+ $unixDownloadAndExtract = new UnixDownloadAndExtract($downloadZip, $extractZip, $authHelper);
+
+ $tmpZipFile = uniqid('tmpZipFile', true);
+ $extractedPath = uniqid('extractedPath', true);
+
+ $downloadZip->expects(self::once())
+ ->method('downloadZipAndReturnLocalPath')
+ ->with(
+ self::isInstanceOf(RequestInterface::class),
+ self::isType('string'),
+ )
+ ->willReturn($tmpZipFile);
+
+ $extractZip->expects(self::once())
+ ->method('to')
+ ->with(
+ $tmpZipFile,
+ self::isType('string'),
+ )
+ ->willReturn($extractedPath);
+
+ $downloadUrl = 'https://test-uri/' . uniqid('downloadUrl', true);
+ $requestedPackage = new Package(ExtensionName::normaliseFromString('foo'), 'foo/bar', '1.2.3', $downloadUrl);
+
+ $downloadedPackage = $unixDownloadAndExtract->__invoke($targetPlatform, $requestedPackage);
+
+ self::assertSame($requestedPackage, $downloadedPackage->package);
+ self::assertSame($extractedPath, $downloadedPackage->extractedSourcePath);
+ }
+}
diff --git a/test/unit/Downloading/WindowsDownloadAndExtractTest.php b/test/unit/Downloading/WindowsDownloadAndExtractTest.php
new file mode 100644
index 0000000..448bdd5
--- /dev/null
+++ b/test/unit/Downloading/WindowsDownloadAndExtractTest.php
@@ -0,0 +1,84 @@
+createMock(DownloadZip::class);
+ $extractZip = $this->createMock(ExtractZip::class);
+ $authHelper = $this->createMock(AuthHelper::class);
+ $packageReleaseAssets = $this->createMock(PackageReleaseAssets::class);
+ $windowsDownloadAndExtract = new WindowsDownloadAndExtract(
+ $downloadZip,
+ $extractZip,
+ $authHelper,
+ $packageReleaseAssets,
+ );
+
+ $packageReleaseAssets->expects(self::once())
+ ->method('findWindowsDownloadUrlForPackage')
+ ->with($targetPlatform, self::isInstanceOf(Package::class))
+ ->willReturn(uniqid('windowsDownloadUrl', true));
+
+ $tmpZipFile = uniqid('tmpZipFile', true);
+ $extractedPath = uniqid('extractedPath', true);
+
+ $downloadZip->expects(self::once())
+ ->method('downloadZipAndReturnLocalPath')
+ ->with(
+ self::isInstanceOf(RequestInterface::class),
+ self::isType('string'),
+ )
+ ->willReturn($tmpZipFile);
+
+ $extractZip->expects(self::once())
+ ->method('to')
+ ->with(
+ $tmpZipFile,
+ self::isType('string'),
+ )
+ ->willReturn($extractedPath);
+
+ $requestedPackage = new Package(ExtensionName::normaliseFromString('foo'), 'foo/bar', '1.2.3', 'https://test-uri/' . uniqid('downloadUrl', true));
+
+ $downloadedPackage = $windowsDownloadAndExtract->__invoke($targetPlatform, $requestedPackage);
+
+ self::assertSame($requestedPackage, $downloadedPackage->package);
+ self::assertStringContainsString(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'pie_downloader_', $downloadedPackage->extractedSourcePath);
+ }
+}
diff --git a/test/unit/ExtensionNameTest.php b/test/unit/ExtensionNameTest.php
new file mode 100644
index 0000000..da3e657
--- /dev/null
+++ b/test/unit/ExtensionNameTest.php
@@ -0,0 +1,104 @@
+
+ *
+ * @psalm-suppress PossiblyUnusedMethod https://github.com/psalm/psalm-plugin-phpunit/issues/131
+ */
+ public static function validExtensionNamesProvider(): array
+ {
+ return [
+ 'ext-sodium' => ['ext-sodium', 'sodium'],
+ 'sodium' => ['sodium', 'sodium'],
+ ];
+ }
+
+ #[DataProvider('validExtensionNamesProvider')]
+ public function testValidExtensionNames(string $givenExtensionName, string $expectedNormalisedName): void
+ {
+ $extensionName = ExtensionName::normaliseFromString($givenExtensionName);
+
+ self::assertSame($expectedNormalisedName, $extensionName->name());
+ self::assertSame('ext-' . $expectedNormalisedName, $extensionName->nameWithExtPrefix());
+ }
+
+ /**
+ * @return array
+ *
+ * @psalm-suppress PossiblyUnusedMethod https://github.com/psalm/psalm-plugin-phpunit/issues/131
+ */
+ public static function invalidExtensionNamesProvider(): array
+ {
+ $invalidExtensionNames = ['', 'kebab-case', 'money$ext'];
+
+ return array_combine($invalidExtensionNames, array_map(static fn ($extensionName) => [$extensionName], $invalidExtensionNames));
+ }
+
+ #[DataProvider('invalidExtensionNamesProvider')]
+ public function testInvalidExtensionNames(string $invalidExtensionName): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage(<<setPhpExt(['extension-name' => 'ext-something_else']);
+
+ self::assertSame('something_else', ExtensionName::determineFromComposerPackage($composerCompletePackage)->name());
+ }
+
+ public function testFromComposerPackageWithInvalidPhpExtExtensionNameType(): void
+ {
+ $composerCompletePackage = new CompletePackage('vendor/foo', '1.2.3.0', '1.2.3');
+ /** @psalm-suppress InvalidArgument - the type says otherwise, but in runtime, this might be possible in theory */
+ $composerCompletePackage->setPhpExt(['extension-name' => null]);
+
+ self::assertSame('foo', ExtensionName::determineFromComposerPackage($composerCompletePackage)->name());
+ }
+
+ public function testFromComposerPackageWithEmptyPhpExtExtensionName(): void
+ {
+ $composerCompletePackage = new CompletePackage('vendor/foo', '1.2.3.0', '1.2.3');
+ $composerCompletePackage->setPhpExt(['extension-name' => '']);
+
+ self::assertSame('foo', ExtensionName::determineFromComposerPackage($composerCompletePackage)->name());
+ }
+
+ public function testFromComposerPackageWithoutPhpExtExtensionName(): void
+ {
+ $composerCompletePackage = new CompletePackage('vendor/foo', '1.2.3.0', '1.2.3');
+ $composerCompletePackage->setPhpExt(['priority' => 80]);
+
+ self::assertSame('foo', ExtensionName::determineFromComposerPackage($composerCompletePackage)->name());
+ }
+
+ public function testFromComposerPackageWithoutPhpExt(): void
+ {
+ $composerCompletePackage = new CompletePackage('vendor/foo', '1.2.3.0', '1.2.3');
+
+ self::assertSame('foo', ExtensionName::determineFromComposerPackage($composerCompletePackage)->name());
+ }
+}
diff --git a/test/unit/Platform/ArchitectureTest.php b/test/unit/Platform/ArchitectureTest.php
new file mode 100644
index 0000000..d785786
--- /dev/null
+++ b/test/unit/Platform/ArchitectureTest.php
@@ -0,0 +1,38 @@
+
+ *
+ * @psalm-suppress PossiblyUnusedMethod https://github.com/psalm/psalm-plugin-phpunit/issues/131
+ */
+ public static function architectureMapProvider(): array
+ {
+ return [
+ 'x64' => ['x64', Architecture::x86_64],
+ 'x86_64' => ['x86_64', Architecture::x86_64],
+ 'AMD64' => ['AMD64', Architecture::x86_64],
+ 'arm64' => ['arm64', Architecture::arm64],
+ 'x86' => ['x86', Architecture::x86],
+ 'something' => ['something', Architecture::x86],
+ ];
+ }
+
+ /** @param non-empty-string $architectureString */
+ #[DataProvider('architectureMapProvider')]
+ public function testParseArchitecture(string $architectureString, Architecture $expectedArchitecture): void
+ {
+ self::assertSame($expectedArchitecture, Architecture::parseArchitecture($architectureString));
+ }
+}
diff --git a/test/unit/Platform/TargetPhp/PhpBinaryPathBasedPlatformRepositoryTest.php b/test/unit/Platform/TargetPhp/PhpBinaryPathBasedPlatformRepositoryTest.php
new file mode 100644
index 0000000..83d0e4a
--- /dev/null
+++ b/test/unit/Platform/TargetPhp/PhpBinaryPathBasedPlatformRepositoryTest.php
@@ -0,0 +1,49 @@
+createMock(PhpBinaryPath::class);
+ $phpBinaryPath->expects(self::once())
+ ->method('version')
+ ->willReturn('8.1.0');
+ $phpBinaryPath->expects(self::once())
+ ->method('extensions')
+ ->willReturn([
+ 'json' => '8.1.0-extra',
+ 'foo' => '8.1.0',
+ 'without-version' => '0',
+ 'another' => '1.2.3-alpha.34',
+ ]);
+
+ $platformRepository = new PhpBinaryPathBasedPlatformRepository($phpBinaryPath);
+
+ self::assertSame(
+ [
+ 'php:8.1.0',
+ 'ext-json:8.1.0',
+ 'ext-foo:8.1.0',
+ 'ext-without-version:0',
+ 'ext-another:1.2.3-alpha.34',
+ ],
+ array_map(
+ static fn (PackageInterface $package): string => $package->getName() . ':' . $package->getPrettyVersion(),
+ $platformRepository->getPackages(),
+ ),
+ );
+ }
+}
diff --git a/test/unit/Platform/TargetPhp/PhpBinaryPathTest.php b/test/unit/Platform/TargetPhp/PhpBinaryPathTest.php
new file mode 100644
index 0000000..44ba903
--- /dev/null
+++ b/test/unit/Platform/TargetPhp/PhpBinaryPathTest.php
@@ -0,0 +1,122 @@
+version(),
+ );
+ }
+
+ public function testFromPhpConfigExecutable(): void
+ {
+ $process = (new Process(['which', 'php-config']));
+ $exitCode = $process->run();
+ $phpConfigExecutable = trim($process->getOutput());
+
+ if ($exitCode !== 0 || ! file_exists($phpConfigExecutable) || ! is_executable($phpConfigExecutable)) {
+ self::markTestSkipped('Needs php-config in path to run this test');
+ }
+
+ $phpBinary = PhpBinaryPath::fromPhpConfigExecutable($phpConfigExecutable);
+
+ // NOTE: this makes an assumption that the `php-config` in path is the same as the version being executed
+ // In most cases, this will be the cases (e.g. in CI, running locally), but if you're trying to test this and
+ // the versions are not matching, that's probably why.
+ // @todo improve this assertion in future, if it becomes problematic
+ self::assertSame(
+ sprintf('%s.%s.%s', PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION),
+ $phpBinary->version(),
+ );
+ }
+
+ public function testExtensions(): void
+ {
+ $exts = get_loaded_extensions();
+ $extVersions = array_map(
+ static function ($extension) {
+ $extVersion = phpversion($extension);
+ if ($extVersion === false) {
+ return '0';
+ }
+
+ return $extVersion;
+ },
+ $exts,
+ );
+ self::assertSame(
+ array_combine($exts, $extVersions),
+ PhpBinaryPath::fromCurrentProcess()
+ ->extensions(),
+ );
+ }
+
+ public function testOperatingSystem(): void
+ {
+ self::assertSame(
+ defined('PHP_WINDOWS_VERSION_BUILD') ? OperatingSystem::Windows : OperatingSystem::NonWindows,
+ PhpBinaryPath::fromCurrentProcess()
+ ->operatingSystem(),
+ );
+ }
+
+ public function testMajorMinorVersion(): void
+ {
+ self::assertSame(
+ PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
+ PhpBinaryPath::fromCurrentProcess()
+ ->majorMinorVersion(),
+ );
+ }
+
+ public function testMachineType(): void
+ {
+ $myUnameMachineType = php_uname('m');
+ assert($myUnameMachineType !== '');
+ self::assertSame(
+ Architecture::parseArchitecture($myUnameMachineType),
+ PhpBinaryPath::fromCurrentProcess()
+ ->machineType(),
+ );
+ }
+
+ public function testPhpIntSize(): void
+ {
+ self::assertSame(
+ PHP_INT_SIZE,
+ PhpBinaryPath::fromCurrentProcess()
+ ->phpIntSize(),
+ );
+ }
+}
diff --git a/test/unit/Platform/TargetPhp/ResolveTargetPhpToPlatformRepositoryTest.php b/test/unit/Platform/TargetPhp/ResolveTargetPhpToPlatformRepositoryTest.php
new file mode 100644
index 0000000..49ce656
--- /dev/null
+++ b/test/unit/Platform/TargetPhp/ResolveTargetPhpToPlatformRepositoryTest.php
@@ -0,0 +1,41 @@
+getPackages(),
+ static function (BasePackage $package): bool {
+ return $package->getPrettyName() === 'php';
+ },
+ );
+
+ self::assertCount(1, $phpPackages);
+ assert(count($phpPackages) > 0);
+
+ $phpPackage = $phpPackages[array_key_first($phpPackages)];
+
+ self::assertSame($php->version(), $phpPackage->getPrettyVersion());
+ }
+}
diff --git a/test/unit/Platform/TargetPlatformTest.php b/test/unit/Platform/TargetPlatformTest.php
new file mode 100644
index 0000000..5f0c329
--- /dev/null
+++ b/test/unit/Platform/TargetPlatformTest.php
@@ -0,0 +1,101 @@
+createMock(PhpBinaryPath::class);
+ $phpBinaryPath->expects(self::any())
+ ->method('operatingSystem')
+ ->willReturn(OperatingSystem::Windows);
+ $phpBinaryPath->expects(self::any())
+ ->method('machineType')
+ ->willReturn(Architecture::x86);
+ $phpBinaryPath->expects(self::any())
+ ->method('phpinfo')
+ ->willReturn(<<<'TEXT'
+phpinfo()
+PHP Version => 8.3.6
+
+System => Windows NT MYCOMPUTER 10.0 build 19045 (Windows 10) AMD64
+Build Date => Apr 10 2024 14:51:55
+Build System => Microsoft Windows Server 2019 Datacenter [10.0.17763]
+Compiler => Visual C++ 2019
+Architecture => x64
+Configure Command => cscript /nologo /e:jscript configure.js "--enable-snapshot-build" "--enable-debug-pack" "--with-pdo-oci=..\..\..\..\instantclient\sdk,shared" "--with-oci8-19=..\..\..\..\instantclient\sdk,shared" "--enable-object-out-dir=../obj/" "--enable-com-dotnet=shared" "--without-analyzer" "--with-pgo"
+Server API => Command Line Interface
+Virtual Directory Support => enabled
+Configuration File (php.ini) Path =>
+Loaded Configuration File => C:\php-8.3.6\php.ini
+Scan this dir for additional .ini files => (none)
+Additional .ini files parsed => (none)
+PHP API => 20230831
+PHP Extension => 20230831
+Zend Extension => 420230831
+Zend Extension Build => API420230831,TS,VS16
+PHP Extension Build => API20230831,TS,VS16
+TEXT);
+
+ $platform = TargetPlatform::fromPhpBinaryPath($phpBinaryPath);
+
+ self::assertSame(OperatingSystem::Windows, $platform->operatingSystem);
+ self::assertSame(WindowsCompiler::VS16, $platform->windowsCompiler);
+ self::assertSame(ThreadSafetyMode::ThreadSafe, $platform->threadSafety);
+ self::assertSame(Architecture::x86_64, $platform->architecture);
+ }
+
+ public function testLinuxPlatform(): void
+ {
+ $phpBinaryPath = $this->createMock(PhpBinaryPath::class);
+ $phpBinaryPath->expects(self::any())
+ ->method('operatingSystem')
+ ->willReturn(OperatingSystem::NonWindows);
+ $phpBinaryPath->expects(self::any())
+ ->method('machineType')
+ ->willReturn(Architecture::x86_64);
+ $phpBinaryPath->expects(self::any())
+ ->method('phpinfo')
+ ->willReturn(<<<'TEXT'
+phpinfo()
+PHP Version => 8.3.6
+
+System => Linux myhostname 1.2.3 Ubuntu x86_64
+Build Date => Apr 11 2024 20:23:38
+Build System => Linux
+Server API => Command Line Interface
+Virtual Directory Support => disabled
+Configuration File (php.ini) Path => /etc/php/8.3/cli
+Loaded Configuration File => /etc/php/8.3/cli/php.ini
+Scan this dir for additional .ini files => /etc/php/8.3/cli/conf.d
+Additional .ini files parsed => (none)
+PHP API => 20230831
+PHP Extension => 20230831
+Zend Extension => 420230831
+Zend Extension Build => API420230831,NTS
+PHP Extension Build => API20230831,NTS
+Debug Build => no
+Thread Safety => disabled
+TEXT);
+
+ $platform = TargetPlatform::fromPhpBinaryPath($phpBinaryPath);
+
+ self::assertSame(OperatingSystem::NonWindows, $platform->operatingSystem);
+ self::assertSame(null, $platform->windowsCompiler);
+ self::assertSame(ThreadSafetyMode::NonThreadSafe, $platform->threadSafety);
+ self::assertSame(Architecture::x86_64, $platform->architecture);
+ }
+}
diff --git a/test/unit/Platform/ThreadSafetyModeTest.php b/test/unit/Platform/ThreadSafetyModeTest.php
new file mode 100644
index 0000000..712db06
--- /dev/null
+++ b/test/unit/Platform/ThreadSafetyModeTest.php
@@ -0,0 +1,19 @@
+asShort());
+ self::assertSame('nts', ThreadSafetyMode::NonThreadSafe->asShort());
+ }
+}