> */ private array $listeners = []; /** * Optional WordPress action dispatcher (injectable for tests). * * @var Closure(string, DomainEvent): void|null */ private ?Closure $action_dispatcher; /** * Map of event_name → hook suffix after bookline/. * * @var array */ private array $hook_map = [ 'booking.created' => 'bookline/booking/created', 'booking.rescheduled' => 'bookline/booking/rescheduled', 'booking.cancelled' => 'bookline/booking/cancelled', 'booking.status_changed' => 'bookline/booking/status_changed', 'booking.hold_released' => 'bookline/booking/hold_released', ]; /** * Constructor. * * @param Closure(string, DomainEvent): void|null $action_dispatcher Override for tests. */ public function __construct( ?Closure $action_dispatcher = null ) { $this->action_dispatcher = $action_dispatcher; } /** * Subscribe a listener to an event class or '*' for every event. * * @param string $event_class Event FQCN or '*'. * @param Closure(DomainEvent): void $listener Listener. * * @since 0.1.0 */ public function subscribe( string $event_class, Closure $listener ): void { $this->listeners[ $event_class ][] = $listener; } /** * Dispatch a single event to listeners then WordPress actions. * * @param DomainEvent $event Event. * * @since 0.1.0 */ public function dispatch( DomainEvent $event ): void { $class = $event::class; foreach ( $this->listeners[ $class ] ?? [] as $listener ) { $listener( $event ); } foreach ( $this->listeners['*'] ?? [] as $listener ) { $listener( $event ); } $hook = $this->hook_map[ $event->event_name() ] ?? ( 'bookline/' . str_replace( '.', '/', $event->event_name() ) ); $dispatcher = $this->action_dispatcher; if ( null !== $dispatcher ) { $dispatcher( $hook, $event ); return; } if ( function_exists( 'do_action' ) && '' !== $hook ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- hook from allow-listed map. do_action( $hook, $event ); } } /** * Dispatch many events in order. * * @param list $events Events. * * @since 0.1.0 */ public function dispatch_all( array $events ): void { foreach ( $events as $event ) { $this->dispatch( $event ); } } }