All classes live under the Webware\MessageBus\Event namespace (PSR-4 root: src/).
Core Event Types
EventInterface
interface EventInterface
{
public function getName(): string;
public function getParam(string $name, mixed $default = null): mixed;
/** @return array<string, mixed> */
public function getParams(): array;
public function getTarget(): ?object;
public function setName(string $name): void;
public function setParam(string $name, mixed $value): void;
/** @param array<string, mixed> $params */
public function setParams(array $params): void;
public function setTarget(object $target): void;
}
Event
Default EventInterface implementation. Also implements EventPropagationInterface via
EventPropagationTrait.
class Event implements EventInterface, EventPropagationInterface
{
/** @param array<string, mixed> $params */
public function __construct(
?string $name = null,
?object $target = null,
array $params = [],
) {}
}
If $name is omitted, getName() falls back to static::class - useful for the Command\*/Query\*
subclasses below, which don't set a name explicitly.
EventPropagationInterface / EventPropagationTrait
interface EventPropagationInterface
{
public function isPropagationStopped(): bool;
public function stopPropagation(): void;
}
trait EventPropagationTrait
{
protected bool $propagationStopped = false;
public function isPropagationStopped(): bool;
public function stopPropagation(bool $flag = true): void;
}
The trait's stopPropagation() accepts an optional bool $flag = true, letting callers re-enable
propagation (stopPropagation(false)) even though the interface only requires a no-argument method.
EventAwareInterface / EventAwareTrait
Lets a message or result object carry a domain event that the post-handle middleware can dispatch
alongside the built-in *PostHandleEvent.
interface EventAwareInterface
{
public function getEvent(): ?EventInterface;
public function setEvent(EventInterface $event): void;
}
trait EventAwareTrait
{
public function getEvent(): ?EventInterface;
public function setEvent(EventInterface $event): void;
}
ListenerInterface
Marker/callable-shape interface for object listeners (as opposed to plain closures or callable strings).
interface ListenerInterface
{
public function __invoke(EventInterface $event): void;
}
Command Events
Webware\MessageBus\Event\Command:
-
CommandPreHandleEvent:extends Event. Constructed with aCommandInterface $command(getTarget()returns the command).getCommand(): CommandInterface. -
CommandPostHandleEvent:extends Event. Constructed with aCommandResultInterface $result(getTarget()returns the result).getCommand(): CommandInterface(delegates to$result->getCommand()),getResult(): CommandResultInterface.
Query Events
Webware\MessageBus\Event\Query:
-
QueryPreHandleEvent:extends Event. Constructed with aQueryInterface $query.getQuery(): QueryInterface. -
QueryPostHandleEvent:extends Event. Constructed with aQueryResultInterface $result.getQuery(): QueryInterface(delegates to$result->getQuery()),getResult(): QueryResultInterface.
Middleware
Webware\MessageBus\Event\Middleware, all implementing Webware\MessageBus\MiddlewareInterface.
|
Important
|
webware/message-bus v2 changed MiddlewareInterface::process() to take a
PipelineHandlerInterface $next instead of the v1 MessageHandlerInterface $handler.
PipelineHandlerInterface is @internal and is meant to be type-hinted, not implemented - the
pipeline supplies the implementation (Next / Handler\EmptyPipelineHandler).
|
| Class | Dispatches | Behavior |
|---|---|---|
|
|
If |
|
|
If |
|
|
Same pattern as |
|
|
Same pattern as |
Each middleware's constructor takes a single Psr\EventDispatcher\EventDispatcherInterface $eventDispatcher.
Because webware/message-bus's MessageHandlerMiddleware forwards the handler's ResultInterface back
into the pipeline as the next "message" ($next->handle($result)), a *PostHandleMiddleware placed
after it in priority order receives the result - not the original command/query - as $message. This is
why the pipeline priorities matter: pre-handle middleware needs a higher priority than the message handler,
post-handle middleware needs a lower one.
|
Note
|
MessageHandlerMiddleware resolves the handler method via a StrategyInterface -
HandleStrategy (default) calls handle(), ClassnameStrategy calls a method named after the
message's short class name (lcfirst). Handlers must implement the (empty marker)
CommandHandlerInterface or QueryHandlerInterface.
|
|
Note
|
CommandResultInterface/QueryResultInterface are @internal in webware/message-bus
v2 - they exist for type-hinting only (CommandResult/QueryResult are final), not for
implementation.
|
Container Factories
Webware\MessageBus\Event\Container, all @internal - wired automatically by ConfigProvider, not
intended for direct instantiation by consumers:
-
CommandPreHandleMiddlewareFactory,CommandPostHandleMiddlewareFactory,QueryPreHandleMiddlewareFactory,QueryPostHandleMiddlewareFactory- each resolvesPsr\EventDispatcher\EventDispatcherInterfacefrom the container and constructs the corresponding middleware. -
ListenerProviderAggregateFactory- builds aPhly\EventDispatcher\ListenerProvider\ListenerProviderAggregatefrom thelisteners/listener_providersconfig (see below), attaching aPrioritizedListenerProvider(for shape entries with apriority) and anAttachableListenerProvider(for everything else) to it, plus any additional listener providers registered underlistener_providers.
ConfigProvider
final readonly class ConfigProvider
{
public const string LISTENER_KEY = 'listeners';
public const string LISTENER_PROVIDER_KEY = 'listener_providers';
public function __invoke(): array;
}
Returned config shape:
[
'dependencies' => [
'aliases' => [
Psr\EventDispatcher\EventDispatcherInterface::class => Phly\EventDispatcher\EventDispatcher::class,
Psr\EventDispatcher\ListenerProviderInterface::class => Phly\EventDispatcher\ListenerProvider\ListenerProviderAggregate::class,
],
'factories' => [
// ListenerProviderAggregate::class and the four *Middleware::class => *Factory::class entries
],
],
Webware\MessageBus\MessageBusInterface::class => [
// BusProvider::MIDDLEWARE_PIPELINE_KEY
'middleware_pipeline' => [
['middleware' => Middleware\CommandPreHandleMiddleware::class, 'priority' => 100],
['middleware' => Middleware\CommandPostHandleMiddleware::class, 'priority' => -100],
],
],
'listeners' => [],
'listener_providers' => [],
]
Note the aliases assume phly/phly-event-dispatcher's concrete EventDispatcher class - its constructor
requires a ListenerProviderInterface, so it needs its own factory registered
(Phly\EventDispatcher\EventDispatcherFactory) in any container you assemble; this package does not
register that factory for you (see Getting Started).
listeners config shape
array<class-string, array<int, string|array{listener: callable|class-string, priority?: int}>>
Keyed by event class (or any object type dispatched through the event dispatcher). Each entry is either:
-
A plain string - a service id to resolve from the container, or (if not registered) a
callablestring such as a function name. -
A shape array
['listener' => callable|class-string, 'priority' => int]-priorityis optional; when present, the listener is attached to thePrioritizedListenerProvider(descending priority order), and when absent, to theAttachableListenerProvider(attach order, deduplicated).
listener_providers config shape
array<class-string>
A list of service ids resolving to Psr\EventDispatcher\ListenerProviderInterface implementations. Ids
that aren't registered in the container are silently skipped.