PHP Prototype Design Pattern Creational Pattern

PHP Prototype Pattern

A short screencast: the Prototype pattern in PHP — cloning an object with clone and __clone() without re-running the constructor.

Prototype Pattern — open the lesson page

Overview

In this screencast we clone a report. A plain clone in PHP copies nested objects by reference, so the original changes too. The __clone() method makes the copy deep: the author is cloned, and the clone receives a fresh id — the original stays untouched.

UML diagram

classDiagram class Author { +string name } class Report { -int id -string title -Author author -array sections +__construct(id: int, title: string, author: Author, sections: array) +addSection(section: string) void +describe() string +__clone() } Report *-- Author : contains Report ..> Report : clones

Code

Author.php

class Author
{
    public function __construct(public string $name) {}
}

Report.php

class Report
{
    private static int $nextId = 2;

    public function __construct(
        public int $id,
        public string $title,
        public Author $author,
        public array $sections = [],
    ) {}

    public function addSection(string $section): void
    {
        $this->sections[] = $section;
    }

    public function describe(): string
    {
        return "#{$this->id} {$this->title} by {$this->author->name}: " . implode('; ', $this->sections);
    }

    public function __clone()
    {
        $this->id = self::$nextId++;
        $this->author = clone $this->author;
    }
}

demo.php

require_once 'Author.php';
require_once 'Report.php';

$base = new Report(1, 'Q3 Report', new Author('Anna'), ['intro']);

$copy = clone $base;
$copy->title = 'Q3 Report (rev)';
$copy->author->name = 'Bob';
$copy->addSection('summary');

echo $base->describe();
echo PHP_EOL;
echo $copy->describe();

Note: why __clone()

Without __clone() cloning in PHP is shallow: $base->author and $copy->author are the same object. Rename the author in the copy and you rename it in the original too. __clone() hooks in right after the fields are copied, so you can re-clone nested objects and take a fresh id from the counter. That way the clone gets its own state and never corrupts the prototype.

Example output

Running php demo.php in the terminal produces:

#1 Q3 Report by Anna: intro
#2 Q3 Report (rev) by Bob: intro; summary

Advantages

  • Clones complex objects without coupling to their classes.
  • Avoids re-running expensive initialisation when building similar objects.
  • New variants come from tweaking a copy instead of building from scratch.
  • Simplifies adding objects configured at runtime.

When to use

  • When creating an object is costlier than copying it.
  • When you need many similar objects with small differences.
  • When concrete classes are chosen at runtime.
  • When you want to avoid a rigid factory hierarchy.

Usage in Laravel

In Laravel a prototype is model cloning: Illuminate\Database\Eloquent\Model::replicate() makes a copy of a model with the same attributes, which is then adjusted before saving. The same idea is used when an HTTP request is cloned (clone $request) and in tests where a copy of an object is configured independently.