Add EventManager and related classes

This commit is contained in:
Austen McDonald
2016-05-08 13:15:04 -07:00
parent 8b8b7e5299
commit 9ac276ca37
4 changed files with 181 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace LotGD\Core;
interface EventHandler
{
/**
* Called when an event is published that is handled by this class.
*
* @param string $event Name of this event.
* @param array $context Arbitrary dictionary representing context around this event.
* @return array|null Return an array if you want to make changes to the $context before
* the next handler is called. Otherwise, return null.
*/
public static function handleEvent(string $event, array $context): mixed;
}
+103
View File
@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
namespace LotGD\Core;
use LotGD\Core\Models\EventSubscription;
use LotGD\Core\EventHandler;
use LotGD\Core\Excpetions\ClassNotFoundException;
use LotGD\Core\Excpetions\SubscriptionNotFoundException;
/**
* Manages a simple publish/subscribe system based on regular expressions
* matching event names and running a fixed
*/
class EventManager
{
/**
* Publish an event. Will immediately cause handleEvent() to be called on all
* subscribed classes. This does not ensure any order in which the handlers
* are run.
*
* @param string $event The name of the event to publish.
*/
public function publish(string $event, array $context)
{
// For right now, implement the naive approach of iterating every entry
// in the subscription database, checking the regular expression. We
// will need a cache :)
// TODO: Add an in-memory cache here. Will likely only be in the 1000s of
// patterns, so no need to go to the remote key-value store.
$subscriptions = EventSubscription::findAll();
foreach ($subscriptions as $s) {
if (preg_match($s->getPattern(), $event)) {
$class = $s->getClass();
$c = $class::handleEvent($event, $context);
if ($c !== null) {
$context = $c;
}
}
}
}
/**
* Create a new event subscription, registering $class to receive the handleEvent()
* method every time an event matching $pattern is published.
*
* @param string $pattern Regular expression, in PHP format, to match against
* published event names.
* @param string $class Fully qualified class name, which implements the
* EventHandler interface, that will receive the handleEvent() method call when
* events matching $pattern are published.
* @throws ClassNotFoundException if class cannot be resolved into a class.
* @throws WrongTypeException if class does not implement the EventHandler
* interface or the pattern is not a valid regular expression.
*/
public function subscribe(string $pattern, string $class)
{
try {
// Can we resolve this class?
$klass = new \ReflectionClass($class);
} catch (LogicException $Exception) {
// Currently we do the same thing on not found as on some other
// exception. Maybe we should do something different.
throw new ClassNotFoundException();
} catch (ReflectionException $Exception) {
throw new ClassNotFoundException();
}
// Check if the class implements EventHandler.
$interface = EventHandler::class;
if (!$klass->implementsInterface($interface)) {
throw new WrongTypeException("Class does not implement {$interface}");
}
// Validate the regular expression.
if (preg_match($pattern, null) === false) {
throw new WrontTypeException('Invalid regular expression');
}
EventSubscription::create([
'pattern' => $pattern,
'class' => $class
]);
}
/**
* Remove an event subscription, unregistering $class to receive the handleEvent()
* method when $pattern is published.
*
* @param string $pattern Regular expression, in PHP format, to match against
* published event names.
* @param string $class Fully qualified class name.
* @throws SubscriptionNotFoundException if the specified subscription does not exist.
*/
public function unsubscribe(string $pattern, string $class)
{
EventSubscriotion::delete([
'pattern' => $pattern,
'class' => $class
]);
}
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Exceptions;
/**
* Exception if an event subscription does not exist.
*/
class SubscriptionNotFoundException extends CoreException {
}
+51
View File
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Models;
use Doctrine\ORM\EntityManagerInterface;
use LotGD\Core\Tools\Model\Creator;
use LotGD\Core\Tools\Model\Deletor;
/**
* An event name to class binding that represents that class listening for that
* event.
* @Entity
* @Table(name="event_subscriptions")
*/
class EventSubscription
{
use Creator;
use Deletor;
/** @Id @Column(type="string"); */
private $pattern;
/** @Id @Column(type="string"); */
private $class;
/** @var array */
private static $fillable = [
"pattern",
"class",
];
/**
* Returns the pattern used to match against event names for this subscription.
* Format is PHP regular expressions.
* @return string
*/
public function getPattern(): string
{
return $this->pattern;
}
/**
* Returns the class name subscribed to this event.
* @return string
*/
public function getClass(): string
{
return $this->class;
}
}