Using the Observer Pattern in PHP
The observer pattern is a behavioral design pattern that defines a one-to-many relationship between objects such that when the state of an object (publisher) changes, the other objects (subscribers) are notified.
Article Highlights
- The observer pattern has a publisher object which updates all its subscribers when its state changes.
- Benefits â Â A change in the state of one entity is propagated to other entities.
- Con â Sends updates in a random order which can be detrimental if observers have shared data dependency where sequence matters.
Observer Design Pattern PHP Code Example
<?php
interface Publisher {
public function registerDisplay($display);
public function removeDisplay($display);
public function updateDisplays();
}
interface Observer {
public function update($sportsNews, $politicsNews, $weatherNews);
}
/*
This class represents the publisher class now. NewsData implements publisher and also has an array of references to the subscriber objects.
*/
class NewsData implements Publisher {
private $displays; //List of subscribers.
private $sportsNews;
private $politicsNews;
private $weatherNews;
public function __construct() {
$this->displays = [];
}
public function registerDisplay($display) {
$this->displays[$display->getID()] = $display; //Display has a unique ID used as array index. Helpful for removal.
}
public function removeDisplay($display) {
unset($this->displays[$display->getID()]);
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getSportsNews() {
return [
"Team A vs Team B: Scorecard",
"Team C out of Super 5 league",
"New tournament schedule announced"
];
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getPoliticsNews() {
return [
"Mr.John elected as senator",
"Government to revise the prices of gas",
"Elections delays expected amidst political choas"
];
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getWeatherNews() {
return [
"Heavy rains forecast from next week",
"Snow storm expected in some parts of states",
"Global Warming: Where are we heading"
];
}
/*
This method runs when station pushes the latest news.
This is a pseudo function and we don't have to worry about details on how
it interact with the station
*/
public function update() {
$this->sportsNews = $this->getSportsNews();
$this->politicsNews = $this->getPoliticsNews();
$this->weatherNews = $this->getWeatherNews();
$this->updateDisplays();
}
public function updateDisplays() {
foreach($this->displays as $display) {
$display->update($this->sportsNews, $this->politicsNews, $this->weatherNews);
}
}
}
class SportsDisplay implements Observer {
private $ID;
function __construct() {
$this->ID = "01";
}
/*
This method is supposed to update the application's display for sports updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($sportsNews, $politicsNews, $weatherNews) {
echo "Sports\n";
foreach($sportsNews as $n) {
echo "- ".$n."\n";
}
}
public function getID() {
return $this->ID;
}
}
class PoliticsDisplay implements Observer {
private $ID;
function __construct() {
$this->ID = "02";
}
/*
This method is supposed to update the application's display for sports updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($sportsNews, $politicsNews, $weatherNews) {
echo "Politics\n";
foreach($politicsNews as $n) {
echo "- ".$n."\n";
}
}
public function getID() {
return $this->ID;
}
}
class WeatherDisplay implements Observer {
private $ID;
function __construct() {
$this->ID = "03";
}
/*
This method is supposed to update the application's display for sports updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($sportsNews, $politicsNews, $weatherNews) {
echo "Weather\n";
foreach($politicsNews as $n) {
echo "- ".$n."\n";
}
}
public function getID() {
return $this->ID;
}
}
$newsDataObject = new NewsData();
$sportsDisplay = new SportsDisplay();
$politicsDisplay = new PoliticsDisplay();
$weatherDisplay = new WeatherDisplay();
$newsDataObject->registerDisplay($sportsDisplay);
$newsDataObject->registerDisplay($politicsDisplay);
$newsDataObject->registerDisplay($weatherDisplay);
$newsDataObject->update();
/*
Sports
- Team A vs Team B: Scorecard
- Team C out of Super 5 league
- New tournament schedule announced
Politics
- Mr.John elected as senator
- Government to revise the prices of gas
- Elections delays expected amidst political choas
Weather
- Mr.John elected as senator
- Government to revise the prices of gas
- Elections delays expected amidst political choas
*/
?>
Table of Contents
- What is the Observer Pattern?
- Observer Pattern Example
- Problem đ
- Solution đ
- An Alternative to the Hollywood Principle
- Benefits of the Observer Pattern
- Complete Architecture
- Observer Pattern PHP Pseudocode Example
- Pros and Cons of the Observer Pattern in PHP
- Where is the Observer Pattern Used?
- Summary

What is the Observer Pattern
âThe observer pattern is a behavioral design pattern that defines a one-to-many relationship between objects such that when the state of an object (publisher) changes, the other objects (subscribers) are notified.â

Observer Pattern Example
CentralSight is a news aggregator service that recently opened its API to application developers. A team of developers has been hired to create an application that would pull data from the CentralSight API and show three displays for politics, sports & weather news.
The API itself has two components: News Station and API. The API knows how to fetch data from the station (Developers donât have to worry about this mechanism). Developers will add a third layer to this hierarchy by introducing an application (displays). We can also call use the term âpresentational layerâ for this layer.
The API knows how to update the application state by relaying the latest updates. The existing documentation will help developers understand the update logic in the API code. Hereâs a top view of these layers.

Letâs also have an abstract overview of the NewsData class, which is the API object sitting between the two layers.Â

Developers are now clear about the update logic in the NewsData. They will add code to the update()
function because thatâs the one being called whenever the station sends over updates. So, hereâs the pseudocode of the first implementation.
<?php
/*
This class represents the API object that is supposed to get data from the station.
This is a pseudo method for demonstration purposes and therefore it will use dummy data.
*/
class NewsData {
private $sportsDisplay;
private $politicsDisplay;
private $weatherDisplay;
public function __construct() {
$this->sportsDisplay = new SportsDisplay();
$this->politicsDisplay = new PoliticsDisplay();
$this->weatherDisplay = new WeatherDisplay();
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getSportsNews() {
return [
"Team A vs Team B: Scorecard",
"Team C out of Super 5 league",
"New tournament schedule announced"
];
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getPoliticsNews() {
return [
"Mr.John elected as a senator",
"Government to revise the prices of gas",
"Elections delays expected amidst political choas"
];
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getWeatherNews() {
return [
"Heavy rains forecast from next week",
"Snow storm expected in some parts of states",
"Global Warming: Where are we heading"
];
}
/*
This method runs when station pushes the latest news.
This is a pseudo function and we don't have to worry about details on how
it interact with the station
*/
public function update() {
$sportsUpdates = $this->getSportsNews();
$politicsUpdates = $this->getPoliticsNews();
$weatherUpdates = $this->getWeatherNews();
/*
This is how the current method send updates to the application.
*/
$this->sportsDisplay->update($sportsUpdates);
$this->politicsDisplay->update($politicsUpdates);
$this->weatherDisplay->update($weatherUpdates);
}
}
class SportsDisplay {
/*
This method is supposed to update the application's display for sports updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($news) {
echo "Sports\n";
foreach($news as $n) {
echo "- ".$n."\n";
}
}
}
class PoliticsDisplay {
/*
This method is supposed to update the application's display for politics updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($news) {
echo "Politics\n";
foreach($news as $n) {
echo "- ".$n."\n";
}
}
}
class WeatherDisplay {
/*
This method is supposed to update the application's display for weather updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($news) {
echo "Weather\n";
foreach($news as $n) {
echo "- ".$n."\n";
}
}
}
?>
Test driving this code yields the following output.
$newsDataObject = new NewsData();
$newsDataObject->update();
/*
Sports
- Team A vs Team B: Scorecard
- Team C out of Super 5 league
- New tournament schedule announced
Politics
- Mr.John elected as a senator
- Government to revise the prices of gas
- Elections delays expected amidst political choas
Weather
- Heavy rains forecast from next week
- Snow storm expected in some parts of states
- Global Warming: Where are we heading
*/
The NewsData uses concrete display classes and disburses updates by passing data to update()
on every display class.
The pseudocode works fine, as the output shows. However, some aspects can be problematic from a design perspective.
Problem đ
Open-closed principle
The open-closed principle says, âA class should be closed to modification but open to extension.â Realistically, you cannot completely factor out modification. Still, the best you can do is minimize them so that a change doesnât propel a chain reaction.Â
The current implementation uses a concrete implementation and thus tightly couples the NewsData module with the application. These two are supposed to be independent layers with some form of communication. So, what are the downsides as a result?
Consider adding or removing displays or changing the concrete implementation that would affect the current understanding of the update() function. In both these scenarios, we must modify existing code and potentially introduce bugs as the application scales.
Lack of abstraction
âProgram to interfaces (abstractions) not implementationsâ is a key to flexible design. This fact is somehow related to what we have just talked about. The current implementation refers to concrete instances and lacks a common interface.
Consequently, the NewsData binds to these instances, and any changes occurring to them can easily project out to the NewsData. However, a common structure for defining an interface for the display classes exists. So, developers will make sure to define one.
Runtime operations
The current implementation closes the door to adding or removing displays in the runtime. The lack of dynamic structure is a sign of rigid and inflexible design. A sophisticated enterprise-grade application usually expects dynamic behavior that is missing in this context.
Solution đ
Publisher & subscriber
There is an interesting analogy if you can think about Youtube. Viewers subscribe to their favorite channels. The viewers (subscribers) get notifications about new content uploaded to the channel.
We can use the term âPublisherâ for the channel because it publishes the content. The viewers are âSubscribersâ or âObserversâ who receive updates about new content.

Pretty intuitive and interesting. But how does that relate to the subject here?
Introducing the observer pattern
Similar terminology translates as it is in the observer pattern. We have a publisher, the NewsData object. The display objects are âobserversâ or âsubsribersâ.Â
There is a one-to-many relationship between the publisher and observers. It is a simple idea, and one publisher relays updates to one or more than one observers.

In this design, the publisher class doesnât program to concrete observer classes, just as we did before. Instead, the publisher class programs to the Observer interface. As long as a concrete class implements observer, the publisher can add it to its subscriberâs list and send updates without worrying about the underlying details.
The publisher class has an array of subscribers/observers. Observers can subscribe/unsubscribe in the runtime without affecting the publisher class. Besides, developers can add observers without modifying the existing code.
So, the observer pattern addresses our concerns with the former design.
Publisher & Subscriber Interface
The two important interfaces are Publisher and Subscriber. Observer the methods they declare.Â
interface Publisher {
public function registerDisplay($display);
public function removeDisplay($display);
public function updateDisplays();
}
interface Observer {
public function update($sportsNews, $politicsNews, $weatherNews);
}
Quite intuitive, arenât they? You may wonder why we are passing all three news data to update(). We will see why when we implement this interface.
NewsData: The publisher
The NewData class implements the Publisher interface. The most important features are:
- It has an instance variable
$displays
which is supposed to be an array of subscribers. registerDisplay()
adds a display (subscriber).removeDisplay()
removes a display (subscriber) by an index, a unique display ID.ÂupdateDisplays()
iterate through the$displays
array calling update on every display (subscriber).
/*
This class represents the publisher class now. NewsData implements publisher and also has an array of references to the subscriber objects.
*/
class NewsData implements Publisher {
private $displays; //List of subscribers.
private $sportsNews;
private $politicsNews;
private $weatherNews;
public function __construct() {
$this->displays = [];
}
public function registerDisplay($display) {
$this->displays[$display->getID()] = $display; //Display has a unique ID used as array index. Helpful for removal.
}
public function removeDisplay($display) {
unset($this->displays[$display->getID()]);
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getSportsNews() {
return [
"Team A vs Team B: Scorecard",
"Team C out of Super 5 league",
"New tournament schedule announced"
];
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getPoliticsNews() {
return [
"Mr.John elected as a senator",
"Government to revise the prices of gas",
"Elections delays expected amidst political choas"
];
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getWeatherNews() {
return [
"Heavy rains forecast from next week",
"Snow storm expected in some parts of states",
"Global Warming: Where are we heading"
];
}
/*
This method runs when station pushes the latest news.
This is a pseudo function and we don't have to worry about details on how
it interact with the station
*/
public function update() {
$this->sportsNews = $this->getSportsNews();
$this->politicsNews = $this->getPoliticsNews();
$this->weatherNews = $this->getWeatherNews();
$this->updateDisplays();
}
public function updateDisplays() {
foreach($this->displays as $display) {
$display->update($this->sportsNews, $this->politicsNews, $this->weatherNews);
}
}
}
Display classes: Observers
The display classes implement the Observer interface. The update() function now takes all three instances of data (sports, politics, weather), and thatâs because the NewsData class now doesnât know about the concrete instance. From a NewsData perspective, it calls update()
on an Observer and doesnât care about the specific type.
That adds some redundancy on the observerâs end, but it is a trade-off. Type-checking in NewsData class will defeat the purpose. We can also use a factory to deal with it, adding more complexity.
So, letâs continue without worrying much about these parameters now.
class SportsDisplay implements Observer {
private $ID;
function __construct() {
$this->ID = "01";
}
/*
This method is supposed to update the application's display for sports updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($sportsNews, $politicsNews, $weatherNews) {
echo "Sports\n";
foreach($sportsNews as $n) {
echo "- ".$n."\n";
}
}
public function getID() {
return $this->ID;
}
}
class PoliticsDisplay implements Observer {
private $ID;
function __construct() {
$this->ID = "02";
}
/*
This method is supposed to update the application's display for sports updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($sportsNews, $politicsNews, $weatherNews) {
echo "Politics\n";
foreach($politicsNews as $n) {
echo "- ".$n."\n";
}
}
public function getID() {
return $this->ID;
}
}
class WeatherDisplay implements Observer {
private $ID;
function __construct() {
$this->ID = "03";
}
/*
This method is supposed to update the application's display for sports updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($sportsNews, $politicsNews, $weatherNews) {
echo "Weather\n";
foreach($weatherNews as $n) {
echo "- ".$n."\n";
}
}
public function getID() {
return $this->ID;
}
}
Time to check some updates
Letâs put the observer pattern to the test.
$newsDataObject = new NewsData();
$sportsDisplay = new SportsDisplay();
$politicsDisplay = new PoliticsDisplay();
$weatherDisplay = new WeatherDisplay();
$newsDataObject->registerDisplay($sportsDisplay);
$newsDataObject->registerDisplay($politicsDisplay);
$newsDataObject->registerDisplay($weatherDisplay);
$newsDataObject->update();
/*
Sports
- Team A vs Team B: Scorecard
- Team C out of Super 5 league
- New tournament schedule announced
Politics
- Mr.John elected as a senator
- Government to revise the prices of gas
- Elections delays expected amidst political choas
Weather
- Heavy rains forecast from next week
- Snow storm expected in some parts of states
- Global Warming: Where are we heading
*/
An Alternative to the Hollywood Principle
The current design relies on the Hollywood principle, which says, âDonât call us, we will call you,â which means that the publisher sends over the update. The observers just wait for the publisherâs call.Â
Thatâs one reason we passed all the news parameters to the update() function. An alternative approach is to open up the publisherâs getter methods for the public. The observers will call the appropriate getters on the publisherâs object.
The Observer interface changes as follows.
interface Observer {
public function update();
}
The publisher calls update()
on the observers without passing any parameters. The observers now have a reference to the NewsData object and call the appropriate getter method to fetch the data of their interest.
Benefits of the Observer Pattern
- A change in the state of one entity is propagated to other entities.
- Add or remove listeners or observers more easily.Â
- Establishes one-to-many relationships between entities without coupling them.
Complete Architecture | Observer Pattern in PHP

Observer Pattern PHP Pseudocode Example
<?php
interface Publisher {
public function registerDisplay($display);
public function removeDisplay($display);
public function updateDisplays();
}
interface Observer {
public function update($sportsNews, $politicsNews, $weatherNews);
}
/*
This class represents the publisher class now. NewsData implements publisher and also has an array of references to the subscriber objects.
*/
class NewsData implements Publisher {
private $displays; //List of subscribers.
private $sportsNews;
private $politicsNews;
private $weatherNews;
public function __construct() {
$this->displays = [];
}
public function registerDisplay($display) {
$this->displays[$display->getID()] = $display; //Display has a unique ID used as array index. Helpful for removal.
}
public function removeDisplay($display) {
unset($this->displays[$display->getID()]);
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getSportsNews() {
return [
"Team A vs Team B: Scorecard",
"Team C out of Super 5 league",
"New tournament schedule announced"
];
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getPoliticsNews() {
return [
"Mr.John elected as senator",
"Government to revise the prices of gas",
"Elections delays expected amidst political choas"
];
}
/*
The pseudo method uses dummy data for demo purposes
*/
private function getWeatherNews() {
return [
"Heavy rains forecast from next week",
"Snow storm expected in some parts of states",
"Global Warming: Where are we heading"
];
}
/*
This method runs when station pushes the latest news.
This is a pseudo function and we don't have to worry about details on how
it interact with the station
*/
public function update() {
$this->sportsNews = $this->getSportsNews();
$this->politicsNews = $this->getPoliticsNews();
$this->weatherNews = $this->getWeatherNews();
$this->updateDisplays();
}
public function updateDisplays() {
foreach($this->displays as $display) {
$display->update($this->sportsNews, $this->politicsNews, $this->weatherNews);
}
}
}
class SportsDisplay implements Observer {
private $ID;
function __construct() {
$this->ID = "01";
}
/*
This method is supposed to update the application's display for sports updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($sportsNews, $politicsNews, $weatherNews) {
echo "Sports\n";
foreach($sportsNews as $n) {
echo "- ".$n."\n";
}
}
public function getID() {
return $this->ID;
}
}
class PoliticsDisplay implements Observer {
private $ID;
function __construct() {
$this->ID = "02";
}
/*
This method is supposed to update the application's display for sports updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($sportsNews, $politicsNews, $weatherNews) {
echo "Politics\n";
foreach($politicsNews as $n) {
echo "- ".$n."\n";
}
}
public function getID() {
return $this->ID;
}
}
class WeatherDisplay implements Observer {
private $ID;
function __construct() {
$this->ID = "03";
}
/*
This method is supposed to update the application's display for sports updates.
This is a pseudo method for demonstration purposes so the method will print the output
to the console.
*/
public function update($sportsNews, $politicsNews, $weatherNews) {
echo "Weather\n";
foreach($politicsNews as $n) {
echo "- ".$n."\n";
}
}
public function getID() {
return $this->ID;
}
}
$newsDataObject = new NewsData();
$sportsDisplay = new SportsDisplay();
$politicsDisplay = new PoliticsDisplay();
$weatherDisplay = new WeatherDisplay();
$newsDataObject->registerDisplay($sportsDisplay);
$newsDataObject->registerDisplay($politicsDisplay);
$newsDataObject->registerDisplay($weatherDisplay);
$newsDataObject->update();
/*
Sports
- Team A vs Team B: Scorecard
- Team C out of Super 5 league
- New tournament schedule announced
Politics
- Mr.John elected as senator
- Government to revise the prices of gas
- Elections delays expected amidst political choas
Weather
- Mr.John elected as senator
- Government to revise the prices of gas
- Elections delays expected amidst political choas
*/
?>
Pros and Cons of the Observer Pattern in PHP
Pros | Cons |
Satisfies the open/closed principle: Open to adding more subscribers without modifying the existing code. We can also add more publishers. | Sends updates in a random order which can be detrimental if observers have shared data dependency where sequence matters. |
Publishers and observers are loosely coupled and can be reused. | |
Can add or remove observers in the runtime. |
Where is the Observer Pattern Used
- Applications with messaging and notification features.
- Newsletters, content sharing, and social platforms.
- In graphical user interfaces to propagate events to other elements (event listeners).
What is the Observer pattern?
The observer pattern is a behavioral design pattern that defines a one-to-many relationship between objects such that when the state of an object (publisher) changes, the other objects (subscribers) are notified.
Using the Observer Pattern in PHP Today
Phew! Thatâs a lot of content to absorb. Letâs have a rather summary of what we did in this article. This article is about the observer pattern, a behavioral design pattern that defines a one-to-many relationship between objects such that when the state of an object (publisher) changes, the other objects (subscribers) are notified.
The article features a news aggregator API CentralSight which has opened application APIs. Developers have been assigned to add a presentational layer, an application with three displays featuring sports, politics, and weather updates, receiving the data from the API.
The developers develop a solution that works but violates the fundamentals of flexible software design. Contemplating further, they identify a mechanism similar to what we regularly see when subscribing to a favorite channel and getting notifications when new content is live.
The observer pattern is based on this mechanism, where a publisher pushes notifications or updates, and the observers receive them. In this application, the NewsData object acts as a publisher, and the applicationâs displays act as observers.
With observer patterns, the publisher and observers act loosely coupled entities open to extension and closed to modification. This publisher is least interested in the concrete implementations because it programs to the Observer interface.
As a result, developers created a flexible and maintainable solution, which was the ultimate goal.
Thatâs all. See you in another design pattern article.
Books on Design Patterns
Want to learn more about Design Patterns? There are many great resources online. We recommend the following books for your collection as they both can teach you the theoretical and the application of using design patterns in your day-to-day programming. Feel free to use the following Amazon affiliate links if youâd like to purchase them and a way to support our efforts.
Design Patterns: Elements of Reusable Object-Oriented Software

This is the book that started it all. I believe that every programmer should have a referenced copy to this book at some point in their career. There have been many updates and excellent newer content through the years, but this is a classic. It still stands the test of time and is just as relevant for today as it was in the original printing in the 90s.
Learning PHP Design Patterns

This is an excellent book to go beyond the theory and apply it to writing good PHP code. Learning PHP Design Patterns is published by the popular OâReilly media company. OâReilly consistently publishes some of the most useful reference material related to software development. They are known to provide materials that thoroughly cover a topic in a way that is simple to understand. I recommend this book to every PHP developer.
Want to see our full review of books on design patterns? Read our huge review article on over 15 design pattern books.
Design Patterns in PHP Learning Series.
This article is part of our series of design patterns in PHP. We are going through all of the patterns and showing how they can help you build better applications. Browse through our full list of patterns below.
- What are Design Patterns in PHP?
- Books on Design Patterns
- Builder Design Pattern
- Strategy Design Pattern
- Factory Design Pattern
- Prototype Design Pattern
- Singleton Design Pattern
- Adapter Design Pattern
- Bridge Design Pattern
- Iterator Design Pattern
- Composite Design Pattern
- Decorator Design Pattern
- Facade Design Pattern
- Flyweight Design Pattern
- Proxy Design Pattern
- State Design Pattern
- Observer Design Pattern
- Momento Design Pattern
- Mediator Design Pattner
- Chain of Responsibility Design Pattern