A man using a binoculars looking ahead
Image from Free-Photos by Pixabay

Observer Pattern In PHP 8+

Rafael Melo
CodeX
5 min readJul 14, 2021

--

The easiest guide to implementing the observer pattern in PHP 8+

One of the most used design patterns in the PHP world is the Observer, it’s very useful when we have several objects which are dependent on another object and are required to perform an action when the state of that object changes, or an object needs to notify others without knowing who they are or how many there are.

🧐 The Observer Design Pattern

The Observer Design Pattern is a behavioral design pattern that lets an object notify other objects when an event occurs, like a state change or a method execution.

This pattern is composed of an object called Observable (or Subject/Publisher, in some sources) and many other objects called Observers (or Subscribers). The Observable holds a list o Observers as property and notifies them automatically of any state changes, generally calling one of the observer’s methods.

The observer pattern is generally used for implementing event handlers in an application without creating a dependency between the main class and the event listeners' objects.

👷‍♂️ Implementation

In our example, we’ll create a simple system that can create users and store their data in memory. And then we’ll implement a notification every time that a user is created

Firstly, let’s create a User class only with name and email properties.

class User
{
public function __construct(
private string $name,
private string $email
) {}
}

Then, let’s create an UserRepository to store and handle user’s data:

The UsersRepository class will implement Singleton Pattern to guarantee that the data will be persisted properly

class UsersRepository
{
private array $users = [];
private static $instance; private function __construct() {}
private function __clone() {}
public function createUser(User $user)
{
$this->users[] = $user;
}
public static function getInstance()
{
if(self::$instance === null) {
self::$instance = new self;
}
return self::$instance;
}
}

PHP has built-in interfaces that help us to implement the Observer Design Pattern. To implement the Observable object we can use the SplSubject interface, and for the Observers, we can use the SplObserver Interface.

So, we’ll implement the SplSubject on UsersRepository class, thus, that class will be able to notify other classes when your state changes.

The SplSubject interface specifies that we must implement the attach method to subscribe a new observer, the detach method to unsubscribe an observer, and the notify method that notifies all the subscribed observers.

First of all, I’ll show the final result, and then we’ll go through the code together step by step. So, the final class will look like this:

class UsersRepository implements SplSubject
{
private \SplObjectStorage $observers;

private array $users = [];
private static $instance; private function __construct()
{
$this->observers = new \SplObjectStorage;
}
private function __clone() {}
public function createUser(User $user): void
{
$this->users[] = $user;
$this->notify();
}
public function attach(\SplObserver $observer): void
{
$this->observers->attach($observer);
}
public function detach(\SplObserver $observer): void
{
$this->observers->detach($observer);
}
public function notify(): void
{
foreach($this->observers as $observer) {
$observer->update($this);
}
}
public static function getInstance(): self
{
if(self::$instance === null) {
self::$instance = new self;
}
return self::$instance;
}
}

The first step is to implement the property that will hold all the observers attached to the Subject and initialize it in the constructor:

PHP has a built-in class to deal with an object set called SplObjectStorage. It’s useful in cases involving the need to uniquely identify objects, and will be used in our Observer Pattern implementation to hold our Observers set.

private \SplObjectStorage $observers;private function __construct()
{
$this->observers = new \SplObjectStorage;
}

Then, all that remains is to implement the methods to handle the subscription (attach), unsubscription (detach), and notification (notify) methods. For the first and second methods, we’ll use the attach and detach SplObjectStorage’s methods passing an observer as the parameter. For the last one, we’ll iterate over our observers’ pool calling the update method of each one passing the Subject class as the parameter.

We also need to add a call to notify method after the user is created in createUser method.

public function createUser(User $user): void
{
$this->users[] = $user;
$this->notify();
}
public function attach(\SplObserver $observer): void
{
$this->observers->attach($observer);
}
public function detach(\SplObserver $observer): void
{
$this->observers->detach($observer);
}
public function notify(): void
{
foreach($this->observers as $observer) {
$observer->update($this);
}
}

All right, our UsersRepository is done. Now, we have to create a class that will be notified every time that a user is created. For this example, we’ll create a dummy email sender class that will implement the SplObserver interface:

The SplObserver interface specifies that we must implement the update method that will receive an object that implements SplSubject as a unique parameter.

class Email implements \SplObserver
{
public function update(\SplSubject $subject): void
{
echo "Email class has been notified!\n";
}
}

Now, we just have to create a script to test our Observer Pattern implementation:

<?php# First, we get an instance of UsersRepository and Email classes
$usersRepository = UsersRepository::getInstance();
$emailSender = new Email();
# Then, we attach the Email class to UsersRepository observers' pool
$usersRepository->attach($emailSender);
# Now we may create a new user
$user = new User('Rafael', 'rafaelmelo.programmer@gmail.com');
$usersRepository->createUser($user);

Execute this script will result in:

$ Email class has been notified!

📉 Advantages

The observer design pattern allows us to create a one-to-many relationship between objects, and, the best part: the main object doesn’t know anything about its dependent objects.

What does that mean? Well, that allows us to implement other dependent objects without changing the main object, in other words, that brings us the so desired low coupling and high cohesion. Moreover, it’s in agreement with the Open-Closed Principle described in SOLID principles.

In practice, back to our example, we could implement other notification types in addition to the already implemented email notification, like SMS, without changing the UsersRepository class.

📈 Disadvantages

Despite the benefits, abuse of the Observer Pattern implementation can bring unnecessary complexity to the system.

Furthermore, we don’t have control over the observers' notification order, which can cause bugs or unexpected behavior if the system is not well structured.

✅ Conclusion

The Observer Pattern is one of the most used design patterns in the PHP world, it can bring flexibility to an application and it’s useful when we have several objects which are dependent on another object and are required to perform an action when the state of that object changes, or an object needs to notify others.

It’s quite simple to implement, principally in PHP where we have built-in interfaces and classes that help us to speed up the development process.

The observer pattern brings us low coupling and high cohesion to the system. On the other hand, it can cause unnecessary complexity to code and unexpected side effects.

If you want to go deeper into this subject, I recommend visiting all the references I left below!

📚 References

Official SplObserver documentation

Official SplSubject documentation

Observer (refactoring.guru)

Event (computing) — Wikipedia

Understanding the Observer Pattern by Ignatius Teo

--

--

Rafael Melo
CodeX
Writer for

I’m passionate about learning and teaching technology. Full-stack developer att Curseduca. JavaScript/Node and PHP developer.