Design Patterns in PHP - Observer Pattern
So far in my series of articles on design patterns in PHP we’ve looked at a creational pattern, a structural pattern and a behavioral pattern. Today I’ll be taking a closer look at another behavioral pattern - the observer. The observer pattern (also known as the Subscribe/Publish Pattern) is used to notify one object, the observer, about a change of state from another object, the subject.
The implementation of the observer pattern is a fairly simple one, as illustrated in the following UML class diagram (courtesy of Wikipedia):
![]()
The implementation of this pattern relies on 2 base objects - the subject and one or more observer(s). The observers register themselves with the subject, who’s job is then to notify the observer on any change of state or progress.
In PHP we are lucky to have some predefined interfaces for this pattern in the SPL. Using the SplObserver and SplSubject interfaces we can quickly create an implementation of this pattern.
Let us suppose that we have a script which is performing a particular action on a list of users. We may or may not want to log the fact that this action has occurred - perhaps this is an option given to each user in the application. One way to achieve this notification is by using an observer object. In the example below a User_List is asked to update each User, and when it does it will notify any observers that the status has changed.
Users.inc.php
<?php
class Users implements SplSubject
{
private
$_users,
$_observers,
$_currentUser;
public function __construct(array $users)
{
$this->_users = $users;
$this->_observers = array();
}
/**
* Updates the users
*/
public function updateUsers()
{
foreach ($this->_users as $user)
{
$this->_currentUser = $user;
//Do update
$this->notify();
}
$this->_currentUser = null;
}
/**
* Returns the item currently being updated
*
* @return $user
*/
public function getCurrent()
{
return $this->_currentUser;
}
/**
* Attach an observer
*
* @param SplObserver $observer
*/
public function attach(SplObserver $observer)
{
array_push($this->_observers, $observer);
}
/**
* Detach an observer
*
* @param SplObserver $observer
*/
public function detach(SplObserver $observer)
{
foreach ($this->_observers as $key => $item)
{
if ($observer == $item) {
unset($this->_observers[$key]);
}
}
}
/**
* Send notification to all observers
*/
public function notify()
{
foreach ($this->_observers as $key => $item) {
$item->update($this);
}
}
}
UserUpdateLogger.inc.php
<?php
class UserUpdateLogger implements SplObserver
{
/**
* Log the fact that a user has been updated
*
* @param SplSubject $subject
*/
public function update(SplSubject $subject)
{
echo 'Updated: ' . $subject->getCurrent() . "\n";
}
}
Controller.php
<?php
$userArray = array(
'user1',
'user2',
'user3',
'user4',
);
$logger = new UserUpdateLogger();
$users = new Users($userArray);
$users->attach($logger);
$users->updateUsers();
The above example is fairly trivial so as to explain the point, but as you can see, the Users class could have multiple observers attached - perhaps one to log the output to a file or send an e-mail. The users class could also have no observers attached at all and would still function correctly.
In conclusion, the observer pattern can be used when there is a need to dynamically attach and detach objects which need to be notified of a certain action, without coupling any particular objects together too tightly.
As always, comments on twitter are welcome, @labelmedia.