Skip to content

Basic rules of SOLID Principles

Posted on:December 10, 2022 at 12:00 PM

SOLID principles are same for all language. Here’s an example of how you can apply the SOLID principles in PHP:

SOLID stands for:

  1. Single Responsibility Principle (SRP): A class should have only one reason to change.
class UserManager {
    public function registerUser($userData) {
        // Code to register a new user
    }

    public function deleteUser($userId) {
        // Code to delete a user
    }
}

class EmailService {
    public function sendEmail($to, $subject, $body) {
        // Code to send an email
    }
}

In this example, the UserManager class is responsible for user-related operations, such as registering and deleting users. The EmailService class is responsible for sending emails. Each class has a single responsibility, which makes them easier to understand, maintain, and test.

  1. Open-Closed Principle (OCP): Software entities (classes, modules, functions) should be open for extension but closed for modification.
interface PaymentGateway {
    public function processPayment($amount);
}

class StripePaymentGateway implements PaymentGateway {
    public function processPayment($amount) {
        // Code to process payment using Stripe
    }
}

class PayPalPaymentGateway implements PaymentGateway {
    public function processPayment($amount) {
        // Code to process payment using PayPal
    }
}

In this example, the PaymentGateway interface defines a common method processPayment(). The StripePaymentGateway and PayPalPaymentGateway classes implement this interface and provide their own implementation. If you need to add a new payment gateway, you can create a new class that implements the PaymentGateway interface without modifying the existing classes, ensuring they are closed for modification but open for extension.

  1. Liskov Substitution Principle (LSP): Objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program.
class Shape {
    public function getArea() {
        // Common code to calculate area
    }
}

class Rectangle extends Shape {
    private $width;
    private $height;

    public function setWidth($width) {
        // Code to set width
    }

    public function setHeight($height) {
        // Code to set height
    }

    public function getArea() {
        // Code to calculate area of rectangle
    }
}

class Circle extends Shape {
    private $radius;

    public function setRadius($radius) {
        // Code to set radius
    }

    public function getArea() {
        // Code to calculate area of circle
    }
}

In this example, the Shape class is the superclass, and Rectangle and Circle are its subclasses. They both inherit the getArea() method, but each provides its own implementation. According to the LSP, you can replace an instance of the Shape class with an instance of Rectangle or Circle without affecting the correctness of the program.

  1. Interface Segregation Principle (ISP): Clients should not be forced to depend on interfaces they do not use.
interface Printer {
    public function print();
}

interface Scanner {
    public function scan();
}

class AllInOnePrinter implements Printer, Scanner {
    public function print() {
        // Code to print
    }

    public function scan() {
        // Code to scan
    }
}

class LaserPrinter implements Printer {
    public function print() {
        // Code to print
    }
}

In this example, the Printer interface defines the print() method, and the Scanner interface defines the scan() method. The AllInOnePrinter class implements both interfaces, as it can

perform both printing and scanning. The LaserPrinter class only implements the Printer interface because it can only perform printing. By separating interfaces based on client requirements, you ensure that clients are not forced to depend on methods they don’t need.

  1. Dependency Inversion Principle (DIP): High-level modules should not depend on low-level modules. Both should depend on abstractions.
interface DatabaseConnection {
    public function connect();
}

class MySqlConnection implements DatabaseConnection {
    public function connect() {
        // Code to connect to MySQL database
    }
}

class PostgreSqlConnection implements DatabaseConnection {
    public function connect() {
        // Code to connect to PostgreSQL database
    }
}

class UserRepository {
    private $dbConnection;

    public function __construct(DatabaseConnection $dbConnection) {
        $this->dbConnection = $dbConnection;
    }

    public function getUsers() {
        $this->dbConnection->connect();
        // Code to fetch users from the database
    }
}

In this example, the DatabaseConnection interface represents a database connection, and the MySqlConnection and PostgreSqlConnection classes are specific implementations of that interface for different database types. The UserRepository class depends on the DatabaseConnection abstraction through constructor injection. This allows the UserRepository to be decoupled from specific database implementations, making it easier to switch between different databases without modifying the repository class.

These examples demonstrate how SOLID principles can be applied in PHP to achieve better software design, maintainability, and flexibility.