Консоль Symfony: событие не запускается

Следуя этим инструкциям, я установил Symfony 3.4 с помощью следующей команды.

composer.phar create-project symfony/framework-standard-edition my_project_name
php bin/console help
4

Затем, следуя этим другим инструкциям, я добавлен диспетчер событий в стандартное консольное приложение, а также прослушиватель событий

#File: my_project_name
php bin/console help
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);

Однако, когда я запускаю консольную команду Symfony,

php bin/console help

мое мероприятие не срабатывает. Я ожидал, что приведенный выше код остановится, когда Symfony вызовет мой ConsoleEvents::COMMAND слушатель.

Я провел небольшую отладку, и кажется, что вызов $application->run удалил мое событие из диспетчера ?! Прежде чем я пойду в кроличью нору, выполнив правильный объем отладки, я хотел проверить, есть ли что-нибудь очевидное, что я делаю неправильно, или есть ли известная наука, чтобы исправить это. Есть ли другой способ добавления событий в стандартное консольное приложение Symfony?


person Alan Storm    schedule 15.12.2017    source источник
comment
Попробуйте: $ dispatcher = $ kernel- ›getContainer () -› get ('event_dispatcher');   -  person Cerad    schedule 15.12.2017
comment
@Cerad у нас нет готового контейнера до $kernel->boot(), поэтому он не будет работать как есть, поэтому добавьте $kernel->boot() перед кодом, и он должен работать.   -  person yceruto    schedule 15.12.2017
comment
Вызовите $ kernel- ›boot () перед получением контейнера. Кажется, работает. Ядро защищает себя от многократной загрузки.   -  person Cerad    schedule 15.12.2017
comment
Это метод FrameworkBundle: Application: doRun, который заменяет ваш диспетчер диспетчером контейнера. Я полагаю, если бы вы действительно хотели, вы также могли бы отменить его.   -  person Cerad    schedule 15.12.2017
comment
Спасибо обоим - вместо того, чтобы создать диспетчер, выполнить раннюю загрузку ядра, а затем получить диспетчер из контейнера приложения $kernel->boot(); $dispatcher = $kernel->getContainer()->get('event_dispatcher'); и использовать этот диспетчер вместо ранее созданного, похоже, сработало. Однако оставим вопрос открытым, так как ранняя загрузка вручную кажется немного грязной, и кажется, что должен быть лучший способ сделать это. С удовольствием проголосую за этот формально введенный ответ и отметим его как лучший, если не будет ничего лучше.   -  person Alan Storm    schedule 15.12.2017


Ответы (1)


Думаю, мне следовало прочитать вопрос внимательнее. До самого конца. Я был как бы зациклен на том, как изменить bin / console.

В любом случае (каламбур) вы можете добавить слушателя команд точно так же, как добавить любой прослушиватель событий.

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";
    }
}

А благодаря автоматическому подключению и автонастройке по умолчанию вам даже не нужно ничего добавлять в свой services.yml. Просто работает.

=============================

Оригинальный ответ

Я думаю, вы могли бы объединить компонент консоли с документацией по фреймворку консоли.

Начните с ответа на свой вопрос, отредактировав файл bin / console, установленный фреймворком:

# 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);

Это совершенно правильный код, основанный на использовании класса Application Framework.

Однако вторая ссылка в вашем вопросе указывает на документацию по компонентам консоли, которая основана на классе приложения компонента.

use Symfony\Component\Console\Application as ComponentApplication;

Класс ComponentApplication ничего не знает о ядре или контейнере. Он в значительной степени автономен и предоставляет средства для внедрения диспетчера событий через ComponentApplication :: setDispatcher. Код, который вы пробовали в своем вопросе, работал бы нормально, если бы вы использовали класс ComponentApplication, хотя вам придется регистрировать свои собственные команды.

FrameworkApplication извлекает контейнер из ядра, а затем извлекает из него диспетчер событий и передает его в setDispatcher. Он также использует ядро ​​для регистрации команд. На самом деле ничто в классе не полагается на незагруженное ядро. Сам класс ядра защищает от многократной загрузки.

Так что загрузка ядра и доступ к контейнеру перед вызовом FrameworkApplication :: run () - это нормально.

И я предполагаю, что если вы действительно хотите сделать это «не грязным» способом, просто расширьте класс Application и добавьте своего слушателя в метод doRun.

person Cerad    schedule 15.12.2017
comment
Это вам раньше за вашу помощь и за вашу перспективу @Cerad. Немного переосмыслив: я подхожу к этому с (вероятно, слишком идеализированной) точки зрения, что связанное консольное приложение Symfony является не только полезным инструментом, но и эталонным приложением для лучших практик использования компонента консоли Symfony для При создании приложения я ожидал, что образец кода для компонента будет работать в консольном приложении. - person Alan Storm; 16.12.2017
comment
Что касается загрузки ядра, грязное или нет: хотя это решение работает и является прекрасным решением (и то, что я, вероятно, выберу), оно заставляет меня (клиентского программиста, который просто хочет добавить событие) к 1. Быть известно о загрузке ядра и 2. Взять на себя ответственность, если консольное приложение Symfony когда-либо вносит изменения, предполагающие его загрузчик ядра. Чистым решением было бы такое, которое позволяет добавить моего слушателя без необходимости знать что-либо о том, как приложение выполняет свою работу. - person Alan Storm; 16.12.2017
comment
Консоли приложений на основе платформы - это не то же самое, что консоли приложений на основе компонентов. bin / console просто не предназначена для модификации разработчиком. Если вам по какой-то причине нужно, чтобы он работал иначе, клонируйте его и настройте соответствующим образом. - person Cerad; 16.12.2017
comment
Думаю, я наконец понял вопрос. Обновленный ответ. - person Cerad; 16.12.2017
comment
Спасибо, что нашли время разобрать мой вопрос: я не являюсь постоянным разработчиком Symfony. Это ценится! - person Alan Storm; 18.12.2017