Skip to content

Commit 03dfe7a

Browse files
authored
Merge pull request #271 from asgrim/improve-package-not-found-messaging
Improve package not found messaging
2 parents a4e2ca7 + 411acc5 commit 03dfe7a

File tree

12 files changed

+307
-54
lines changed

12 files changed

+307
-54
lines changed

src/Command/BuildCommand.php

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
use Php\Pie\ComposerIntegration\PieComposerRequest;
1111
use Php\Pie\ComposerIntegration\PieOperation;
1212
use Php\Pie\DependencyResolver\DependencyResolver;
13+
use Php\Pie\DependencyResolver\InvalidPackageName;
14+
use Php\Pie\DependencyResolver\UnableToResolveRequirement;
15+
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
1316
use Psr\Container\ContainerInterface;
1417
use Symfony\Component\Console\Attribute\AsCommand;
1518
use Symfony\Component\Console\Command\Command;
@@ -28,6 +31,7 @@ public function __construct(
2831
private readonly ContainerInterface $container,
2932
private readonly DependencyResolver $dependencyResolver,
3033
private readonly ComposerIntegrationHandler $composerIntegrationHandler,
34+
private readonly FindMatchingPackages $findMatchingPackages,
3135
) {
3236
parent::__construct();
3337
}
@@ -41,8 +45,19 @@ public function configure(): void
4145

4246
public function execute(InputInterface $input, OutputInterface $output): int
4347
{
44-
$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);
45-
$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
48+
$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);
49+
try {
50+
$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
51+
} catch (InvalidPackageName $invalidPackageName) {
52+
return CommandHelper::handlePackageNotFound(
53+
$invalidPackageName,
54+
$this->findMatchingPackages,
55+
$output,
56+
$targetPlatform,
57+
$this->container,
58+
);
59+
}
60+
4661
$forceInstallPackageVersion = CommandHelper::determineForceInstallingPackageVersion($input);
4762

4863
$composer = PieComposerFactory::createPieComposer(
@@ -58,12 +73,23 @@ public function execute(InputInterface $input, OutputInterface $output): int
5873
),
5974
);
6075

61-
$package = ($this->dependencyResolver)(
62-
$composer,
63-
$targetPlatform,
64-
$requestedNameAndVersion,
65-
$forceInstallPackageVersion,
66-
);
76+
try {
77+
$package = ($this->dependencyResolver)(
78+
$composer,
79+
$targetPlatform,
80+
$requestedNameAndVersion,
81+
$forceInstallPackageVersion,
82+
);
83+
} catch (UnableToResolveRequirement $unableToResolveRequirement) {
84+
return CommandHelper::handlePackageNotFound(
85+
$unableToResolveRequirement,
86+
$this->findMatchingPackages,
87+
$output,
88+
$targetPlatform,
89+
$this->container,
90+
);
91+
}
92+
6793
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix()));
6894

6995
// Now we know what package we have, we can validate the configure options for the command and re-create the

src/Command/CommandHelper.php

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,45 @@
55
namespace Php\Pie\Command;
66

77
use Composer\Composer;
8+
use Composer\Package\CompletePackageInterface;
89
use Composer\Package\Version\VersionParser;
910
use Composer\Repository\ComposerRepository;
1011
use Composer\Repository\PathRepository;
1112
use Composer\Repository\VcsRepository;
1213
use Composer\Util\Platform;
1314
use InvalidArgumentException;
15+
use OutOfRangeException;
16+
use Php\Pie\ComposerIntegration\PieComposerFactory;
17+
use Php\Pie\ComposerIntegration\PieComposerRequest;
18+
use Php\Pie\DependencyResolver\InvalidPackageName;
1419
use Php\Pie\DependencyResolver\Package;
1520
use Php\Pie\DependencyResolver\RequestedPackageAndVersion;
21+
use Php\Pie\DependencyResolver\UnableToResolveRequirement;
22+
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
1623
use Php\Pie\Platform as PiePlatform;
1724
use Php\Pie\Platform\OperatingSystem;
1825
use Php\Pie\Platform\TargetPhp\PhpBinaryPath;
1926
use Php\Pie\Platform\TargetPhp\PhpizePath;
2027
use Php\Pie\Platform\TargetPlatform;
28+
use Psr\Container\ContainerInterface;
2129
use Symfony\Component\Console\Command\Command;
2230
use Symfony\Component\Console\Input\InputArgument;
2331
use Symfony\Component\Console\Input\InputInterface;
2432
use Symfony\Component\Console\Input\InputOption;
2533
use Symfony\Component\Console\Output\OutputInterface;
34+
use Throwable;
2635
use Webmozart\Assert\Assert;
2736

2837
use function array_key_exists;
38+
use function array_map;
39+
use function count;
2940
use function is_array;
3041
use function is_string;
3142
use function reset;
3243
use function sprintf;
44+
use function str_starts_with;
3345
use function strtolower;
46+
use function substr;
3447
use function trim;
3548

3649
use const PHP_VERSION;
@@ -326,4 +339,92 @@ public static function listRepositories(Composer $composer, OutputInterface $out
326339
));
327340
}
328341
}
342+
343+
public static function handlePackageNotFound(
344+
InvalidPackageName|UnableToResolveRequirement $exception,
345+
FindMatchingPackages $findMatchingPackages,
346+
OutputInterface $output,
347+
TargetPlatform $targetPlatform,
348+
ContainerInterface $container,
349+
): int {
350+
$pieComposer = PieComposerFactory::createPieComposer(
351+
$container,
352+
PieComposerRequest::noOperation(
353+
$output,
354+
$targetPlatform,
355+
),
356+
);
357+
358+
$requestedPackageName = $exception->requestedPackageAndVersion->package;
359+
if (str_starts_with($requestedPackageName, 'ext-')) {
360+
$requestedPackageName = substr($requestedPackageName, 4);
361+
}
362+
363+
$output->writeln('');
364+
$output->writeln(sprintf('<error>Could not install package: %s</error>', $requestedPackageName));
365+
$output->writeln($exception->getMessage());
366+
367+
try {
368+
$matches = array_map(
369+
static function (array $match) use ($output, $pieComposer): array {
370+
$composerMatchingPackage = $pieComposer->getRepositoryManager()->findPackage($match['name'], '*');
371+
372+
// Attempts to augment the Composer packages found with the PIE extension name
373+
if ($composerMatchingPackage instanceof CompletePackageInterface) {
374+
try {
375+
$match['extension-name'] = Package
376+
::fromComposerCompletePackage($composerMatchingPackage)
377+
->extensionName()
378+
->name();
379+
} catch (Throwable $t) {
380+
$output->writeln(
381+
sprintf(
382+
'Tried looking up extension name for %s, but failed: %s',
383+
$match['name'],
384+
$t->getMessage(),
385+
),
386+
OutputInterface::VERBOSITY_VERY_VERBOSE,
387+
);
388+
}
389+
}
390+
391+
return $match;
392+
},
393+
$findMatchingPackages->for($pieComposer, $requestedPackageName),
394+
);
395+
396+
if (count($matches)) {
397+
$output->writeln('');
398+
if (count($matches) === 1) {
399+
$output->writeln('<info>Did you mean this?</info>');
400+
} else {
401+
$output->writeln('<info>Did you mean one of these?</info>');
402+
}
403+
404+
array_map(
405+
static function (array $match) use ($output): void {
406+
$output->writeln(sprintf(
407+
' - %s%s: %s',
408+
$match['name'],
409+
array_key_exists('extension-name', $match) && is_string($match['extension-name'])
410+
? ' (provides extension: ' . $match['extension-name'] . ')'
411+
: '',
412+
$match['description'] ?? 'no description available',
413+
));
414+
},
415+
$matches,
416+
);
417+
}
418+
} catch (OutOfRangeException) {
419+
$output->writeln(
420+
sprintf(
421+
'Tried searching for "%s", but nothing was found.',
422+
$requestedPackageName,
423+
),
424+
OutputInterface::VERBOSITY_VERBOSE,
425+
);
426+
}
427+
428+
return 1;
429+
}
329430
}

src/Command/DownloadCommand.php

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
use Php\Pie\ComposerIntegration\PieComposerRequest;
1111
use Php\Pie\ComposerIntegration\PieOperation;
1212
use Php\Pie\DependencyResolver\DependencyResolver;
13+
use Php\Pie\DependencyResolver\InvalidPackageName;
14+
use Php\Pie\DependencyResolver\UnableToResolveRequirement;
15+
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
1316
use Psr\Container\ContainerInterface;
1417
use Symfony\Component\Console\Attribute\AsCommand;
1518
use Symfony\Component\Console\Command\Command;
@@ -28,6 +31,7 @@ public function __construct(
2831
private readonly ContainerInterface $container,
2932
private readonly DependencyResolver $dependencyResolver,
3033
private readonly ComposerIntegrationHandler $composerIntegrationHandler,
34+
private readonly FindMatchingPackages $findMatchingPackages,
3135
) {
3236
parent::__construct();
3337
}
@@ -43,8 +47,19 @@ public function execute(InputInterface $input, OutputInterface $output): int
4347
{
4448
CommandHelper::validateInput($input, $this);
4549

46-
$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);
47-
$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
50+
$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);
51+
try {
52+
$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
53+
} catch (InvalidPackageName $invalidPackageName) {
54+
return CommandHelper::handlePackageNotFound(
55+
$invalidPackageName,
56+
$this->findMatchingPackages,
57+
$output,
58+
$targetPlatform,
59+
$this->container,
60+
);
61+
}
62+
4863
$forceInstallPackageVersion = CommandHelper::determineForceInstallingPackageVersion($input);
4964

5065
$composer = PieComposerFactory::createPieComposer(
@@ -60,12 +75,23 @@ public function execute(InputInterface $input, OutputInterface $output): int
6075
),
6176
);
6277

63-
$package = ($this->dependencyResolver)(
64-
$composer,
65-
$targetPlatform,
66-
$requestedNameAndVersion,
67-
$forceInstallPackageVersion,
68-
);
78+
try {
79+
$package = ($this->dependencyResolver)(
80+
$composer,
81+
$targetPlatform,
82+
$requestedNameAndVersion,
83+
$forceInstallPackageVersion,
84+
);
85+
} catch (UnableToResolveRequirement $unableToResolveRequirement) {
86+
return CommandHelper::handlePackageNotFound(
87+
$unableToResolveRequirement,
88+
$this->findMatchingPackages,
89+
$output,
90+
$targetPlatform,
91+
$this->container,
92+
);
93+
}
94+
6995
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix()));
7096

7197
try {

src/Command/InfoCommand.php

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
use Php\Pie\ComposerIntegration\PieComposerRequest;
99
use Php\Pie\ComposerIntegration\PieOperation;
1010
use Php\Pie\DependencyResolver\DependencyResolver;
11+
use Php\Pie\DependencyResolver\InvalidPackageName;
12+
use Php\Pie\DependencyResolver\UnableToResolveRequirement;
13+
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
1114
use Psr\Container\ContainerInterface;
1215
use Symfony\Component\Console\Attribute\AsCommand;
1316
use Symfony\Component\Console\Command\Command;
@@ -26,6 +29,7 @@ final class InfoCommand extends Command
2629
public function __construct(
2730
private readonly ContainerInterface $container,
2831
private readonly DependencyResolver $dependencyResolver,
32+
private readonly FindMatchingPackages $findMatchingPackages,
2933
) {
3034
parent::__construct();
3135
}
@@ -43,7 +47,17 @@ public function execute(InputInterface $input, OutputInterface $output): int
4347

4448
$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);
4549

46-
$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
50+
try {
51+
$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
52+
} catch (InvalidPackageName $invalidPackageName) {
53+
return CommandHelper::handlePackageNotFound(
54+
$invalidPackageName,
55+
$this->findMatchingPackages,
56+
$output,
57+
$targetPlatform,
58+
$this->container,
59+
);
60+
}
4761

4862
$composer = PieComposerFactory::createPieComposer(
4963
$this->container,
@@ -58,12 +72,23 @@ public function execute(InputInterface $input, OutputInterface $output): int
5872
),
5973
);
6074

61-
$package = ($this->dependencyResolver)(
62-
$composer,
63-
$targetPlatform,
64-
$requestedNameAndVersion,
65-
CommandHelper::determineForceInstallingPackageVersion($input),
66-
);
75+
try {
76+
$package = ($this->dependencyResolver)(
77+
$composer,
78+
$targetPlatform,
79+
$requestedNameAndVersion,
80+
CommandHelper::determineForceInstallingPackageVersion($input),
81+
);
82+
} catch (UnableToResolveRequirement $unableToResolveRequirement) {
83+
return CommandHelper::handlePackageNotFound(
84+
$unableToResolveRequirement,
85+
$this->findMatchingPackages,
86+
$output,
87+
$targetPlatform,
88+
$this->container,
89+
);
90+
}
91+
6792
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix()));
6893

6994
$output->writeln(sprintf('Extension name: %s', $package->extensionName()->name()));

0 commit comments

Comments
 (0)