4.6 KiB
4.6 KiB
Beacon is an Event Management system built originally for use in the Phred Framework, but opened to the public to install in any codebase. It follows a strict PSR-14 (Event Dispatcher) implementation with an "Enterprise-First" architecture.
Features & Functionality (Mind Map)
-
Event Dispatcher (The "Engine")
- Primary entry point:
$dispatcher->dispatch($event) - Returns the modified/original event object (PSR-14)
- Contract Isolation: Split into
BeaconContracts(Interfaces) andBeacon(Implementation). - Bridge Support:
BeaconBridgesfor third-party adapters (Symfony, Laravel). - Middleware Pipeline: Onion-style pipeline for pre/post dispatch logic.
- Resiliency: Configurable error handling (Fail-fast vs. Continue/Log).
- Primary entry point:
-
Event Listeners & Providers
- Listener Provider: Separate PSR-14 component to resolve listeners for an event.
- Class-based Subscribers: Implement
SubscriberInterfacefor multiple events. - Priority System: Higher integer = higher priority (runs earlier).
- Wildcard Support: Pattern matching (e.g.,
App.User.*) translated internally to cached regex for efficiency. - Lazy Loading: Listeners instantiated only when needed via PSR-11 container.
-
Event Objects & Context
- Plain Old PHP Objects (POPOs): Any object can be an event.
- Event Envelope: Optional wrapper to carry immutable metadata/context via a
ContextMap. - Stoppable Events: Implements
StoppableEventInterface(PSR-14). - Inheritance Resolution: Automatically resolves listeners for parent classes and interfaces; includes a configuration toggle for high-performance interface skipping.
-
Discovery & Registration
- Attribute Discovery: PHP 8.2
#[AsEventListener]for zero-config registration. - Discovery Logic: A
ListenerDiscoveryservice scans directories (using PSR-4 mapping or explicit paths). - Discovery Cache: PSR-16 cache support for scanning and mapping attributes (eliminates runtime scanning).
- DI Integration: Accept a PSR-11 container to resolve listener instances; discovery yields service IDs or class names for lazy instantiation.
- Manual Registration: Fluent API for one-off listeners or closures (uses
addListener).
- Attribute Discovery: PHP 8.2
-
Integration & Advanced Features
- Async Dispatching: Offloaded via
QueueingDispatcherdecorator (in Bridges); serializes theEventEnvelope. - PII & Logging: Masking and sensitive data handling live strictly in Bridges or Middleware, keeping the engine core pure.
- Resiliency & Error Handling:
continuemode emits aDispatchFailedevent containing the listener, exception, and original event payload for audit/retry. - Enterprise Telemetry: Trace execution timing and listener flow.
- Testing Helpers: PHPUnit traits for easy event assertions.
- Async Dispatching: Offloaded via
Concepts: Unified Event Listening
The Dispatcher API
The primary way to trigger an event is through the $dispatch() method.
$dispatcher->dispatch($event);
Listener Registration
We prefer structured class-based listeners, but support closures for lightweight tasks.
addListener(string $event, callable $listener, int $priority = 0)addSubscriber(SubscriberInterface $subscriber)
API Examples (Drafts)
Basic Dispatching
$dispatcher = new EventDispatcher($provider);
$event = new UserRegistered($user);
// "Enterprise" Dispatching
$dispatcher->dispatch($event);
Middleware Example
class LoggingMiddleware implements DispatchMiddlewareInterface {
public function handle(object $event, callable $next): object {
$log->info("Dispatching: " . $event::class);
return $next($event);
}
}
Class-based Subscriber (Unified)
class UserSubscriber implements SubscriberInterface {
public static function getSubscribedEvents(): array {
return [
UserRegistered::class => 'onUserRegistered',
UserDeleted::class => ['onUserDeleted', 10], // with priority
];
}
public function onUserRegistered(UserRegistered $event): void { /* ... */ }
}
Attribute-based Discovery
#[AsEventListener(event: UserRegistered::class, priority: 10)]
class SendWelcomeEmailListener {
public function __invoke(UserRegistered $event): void { /* ... */ }
}
Stoppable Events (with Convenience Trait)
class OrderEvent implements StoppableEventInterface {
use CanStopPropagation; // Provides stopPropagation() and isPropagationStopped()
}
Contextual Data (Envelope)
$envelope = new EventEnvelope($event, ['request_id' => 'abc-123']);
$dispatcher->dispatch($envelope);