Traits

Serenading Complexity into Elegance with Harmonious Traits

There is a fundamental problem in programming languages that support Multiple Inheritance. PHP is not one of those languages, but let’s walk through it anyways just so that you understand the issue.

Assume there is a parent class that contains a specific method, such as drive. There are two children classes, class 1 and 2, and they each extend the parent class. They inherit the drive method and they override it.

Now, PHP doesn’t support what I’m about to tell you since this is a multiple inheritance issue. Another class, class 3, extends both class 1 and class 2. How does it know which drive method to use? Which method will it be inheriting? The one from class 1 or the one from class 2.

So that’s the multiple inheritance problem. There are times when you should extend from more than one class. So far, we can implement multiple methods, but we can’t extend the functionality of multiple classes. Traits are there to solve this issue. Just like abstract classes, you can’t instantiate Traits, but you can inherit them.

Recap: We haven’t done the Car classes in some time. Let’s take a look at the ones that we’ll do.

To begin, let’s create our test file.

<?php
// Object Arguments
require_once("Car.php");
$subaru = new Car("2019", "Subaru", "WRX STi");

Our Car class extends the Vehicle class, which means that it inherits all of the properties and methods from the Vehicle class. The Car class itself has a few constants, properties, and methods. Most of the methods are just getters and setters with the exception of a couple of methods such as turnOn() and turnOff().

If we step back and start thinking about what every gasoline powered car has, that’s an Engine and a Transmission. Instead of loading everything into our Car class, we’ll create separate Engine and Transmission classes. Why Engine and Transmission? We can go through everything within a car, but we’ve just chosen these as simple examples.

Realistically, any object within a car can have its own class with properties. Think about a seat inside of a car. It represents a specific object. It has specific details such as dimensions and material type, so it needs to be its own separate object. We want to use that object and implement it within our Car class. Even though it doesn’t make sense to inherit properties from a Seat, even if we did, we couldn’t extend multiple classes. Just to prove it to ourselves, let’s try it out.

<?php
require_once('Vehicle.php');

class Engine {
  //...
}

class Transmission {
  //...
}

class Car extends Vehicle
{
  //...
}

If we wanted to extend the Engine class, we can’t do something like this:

class Car extends Vehicle, Engine { … }

Your editor will automatically highlight the Engine and say that it is incorrect syntax. So what can we do? First let’s add a method to the Engine class and change the class to a Trait since the Engine is technically a trait of a car.

<?php
trait Engine {

    public function check_oil_level() {
        echo "Engine oil level good";
    }
}

Let’s do the same for our Transmission Trait.

<?php
trait Transmission {

    public function check_oil_level() {
        echo "Transmission oil level good";
    }
}

Both the Engine and the Transmission traits have the same method name, check_oil_level(). The Engine Trait method returns “Engine oil level good” and the Transmission Trait method returns “Transmission oil level good.”

Great, how do we use the traits within our Car class now? With the use keyword. Let’s build up on this like we normally do and start simple, by implementing the Transmission trait and checking the transmission oil level.

<?php
//...
require_once("Transmission.php");

class Car extends Vehicle {
  
  use Transmission;
  
  //...
}

$subaru = new Car("2019", "Subaru", "WRX STi");
$subaru->check_oil_level();

We should get the following response: “Transmission oil level good.”

We can still call methods from our Vehicle parent class by calling the method name like normal.

echo $subaru->getYear(); // example from Parent class

Let’s move to the next step by adding our Engine trait to the Car class.

We get the following error: Trait method ‘check_oil_level’ will not be applied, because it collides with ‘Transmission.’ If you still wanted to give it a shot, refresh your page and you’ll get the following error.

How do we get around this? We have to tell PHP which check_oil_level() method we want to use. How can we do that? With special syntax. Once you use both of the traits, add curly braces and use the insteadof keyword. I want to use this method instead of this other method.

<?php
//...
require_once("Transmission.php");
require_once("Engine.php");

class Car extends Vehicle {
  
  use Transmission, Engine {
    Transmission::check_oil_level insteadof Engine;
  }
    
  //...
}

$subaru = new Car("2019", "Subaru", "WRX STi");
$subaru->check_oil_level();

Now when we call the check_oil_level(), PHP knows which method to call. Great, that solves one problem. But what if we wanted to check our engine oil as well? You’ll just have to rename one of them within the Car class. Our Transmission check_oil_level() will be used as the default. Our Engine oil check will be named check_engine_oil_level().

Calling our methods now yields the proper results. That’s it when it comes to Traits. If you start receiving errors, it’s most likely due to method name collisions, which you now know how to redefine.

Interfaces

DESIGNING ELEGANT SOLUTIONS THROUGH INTERFACE MAGIC

PHP – P61: INTERFACE

The topic of interfaces. An interface resembles an abstract class but there are a few differences. With an interface, you can only define the functionality.

Traits

Serenading Complexity into Elegance with Harmonious Traits

PHP – P62: Traits

There is a fundamental problem in programming languages that support Multiple Inheritance. Traits solve that problem.

Object Arguments

CONVEYING INSIGHTS BY PASSING OBJECTS AS MESSENGERS

PHP – P63: OBJECTS ARGUMENTS

We’ve looked at passing numerous different data types as arguments to methods, but we haven’t looked at passing objects as arguments to methods.

Leave a Reply