CreationalPHPverifiedVerified
Prototype Pattern in PHP
Creates new objects by cloning an existing instance, avoiding the cost of building from scratch.
How to Implement the Prototype Pattern in PHP
1Step 1: Define a cloneable prototype
class Shape
{
public function __construct(
public string $type,
public float $x = 0,
public float $y = 0,
public string $color = 'black',
) {}
// PHP has built-in clone support via __clone
public function __clone(): void
{
// Deep copy logic for nested objects goes here
}
}2Step 2: Implement a prototype registry
class ShapeRegistry
{
/** @var array<string, Shape> */
private array $prototypes = [];
public function register(string $key, Shape $prototype): void
{
$this->prototypes[$key] = $prototype;
}
public function create(string $key): Shape
{
if (!isset($this->prototypes[$key])) {
throw new \RuntimeException("Prototype not found: {$key}");
}
return clone $this->prototypes[$key];
}
}3Step 3: Use the prototype pattern
$registry = new ShapeRegistry();
$registry->register('redCircle', new Shape('circle', color: 'red'));
$registry->register('blueSquare', new Shape('square', color: 'blue'));
$shape1 = $registry->create('redCircle');
$shape2 = $registry->create('redCircle');
$shape1->x = 10;
echo $shape1->x; // 10
echo $shape2->x; // 0 (independent clone)<?php
declare(strict_types=1);
// [step] Define a deep-cloneable document model
final class DocumentStyle
{
public function __construct(
public string $fontFamily = 'Arial',
public int $fontSize = 12,
public string $color = '#000000',
public bool $bold = false,
public bool $italic = false,
) {}
}
final class DocumentBlock
{
public function __construct(
public string $type,
public string $content,
public DocumentStyle $style,
/** @var array<string, mixed> */
public array $metadata = [],
) {}
public function __clone(): void
{
// Deep clone nested objects
$this->style = clone $this->style;
}
}
final class Document
{
/** @var DocumentBlock[] */
private array $blocks = [];
private \DateTimeImmutable $createdAt;
public function __construct(
public string $title,
public string $author,
) {
$this->createdAt = new \DateTimeImmutable();
}
public function addBlock(DocumentBlock $block): void
{
$this->blocks[] = $block;
}
/** @return DocumentBlock[] */
public function getBlocks(): array
{
return $this->blocks;
}
public function getCreatedAt(): \DateTimeImmutable
{
return $this->createdAt;
}
// [step] Deep clone to ensure all nested objects are independent copies
public function __clone(): void
{
$this->blocks = array_map(fn(DocumentBlock $b) => clone $b, $this->blocks);
$this->createdAt = new \DateTimeImmutable();
}
}
// [step] Implement a typed prototype registry with validation
final class DocumentTemplateRegistry
{
/** @var array<string, Document> */
private array $templates = [];
public function register(string $key, Document $template): void
{
if (isset($this->templates[$key])) {
throw new \InvalidArgumentException("Template '{$key}' already registered");
}
$this->templates[$key] = $template;
}
public function create(string $key, ?string $newTitle = null, ?string $newAuthor = null): Document
{
if (!isset($this->templates[$key])) {
throw new \InvalidArgumentException("Template not found: {$key}");
}
$doc = clone $this->templates[$key];
if ($newTitle !== null) $doc->title = $newTitle;
if ($newAuthor !== null) $doc->author = $newAuthor;
return $doc;
}
public function has(string $key): bool
{
return isset($this->templates[$key]);
}
/** @return string[] */
public function listTemplates(): array
{
return array_keys($this->templates);
}
}Prototype Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Prototype Pattern in the Real World
“Think of a cell dividing through mitosis. Instead of building a new cell from raw amino acids, the existing cell duplicates itself — copying its DNA, organelles, and membrane. The result is a fully functional copy produced far faster than assembling one molecule at a time.”