PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

SOLID principles in PHP – theory into code

by Henryk Tews / Tuesday, 12 April 2022 / Published in PHP

SOLID principles are five design guidelines that make object-oriented code maintainable, extensible, and testable. They are not rules to be applied mechanically but tools for reasoning about design decisions. I show each principle in PHP with a before/after example and a concrete connection to Magento 2 architecture.

S – Single Responsibility Principle

A class should have one reason to change – one responsibility.

<?php

// BAD - three responsibilities in one class
class OrderProcessor
{
    public function process(array $order): void
    {
        // 1. Business logic
        $total = array_sum(array_column($order['items'], 'price'));

        // 2. Database access
        $this->pdo->prepare('INSERT INTO orders ...')->execute([$total]);

        // 3. Sending email
        mail($order['email'], 'Order confirmed', "Total: {$total}");
    }
}

// GOOD - one responsibility each
class OrderCalculator     { public function total(array $order): float { ... } }
class OrderRepository     { public function save(Order $order): void { ... } }
class OrderEmailNotifier  { public function sendConfirmation(Order $order): void { ... } }

class OrderService
{
    public function __construct(
        private OrderCalculator $calculator,
        private OrderRepository $repository,
        private OrderEmailNotifier $notifier
    ) {}

    public function process(array $orderData): void
    {
        $total = $this->calculator->total($orderData);
        $order = new Order($orderData, $total);
        $this->repository->save($order);
        $this->notifier->sendConfirmation($order);
    }
}

O – Open/Closed Principle

Open for extension, closed for modification.

<?php

// BAD - adding a shipping method requires modifying this class
class ShippingCalculator
{
    public function calculate(string $method, float $weight): float
    {
        if ($method === 'flat')   return 9.99;
        if ($method === 'weight') return $weight * 2.5;
        // Adding 'express' requires editing this file
        throw new \InvalidArgumentException("Unknown: {$method}");
    }
}

// GOOD - add new method by adding a new class, zero modification
interface ShippingStrategyInterface
{
    public function calculate(float $weight): float;
}

class FlatRateShipping   implements ShippingStrategyInterface { ... }
class WeightBasedShipping implements ShippingStrategyInterface { ... }
class ExpressShipping     implements ShippingStrategyInterface { ... } // new - no existing code changed

L – Liskov Substitution Principle

Objects of a subtype must be usable wherever the parent type is expected.

<?php

// BAD - subclass weakens the contract
class Rectangle
{
    public function __construct(protected int $width, protected int $height) {}
    public function area(): int { return $this->width * $this->height; }
    public function setWidth(int $w): void  { $this->width = $w; }
    public function setHeight(int $h): void { $this->height = $h; }
}

class Square extends Rectangle
{
    // Square MUST have equal sides, so it changes setWidth/setHeight behaviour
    public function setWidth(int $w): void  { $this->width = $this->height = $w; }
    public function setHeight(int $h): void { $this->width = $this->height = $h; }
}

// This breaks LSP:
function testRectangle(Rectangle $r): void {
    $r->setWidth(5);
    $r->setHeight(3);
    assert($r->area() === 15); // fails for Square! area = 9
}

// GOOD - separate types with a shared interface
interface ShapeInterface { public function area(): int; }
class Rectangle implements ShapeInterface { ... }
class Square    implements ShapeInterface { ... }

I – Interface Segregation Principle

Clients should not be forced to depend on methods they do not use.

<?php

// BAD - fat interface forces implementation of unneeded methods
interface ProductInterface
{
    public function getName(): string;
    public function getPrice(): float;
    public function getWeight(): float;
    public function getDownloadUrl(): string; // only for digital products!
    public function getShippingClass(): string; // only for physical products!
}

// GOOD - segregated interfaces
interface ProductInterface         { public function getName(): string; public function getPrice(): float; }
interface PhysicalProductInterface { public function getWeight(): float; public function getShippingClass(): string; }
interface DigitalProductInterface  { public function getDownloadUrl(): string; }

class PhysicalProduct implements ProductInterface, PhysicalProductInterface { ... }
class DigitalProduct  implements ProductInterface, DigitalProductInterface  { ... }

D – Dependency Inversion Principle

Depend on abstractions, not concretions.

<?php

// BAD - high-level class depends on concrete low-level class
class OrderService
{
    private MySQLOrderRepository $repository; // concrete dependency

    public function __construct()
    {
        $this->repository = new MySQLOrderRepository(); // new = tight coupling
    }
}

// GOOD - depend on abstraction, inject implementation
class OrderService
{
    public function __construct(
        private OrderRepositoryInterface $repository // interface = abstraction
    ) {}
}

// In di.xml - bind interface to implementation
// <preference for="OrderRepositoryInterface" type="MySQLOrderRepository"/>

// In tests - inject mock
$service = new OrderService($this->createMock(OrderRepositoryInterface::class));

SOLID in Magento 2

Magento 2’s architecture embodies all five principles:

  • SRP – separate Repository, Model, ResourceModel, Collection classes
  • OCP – plugins and observers extend behaviour without modifying core
  • LSP – preferences must honour the interface contract of the replaced class
  • ISP – @api interfaces are fine-grained (ProductRepositoryInterface vs ProductManagementInterface)
  • DIP – constructors depend on interfaces; di.xml binds implementations

Summary

SOLID is not a checklist to tick off. It is a vocabulary for discussing design tradeoffs. When a class is hard to test – suspect SRP or DIP violation. When adding a feature requires modifying many existing classes – suspect OCP violation. Internalise the principles as diagnostic tools rather than rules, and your design instincts improve naturally.

About Henryk Tews

What you can read next

PHP 7.2 – object type hint, sodium instead of mcrypt, deprecations

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}