Observer pattern in PHP

Observer pattern in PHP

Observer pattern is the one I encounter the most. In all object-oriented frameworks I have seen an Observer pattern implemented either in its pure form or by means of the Mediator, also known as event dispatcher. It is one of behavioral patterns therefore it defines the interaction between the objects. It does it in the following way: the Subject keeps the list of Observers and notifies them when some event happens. In order to be notified all observers must implement a particular interface, usually called Observer with single method notify() – that is the only thing that subject requires from its observers. So Subject class gets decoupled from the concrete classes of its Observers that ensures better reusability. Here is a class diagram:

Observer pattern

In simple terms when the object of type A (Subject) wants to notify objects of type B, C or D (Concrete Observers) it can do it without knowing what particular classes they represent. The only condition is that Observers must implement the Observer interface containing a single method called notify(). The class A is only aware of the Observer interface and not of classes B, C or D. The subject has to be positive that his observers have the method notify() to call if needed. This need usually comes on some event: a user clicks the interface element, the form construction finishes, the new request appears, the entity is fetched from database (or is about to insert, or get inserted, or is about to update or get updated) and so forth. The Subject can be anything: the form, the form builder, Doctrine framework, Symfony kernel. It can run third-party code plugged in it as an Observer at specific points of its lifecycle (the events).

This allows the subject to send notifications to whatever observer. On the other hand, the observer can listen to events from any subject. Both become decoupled, reusable and separately distributable.

You are welcome to explore my implementation of the Observer pattern here.

As you could see, the Subject uses all its Observers whenever it throws any notification. Whether the Observers interested or not in this particular notification, they are notified anyway. It is often useful when particular event happens to notify only the Observers interested in this event and ignore others. The solution is to turn Observer into EventListener and subscribe them to particular Event of Subject. In this case the code would be similar to :

SubjectInterface:

/**
 * All subjects must implement this interface.
 * Two type of events can be emitted: INITIALIZED and FINALIZED.
 * Event listeners can subscribe to listen one of them via 
 * `addEventListener()` method.
 * 
 * `addEventListener()` is an analog of `attach()` method in `\SplSubject`.
 * 
 * This interface can be extended by `removeEventListener()` to detach listeners.
 */
interface SubjectInterface
{
    const INITIALIZED = "subject_initialized";
    const FINALIZED = "subject_finalized";

    /**
     * Attach the listener to an event.
     * It will be called when this event happens.
     *
     * @param $event Event to listen to.
     * @param ListenerInterface $listener Listener of the specified event.
     */
    public function addEventListener($event, ListenerInterface $listener);
}

ListenerInterface:

/**
 * All listeners must implement this interface. 
 * 
 * `onEvent()` method is called from inside the subject.
 * 
 * It allows execution of arbitrary logic at some point of subject's lifecycle:
 * when the subject gets initialized or the subject gets finalized.
 * Listeners are usually attached to only one type of event.
 */
interface ListenerInterface
{
    /**
     * Method that is called by subject when the particular event happens
     *
     * @param Event $event The object containing all necessary information
     */
    public function onEvent(Event $event);
}

Event:

/**
 * Event interface represents the generic event. 
 * It is created by the subject and passed to its listeners and contains all 
 * needed information.
 */
interface Event
{
    /**
     * Get the reference to a subject that has emitted this event.
     *
     * @return SubjectInterface
     */
    public function getSubject();

    /**
     * Get whatever variable associated with the event.
     *
     * @return mixed
     */
    public function getWhatever();
}

The use:

// Create new object implementing SubjectInterface.

$order = new Subject();

// Add some listeners: SendConfirmationEmailListener, LogOrder and LogSuccessfulCommand.

// All these concrete classes implement ListenerInterface.

$order->addEventListener(SubjectInterface::INITIALIZED, new SendConfirmationEmailListener());
$order->addEventListener(SubjectInterface::INITIALIZED, new LogOrder());
$order->addEventListener(SubjectInterface::FINALIZED, new LogSuccessfulOrder());

// Evolve subject.

$order->init(); // SendConfirmationEmailListener and LogOrder get called.

$order->finalize(); // LogSuccessfulOrder gets called.

In the second part, I will show you how to use the event system of Symfony form component to make use of dependent selects.