diff --git a/composer.json b/composer.json
index 3542a07..43e1b56 100644
--- a/composer.json
+++ b/composer.json
@@ -28,7 +28,7 @@
"require": {
"php": "8.1.*||8.2.*||8.3.*",
"ext-zip": "*",
- "composer/composer": "^2.7",
+ "composer/composer": "dev-main@dev",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"illuminate/container": "^10.47",
diff --git a/composer.lock b/composer.lock
index e8d4935..1189dc7 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "694d5bf1b96fcceae6af19c03eb55095",
+ "content-hash": "fb31ac50e2d4c78585e5dc67fbef9477",
"packages": [
{
"name": "composer/ca-bundle",
@@ -157,16 +157,16 @@
},
{
"name": "composer/composer",
- "version": "2.7.1",
+ "version": "dev-main",
"source": {
"type": "git",
"url": "https://github.com/composer/composer.git",
- "reference": "aaf6ed5ccd27c23f79a545e351b4d7842a99d0bc"
+ "reference": "b12a88b7f3313e9dbae5b58085323b8328d10296"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/composer/zipball/aaf6ed5ccd27c23f79a545e351b4d7842a99d0bc",
- "reference": "aaf6ed5ccd27c23f79a545e351b4d7842a99d0bc",
+ "url": "https://api.github.com/repos/composer/composer/zipball/b12a88b7f3313e9dbae5b58085323b8328d10296",
+ "reference": "b12a88b7f3313e9dbae5b58085323b8328d10296",
"shasum": ""
},
"require": {
@@ -205,6 +205,7 @@
"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"
],
@@ -251,7 +252,7 @@
"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/2.7.1"
+ "source": "https://github.com/composer/composer/tree/main"
},
"funding": [
{
@@ -267,7 +268,7 @@
"type": "tidelift"
}
],
- "time": "2024-02-09T14:26:28+00:00"
+ "time": "2024-03-22T08:29:43+00:00"
},
{
"name": "composer/metadata-minifier",
@@ -5740,7 +5741,9 @@
],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {
+ "composer/composer": 20
+ },
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
diff --git a/src/Command/DownloadCommand.php b/src/Command/DownloadCommand.php
index 5f8cef2..9fe973a 100644
--- a/src/Command/DownloadCommand.php
+++ b/src/Command/DownloadCommand.php
@@ -8,10 +8,12 @@
use InvalidArgumentException;
use Php\Pie\DependencyResolver\DependencyResolver;
use Php\Pie\Downloading\DownloadAndExtract;
+use Php\Pie\TargetPhp\PhpBinaryPath;
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;
@@ -30,6 +32,7 @@
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';
public function __construct(
private readonly DependencyResolver $dependencyResolver,
@@ -47,18 +50,35 @@ public function configure(): void
InputArgument::REQUIRED,
'The extension name and version constraint to use, in the format {ext-name}{?:version-constraint}{?@dev-branch-name}, for example `ext-debug:^1.0`',
);
+ $this->addOption(
+ self::OPTION_WITH_PHP_CONFIG,
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'The path to `php-config` to use',
+ );
}
public function execute(InputInterface $input, OutputInterface $output): int
{
+ $phpBinaryPath = PhpBinaryPath::fromCurrentProcess();
+
+ /** @var mixed $withPhpConfig */
+ $withPhpConfig = $input->getOption(self::OPTION_WITH_PHP_CONFIG);
+ if (is_string($withPhpConfig) && $withPhpConfig !== '') {
+ $phpBinaryPath = PhpBinaryPath::fromPhpConfigExecutable($withPhpConfig);
+ }
+
+ $output->writeln(sprintf('You are running PHP %s', PHP_VERSION));
+ $output->writeln(sprintf('Target PHP installation: %s (from %s)', $phpBinaryPath->version(), $phpBinaryPath->phpBinaryPath));
+
$requestedNameAndVersionPair = $this->requestedNameAndVersionPair($input);
$package = ($this->dependencyResolver)(
+ $phpBinaryPath,
$requestedNameAndVersionPair['name'],
$requestedNameAndVersionPair['version'],
);
- $output->writeln(sprintf('You are running PHP %s', PHP_VERSION));
$output->writeln(sprintf('Found package: %s (version: %s)', $package->name, $package->version));
$output->writeln(sprintf('Dist download URL: %s', $package->downloadUrl ?? '(none)'));
diff --git a/src/Container.php b/src/Container.php
index 73c6238..a84ce4c 100644
--- a/src/Container.php
+++ b/src/Container.php
@@ -8,10 +8,7 @@
use Composer\Factory as ComposerFactory;
use Composer\IO\ConsoleIO;
use Composer\IO\IOInterface;
-use Composer\IO\NullIO;
use Composer\Repository\CompositeRepository;
-use Composer\Repository\PlatformRepository;
-use Composer\Repository\RepositoryFactory;
use Composer\Repository\RepositorySet;
use Composer\Util\AuthHelper;
use Composer\Util\Platform;
@@ -24,6 +21,7 @@
use Php\Pie\Downloading\DownloadZip;
use Php\Pie\Downloading\ExtractZip;
use Php\Pie\Downloading\UnixDownloadAndExtract;
+use Php\Pie\TargetPhp\ResolveTargetPhpToPlatformRepository;
use Psr\Container\ContainerInterface;
use RuntimeException;
use Symfony\Component\Console\Helper\HelperSet;
@@ -51,7 +49,7 @@ public static function factory(): ContainerInterface
});
$container->singleton(Composer::class, static function (ContainerInterface $container): Composer {
$io = $container->get(IOInterface::class);
- $composer = ComposerFactory::create($io);
+ $composer = (new ComposerFactory())->createComposer($io, [], true);
$io->loadConfiguration($composer->getConfig());
return $composer;
@@ -59,13 +57,14 @@ public static function factory(): ContainerInterface
$container->singleton(
DependencyResolver::class,
- static function (): DependencyResolver {
+ static function (ContainerInterface $container): DependencyResolver {
+ $composer = $container->get(Composer::class);
$repositorySet = new RepositorySet();
- $repositorySet->addRepository(new CompositeRepository(RepositoryFactory::defaultReposWithDefaultManager(new NullIO())));
+ $repositorySet->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories()));
return new ResolveDependencyWithComposer(
- new PlatformRepository(),
$repositorySet,
+ new ResolveTargetPhpToPlatformRepository(),
);
},
);
diff --git a/src/DependencyResolver/DependencyResolver.php b/src/DependencyResolver/DependencyResolver.php
index 908888a..57a4316 100644
--- a/src/DependencyResolver/DependencyResolver.php
+++ b/src/DependencyResolver/DependencyResolver.php
@@ -4,9 +4,11 @@
namespace Php\Pie\DependencyResolver;
+use Php\Pie\TargetPhp\PhpBinaryPath;
+
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
interface DependencyResolver
{
/** @throws UnableToResolveRequirement */
- public function __invoke(string $packageName, string|null $requestedVersion): Package;
+ public function __invoke(PhpBinaryPath $phpBinaryPath, string $packageName, string|null $requestedVersion): Package;
}
diff --git a/src/DependencyResolver/ResolveDependencyWithComposer.php b/src/DependencyResolver/ResolveDependencyWithComposer.php
index 995b67e..f70c186 100644
--- a/src/DependencyResolver/ResolveDependencyWithComposer.php
+++ b/src/DependencyResolver/ResolveDependencyWithComposer.php
@@ -6,23 +6,29 @@
use Composer\Package\CompletePackageInterface;
use Composer\Package\Version\VersionSelector;
-use Composer\Repository\PlatformRepository;
use Composer\Repository\RepositorySet;
+use Php\Pie\TargetPhp\PhpBinaryPath;
+use Php\Pie\TargetPhp\ResolveTargetPhpToPlatformRepository;
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
final class ResolveDependencyWithComposer implements DependencyResolver
{
public function __construct(
- private readonly PlatformRepository $platformRepository,
private readonly RepositorySet $repositorySet,
+ private readonly ResolveTargetPhpToPlatformRepository $resolveTargetPhpToPlatformRepository,
) {
}
- public function __invoke(string $packageName, string|null $requestedVersion): Package
+ public function __invoke(PhpBinaryPath $phpBinaryPath, string $packageName, string|null $requestedVersion): Package
{
- $package = (new VersionSelector($this->repositorySet, $this->platformRepository))
+ $package = (new VersionSelector(
+ $this->repositorySet,
+ ($this->resolveTargetPhpToPlatformRepository)($phpBinaryPath),
+ ))
->findBestCandidate($packageName, $requestedVersion);
+ // @todo check it is a `php-ext` or `php-ext-zend`
+
if (! $package instanceof CompletePackageInterface) {
throw UnableToResolveRequirement::fromRequirement($packageName, $requestedVersion);
}
diff --git a/src/TargetPhp/PhpBinaryPath.php b/src/TargetPhp/PhpBinaryPath.php
index d57aa5a..9d364c0 100644
--- a/src/TargetPhp/PhpBinaryPath.php
+++ b/src/TargetPhp/PhpBinaryPath.php
@@ -8,8 +8,10 @@
use Symfony\Component\Process\Process;
use Webmozart\Assert\Assert;
+use function trim;
+
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
-final class PhpBinaryPath
+class PhpBinaryPath
{
/** @param non-empty-string $phpBinaryPath */
private function __construct(readonly string $phpBinaryPath)
@@ -22,6 +24,7 @@ public function version(): string
->mustRun()
->getOutput());
Assert::stringNotEmpty($phpVersion, 'Could not determine PHP version');
+
return $phpVersion;
}
@@ -32,13 +35,15 @@ public static function fromPhpConfigExecutable(string $phpConfig): self
->mustRun()
->getOutput());
Assert::stringNotEmpty($phpExecutable, 'Could not find path to PHP executable.');
+
return new self($phpExecutable);
}
public static function fromCurrentProcess(): self
{
- $phpExecutable = trim((new PhpExecutableFinder())->find());
+ $phpExecutable = trim((string) (new PhpExecutableFinder())->find());
Assert::stringNotEmpty($phpExecutable, 'Could not find path to PHP executable.');
+
return new self($phpExecutable);
}
}
diff --git a/src/TargetPhp/ResolveTargetPhpToPlatformRepository.php b/src/TargetPhp/ResolveTargetPhpToPlatformRepository.php
new file mode 100644
index 0000000..9e3eaac
--- /dev/null
+++ b/src/TargetPhp/ResolveTargetPhpToPlatformRepository.php
@@ -0,0 +1,16 @@
+ $phpBinaryPath->version()]);
+ }
+}
diff --git a/test/integration/Command/DownloadCommandTest.php b/test/integration/Command/DownloadCommandTest.php
index 1261236..e58aa7e 100644
--- a/test/integration/Command/DownloadCommandTest.php
+++ b/test/integration/Command/DownloadCommandTest.php
@@ -26,13 +26,18 @@ public function setUp(): void
public function testDownloadCommand(): void
{
- $this->commandTester->execute(['requested-package-and-version' => 'ramsey/uuid']);
+ 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);
+ }
+
+ // 1.0.0 is only compatible with PHP 8.3.0
+ $this->commandTester->execute(['requested-package-and-version' => 'asgrim/example-pie-extension:1.0.0']);
$this->commandTester->assertCommandIsSuccessful();
$outputString = $this->commandTester->getDisplay();
- self::assertStringContainsString('Found package: ramsey/uuid (version: ', $outputString);
- self::assertStringContainsString('Dist download URL: https://api.github.com/repos/ramsey/uuid/zipball/', $outputString);
+ self::assertStringContainsString('Found package: asgrim/example-pie-extension (version: 1.0.0)', $outputString);
+ self::assertStringContainsString('Dist download URL: https://api.github.com/repos/asgrim/example-pie-extension/zipball/', $outputString);
}
public function testDownloadCommandFailsWhenUsingIncompatiblePhpVersion(): void
@@ -42,6 +47,7 @@ public function testDownloadCommandFailsWhenUsingIncompatiblePhpVersion(): void
}
$this->expectException(UnableToResolveRequirement::class);
- $this->commandTester->execute(['requested-package-and-version' => 'phpunit/phpunit:^11.0']);
+ // 1.0.0 is only compatible with PHP 8.3.0
+ $this->commandTester->execute(['requested-package-and-version' => 'asgrim/example-pie-extension:1.0.0']);
}
}
diff --git a/test/unit/DependencyResolver/ResolveDependencyWithComposerTest.php b/test/unit/DependencyResolver/ResolveDependencyWithComposerTest.php
index 1aca5a1..0e24daa 100644
--- a/test/unit/DependencyResolver/ResolveDependencyWithComposerTest.php
+++ b/test/unit/DependencyResolver/ResolveDependencyWithComposerTest.php
@@ -6,11 +6,12 @@
use Composer\IO\NullIO;
use Composer\Repository\CompositeRepository;
-use Composer\Repository\PlatformRepository;
use Composer\Repository\RepositoryFactory;
use Composer\Repository\RepositorySet;
use Php\Pie\DependencyResolver\ResolveDependencyWithComposer;
use Php\Pie\DependencyResolver\UnableToResolveRequirement;
+use Php\Pie\TargetPhp\PhpBinaryPath;
+use Php\Pie\TargetPhp\ResolveTargetPhpToPlatformRepository;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
@@ -19,6 +20,7 @@
final class ResolveDependencyWithComposerTest extends TestCase
{
private RepositorySet $repositorySet;
+ private ResolveTargetPhpToPlatformRepository $resolveTargetPhpToPlatformRepository;
public function setUp(): void
{
@@ -26,14 +28,21 @@ public function setUp(): void
$this->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::once())
+ ->method('version')
+ ->willReturn('8.2.0');
+
$package = (new ResolveDependencyWithComposer(
- new PlatformRepository([], ['php' => '8.2.0']),
$this->repositorySet,
- ))('phpunit/phpunit', '^11.0');
+ $this->resolveTargetPhpToPlatformRepository,
+ ))($phpBinaryPath, 'phpunit/phpunit', '^11.0');
self::assertSame('phpunit/phpunit', $package->name);
}
@@ -55,12 +64,18 @@ public static function unresolvableDependencies(): array
#[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']);
+
$this->expectException(UnableToResolveRequirement::class);
(new ResolveDependencyWithComposer(
- new PlatformRepository([], $platformOverrides),
$this->repositorySet,
+ $this->resolveTargetPhpToPlatformRepository,
))(
+ $phpBinaryPath,
$package,
$version,
);
diff --git a/test/unit/TargetPhp/PhpBinaryPathTest.php b/test/unit/TargetPhp/PhpBinaryPathTest.php
index b59fead..05c7551 100644
--- a/test/unit/TargetPhp/PhpBinaryPathTest.php
+++ b/test/unit/TargetPhp/PhpBinaryPathTest.php
@@ -8,6 +8,8 @@
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
+use const PHP_VERSION;
+
#[CoversClass(PhpBinaryPath::class)]
final class PhpBinaryPathTest extends TestCase
{