In the world of PHP, it seems like there has been a flurry of different inheritance and contract patterns being used. Unfortunately, a lot of developers seem to be running around like headless chickens, making things far more complicated than they need to be. It seems like traits and interfaces are all the rage, while using traditional abstract classes or generic class inheritance is now somehow a bad taste in the PHP community. This is a big problem honestly, because a lot of the day to day development we rely on couldn't work with just traits and interfaces. This being said, traits and interfaces have their place too. So, in a way to clarify some of the confusion, I hope to explain the some of the use cases for type of inheritance and what they actually are doing.

Class

It's a bit hard to describe OOP inheritance without first describing the class implementation in PHP. A class is a mutable object with visibility constraints of both properties and functions. In plain English, this means that a class is an object that can change, be created multiple times and has visible and hidden functions and properties. Think of classes as completed cars, everything is working and you can make a ton of them if you want to. While classes are the last leaf in our inheritance tree, we have to understand them because they are the end goal. At run time we want our class to have all of the functions and properties all in a row and ready for action. To do this, we can use a few methods of inheritance.

Inheriting Another Class - extends

While not always the most kosher of inheritance types, there are times where you want to inherit from a completely functional class. An example of this in Laravel is Illuminate\Http\Request which extends Symfony\Component\HttpFoundation\Request. Sure, the Symfony Request class is completely functional and incredibly powerful, but, by extending it: Laravel is able to add properties, override methods, and even include new ones to make the Request object easier to work with. In our inheritance tree, any time we need a Symfony Request object, we should be able to use Laravel's Request. It is a direct child. This is a bit like saying you like your friends car, but you really want to have it in red instead of blue, and when you hit the horn you want the lights to flash. What's great is that you can keep this going and a different class could eventually extend Laravel's Request and be a substitute for the Symfony Request class.

Abstract Classes

There are times where you want to write all of the shared code for a class, but you need an actual implementation to be made in order for things to go smoothly. In this case, you want an abstract class. Abstract classes give implementations of properties and functions while saying the parent class can't actually be implemented. In our car analogy, the abstract class is a bit like a concept car, you can't actually drive the concept but you can have a production car that is based on it. In Laravel, we're used to extending abstract classes all the time, whether you realize it or not. Controllers and Models all extend Illuminate\Routing\Controllers\Controller or Illuminate\Database\Eloquent\Model which are abstract classes.

Abstract Methods

On cool thing about abstract classes is that they can include abstract methods. Abstract methods allow for implementations of some functionality, while leaving other functionality up to the classes that extend it. And abstract methods have to be implemented in the child class! It's a bit like our concept car designers knowing that there will have to be some sort of airbag deployment procedure, but since it is clearly going to be different in the US than it will be in Europe, we'll just leave it up to those design crews to make something that works for them and not waste time making something that will just be thrown out.

Interfaces

Now we get to the cool things that seem to be making everyone's head explode these days. Interfaces are just saying here's a set of functionality that needs to happen and here's what needs to be spit out of the other side. If it sounds a bit like abstract methods, you'd be right! However, the difference between an interface and an abstract class is that interfaces provide absolutely no inheritance or implementation. When you implement an interface, the interface is simply saying "here's some things that need to get done". In our car analogy, interfaces are the blueprint or the feature list, nothing more.

Traits

Traits are an interesting bit of kit in programming. Honestly, I went for 9 years without touching a single trait. But, I see them being used where a more traditional parent relationship would work a good deal better. All traits are is a way of taking a bunch of code and copying and pasting it before the compiler sees our final class. More importantly, traits usually should be pretty small. An example of this is the User model traits in App-Toolkit, while the BaseModel trait is pretty much everything you need in one line of code, it is actually broken down into small traits that only implement one or two functions which allows for massive customization. For our car, traits are a bit like the features or options packages that you get in a car: want to upgrade the stereo, well we've got a less painful way than you having to rip out all the wires and do it yourself!

I hope this clarifies a bit of what the different inheritance techniques in OOP are and when to use them. If you have questions, feel free to ask them in the comments.