Skip to content

Navigation Menu

Sign in
Appearance settings
Sign up
Appearance settings

Commit cb4d4a2

Browse filesBrowse the repository at this point in the historyBrowse files
minor symfony#65617 [FrameworkBundle] Suggest more packages for unknown commands (michaelthieulin)
This PR was merged into the 8.2 branch. Discussion ---------- [FrameworkBundle] Suggest more packages for unknown commands | Q | A | ------------- | --- | Branch? | 8.2 | Bug fix? | no | New feature? | no | Deprecations? | no | Issues | Fix symfony#59459 | License | MIT Following up on symfony#59472, closed with "please submit again when you want". This applies the limit `@GromNaN` proposed and `@chalasr` agreed on there: only packages having a recipe in `symfony/recipes`, and only when they expose a command namespace no Symfony command already uses. That rules out five of the six bundles the previous attempt proposed, which live in `symfony/recipes-contrib` or have no recipe at all. The rule is written above the constant so the next person adding an entry knows where the line is, and an entry that stops qualifying can be dropped the way `sensio/generator-bundle` and `symfony/web-server-bundle` were. Eight namespaces are added, plus `doctrine:migrations` and `make:admin` as subkeys of existing ones. Every name was checked against the `#[AsCommand]` names upstream. A vendor publishing several bundles under one prefix only gets its own subkey: `lexik/maintenance-bundle` and `lexik/translation-bundle` also live under `lexik:`, so mapping the whole namespace to the JWT bundle would be wrong. Five of the new entries therefore have no `_default`, which the current code cannot express, as it reads `_default` without `isset()`. Hence the `?? null`. No existing namespace is affected, all three have a `_default`. Commits ------- 69ecfb2 [FrameworkBundle] Suggest more packages for unknown commands
2 parents 9e3449f + 69ecfb2 commit cb4d4a2
Copy full SHA for cb4d4a2

2 files changed

+103-5Lines changed: 103 additions & 5 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/Symfony/Bundle/FrameworkBundle/EventListener/SuggestMissingPackageSubscriber.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/EventListener/SuggestMissingPackageSubscriber.php
+43-5Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,58 @@
2626
*/
2727
final class SuggestMissingPackageSubscriber implements EventSubscriberInterface
2828
{
29+
/**
30+
* Each namespace maps to the package providing it, limited to packages having a recipe in symfony/recipes
31+
* or belonging to a family of packages that do, like symfony/ux-*. A "_default" requires a namespace that
32+
* no Symfony command uses and that a single package owns; otherwise the package only gets the
33+
* sub-namespaces of the commands it provides.
34+
*/
2935
private const PACKAGES = [
36+
'api' => [
37+
'_default' => ['ApiPlatformBundle', 'api-platform/symfony'],
38+
],
39+
'bazinga' => [
40+
'js-translation' => ['BazingaJsTranslationBundle', 'willdurand/js-translation-bundle'],
41+
],
42+
'debug' => [
43+
'live-component' => ['LiveComponentBundle', 'symfony/ux-live-component'],
44+
'twig-component' => ['TwigComponentBundle', 'symfony/ux-twig-component'],
45+
],
3046
'doctrine' => [
3147
'fixtures' => ['DoctrineFixturesBundle', 'doctrine/doctrine-fixtures-bundle --dev'],
48+
'migrations' => ['DoctrineMigrationsBundle', 'doctrine/doctrine-migrations-bundle'],
3249
'mongodb' => ['DoctrineMongoDBBundle', 'doctrine/mongodb-odm-bundle'],
3350
'_default' => ['Doctrine ORM', 'symfony/orm-pack'],
3451
],
52+
'hautelook' => [
53+
'fixtures' => ['HautelookAliceBundle', 'hautelook/alice-bundle --dev'],
54+
],
55+
'league' => [
56+
'oauth2-server' => ['LeagueOAuth2ServerBundle', 'league/oauth2-server-bundle'],
57+
],
58+
'lexik' => [
59+
'jwt' => ['LexikJWTAuthenticationBundle', 'lexik/jwt-authentication-bundle'],
60+
],
3561
'make' => [
62+
'admin' => ['EasyAdminBundle', 'easycorp/easyadmin-bundle'],
3663
'_default' => ['MakerBundle', 'symfony/maker-bundle --dev'],
3764
],
65+
'sass' => [
66+
'_default' => ['SassBundle', 'symfonycasts/sass-bundle'],
67+
],
3868
'server' => [
3969
'_default' => ['Debug Bundle', 'symfony/debug-bundle --dev'],
4070
],
71+
'tailwind' => [
72+
'_default' => ['TailwindBundle', 'symfonycasts/tailwind-bundle'],
73+
],
74+
'ux' => [
75+
'icons' => ['UXIconsBundle', 'symfony/ux-icons'],
76+
'install' => ['UXToolkitBundle', 'symfony/ux-toolkit'],
77+
'native' => ['UXNativeBundle', 'symfony/ux-native'],
78+
'toolkit' => ['UXToolkitBundle', 'symfony/ux-toolkit'],
79+
'translator' => ['UxTranslatorBundle', 'symfony/ux-translator'],
80+
],
4181
];
4282

4383
public function onConsoleError(ConsoleErrorEvent $event): void
@@ -52,12 +92,10 @@ public function onConsoleError(ConsoleErrorEvent $event): void
5292
return;
5393
}
5494

55-
if (isset(self::PACKAGES[$namespace][$command])) {
95+
if ($exact = isset(self::PACKAGES[$namespace][$command])) {
5696
$suggestion = self::PACKAGES[$namespace][$command];
57-
$exact = true;
58-
} else {
59-
$suggestion = self::PACKAGES[$namespace]['_default'];
60-
$exact = false;
97+
} elseif (!$suggestion = self::PACKAGES[$namespace]['_default'] ?? null) {
98+
return;
6199
}
62100

63101
$error = $event->getError();
Collapse file

‎src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php
+60Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Tests\Console;
1313

14+
use PHPUnit\Framework\Attributes\DataProvider;
1415
use PHPUnit\Framework\Attributes\Group;
1516
use PHPUnit\Framework\Attributes\IgnoreDeprecations;
1617
use PHPUnit\Framework\MockObject\MockObject;
@@ -228,6 +229,65 @@ public function testSuggestingPackagesWithPartialMatchAndAlternatives()
228229
$this->assertDoesNotMatchRegularExpression('/You may be looking for a command provided by/', $result);
229230
}
230231

232+
#[DataProvider('provideCommandsSuggestingAPackage')]
233+
public function testSuggestingPackageForCommand(string $command, string $bundle, string $package)
234+
{
235+
$result = $this->createEventForSuggestingPackages($command);
236+
237+
$this->assertStringContainsString(\sprintf('provided by the "%s" which is currently not installed. Try running "composer require %s".', $bundle, $package), $result);
238+
}
239+
240+
public static function provideCommandsSuggestingAPackage(): iterable
241+
{
242+
yield 'api:openapi:export' => ['api:openapi:export', 'ApiPlatformBundle', 'api-platform/symfony'];
243+
yield 'bazinga:js-translation:dump' => ['bazinga:js-translation:dump', 'BazingaJsTranslationBundle', 'willdurand/js-translation-bundle'];
244+
yield 'debug:live-component' => ['debug:live-component', 'LiveComponentBundle', 'symfony/ux-live-component'];
245+
yield 'debug:twig-component' => ['debug:twig-component', 'TwigComponentBundle', 'symfony/ux-twig-component'];
246+
yield 'doctrine:fixtures:load' => ['doctrine:fixtures:load', 'DoctrineFixturesBundle', 'doctrine/doctrine-fixtures-bundle --dev'];
247+
yield 'doctrine:migrations:migrate' => ['doctrine:migrations:migrate', 'DoctrineMigrationsBundle', 'doctrine/doctrine-migrations-bundle'];
248+
yield 'doctrine:mongodb:schema:create' => ['doctrine:mongodb:schema:create', 'DoctrineMongoDBBundle', 'doctrine/mongodb-odm-bundle'];
249+
yield 'doctrine:schema:update' => ['doctrine:schema:update', 'Doctrine ORM', 'symfony/orm-pack'];
250+
yield 'hautelook:fixtures:load' => ['hautelook:fixtures:load', 'HautelookAliceBundle', 'hautelook/alice-bundle --dev'];
251+
yield 'league:oauth2-server:create-client' => ['league:oauth2-server:create-client', 'LeagueOAuth2ServerBundle', 'league/oauth2-server-bundle'];
252+
yield 'lexik:jwt:generate-keypair' => ['lexik:jwt:generate-keypair', 'LexikJWTAuthenticationBundle', 'lexik/jwt-authentication-bundle'];
253+
yield 'make:admin:dashboard' => ['make:admin:dashboard', 'EasyAdminBundle', 'easycorp/easyadmin-bundle'];
254+
yield 'make:controller' => ['make:controller', 'MakerBundle', 'symfony/maker-bundle --dev'];
255+
yield 'sass:build' => ['sass:build', 'SassBundle', 'symfonycasts/sass-bundle'];
256+
yield 'server:dump' => ['server:dump', 'Debug Bundle', 'symfony/debug-bundle --dev'];
257+
yield 'tailwind:build' => ['tailwind:build', 'TailwindBundle', 'symfonycasts/tailwind-bundle'];
258+
yield 'ux:icons:import' => ['ux:icons:import', 'UXIconsBundle', 'symfony/ux-icons'];
259+
yield 'ux:install' => ['ux:install', 'UXToolkitBundle', 'symfony/ux-toolkit'];
260+
yield 'ux:native:build-configs' => ['ux:native:build-configs', 'UXNativeBundle', 'symfony/ux-native'];
261+
yield 'ux:toolkit:create-kit' => ['ux:toolkit:create-kit', 'UXToolkitBundle', 'symfony/ux-toolkit'];
262+
yield 'ux:translator:warm-cache' => ['ux:translator:warm-cache', 'UxTranslatorBundle', 'symfony/ux-translator'];
263+
}
264+
265+
public function testNotSuggestingPackageForUnknownNamespace()
266+
{
267+
$result = $this->createEventForSuggestingPackages('unknown:command');
268+
$this->assertStringNotContainsString('You may be looking for a command provided by', $result);
269+
}
270+
271+
public function testNotSuggestingPackageWithoutDefaultAndPartialMatch()
272+
{
273+
$result = $this->createEventForSuggestingPackages('ux:unknown');
274+
$this->assertStringNotContainsString('You may be looking for a command provided by', $result);
275+
}
276+
277+
public function testNotSuggestingPackageForACommandOfACoreNamespace()
278+
{
279+
$result = $this->createEventForSuggestingPackages('debug:route');
280+
$this->assertStringNotContainsString('You may be looking for a command provided by', $result);
281+
}
282+
283+
public function testSuggestingPackagesWithExactMatchAndAlternatives()
284+
{
285+
// a partially installed namespace: DoctrineBundle is there, the fixtures bundle is not
286+
$result = $this->createEventForSuggestingPackages('doctrine:fixtures:load', ['doctrine', 'doctrine:database', 'doctrine:schema']);
287+
288+
$this->assertStringContainsString('provided by the "DoctrineFixturesBundle"', $result);
289+
}
290+
231291
private function createEventForSuggestingPackages(string $command, array $alternatives = []): string
232292
{
233293
$error = new CommandNotFoundException('', $alternatives);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.