← Back to PHP Course | Chapter 14: Advanced PHP | Lesson 9 of 24

PHP Design Patterns

Design patterns are tried-and-true solutions to common coding puzzles, like well-known recipes. Using them helps other programmers quickly recognize how your code is organized.

What are Design Patterns?

Design patterns are named, battle-tested solutions to recurring design problems -- they give developers a shared vocabulary ('just use a Factory here') instead of everyone reinventing similar structures independently.

Example: What are Design Patterns?

php
<?php
// Print "A named, reusable solution to a recurring design problem -- like 'use a Factory here'" to the output
echo "A named, reusable solution to a recurring design problem -- like 'use a Factory here'";
?>

The Singleton Pattern

The Singleton pattern guarantees a class can only ever be instantiated once and exposes a single global access point to that instance, commonly used for things like a shared database connection.

Example: The Singleton Pattern

php
<?php
// Define the class `Database`
class Database {
    // Declare the private static property `$instance`, set to `null`
    private static $instance = null;
    // Define the constructor `__construct` with no parameters
    private function __construct() {}
    // Define the function `getInstance` with no parameters
    static function getInstance() {
        // Check whether `self::$instance === null`
        if (self::$instance === null) {
            self::$instance = new self();
        }
        // Return `self::$instance` from this function
        return self::$instance;
    }
}
// Declare `$db1`, set to `Database::getInstance()`
$db1 = Database::getInstance();
// Declare `$db2`, set to `Database::getInstance()`
$db2 = Database::getInstance();
// Print a detailed dump (with types) of `$db1 === $db2`
var_dump($db1 === $db2);
?>

The Factory Pattern

The Factory pattern hides object creation behind a dedicated method or class, so calling code asks for 'a Shape' without needing to know whether it gets a Circle or a Square -- useful when creation logic is complex or varies by input.

Example: The Factory Pattern

php
<?php
// Define the interface `Shape` describing required methods
interface Shape {
    // Define the function `draw` with no parameters
    function draw();
}
// Define the class `Circle`, implementing `Shape`
class Circle implements Shape {
    // Define the function `draw` with no parameters
    function draw() { echo "Drawing a circle"; }
}
// Define the class `ShapeFactory`
class ShapeFactory {
    // Define the function `create` taking `$type`
    static function create($type) {
        if ($type === "circle") return new Circle();
    }
}
// Declare `$shape`, set to `ShapeFactory::create("circle")`
$shape = ShapeFactory::create("circle");
$shape->draw();
?>

The Observer Pattern

The Observer pattern lets one subject object notify a list of registered observer objects whenever its state changes, which is the foundation behind most event-listener and pub-sub systems.

Example: The Observer Pattern

php
<?php
// Define the class `Subject`
class Subject {
    // Declare the private property `$observers`, set to `[]`
    private $observers = [];
    // Define the function `subscribe` taking `$observer`
    function subscribe($observer) {
        $this->observers[] = $observer;
    }
    // Define the function `notify` taking `$event`
    function notify($event) {
        // Loop over `$this->observers`, binding each item to `$observer`
        foreach ($this->observers as $observer) {
            $observer($event);
        }
    }
}
// Create a new `Subject` instance, stored in `$subject`
$subject = new Subject();
$subject->subscribe(function ($event) {
    // Print "Observer received: $event" to the output
    echo "Observer received: $event";
});
$subject->notify("state changed");
?>

Best Practices for Patterns

Patterns solve real problems but add indirection -- applying a Factory or Observer to a five-line script adds complexity without benefit, so reach for a pattern only once a project's genuine needs justify it.

Example: Best Practices for Patterns

php
<?php
// A five-line script doesn't need a Factory or Observer -- that's added complexity with no benefit
function greet() {
    echo "Hello!";
}
greet();
?>
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Applying a design pattern where a simple function would do, which only adds complexity.
  2. Using Singleton for everything, which creates hidden global state that is hard to test.
  3. Making classes depend on each other directly, which defeats the purpose of patterns like Observer.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.