SOLID principles in PHP
Tags: php, solidprinciples, oop
Published: 2023-08-24 07:04:45

## 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
    }
}
```


## 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
    }
}
```
## 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
    }
}
```

## 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
    }
}
```


## 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
    }
}
```

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;
    }
}
```

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.