From fc9b680f4d7d7359b7c7b9ca1aeafdaec08be1d9 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Tue, 4 May 2021 13:31:51 -0700 Subject: [PATCH] (REF) CiviEventDispatcher - Move service-listener stub to standalone class This change makes it easier for reflective tools (e.g. `cv debug:event-dispatcher`) to recognize service-based listeners. Before: `addListenerServce()` creates a stub for the target service+method. The stub is an anonymous `function`. After: `addListenerService()` creates a staub for the target service+method. The stub is based on invokable class. --- Civi/Core/CiviEventDispatcher.php | 8 +- Civi/Core/Event/ServiceListener.php | 87 +++++++++++++++++++ .../Civi/Core/Event/ServiceListenerTest.php | 62 +++++++++++++ 3 files changed, 150 insertions(+), 7 deletions(-) create mode 100644 Civi/Core/Event/ServiceListener.php create mode 100644 tests/phpunit/Civi/Core/Event/ServiceListenerTest.php diff --git a/Civi/Core/CiviEventDispatcher.php b/Civi/Core/CiviEventDispatcher.php index ac104537d8..195fc1ccfe 100644 --- a/Civi/Core/CiviEventDispatcher.php +++ b/Civi/Core/CiviEventDispatcher.php @@ -80,13 +80,7 @@ class CiviEventDispatcher extends EventDispatcher { throw new \InvalidArgumentException('Expected an array("service", "method") argument'); } - $this->addListener($eventName, function($event) use ($callback) { - static $svc; - if ($svc === NULL) { - $svc = \Civi::container()->get($callback[0]); - } - return call_user_func([$svc, $callback[1]], $event); - }, $priority); + $this->addListener($eventName, new \Civi\Core\Event\ServiceListener($callback), $priority); } /** diff --git a/Civi/Core/Event/ServiceListener.php b/Civi/Core/Event/ServiceListener.php new file mode 100644 index 0000000000..ae5b70c8f6 --- /dev/null +++ b/Civi/Core/Event/ServiceListener.php @@ -0,0 +1,87 @@ +inertCb = $callback; + } + + public function __invoke(...$args) { + if ($this->liveCb === NULL) { + $c = $this->container ?: \Civi::container(); + $this->liveCb = [$c->get($this->inertCb[0]), $this->inertCb[1]]; + } + return call_user_func_array($this->liveCb, $args); + } + + public function __toString() { + $class = NULL; + if (\Civi\Core\Container::isContainerBooted()) { + try { + $c = $this->container ?: \Civi::container(); + $class = $c->findDefinition($this->inertCb[0])->getClass(); + } + catch (Throwable $t) { + } + } + if ($class) { + return sprintf('$(%s)->%s() [%s]', $this->inertCb[0], $this->inertCb[1], $class); + } + else { + return sprintf('\$(%s)->%s()', $this->inertCb[0], $this->inertCb[1]); + } + } + + public function __sleep() { + return ['inertCb']; + } + + /** + * @param \Symfony\Component\DependencyInjection\ContainerInterface $container + * @return static + */ + public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container) { + $this->container = $container; + return $this; + } + +} diff --git a/tests/phpunit/Civi/Core/Event/ServiceListenerTest.php b/tests/phpunit/Civi/Core/Event/ServiceListenerTest.php new file mode 100644 index 0000000000..0cf534b175 --- /dev/null +++ b/tests/phpunit/Civi/Core/Event/ServiceListenerTest.php @@ -0,0 +1,62 @@ +setDefinition('test.svlt', new Definition(ServiceListenerTestExample::class, [$rand])) + ->setPublic(TRUE); + + $d = \Civi::dispatcher(); + $d->addListener('hook_civicrm_svlt', (new ServiceListener(['test.svlt', 'onSvlt']))->setContainer($container)); + + // Baseline + $this->assertEquals([], ServiceListenerTestExample::$notes); + $this->assertEquals($changeMe, $rand); + + // First call - instantiate and run + $d->dispatch('hook_civicrm_svlt', GenericHookEvent::create(['foo' => &$changeMe])); + $this->assertEquals($changeMe, 1 + $rand); + $this->assertEquals(["construct($rand)", "onSvlt($rand)"], + ServiceListenerTestExample::$notes); + + // Second call - reuse and run + $d->dispatch('hook_civicrm_svlt', GenericHookEvent::create(['foo' => &$changeMe])); + $this->assertEquals($changeMe, 2 + $rand); + $this->assertEquals(["construct($rand)", "onSvlt($rand)", "onSvlt(" . ($rand + 1) . ")"], + ServiceListenerTestExample::$notes); + } + +} + +class ServiceListenerTestExample { + + /** + * Free-form list of strings. + * + * @var array + */ + public static $notes = []; + + public function __construct($rand) { + self::$notes[] = "construct($rand)"; + } + + public function onSvlt(GenericHookEvent $e) { + self::$notes[] = "onSvlt({$e->foo})"; + $e->foo++; + } + +} -- 2.25.1