You've already forked php-auth
generated from siteworxpro/Php-Template
Initial commit
This commit is contained in:
246
src/Events/Dispatcher.php
Normal file
246
src/Events/Dispatcher.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Siteworxpro\App\Events;
|
||||
|
||||
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Support\Collection;
|
||||
use Siteworxpro\App\Attributes\Events\ListensFor;
|
||||
|
||||
use function React\Async\await;
|
||||
use function React\Async\coroutine;
|
||||
|
||||
/**
|
||||
* Class Dispatcher
|
||||
*
|
||||
* A custom event dispatcher that automatically registers event listeners
|
||||
* based on the ListensFor attribute.
|
||||
*
|
||||
* @package Siteworxpro\App\Events
|
||||
*/
|
||||
class Dispatcher implements DispatcherContract, Arrayable
|
||||
{
|
||||
/**
|
||||
* @var array $listeners Registered event listeners
|
||||
*/
|
||||
private array $listeners = [];
|
||||
|
||||
/**
|
||||
* @var Collection $pushed Pushed events collection
|
||||
*/
|
||||
private Collection $pushed;
|
||||
|
||||
private array $subscribers = [];
|
||||
|
||||
/**
|
||||
* @var string LISTENERS_NAMESPACE The namespace where listeners are located
|
||||
*/
|
||||
private const string LISTENERS_NAMESPACE = 'Siteworxpro\\App\\Events\\Listeners\\';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->pushed = new Collection();
|
||||
$this->registerListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
foreach ($this->pushed as $event => $payload) {
|
||||
$this->dispatch($event, $payload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register event listeners based on the ListensFor attribute.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function registerListeners(): void
|
||||
{
|
||||
// traverse the Listeners directory and register all listeners
|
||||
$listenersPath = __DIR__ . '/Listeners';
|
||||
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($listenersPath));
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile() && $file->getExtension() === 'php') {
|
||||
$relativePath = str_replace($listenersPath . '/', '', $file->getPathname());
|
||||
$className = self::LISTENERS_NAMESPACE . str_replace(['/', '.php'], ['\\', ''], $relativePath);
|
||||
if (class_exists($className)) {
|
||||
$reflectionClass = new \ReflectionClass($className);
|
||||
$attributes = $reflectionClass->getAttributes(ListensFor::class);
|
||||
foreach ($attributes as $attribute) {
|
||||
$instance = $attribute->newInstance();
|
||||
$eventClass = $instance->eventClass;
|
||||
$this->listen($eventClass, new $className());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener for the given events.
|
||||
*
|
||||
* @param $events
|
||||
* @param $listener
|
||||
* @return void
|
||||
*/
|
||||
public function listen($events, $listener = null): void
|
||||
{
|
||||
$this->listeners[$events][] = $listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are listeners for the given event.
|
||||
*
|
||||
* @param $eventName
|
||||
* @return bool
|
||||
*/
|
||||
public function hasListeners($eventName): bool
|
||||
{
|
||||
return isset($this->listeners[$eventName]) && !empty($this->listeners[$eventName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe a subscriber to the dispatcher.
|
||||
*
|
||||
* @param Arrayable $subscriber
|
||||
* @return void
|
||||
*/
|
||||
public function subscribe($subscriber): void
|
||||
{
|
||||
$this->subscribers[] = $subscriber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch an event and halt on the first non-null response.
|
||||
*
|
||||
* @param $event
|
||||
* @param array $payload
|
||||
* @return array|null
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function until($event, $payload = []): array|null
|
||||
{
|
||||
return $this->dispatch($event, $payload, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch an event to its listeners.
|
||||
*
|
||||
* @param $event
|
||||
* @param array $payload
|
||||
* @param bool $halt
|
||||
* @return array|null
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function dispatch($event, $payload = [], $halt = false): array|null
|
||||
{
|
||||
if (is_object($event)) {
|
||||
$eventClass = get_class($event);
|
||||
} else {
|
||||
$eventClass = $event;
|
||||
}
|
||||
|
||||
// Handle subscribers as a coroutine
|
||||
$promise = coroutine(function () use ($event, $payload, $halt, $eventClass, &$responses) {
|
||||
foreach ($this->subscribers as $subscriber) {
|
||||
if (method_exists($subscriber, 'handle')) {
|
||||
$response = $subscriber->handle($event, $payload);
|
||||
$responses[$eventClass] = $response;
|
||||
|
||||
if ($halt && $response !== null) {
|
||||
return $responses;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
$listeners = $this->listeners[$eventClass] ?? null;
|
||||
|
||||
// If no listeners, just await the subscriber promise
|
||||
if ($listeners === null) {
|
||||
return await($promise);
|
||||
}
|
||||
|
||||
$responses = [];
|
||||
foreach ($listeners as $listener) {
|
||||
$response = $listener($event, $payload);
|
||||
$responses[$eventClass] = $response;
|
||||
|
||||
if ($halt && $response !== null) {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
// Await the subscriber promise and merge responses
|
||||
$promiseResponses = await($promise);
|
||||
|
||||
if (is_array($promiseResponses)) {
|
||||
$responses = array_merge($responses, $promiseResponses);
|
||||
}
|
||||
|
||||
return $responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an event to be dispatched later.
|
||||
*
|
||||
* @param $event
|
||||
* @param array $payload
|
||||
* @return void
|
||||
*/
|
||||
public function push($event, $payload = []): void
|
||||
{
|
||||
$this->pushed->put($event, $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush a pushed event, dispatching it if it exists.
|
||||
*
|
||||
* @param $event
|
||||
* @return void
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function flush($event): void
|
||||
{
|
||||
if ($this->pushed->has($event)) {
|
||||
$payload = $this->pushed->get($event);
|
||||
$this->dispatch($event, $payload);
|
||||
$this->pushed->forget([$event]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget a pushed event without dispatching it.
|
||||
*
|
||||
* @param $event
|
||||
* @return void
|
||||
*/
|
||||
public function forget($event): void
|
||||
{
|
||||
$this->pushed->forget([$event]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget all pushed events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function forgetPushed(): void
|
||||
{
|
||||
$this->pushed = new Collection();
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->listeners;
|
||||
}
|
||||
}
|
||||
35
src/Events/Listeners/Database/Connected.php
Normal file
35
src/Events/Listeners/Database/Connected.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Siteworxpro\App\Events\Listeners\Database;
|
||||
|
||||
use Illuminate\Database\Events\ConnectionEstablished;
|
||||
use Illuminate\Database\Events\ConnectionEvent;
|
||||
use Siteworxpro\App\Attributes\Events\ListensFor;
|
||||
use Siteworxpro\App\Events\Listeners\Listener;
|
||||
use Siteworxpro\App\Services\Facades\Logger;
|
||||
|
||||
/**
|
||||
* Class Connected
|
||||
* @package Siteworxpro\App\Events\Listeners\Database
|
||||
*/
|
||||
#[ListensFor(ConnectionEstablished::class)]
|
||||
class Connected extends Listener
|
||||
{
|
||||
/**
|
||||
* @param mixed $event
|
||||
* @param array $payload
|
||||
* @return null
|
||||
*/
|
||||
public function __invoke(mixed $event, array $payload = []): null
|
||||
{
|
||||
if (!($event instanceof ConnectionEvent)) {
|
||||
throw new \TypeError("Invalid event type passed to listener " . static::class);
|
||||
}
|
||||
|
||||
Logger::info("Database connection event", [get_class($event), $event->connectionName]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
14
src/Events/Listeners/Listener.php
Normal file
14
src/Events/Listeners/Listener.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Siteworxpro\App\Events\Listeners;
|
||||
|
||||
/**
|
||||
* Class Listener
|
||||
*
|
||||
* @package Siteworxpro\App\Events\Listeners
|
||||
*/
|
||||
abstract class Listener implements ListenerInterface
|
||||
{
|
||||
}
|
||||
19
src/Events/Listeners/ListenerInterface.php
Normal file
19
src/Events/Listeners/ListenerInterface.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Siteworxpro\App\Events\Listeners;
|
||||
|
||||
/**
|
||||
* Interface ListenerInterface
|
||||
* @package Siteworxpro\App\Events\Listeners
|
||||
*/
|
||||
interface ListenerInterface
|
||||
{
|
||||
/**
|
||||
* @param mixed $event
|
||||
* @param array $payload
|
||||
* @return mixed
|
||||
*/
|
||||
public function __invoke(mixed $event, array $payload = []): mixed;
|
||||
}
|
||||
15
src/Events/Subscribers/Subscriber.php
Normal file
15
src/Events/Subscribers/Subscriber.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Siteworxpro\App\Events\Subscribers;
|
||||
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
|
||||
abstract class Subscriber implements SubscriberInterface, Arrayable
|
||||
{
|
||||
public function toArray(): array
|
||||
{
|
||||
return get_object_vars($this);
|
||||
}
|
||||
}
|
||||
10
src/Events/Subscribers/SubscriberInterface.php
Normal file
10
src/Events/Subscribers/SubscriberInterface.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Siteworxpro\App\Events\Subscribers;
|
||||
|
||||
interface SubscriberInterface
|
||||
{
|
||||
public function handle(string $eventName, mixed $payload): mixed;
|
||||
}
|
||||
Reference in New Issue
Block a user