Observer Pattern
Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 73 of 91.
The Observer Pattern is a behavioral design pattern that establishes a one-to-many relationship between objects. When one object (the subject) changes state, all its dependents (the observers) are automatically notified and updated.
Think of a newsletter subscription. The newsletter is the subject, and subscribers are observers. When a new edition is published, all subscribers receive it automatically without the newsletter needing to know the details of each subscriber.
Here's a basic implementation in PHP:
<?php
interface Observer {
public function update(string $message): void;
}
class Newsletter {
private array $subscribers = [];
public function subscribe(Observer $observer): void {
$this->subscribers[] = $observer;
}
public function publish(string $content): void {
foreach ($this->subscribers as $subscriber) {
$subscriber->update($content);
}
}
}
class EmailSubscriber implements Observer {
public function __construct(private string $email) {}
public function update(string $message): void {
echo "Sending to {$this->email}: $message\n";
}
}
$newsletter = new Newsletter();
$newsletter->subscribe(new EmailSubscriber("alice@example.com"));
$newsletter->subscribe(new EmailSubscriber("bob@example.com"));
$newsletter->publish("New article released!");
Output:
Sending to alice@example.com: New article released!
Sending to bob@example.com: New article released!The pattern decouples the subject from its observers. The Newsletter class doesn't need to know whether it's notifying email subscribers, SMS subscribers, or push notification services. It simply calls update() on anything that implements the Observer interface. This makes adding new observer types effortless without modifying existing code.
Challenge
EasyLet's build a stock price alert system using the Observer Pattern. When a stock's price changes, all registered investors should be notified automatically — a perfect real-world application of this pattern.
You'll organize your code across four files:
Observer.php— Define anObserverinterface with a single methodupdate(string $stockName, float $price): void. This is the contract that all observers must follow to receive notifications.Investors.php— Include the Observer file and create two classes that implement the interface:EmailInvestor— Takes an email address in its constructor (use constructor promotion). Itsupdate()method prints"Email to [email]: [stockName] is now $[price]"SmsInvestor— Takes a phone number in its constructor. Itsupdate()method prints"SMS to [phone]: [stockName] is now $[price]"
Stock.php— Include the Observer file and create aStockclass that acts as the subject. Your stock should:- Store the stock name and current price (use constructor promotion for the name, initialize price to
0.0) - Maintain a private array of observers
- Have a
subscribe(Observer $observer): voidmethod to add observers - Have a
setPrice(float $price): voidmethod that updates the price and notifies all observers
update()method with the stock name and new price.- Store the stock name and current price (use constructor promotion for the name, initialize price to
main.php— Include the Investors and Stock files. You'll receive three inputs: a stock name, an email address, and a phone number.Create a
Stockwith the given name. Subscribe anEmailInvestorwith the email address and anSmsInvestorwith the phone number.Set the stock price to
150.50to trigger notifications to all subscribers.
The Observer Pattern keeps your Stock class decoupled from the specific notification methods. You could easily add a PushInvestor or SlackInvestor later without modifying the Stock class at all.
Cheat sheet
The Observer Pattern establishes a one-to-many relationship where a subject automatically notifies all observers when its state changes.
Key Components:
- Observer Interface — Defines the contract for observers with an
update()method - Subject — Maintains a list of observers and notifies them of state changes
- Concrete Observers — Implement the Observer interface to receive notifications
Basic Implementation:
<?php
interface Observer {
public function update(string $message): void;
}
class Subject {
private array $observers = [];
public function subscribe(Observer $observer): void {
$this->observers[] = $observer;
}
public function notify(string $data): void {
foreach ($this->observers as $observer) {
$observer->update($data);
}
}
}
class ConcreteObserver implements Observer {
public function __construct(private string $name) {}
public function update(string $message): void {
echo "Notifying {$this->name}: $message\n";
}
}
$subject = new Subject();
$subject->subscribe(new ConcreteObserver("Observer1"));
$subject->subscribe(new ConcreteObserver("Observer2"));
$subject->notify("State changed!");
Benefits:
- Decouples the subject from its observers
- Easy to add new observer types without modifying existing code
- Supports dynamic subscription/unsubscription
Try it yourself
<?php
require_once 'Investors.php';
require_once 'Stock.php';
// Read inputs
$stockName = trim(fgets(STDIN));
$email = trim(fgets(STDIN));
$phone = trim(fgets(STDIN));
// TODO: Create a Stock with the given name
// TODO: Subscribe an EmailInvestor with the email address
// TODO: Subscribe an SmsInvestor with the phone number
// TODO: Set the stock price to 150.50 to trigger notifications
?>This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern