Skip to content

Events & Observers

Events let modules communicate without depending on each other directly. A module dispatches an event; other modules observe it and react. Neither side knows about the other.

Events are plain PHP classes. Dispatch them through the EventDispatcherInterface:

PostService.php
<?php
declare(strict_types=1);
namespace Marko\Blog\Service;
use Marko\Core\Event\EventDispatcherInterface;
use Marko\Blog\Event\PostCreatedEvent;
class PostService
{
public function __construct(
private readonly EventDispatcherInterface $eventDispatcher,
) {}
public function createPost(string $title, string $body): Post
{
$post = new Post(title: $title, body: $body);
// ... save to database
$this->eventDispatcher->dispatch(new PostCreatedEvent(post: $post));
return $post;
}
}

Events are simple value objects — no base class needed:

PostCreatedEvent.php
<?php
declare(strict_types=1);
namespace Marko\Blog\Event;
class PostCreatedEvent
{
public function __construct(
public readonly Post $post,
) {}
}

An observer is a class that handles a specific event:

TrackPostCreation.php
<?php
declare(strict_types=1);
namespace App\Analytics\Observer;
use Marko\Blog\Event\PostCreatedEvent;
class TrackPostCreation
{
public function handle(PostCreatedEvent $event): void
{
// Send to analytics, update counters, notify admins, etc.
$postTitle = $event->post->title;
// ...
}
}

Observers are registered in module.php:

module.php
<?php
declare(strict_types=1);
use App\Analytics\Observer\TrackPostCreation;
use Marko\Blog\Event\PostCreatedEvent;
return [
'observers' => [
PostCreatedEvent::class => [
TrackPostCreation::class,
],
],
];

When multiple observers listen to the same event, they run in module priority order (app/modules/vendor/). Within the same module, observers run in the order they’re listed.

Marko packages dispatch events at meaningful points:

EventWhen
LoginEventUser successfully logs in
LogoutEventUser logs out
FailedLoginEventLogin attempt fails
PasswordResetEventPassword is reset
EventWhen
PostCreatedEventNew post is created
PostUpdatedEventPost is modified
PostDeletedEventPost is deleted
CommentCreatedEventNew comment is added

Events are the right tool when:

  • The action and reaction belong to different modules
  • You want zero coupling between the modules
  • Multiple reactions might happen for the same action
  • The reaction is optional (the system works fine without any observers)

If you need to modify a method’s behavior directly, use Plugins instead.