OOP: Composition & Inheritance in PHP

Inheritance is one of the primary tenets of object-oriented programming languages, but each language implements it at least slightly differently. I’m coming from a background in Python, and it took me a bit to understand the different ways objects can extend or inherit behavior in PHP. Here is what I have learned!

Extends

The extends keyword in PHP is inheritance as I knew it — one class inherits behavior from another, but can extend and modify it. The PHP docs are here. Classes at all levels of this hierarchy can be instantiated.

In addition to adding new behavior, subclasses can modify existing behavior by calling its parent. Let’s take a look at an example.

class Clothing {    private $size;
private $description;
public function __construct(int $size, string $description) {
$this->size = $size;
$this->description = $description;
}
public function getSize() : int {
return $this->size;
}
public function getDescription() : string {
return $this->description;
}
}class Pants extends Clothing { public function getDescription() : string {
return 'A pair of pants: ' . parent::getDescription();
}
}

This is a basic example where we have one layer of inheritance — Pants are a piece of Clothing. From this example, we can see that an instance of the Pants class can use getSize from Clothing, and apply modifications to getDescription by using parent.

php > $p = new Pants(10, 'blue and cozy');
php > var_dump($p->getDescription());
string(30) "A pair of pants: blue and cozy"
php > var_dump($p->getSize());
int(10)

Interfaces

The idea of interfaces were new to me — you can read the documentation here. Interfaces define the methods and properties that a class needs to implement, but doesn’t actually implement them. This includes their access levels (public or private or protected) and the types of the inputs and outputs. Names of the functions must match, in addition to signatures and types. Interfaces provide good protection for developers, to ensure that different objects we want to be able to use interchangeably will all have the same call signatures. For this reason, interfaces are not mutable (you can’t redefine a method on a class that uses an interface, and give it a different signature).

Classes can use an interface with either extends or implements, and unlike with class inheritance, can implement more than one (though you can only apply more than one using implements - you can write implements Interface1, Interface2 and so on).

It’s also worth noting that you can define constants on interfaces — if all the classes that will implement an interface need to share constant values, but true class inheritance is overkill, you can still do that with interfaces. Constants on an interface that a class implements can be accessed using self::

A class can both extend another class (inheritance) as well as implement an interface by writing extends <Class1> implements <Interface1>, <Interface2>.

Traits

Traits in PHP are not technically inheritance — they are composition. This allows for code reuse and typically defines a “has a” relationship vs. the “is a” relationship defined by inheritance.

Traits allow you to define methods and implement their behavior and reuse those pieces of logic in other classes without any hierarchy or assumed relationship between different objects that use a given trait, and importantly, with the ability to use as many as you like. Traits can also use other traits. Let’s add on to our example from above:

class Clothing {
private $size;
private $description;
public function __construct(int $size, string $description) {
$this->size = $size;
$this->description = $description;
}
public function getSize() : int {
return $this->size;
}
public function getDescription() : string {
return $this->description;
}
}
class Pants extends Clothing {
use Cotton;
public function getDescription() : string {
return 'A pair of pants: ' . parent::getDescription();
}
}
class Furniture {
public function __construct(int $length) {
$this->length = $length;
}
}
trait Cotton {
public function getWashingInstructions() {
return "washing instructions here";
}
}
class Couch extends Furniture {
use Cotton;
}

We’ve added a new type of item (Furniture), and a specific thing that is a type of furniture (Couch). Couches and pants can share a common feature though: they have a material: Cotton, in this case.

Another important difference between traits and classes that can be inherited is that, while all classes with a __construct method can be instantiated, even those that are inherited by other classes, traits themselves cannot be. They can only be used by classes, which can be instantiated. So in our example above, you could instantiate a Pants instance or a Clothing instance, but you can’t instantiate an instance of Cotton.

A class can implement as many different traits as you like, as long as more than one doesn’t implement the same. function. For example, if we tried to define another trait with the getWashingInstructions method, and tried to apply it to our Couch class as well, we’d see an error that looks like this:

Fatal error: Trait method getWashingInstructions has not been applied, because there are collisions with other trait methods on Couch

You can however, redefine the method on the class itself, and override the behavior entirely. Because there is no hierarchy, if you need to extend the functionality of a trait, you need to do it a bit differently than you would if you needed to extend a function on a class that’s inherited, as we discussed above. I thought this was a great explanation of how to extend a trait’s functionality. Unlike interfaces, traits are mutable — you can change method visibility, names, etc. This means that you can’t define constants on traits the way you can on interfaces — that could lead to some unexpected behavior.

Not covered in this post: namespaces. Namespaces can help you organize your code, and are an important concept in Object Oriented programming, but are beyond the basics of inheritance in PHP, so not covered here!

Senior Software Engineer | www.adriennedomingus.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store