PHP Abstract Factory Pattern
A short screencast: a family of products and factories is built on shared interfaces — the Abstract Factory pattern in PHP.
Overview
In this screencast we build the pattern on interfaces. ProductInterface describes a product, FactoryInterface describes a factory. Concrete factories LaptopFactory and SmartphoneFactory create their products, while the client code (demo.php) works only with abstractions — you can swap one "family" of objects for another without changing the calling code.
UML diagram
Code
ProductInterface.php
interface ProductInterface
{
public function getInfo(): string;
}
Laptop.php
class Laptop implements ProductInterface
{
public function __construct(
private string $name
) {}
public function getInfo(): string
{
return 'Laptop: ' . $this->name;
}
}
Smartphone.php
class Smartphone implements ProductInterface
{
public function __construct(
private string $model
) {}
public function getInfo(): string
{
return 'Smartphone: ' . $this->model;
}
}
FactoryInterface.php
interface FactoryInterface
{
public function createProduct(): ProductInterface;
}
LaptopFactory.php
class LaptopFactory implements FactoryInterface
{
public function createProduct(): ProductInterface
{
return new Laptop('MacBook Pro');
}
}
demo.php
require_once 'ProductInterface.php';
require_once 'Laptop.php';
require_once 'FactoryInterface.php';
require_once 'LaptopFactory.php';
$factory = new LaptopFactory();
$product = $factory->createProduct();
echo $product->getInfo();
Note: why interfaces
The client in demo.php knows nothing about Laptop and LaptopFactory — only ProductInterface and FactoryInterface. That is the essence of the pattern: the code depends on abstractions, and the concrete factory decides which "family" of objects to assemble. Add a SmartphoneFactory, and the client assembles a smartphone without any changes to the calling code.
Note: interface, not an abstract class
In PHP a class can extend only one abstract class but can implement any number of interfaces. Members of one "family" in the Abstract Factory usually share no implementation — they are bound only by a contract, so an interface is the right abstraction:
abstract class AbstractProduct
{
abstract public function getInfo(): string;
}
Such an abstraction immediately consumes the only inheritance slot: Laptop could no longer extend, say, the framework's base model class. Besides, an abstract class mixes contract and implementation — putting a shared method there drags it into every product of the family whether it needs it or not, making the family harder to swap. An interface defines only the type and behavior, leaving the implementation to each product.
Example output
Running php demo.php in the terminal produces:
Laptop: MacBook Pro
Advantages
- Works with a whole family of consistent products.
- The client depends on abstract interfaces, not concrete classes.
- Swapping a family (say, a vendor) leaves client code untouched.
- Guarantees that products created together are compatible.
When to use
- When a system must be independent of how its products are created.
- When there are several products that form consistent families.
- When whole sets of products must be switched at runtime.
- When products of one family must be used together.
Usage in Laravel
In Laravel an abstract factory is a driver manager: Illuminate\Cache\CacheManager, Illuminate\Filesystem\FilesystemManager, Illuminate\Queue\QueueManager. Each creates not a single object but a consistent family of implementations (a cache store, a disk, a queue) chosen by the driver name from configuration. Through Cache::store('redis') or Storage::disk('s3') the client gets the right family without knowing the concrete classes.