SOLID Principles in PHP
We will look up SOLID in PHP in this article
Today i want to explain SOLID principles and how we use them in PHP, firstly lets look closer to those principles.
Single Responsibility
In real life every material has only one ability. As you know you can not make a cup of coffee with your computer. :) Or you can not use a book instead of notebook. Every material is for only for one task. This principle says that every method or class should have only one task. And it will do only that task. You can not add ‘change stock status’ method in a mailer class. Or we should not add another action in fetching database records method.
Every class, method should have only one responsibility.
I guess this is enough to get main point. Let’s look up this from php side.
As you can see we fetch record from db and we have a loop which sends email to users. What was the name of method ? getRecords and what should it do? Fetching Records . Does it? That’s a big NO!. Let’s refactor and see what we should do…
Voila! All methods has only one task to do.. But hold on.. Does this class break the SRP? No, but what if we defined a method which has ability of sendMail method of Mail class? Yeah that’s break the rule…
Open/Closed Principle
Software entities should be open for extension but closed for modification. The image above explain this principle in very good way.
Let’s think that we have shipping methods and integration of this method. And your lead dev comes you and add functionality of X shipping method too? Will you add new integration codes in same file? Nooo!!!!
We should use interfaces to implement new classes and abstract classes to add new abilities to our class.
You can extend those classes but you can not modify those to add more functionality. You should use new implementations to add those abilities.
Benefits of the Open / Closed Principle
- Extend the functionalities of the system, without touching the core of the system.
- We prevent breaking parts of the system by adding new functionalities.
- Ease of testing.
- Separation of the different logics.
Liskov Substitution Principle(LSP)
If S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program.
The Liskov Substitution Principle states that any class that is the child of a parent class should be usable in place of its parent without any unexpected behaviour. This means we have a parent class and we derived 3 sub classes from parent. All Child Classes should work as parent. If x method of parent class return array we can not return object in child class.
We have 4 criteria to check..
- Method signature must be same. All parameters with same method should be same. For example A child class X method has Order $order parameter and B child class X method has Shipping $shipping .. this break LSP both of them should have Order $order which is defined by parent.
- Not to strengthen pre-conditions : Pre-conditions of a child class method can’t be greater then it’s parent.
Any inherited method should not have more conditionals that change the return of that method, such as throwing an
Exception
3. Not to weaken post-conditions :
Inherited methods should return the same type as that of it’s parent
4. Exception types should be same.
A child class X method return FileNotFoundException by parent B child should return same exception. B can not return HttpNotFoundException.
Interface Segregation Principle (ISP)
Basically, what this principle refers to is that we should not create classes with thousands of methods where it ends up being a huge file. Since we are generating a monster class, where most of the time we will only use some of its methods each time.
This means make small classes with specific actions. At first year of my career. I was proud of my class that can do everything :) But lately i understand that it is not maintainable and that is not an option for me any more. Write your classes with what you need in it for special purpose. Not combine everything in it.
The image above summarize the principle in such a good way. We do not need a universal charger that charge every device. We need a mobile charger for our phone and notebook charger for our notebook.
interface ShippingInterface
{
public function connect();
public function despatch();
public function test();}class DHL implements ShippingInterface
{
public function connect()
{
$this->client = $this
->connector->get('https://dhl.com/api'); }
public function despatch()
{
return 'despatched';
}
public function test()
{
return 'testing in localhost';
}
}class Fedex implements ShippingInterface
{
public function connect()
{
$this->client = $this->connector->get('https://dhl.com/api');
}
public function despatch()
{
throw new Exception('Opps! an error');
}
public function test()
{
return 'testing in test server';
}
}
As you can see the despatch method of Fedex class is returning exception which is different from interface that we implement. This should be same with the parent class.
Dependency Inversion Principle (DIP)
Is it something like dependency injection? No , Dependency Injection is a design pattern but Dependency Inversion is a principle. They are not the same thing.
High-level modules should not depend on low-level ones. Both should depend on abstractions
public function shipped()
{
$orders = new Orders();
$shippedOrders = $orders->where('status', 'shipped')->get();
return response()->json(['shippedOrders' => $shippedOrders]);
}
}
The shipped method above looking fine to work but it breaks dependency inversion principle. Why?
- Can we easily test this?
- Is it reuseable?
- Does it do only one particular job?
All answers are No!
We have Eloquent dependency here what if we wanna use another ORM? Thats sucks right? Dependency Inversion says that your methods , classes should not be depend on one thing, seperate that with an interface and just like repository pattern write reuseable , not dependent classes to one thing.
Thank you for reading my article , i want to hear your comments below.
See you on next article