mirror of
https://github.com/danog/amp.git
synced 2024-11-30 04:29:08 +01:00
Add Mediator::unsubscribe() and require explicit subscriber removal
This commit is contained in:
parent
2cb1937553
commit
27f5ab5dae
@ -3,25 +3,53 @@
|
||||
namespace Amp;
|
||||
|
||||
/**
|
||||
* A Promise/Coroutine-aware implementation of the Mediator pattern.
|
||||
* A Promise-aware implementation of the Mediator pattern.
|
||||
*
|
||||
* @link https://en.wikipedia.org/wiki/Mediator_pattern
|
||||
*/
|
||||
class Mediator {
|
||||
private $nextId = "a";
|
||||
private $idEventMap = [];
|
||||
private $eventSubscriberMap = [];
|
||||
|
||||
/**
|
||||
* Invoke the callback when the specified event is published to the Mediator.
|
||||
*
|
||||
* If a subscriber callback returns FALSE (===) or a generator\promise that
|
||||
* resolves to FALSE that callback will be unsubscribed from the event.
|
||||
* Attach an event listener callback to the Mediator
|
||||
*
|
||||
* Listener callbacks are invoked with the signature:
|
||||
*
|
||||
* callback(Mediator $mediator, string $subscriberId, ...$data)
|
||||
*
|
||||
* @param string $eventName The name of the event being subscribed to
|
||||
* @param callable $callback The callback to invoke when the event is published
|
||||
* @return void
|
||||
* @return string Returns listener's subscriber ID
|
||||
*/
|
||||
public function subscribe(string $eventName, callable $callback) {
|
||||
$this->eventSubscriberMap[$eventName][] = $callback;
|
||||
public function subscribe(string $eventName, callable $callback): string {
|
||||
$subscriberId = $this->nextId++;
|
||||
$this->idEventMap[$subscriberId] = $eventName;
|
||||
$this->eventSubscriberMap[$eventName][$subscriberId] = $callback;
|
||||
return $subscriberId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach an event listener from the Mediator
|
||||
*
|
||||
* @param string $subscriberId The subscriber ID generated registering the listener
|
||||
* @return bool Returns TRUE if a listener was removed, FALSE otherwise
|
||||
*/
|
||||
public function unsubscribe(string $subscriberId): bool {
|
||||
if (!isset($this->idEventMap[$subscriberId])) {
|
||||
return false;
|
||||
}
|
||||
$eventName = $this->idEventMap[$subscriberId];
|
||||
unset(
|
||||
$this->idEventMap[$subscriberId],
|
||||
$this->eventSubscriberMap[$eventName][$subscriberId]
|
||||
);
|
||||
// don't leak memory even if it's just an empty array
|
||||
if (empty($this->eventSubscriberMap[$eventName])) {
|
||||
unset($this->eventSubscriberMap[$eventName]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,24 +70,14 @@ class Mediator {
|
||||
}
|
||||
|
||||
private function doPublish(string $eventName, array $data): \Generator {
|
||||
foreach ($this->eventSubscriberMap[$eventName] as $id => $callback) {
|
||||
$promises[$id] = call($callback, ...$data);
|
||||
foreach ($this->eventSubscriberMap[$eventName] as $subscriberId => $callback) {
|
||||
$promises[$subscriberId] = call($callback, $this, $subscriberId, ...$data);
|
||||
}
|
||||
list($errors, $results) = yield Promise\any($promises);
|
||||
// remove subscribers that returned FALSE (===)
|
||||
foreach ($results as $id => $result) {
|
||||
if ($result === false) {
|
||||
unset($this->eventSubscriberMap[$eventName][$id]);
|
||||
}
|
||||
}
|
||||
// don't leak memory even if it's just an empty array
|
||||
if (empty($this->eventSubscriberMap[$eventName])) {
|
||||
unset($this->eventSubscriberMap[$eventName]);
|
||||
}
|
||||
if ($errors) {
|
||||
throw new MultiReasonException(
|
||||
$errors,
|
||||
"Event subscriber(s) threw uncaught exceptions while reacting to {$eventName}"
|
||||
"Mediator subscriber(s) threw uncaught exception(s) while reacting to {$eventName}"
|
||||
);
|
||||
}
|
||||
return \count($results);
|
||||
|
@ -86,10 +86,10 @@ class MediatorTest extends TestCase {
|
||||
$mediator = new Mediator;
|
||||
|
||||
Loop::run(function () use ($mediator) {
|
||||
$mediator->subscribe("event.foo", static function () {});
|
||||
$mediator->subscribe("event.foo", static function () { return false; });
|
||||
$mediator->subscribe("event.bar", static function () {});
|
||||
$mediator->subscribe("event.baz", static function () { return false; });
|
||||
$mediator->subscribe("event.foo", function () {});
|
||||
$mediator->subscribe("event.foo", function ($m, $id) { $m->unsubscribe($id); });
|
||||
$mediator->subscribe("event.bar", function () {});
|
||||
$mediator->subscribe("event.baz", function ($m, $id) { $m->unsubscribe($id); });
|
||||
yield $mediator->publish("event.foo", 42);
|
||||
yield $mediator->publish("event.bar", 42);
|
||||
yield $mediator->publish("event.baz", 42);
|
||||
|
Loading…
Reference in New Issue
Block a user