Reputation: 166066
Following these instructions, I have installed Symfony 3.4 with the following command.
composer.phar create-project symfony/framework-standard-edition my_project_name_3_4
Then, following these other instructions, I've added an event dispatcher to the stock console application, as well as an event listener
#File: my_project_name_3_4/bin/console
/* ... other code ... */
$application = new Application($kernel);
//START: my new code
//create dispatcher and add to application
use Symfony\Component\EventDispatcher\EventDispatcher;
$dispatcher = new EventDispatcher();
$application->setDispatcher($dispatcher);
//add my event to dispatcher
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\ConsoleEvents;
$dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) {
exit("\n\nHey, Symfony called my event! Let's crash this party! \n\n");
});
//END: my new code
$application->run($input);
However, when I run a Symfony console command,
php bin/console help
my event does not fire. I would expect the above code to halt when Symfony invokes my ConsoleEvents::COMMAND
listener.
I've done a little bit of debugging, and it seems like calling $application->run
removed my event from the dispatcher?! Before I go down the rabbit hole of doing the right amount of debugging, I wanted to check in and see if there's anything obvious I'm doing wrong, of if there's known science to fix this. Is there a different way I'm supposed to add events to the stock Symfony console application?
Upvotes: 1
Views: 2232
Reputation: 48865
Guess I should have read the question more carefully. All the way to the end. I was sort of fixated on how to modify bin/console.
In any event (pun intended) you can add a command listener in the same way you would add any event listener.
namespace AppBundle;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class MySubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
// return the subscribed events, their methods and priorities
return array(
ConsoleEvents::COMMAND => array(
array('onCommand', 0),
)
);
}
public function onCommand(ConsoleCommandEvent $event)
{
echo "onCommand\n";
}
}
And thanks to the default autowire and autoconfigure, you don't even have to add anything to your services.yml. It just works.
=============================
Original Answer
I think you might be conflating the console component with the console framework documentation.
Start by answering your question by editing the bin/console file installed by the framework:
# bin/console
use Symfony\Bundle\FrameworkBundle\Console\Application as FrameworkApplication;
$kernel = new AppKernel($env, $debug);
$kernel->boot();
$application = new FrameworkApplication($kernel);
$dispatcher = $kernel->getContainer()->get('event_dispatcher');
$dispatcher->addListener(\Symfony\Component\Console\ConsoleEvents::COMMAND,
function (\Symfony\Component\Console\Event\ConsoleCommandEvent $event) {
exit("\n\nHey, Symfony called my event! Let's crash this party! \n\n");
});
$application->run($input);
It is perfectly valid code based on using the framework Application class.
However, the second link in your question points to the console component documentation which is based on the component application class.
use Symfony\Component\Console\Application as ComponentApplication;
The ComponentApplication class has no knowledge of the kernel or the container. It is pretty much standalone and provides a means to inject an event dispatcher via ComponentApplication::setDispatcher. The code you tried in your question would work just fine if you used the ComponentApplication class though you would have to register your own commands.
The FrameworkApplication pulls the container from the kernel and then pulls the event dispatcher from it and passes it to setDispatcher. It also uses the kernel for registering commands. Nothing in the class actually relies on an unbooted kernel. The kernel class itself guards against being booted multiple times.
So booting the kernel and accessing the container before calling FrameworkApplication::run() is fine.
And I guess that if you really want to do this in a "non-dirty" fashion then just extend the Application class and add your listener in the doRun method.
Upvotes: 1