DEV Community

Cover image for SOLID principles in PHP
Lin Aung
Lin Aung

Posted on

SOLID principles in PHP

What is SOLID?

You may have already heard this term **SOLID** in almost every job descriptions and wonder why it is important. Well the SOLID principles are a set of five design guidelines aimed at making software designs more understandable, flexible, and maintainable. These principles are widely used in object-oriented programming, although they can also be applied in other paradigms.

Pillars of SOLID principles

The acronym SOLID stands for:

  • Single Responsibility Principle (SRP)
  • Open-Closed Principle (OCP)
  • Liskov Substitution Principle (LSP)
  • Interface Segregation Principle (ISP)
  • Dependency Inversion Principle (DIP)

1) Single Responsibility Principle

A class should have only one reason or purpose to serve. It means the class should have only one responsibility. I'll show one by one with PHP examples.

// Single Responsibility Principle

// Shouldn't do
class Report {
    public function generate() {
        // generating report
    }

    public function saveToFile($filename) {
        // save report to file
    }
}

// Should do
class Report {
    public function generate() {
        // generating report
    }
}

class ReportSaver {
    public function saveToFile(Report $report, $filename) {
        // save report to file
    }
}
Enter fullscreen mode Exit fullscreen mode

2) Open-Close Principle

Instances should be open for extension but close for modification. This mean instances (classes,functions,modules or etc.,) can be extendable by another instance but shouldn't allow direct modification to them.

// Open Close Principle

// Shouldn't do
class Rectangle {
    public $width;
    public $height;
}

function area($rectangle) {
    return $rectangle->width * $rectangle->height;
}

// Should do
interface Shape {
    public function area();
}

class Rectangle implements Shape {
    public $width;
    public $height;

    public function area() {
        // w * h
    }
}

class Circle implements Shape {
    public $radius;

    public function area() {
        // πr2
    }
}
Enter fullscreen mode Exit fullscreen mode

3) Liskov Substitution Principle

This mean that objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program. What does that mean? Well let's see the example code for clarifications.

// Liskov Substitution Principle

// Shouldn't do
class Animal {
    public function jump() {
        // code for jumping
    }
}

class Snail extends Animal {
    public function jump() {
        // Sanil can't jump
    }
}

// Good example
abstract class Animal {
    abstract public function eat();
}

class Snail extends Bird {
    public function eat() {
        // every animal can eat
    }
}

class Tiger extends Bird {
    public function eat() {
        // every animal can eat
    }
}
Enter fullscreen mode Exit fullscreen mode

4) Interface Segregation Principle

We shouldn't force the instances to implement the interfaces that they don't use. We should make the interface flexible for most cases.

// Interface Segregation Principle

// Shouldn't do
interface Human {
    public function walk();
    public function swim(); // not everyone can swim
}

// Should do
interface Walkable {
    public function walk();
}

interface Swimmable {
    public function swim();
}

class People implements Walkable, Swimmable {
    public function walk() {
        // walking
    }

    public function swim() {
        // swimming
    }
}
Enter fullscreen mode Exit fullscreen mode

5) Dependency Inversion Principle

This principle aims to decouple high-level modules (which provide complex functionality) from low-level modules (which provide basic functionality) by introducing an abstraction layer between them. This allows both high-level and low-level modules to depend on abstractions rather than on solid implementations, making the system more flexible, extensible, and maintainable.

// Dependency Inversion Principle

// Shouldn't do
class Book {
    public function getContent() {
        // return contents
    }
}

class Printer {
    public function printBook(Book $book) {
        $content = $book->getContent();
        // print the content
    }
}
Enter fullscreen mode Exit fullscreen mode

As you can see in above example, Printer class is depending on Book object and can only print for the Book. What if there is another class called Article that may want to print? We shouldn't modify the Printer class to accept more objects. Instead we provide the abstractions between them.

// Dependency Inversion Principle

// Should do
interface Printable {
    public function getContent();
}

class Book implements Printable {
    public function getContent() {
        return "This is the book content";
    }
}

class Article implements Printable {
    public function getContent() {
        return "This is the article content";
    }
}

class Printer {
    public function printContent(Printable $printable) {
        $content = $printable->getContent();
        echo "Printing content: " . $content;
    }
}
Enter fullscreen mode Exit fullscreen mode

In PHP, these principles help in creating a clean codebase that is easier to debug, understand, and expand. Given that PHP applications often evolve over time, applying SOLID principles can dramatically ease the pain of growth and maintenance.

Top comments (0)