-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[Console] Add auto-completion to debug:event-dispatcher #43859
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,8 @@ | |
use Psr\Container\ContainerInterface; | ||
use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper; | ||
use Symfony\Component\Console\Command\Command; | ||
use Symfony\Component\Console\Completion\CompletionInput; | ||
use Symfony\Component\Console\Completion\CompletionSuggestions; | ||
use Symfony\Component\Console\Input\InputArgument; | ||
use Symfony\Component\Console\Input\InputInterface; | ||
use Symfony\Component\Console\Input\InputOption; | ||
|
@@ -56,7 +58,8 @@ protected function configure() | |
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'), | ||
]) | ||
->setDescription(self::$defaultDescription) | ||
->setHelp(<<<'EOF' | ||
->setHelp( | ||
<<<'EOF' | ||
The <info>%command.name%</info> command displays all configured listeners: | ||
|
||
<info>php %command.full_name%</info> | ||
|
@@ -120,6 +123,47 @@ protected function execute(InputInterface $input, OutputInterface $output): int | |
return 0; | ||
} | ||
|
||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void | ||
{ | ||
if ($input->mustSuggestArgumentValuesFor('event')) { | ||
$dispatcherServiceName = $input->getOption('dispatcher'); | ||
if (!$this->dispatchers->has($dispatcherServiceName)) { | ||
return; | ||
} | ||
|
||
$dispatcher = $this->dispatchers->get($dispatcherServiceName); | ||
$listeners = $dispatcher->getListeners(); | ||
$eventNames = array_keys($listeners); | ||
$listenersName = []; | ||
foreach ($listeners as $listOfListener) { | ||
$listenersName = array_merge($listenersName, $listOfListener); | ||
} | ||
$suggestions->suggestValues(array_merge( | ||
$eventNames, | ||
$listenersName, | ||
)); | ||
|
||
return; | ||
} | ||
|
||
if ($input->mustSuggestOptionValuesFor('dispatcher')) { | ||
$suggestions->suggestValues(array_merge( | ||
$this->dispatchers->getServiceIds(), | ||
$this->dispatchers->getAliases() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not quite sure the dispatchers will be an instance of Container or ContainerBuilder? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Psalm CI job might fail on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
)); | ||
|
||
return; | ||
} | ||
|
||
if ($input->mustSuggestOptionValuesFor('format')) { | ||
$suggestions->suggestValues( | ||
(new DescriptorHelper())->getFormats() | ||
); | ||
|
||
return; | ||
} | ||
} | ||
|
||
private function searchForEvent(EventDispatcherInterface $dispatcher, string $needle): array | ||
{ | ||
$output = []; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Bundle\FrameworkBundle\Tests\Command; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Bundle\FrameworkBundle\Command\EventDispatcherDebugCommand; | ||
use Symfony\Bundle\FrameworkBundle\Console\Application; | ||
use Symfony\Component\Console\Tester\CommandCompletionTester; | ||
use Symfony\Component\DependencyInjection\Container; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\EventDispatcher\EventDispatcher; | ||
use Symfony\Component\HttpKernel\KernelInterface; | ||
|
||
class EventDispatcherDebugCommandCompletionTest extends TestCase | ||
{ | ||
/** | ||
* @dataProvider provideCompletionSuggestions | ||
*/ | ||
public function testComplete(array $input, array $expectedSuggestions) | ||
{ | ||
$tester = $this->createCommandCompletionTester(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you think about only mocking the $dispatchers = $this->createMock(ContainerInterface::class);
$dispatchers->method('get')->willReturn($dispatcher);
$dispatchers->method('has')->willReturn(true);
// ...
$command = new EventDispatcherDebugCommand($dispatchers);
$tester = new CommandCompletionTester($command); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me try this out There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But container interface don't have another method required |
||
|
||
$suggestions = $tester->complete($input); | ||
|
||
$this->assertSame($expectedSuggestions, $suggestions); | ||
} | ||
|
||
public function provideCompletionSuggestions() | ||
{ | ||
yield 'event' => [[''], ['event', 'Listener']]; | ||
yield 'event for other dispatcher' => [['--dispatcher=other_event_dispatcher', 'other'], ['other_event', 'OtherListener']]; | ||
yield 'dispatcher' => [['--dispatcher='], ['event_dispatcher', 'other_event_dispatcher']]; | ||
yield 'format' => [['--format='], ['txt', 'xml', 'json', 'md']]; | ||
} | ||
|
||
private function createCommandCompletionTester(): CommandCompletionTester | ||
{ | ||
$kernel = $this->createMock(KernelInterface::class); | ||
|
||
$kernel | ||
->expects($this->any()) | ||
->method('getBundle') | ||
->willReturnMap([]); | ||
|
||
$kernel | ||
->expects($this->any()) | ||
->method('getBundles') | ||
->willReturn([]); | ||
|
||
$container = new Container(); | ||
|
||
$kernel | ||
->expects($this->any()) | ||
->method('getContainer') | ||
->willReturn($container); | ||
|
||
$dispatcher = new EventDispatcher(); | ||
$otherDispatcher = new EventDispatcher(); | ||
|
||
$dispatcher->addListener('event', 'Listener'); | ||
$otherDispatcher->addListener('other_event', 'OtherListener'); | ||
|
||
$locator = $this->createLocator([ | ||
'event_dispatcher' => $dispatcher, | ||
'other_event_dispatcher' => $otherDispatcher, | ||
]); | ||
|
||
$command = new EventDispatcherDebugCommand($locator); | ||
|
||
$application = new Application($kernel); | ||
|
||
$application->add($command); | ||
|
||
return new CommandCompletionTester($application->find('debug:event-dispatcher')); | ||
} | ||
|
||
private function createLocator(array $linkers) | ||
{ | ||
$locator = $this->createMock(ContainerBuilder::class); | ||
|
||
$locator->expects($this->any()) | ||
->method('has') | ||
->willReturnCallback(function ($dispatcherServiceName) use ($linkers) { | ||
return isset($linkers[$dispatcherServiceName]); | ||
}); | ||
|
||
$locator->expects($this->any()) | ||
->method('get') | ||
->willReturnCallback(function ($dispatcherServiceName) use ($linkers) { | ||
return $linkers[$dispatcherServiceName]; | ||
}); | ||
|
||
$locator->expects($this->any()) | ||
->method('getServiceIds') | ||
->willReturnCallback(function () use ($linkers) { | ||
return array_keys($linkers); | ||
}); | ||
|
||
$locator->expects($this->any()) | ||
->method('getAliases') | ||
->willReturnCallback(function () { | ||
return []; | ||
}); | ||
|
||
return $locator; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why listeners are added to the list of suggested event names? The argument get values like
kernel.response
orSymfony\Component\Mailer\Event\MessageEvent
. I did some tests and listener names are not accepted (ex:Symfony\Component\HttpKernel\EventListener\ResponseListener::onKernelResponse
)