How to implement better code even using PHP
We know that the development of technology today is very fast. Various programming languages have emerged with their own advantages. This makes the previous programming languages seem obsolete, one of which is the PHP programming language.
PHP is one of the programming languages that became popular in the early 2000s. Until now, there are still many programmers who use PHP. But some people think PHP is outdated.
Whereas in the programming concept taught by Uncle Bob, good code is not from the language but from how the code is designed to be easy to maintain. This concept is called the Clean Code concept. In implementing clean code we must understand 5 principles, namely SOLID.
Solid principles, also known as SOLID, are a set of software design principles that aim to create more maintainable, extensible, and scalable code. These principles can be applied to any programming language, including PHP, and are essential for any developer who wants to write high-quality code.
The SOLID principles are:
- Single Responsibility Principle (SRP)
- Open-Closed Principle (OCP)
- Liskov Substitution Principle (LSP)
- Interface Segregation Principle (ISP)
- Dependency Inversion Principle (DIP)
Let’s explore each of these principles in more detail and see how they can be applied in PHP.
- Single Responsibility Principle (SRP)
Single Responsibility Principle (SRP) The SRP states that a class should have only one responsibility. In other words, a class should have only one reason to change. If a class has more than one responsibility, it becomes harder to maintain and modify.
Example in PHP:
class User {
private $db;
public function __construct($db) {
$this->db = $db;
}
public function create($data) {
// validation code
// save user to database
}
public function delete($id) {
// delete user from database
}
public function sendEmail($message) {
// send email to user
}
}
In this example, the User
class has multiple responsibilities: creating users, deleting users, and sending emails. To apply the SRP, we could split the responsibilities into separate classes: User
and EmailSender
.
class User {
private $db;
public function __construct($db) {
$this->db = $db;
}
public function create($data) {
// validation code
// save user to database
}
public function delete($id) {
// delete user from database
}
}
class EmailSender {
private $db;
public function __construct($db) {
$this->db = $db;
}
public function sendEmail($user, $message) {
// send email to user
}
}
2. Open-Closed Principle (OCP)
Open-Closed Principle (OCP) The OCP states that a class should be open for extension but closed for modification. This means that we should be able to add new functionality to a class without changing its existing code.
Example in PHP:
interface PaymentInterface {
public function pay();
}
class Paypal implements PaymentInterface {
public function pay() {
// paypal payment code
}
}
class Stripe implements PaymentInterface {
public function pay() {
// stripe payment code
}
}
class PaymentProcessor {
private $paymentMethod;
public function __construct(PaymentInterface $paymentMethod) {
$this->paymentMethod = $paymentMethod;
}
public function process() {
// payment processing code
$this->paymentMethod->pay();
}
}
In this example, the PaymentProcessor
class is open for extension because we can add new payment methods by creating new classes that implement the PaymentInterface
. The PaymentProcessor
class is closed for modification because we don't need to change its code to add new payment methods.
3. Liskov Subtitution Principle (LSP)
Liskov Substitution Principle (LSP) The LSP states that objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program. In other words, a subclass should behave like its superclass.
Example in PHP:
class Animal {
public function makeSound() {
// make animal sound
}
}
class Cat extends Animal {
public function makeSound() {
// make cat sound
}
}
class Dog extends Animal {
public function makeSound() {
// make dog sound
}
}
function makeAnimalSound(Animal $animal) {
$animal->makeSound();
}
makeAnimalSound(new Cat()); // outputs cat sound
makeAnimalSound(new Dog()); // outputs dog sound
In this example, the Cat
and Dog
classes are subclasses of the Animal
class. The makeAnimalSound
function takes an Animal
object as a parameter, which can be either a Cat
or `Dog
object. Because the Cat
and Dog
classes override the makeSound
method, they behave like their superclass Animal
. Therefore, the makeAnimalSound
function can be used with any Animal
object without affecting the correctness of the program.
4. Interface Segregation Principle (ISP)
Interface Segregation Principle (ISP) The ISP states that a client should not be forced to depend on methods it does not use. In other words, we should separate the interface into smaller and more specific interfaces, so clients can depend only on the methods they need.
Example in PHP:
interface PaymentInterface {
public function pay();
public function refund();
public function void();
}
class Paypal implements PaymentInterface {
public function pay() {
// paypal payment code
}
public function refund() {
// paypal refund code
}
public function void() {
// paypal void code
}
}
class Stripe implements PaymentInterface {
public function pay() {
// stripe payment code
}
public function refund() {
// stripe refund code
}
public function void() {
// stripe void code
}
}
interface PaymentProcessorInterface {
public function processPayment(PaymentInterface $paymentMethod);
}
class PaymentProcessor implements PaymentProcessorInterface {
public function processPayment(PaymentInterface $paymentMethod) {
// payment processing code
$paymentMethod->pay();
}
}
In this example, the PaymentInterface
has three methods: pay
, refund
, and void
. However, the PaymentProcessor
class only needs the pay
method to process a payment. To apply the ISP, we can split the PaymentInterface
into smaller interfaces, such as Payable
, Refundable
, and Voidable
, and have the Paypal
and Stripe
classes implement only the interfaces they need. The PaymentProcessor
class can then depend on the Payable
interface instead of the PaymentInterface
.
5. Depedency Inversion Principle (DIP)
Dependency Inversion Principle (DIP) The DIP states that high-level modules should not depend on low-level modules, but both should depend on abstractions. In other words, we should depend on interfaces or abstract classes instead of concrete classes.
Example in PHP:
interface LoggerInterface {
public function log($message);
}
class FileLogger implements LoggerInterface {
public function log($message) {
// log message to file
}
}
class DatabaseLogger implements LoggerInterface {
public function log($message) {
// log message to database
}
}
class User {
private $logger;
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
public function create($data) {
// validation code
// save user to database
$this->logger->log('User created');
}
public function delete($id) {
// delete user from database
$this->logger->log('User deleted');
}
}
$user = new User(new FileLogger());
$user->create($data);
$user->delete($id);
In this example, the User
class depends on the LoggerInterface
instead of concrete classes such as FileLogger
or DatabaseLogger
. This makes the User
class more flexible because we can use any class that implements the LoggerInterface
. We can also easily switch between different loggers without changing the User
class code.
In conclusion, learning and applying SOLID principles in PHP programming can lead to more maintainable, extensible, and scalable code. These principles help us write code that is easier to understand, test, and modify, and also reduce the risk of introducing bugs or breaking changes in the future. By following these principles, we can write better quality software that meets the needs of our clients and users.
Here are some additional tips for applying SOLID principles in PHP programming:
- Use dependency injection to inject dependencies into classes instead of creating them inside the class.
- Use interfaces or abstract classes to define contracts that classes should adhere to.
- Favor composition over inheritance when designing class hierarchies.
- Use single responsibility principle to make classes and methods small and focused.
- Use open-closed principle to make your code extensible by allowing new features to be added without modifying existing code.
By following these tips and applying SOLID principles, you can write PHP code that is more maintainable, testable, and scalable. Remember that SOLID principles are not a set of rules to follow blindly, but rather guidelines to help you write better code. It is important to understand the principles and apply them in a way that makes sense for your specific situation.