-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
389 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Events; | ||
|
||
final readonly class ItHappened | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Events; | ||
|
||
use Tempest\Events\EventHandler; | ||
|
||
final class MyEventHandler | ||
{ | ||
public static bool $itHappened = false; | ||
|
||
#[EventHandler] | ||
public function handleItHappened(ItHappened $event): void | ||
{ | ||
self::$itHappened = true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
use Tempest\Events\EventBusConfig; | ||
|
||
return new EventBusConfig(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tempest\Discovery; | ||
|
||
use ReflectionClass; | ||
use ReflectionMethod; | ||
use ReflectionNamedType; | ||
use Tempest\Container\Container; | ||
use Tempest\Events\EventBusConfig; | ||
use Tempest\Events\EventHandler; | ||
use Tempest\Support\Reflection\Attributes; | ||
|
||
final readonly class EventBusDiscovery implements Discovery | ||
{ | ||
private const string CACHE_PATH = __DIR__ . '/event-bus-discovery.cache.php'; | ||
|
||
public function __construct( | ||
private EventBusConfig $eventBusConfig, | ||
) { | ||
} | ||
|
||
public function discover(ReflectionClass $class): void | ||
{ | ||
foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { | ||
$eventHandler = Attributes::find(EventHandler::class)->in($method)->first(); | ||
|
||
if (! $eventHandler) { | ||
continue; | ||
} | ||
|
||
$parameters = $method->getParameters(); | ||
|
||
if (count($parameters) !== 1) { | ||
continue; | ||
} | ||
|
||
$type = $parameters[0]->getType(); | ||
|
||
if (! $type instanceof ReflectionNamedType) { | ||
continue; | ||
} | ||
|
||
if (! class_exists($type->getName())) { | ||
continue; | ||
} | ||
|
||
$this->eventBusConfig->addHandler( | ||
eventHandler: $eventHandler, | ||
eventName: $type->getName(), | ||
reflectionMethod: $method, | ||
); | ||
} | ||
} | ||
|
||
public function hasCache(): bool | ||
{ | ||
return file_exists(self::CACHE_PATH); | ||
} | ||
|
||
public function storeCache(): void | ||
{ | ||
file_put_contents(self::CACHE_PATH, serialize($this->eventBusConfig->handlers)); | ||
} | ||
|
||
public function restoreCache(Container $container): void | ||
{ | ||
$handlers = unserialize(file_get_contents(self::CACHE_PATH)); | ||
|
||
$this->eventBusConfig->handlers = $handlers; | ||
} | ||
|
||
public function destroyCache(): void | ||
{ | ||
@unlink(self::CACHE_PATH); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tempest\Events; | ||
|
||
interface EventBus | ||
{ | ||
public function dispatch(object $event): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tempest\Events; | ||
|
||
use ReflectionMethod; | ||
|
||
final class EventBusConfig | ||
{ | ||
public function __construct( | ||
/** @var \Tempest\Events\EventHandler[][] */ | ||
public array $handlers = [], | ||
|
||
/** @var \Tempest\Events\EventBusMiddleware[] */ | ||
public array $middleware = [], | ||
) { | ||
} | ||
|
||
public function addHandler(EventHandler $eventHandler, string $eventName, ReflectionMethod $reflectionMethod): self | ||
{ | ||
$this->handlers[$eventName][] = $eventHandler | ||
->setEventName($eventName) | ||
->setHandler($reflectionMethod); | ||
|
||
return $this; | ||
} | ||
|
||
public function addMiddleware(EventBusMiddleware $middleware): self | ||
{ | ||
$this->middleware[] = $middleware; | ||
|
||
return $this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tempest\Events; | ||
|
||
use Tempest\Container\Container; | ||
use Tempest\Container\Initializer; | ||
use Tempest\Container\Singleton; | ||
|
||
#[Singleton] | ||
final readonly class EventBusInitializer implements Initializer | ||
{ | ||
public function initialize(Container $container): EventBus | ||
{ | ||
return new GenericEventBus( | ||
$container, | ||
$container->get(EventBusConfig::class), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tempest\Events; | ||
|
||
interface EventBusMiddleware | ||
{ | ||
public function __invoke(object $event, callable $next): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tempest\Events; | ||
|
||
use Attribute; | ||
use ReflectionMethod; | ||
|
||
#[Attribute] | ||
final class EventHandler | ||
{ | ||
public string $eventName; | ||
|
||
public ReflectionMethod $handler; | ||
|
||
public function setEventName(string $eventName): self | ||
{ | ||
$this->eventName = $eventName; | ||
|
||
return $this; | ||
} | ||
|
||
public function setHandler(ReflectionMethod $handler): self | ||
{ | ||
$this->handler = $handler; | ||
|
||
return $this; | ||
} | ||
|
||
public function __serialize(): array | ||
{ | ||
return [ | ||
'eventName' => $this->eventName, | ||
'handler_class' => $this->handler->getDeclaringClass()->getName(), | ||
'handler_method' => $this->handler->getName(), | ||
]; | ||
} | ||
|
||
public function __unserialize(array $data): void | ||
{ | ||
$this->eventName = $data['eventName']; | ||
$this->handler = new ReflectionMethod( | ||
objectOrMethod: $data['handler_class'], | ||
method: $data['handler_method'], | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tempest\Events; | ||
|
||
use Closure; | ||
use Tempest\Container\Container; | ||
|
||
final readonly class GenericEventBus implements EventBus | ||
{ | ||
public function __construct( | ||
private Container $container, | ||
private EventBusConfig $eventBusConfig, | ||
) { | ||
} | ||
|
||
public function dispatch(object $event): void | ||
{ | ||
/** @var \Tempest\Events\EventHandler[] $eventHandlers */ | ||
$eventHandlers = $this->eventBusConfig->handlers[$event::class] ?? []; | ||
|
||
foreach ($eventHandlers as $eventHandler) { | ||
$callable = $this->getCallable($eventHandler); | ||
|
||
$callable($event); | ||
} | ||
} | ||
|
||
private function getCallable(EventHandler $eventHandler): Closure | ||
{ | ||
$callable = function (object $event) use ($eventHandler) { | ||
$eventHandler->handler->invoke( | ||
$this->container->get($eventHandler->handler->getDeclaringClass()->getName()), | ||
$event, | ||
); | ||
}; | ||
|
||
$middlewareStack = $this->eventBusConfig->middleware; | ||
|
||
while ($middleware = array_pop($middlewareStack)) { | ||
$callable = fn (object $event) => $this->container->get($middleware::class)($event, $callable); | ||
} | ||
|
||
return $callable; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tempest | ||
{ | ||
use Tempest\Container\GenericContainer; | ||
use Tempest\Events\EventBus; | ||
|
||
/** | ||
* @template TClassName | ||
* @param class-string<TClassName> $className | ||
* @return TClassName | ||
*/ | ||
function get(string $className): object | ||
{ | ||
$container = GenericContainer::instance(); | ||
|
||
return $container->get($className); | ||
} | ||
|
||
function event(object $event): void | ||
{ | ||
$eventBus = get(EventBus::class); | ||
|
||
$eventBus->dispatch($event); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\Tempest\Unit\Events; | ||
|
||
use App\Events\ItHappened; | ||
use App\Events\MyEventHandler; | ||
use PHPUnit\Framework\TestCase; | ||
use ReflectionMethod; | ||
use Tempest\Container\GenericContainer; | ||
use Tempest\Events\EventBusConfig; | ||
use Tempest\Events\EventHandler; | ||
use Tempest\Events\GenericEventBus; | ||
use Tests\Tempest\Unit\Events\Fixtures\MyEventBusMiddleware; | ||
|
||
/** | ||
* @internal | ||
* @small | ||
*/ | ||
class EventBusTest extends TestCase | ||
{ | ||
public function test_it_works(): void | ||
{ | ||
$container = new GenericContainer(); | ||
|
||
$handler = new EventHandler(); | ||
$handler->setHandler(new ReflectionMethod(MyEventHandler::class, 'handleItHappened')); | ||
|
||
$config = new EventBusConfig( | ||
handlers: [ | ||
ItHappened::class => [ | ||
$handler, | ||
], | ||
], | ||
middleware: [ | ||
new MyEventBusMiddleware(), | ||
] | ||
); | ||
|
||
$eventBus = new GenericEventBus($container, $config); | ||
|
||
MyEventHandler::$itHappened = false; | ||
MyEventBusMiddleware::$hit = false; | ||
|
||
$eventBus->dispatch(new ItHappened()); | ||
|
||
$this->assertTrue(MyEventHandler::$itHappened); | ||
$this->assertTrue(MyEventBusMiddleware::$hit); | ||
} | ||
} |
Oops, something went wrong.