What are some best practices for implementing an event listener/event handler system in PHP?

When implementing an event listener/event handler system in PHP, it is important to create a flexible and scalable system that allows multiple listeners to respond to events triggered by the application. One best practice is to use a centralized event dispatcher class that manages event registration, triggering, and handling. This class should allow listeners to subscribe to specific events and provide a way for them to respond to those events when they are triggered.

<?php

class EventDispatcher {
    private $listeners = [];

    public function addListener(string $event, callable $callback) {
        $this->listeners[$event][] = $callback;
    }

    public function dispatch(string $event, $data = null) {
        if (isset($this->listeners[$event])) {
            foreach ($this->listeners[$event] as $callback) {
                call_user_func($callback, $data);
            }
        }
    }
}

// Example of how to use the EventDispatcher class
$dispatcher = new EventDispatcher();

$dispatcher->addListener('user.registered', function($data) {
    echo "User registered: " . $data['username'] . "\n";
});

$dispatcher->addListener('user.logged_in', function($data) {
    echo "User logged in: " . $data['username'] . "\n";
});

$dispatcher->dispatch('user.registered', ['username' => 'john_doe']);
$dispatcher->dispatch('user.logged_in', ['username' => 'jane_smith']);