Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions Tests/DispatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Joomla\Event\Priority;
use Joomla\Event\Tests\Stubs\FirstListener;
use Joomla\Event\Tests\Stubs\SecondListener;
use Joomla\Event\Tests\Stubs\SomethingDynamicListener;
use Joomla\Event\Tests\Stubs\SomethingListener;
use Joomla\Event\Tests\Stubs\ThirdListener;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -717,4 +718,52 @@ public function testRemoveSubscriber()
$this->assertFalse($this->instance->hasListener([$listener, 'onSomething']));
$this->assertFalse($this->instance->hasListener([$listener, 'onAfterSomething']));
}

/**
* @testdox An event dynamic subscriber is registered to the dispatcher
*
* @covers Joomla\Event\Dispatcher
* @uses Joomla\Event\ListenersPriorityQueue
*/
public function testAddDynamicSubscriber()
{
$listener = new SomethingDynamicListener;
$callbacks = $listener->getSubscribedEvents();

// Add our event subscriber
$this->instance->addDynamicSubscriber($listener);

$this->assertTrue($this->instance->hasListener([$listener, 'onBeforeSomething']));
$this->assertTrue($this->instance->hasListener($callbacks['onSomething']));
$this->assertTrue($this->instance->hasListener([$listener, 'onAfterSomething']));
$this->assertTrue($this->instance->hasListener($callbacks['onSomething2'][0]));

$this->assertEquals(Priority::NORMAL, $this->instance->getListenerPriority('onBeforeSomething', [$listener, 'onBeforeSomething']));
$this->assertEquals(Priority::NORMAL, $this->instance->getListenerPriority('onSomething', $callbacks['onSomething']));
$this->assertEquals(Priority::HIGH, $this->instance->getListenerPriority('onAfterSomething', [$listener, 'onAfterSomething']));
$this->assertEquals(Priority::LOW, $this->instance->getListenerPriority('onSomething2', $callbacks['onSomething2'][0]));
}

/**
* @testdox An event dynamic subscriber is removed from the dispatcher
*
* @covers Joomla\Event\Dispatcher
* @uses Joomla\Event\ListenersPriorityQueue
*/
public function testRemoveDynamicSubscriber()
{
$listener = new SomethingDynamicListener;
$callbacks = $listener->getSubscribedEvents();

// Add our event subscriber
$this->instance->addDynamicSubscriber($listener);

// And now remove it
$this->instance->removeDynamicSubscriber($listener);

$this->assertFalse($this->instance->hasListener([$listener, 'onBeforeSomething']));
$this->assertFalse($this->instance->hasListener($callbacks['onSomething']));
$this->assertFalse($this->instance->hasListener([$listener, 'onAfterSomething']));
$this->assertFalse($this->instance->hasListener($callbacks['onSomething2'][0]));
}
}
96 changes: 96 additions & 0 deletions Tests/Stubs/SomethingDynamicListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php
/**
* @copyright Copyright (C) 2005 - 2022 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

namespace Joomla\Event\Tests\Stubs;

use Joomla\Event\DynamicSubscriberInterface;
use Joomla\Event\Event;
use Joomla\Event\Priority;

/**
* A listener listening to some events.
*
* @since __DEPLOY_VERSION__
*/
class SomethingDynamicListener implements DynamicSubscriberInterface
{
/**
* Callback for onSomething.
*
* @var callable
*
* @since __DEPLOY_VERSION__
*/
private $onSomethingCallback;

/**
* Callback for onSomething2.
*
* @var callable
*
* @since __DEPLOY_VERSION__
*/
private $onSomething2Callback;

/**
* Listen to onBeforeSomething.
*
* @param Event $event The event.
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
public function onBeforeSomething(Event $event)
{
}

/**
* Listen to onAfterSomething.
*
* @param Event $event The event.
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
public function onAfterSomething(Event $event)
{
}

/**
* Returns an array of events this subscriber will listen to.
*
* The array keys are event names and the value can be:
*
* - The method name to call (priority defaults to 0)
* - An array composed of the method name to call and the priority
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
*
* @return array
*
* @since __DEPLOY_VERSION__
*/
public function getSubscribedEvents(): array
{
$this->onSomethingCallback = $this->onSomethingCallback ?? function (Event $event) {
};

$this->onSomething2Callback = $this->onSomething2Callback ?? function (Event $event) {
};

return [
'onBeforeSomething' => 'onBeforeSomething',
'onSomething' => $this->onSomethingCallback,
'onAfterSomething' => ['onAfterSomething', Priority::HIGH],
'onSomething2' => [$this->onSomething2Callback, Priority::LOW],
];
}
}
46 changes: 45 additions & 1 deletion src/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*
* @since 1.0
*/
class Dispatcher implements DispatcherInterface
class Dispatcher implements DispatcherInterface, DispatcherDynamicSubscriberInterface
{
/**
* An array of registered events indexed by the event names.
Expand Down Expand Up @@ -420,6 +420,50 @@ public function removeSubscriber(SubscriberInterface $subscriber): void
}
}

/**
* Adds an event dynamic subscriber.
*
* @param DynamicSubscriberInterface $subscriber The subscriber.
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
public function addDynamicSubscriber(DynamicSubscriberInterface $subscriber): void
{
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
if (\is_array($params)) {
$callback = !\is_string($params[0]) && \is_callable($params[0]) ? $params[0] : [$subscriber, $params[0]];
$this->addListener($eventName, $callback, $params[1] ?? Priority::NORMAL);
} else {
$callback = !\is_string($params) && \is_callable($params) ? $params : [$subscriber, $params];
$this->addListener($eventName, $callback);
}
}
}

/**
* Removes an event dynamic subscriber.
*
* @param DynamicSubscriberInterface $subscriber The subscriber.
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
public function removeDynamicSubscriber(DynamicSubscriberInterface $subscriber): void
{
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
if (\is_array($params)) {
$callback = !\is_string($params[0]) && \is_callable($params[0]) ? $params[0] : [$subscriber, $params[0]];
$this->removeListener($eventName, $callback);
} else {
$callback = !\is_string($params) && \is_callable($params) ? $params : [$subscriber, $params];
$this->removeListener($eventName, $callback);
}
}
}

/**
* Dispatches an event to all registered listeners.
*
Expand Down
40 changes: 40 additions & 0 deletions src/DispatcherDynamicSubscriberInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

/**
* Part of the Joomla Framework Event Package
*
* @copyright Copyright (C) 2005 - 2024 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/

namespace Joomla\Event;

/**
* Interface for event dispatcher with support of DynamicSubscriberInterface.
*
* @since __DEPLOY_VERSION__
*/
interface DispatcherDynamicSubscriberInterface
{
/**
* Adds an event dynamic subscriber.
*
* @param DynamicSubscriberInterface $subscriber The subscriber.
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
public function addDynamicSubscriber(DynamicSubscriberInterface $subscriber): void;

/**
* Removes an event dynamic subscriber.
*
* @param DynamicSubscriberInterface $subscriber The subscriber.
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
public function removeDynamicSubscriber(DynamicSubscriberInterface $subscriber): void;
}
43 changes: 43 additions & 0 deletions src/DynamicSubscriberInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/**
* Part of the Joomla Framework Event Package
*
* @copyright Copyright (C) 2005 - 2024 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/

namespace Joomla\Event;

/**
* Interface for event subscribers.
*
* The difference from SubscriberInterface is that it uses non-static method,
* which allows subscriber to configure listeners depending on runtime.
* Additionally, it supports callables.
*
* @since __DEPLOY_VERSION__
*/
interface DynamicSubscriberInterface
{
/**
* Returns an array of events this subscriber will listen to.
*
* The array keys are event names and the value can be:
*
* - The method name to call (priority defaults to 0)
* - An array composed of the method name to call and the priority
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => $callable)
* * array('eventName' => array($callable, $priority))
*
* @return array
*
* @since __DEPLOY_VERSION__
*/
public function getSubscribedEvents(): array;
}