https://blog.devgenius.io/php-7-x-p36-function-arguments-f0b4c131ad1b
We’ll type hint with the inherited class, which is an overview of polymorphism in PHP. We’ll also pass the object as an argument to the constructor. The constructor will then set the property inside the class. This is called dependency injection . We’ll explore both of these topics in detail later, but this is a good overview. Let’s continue on from where we left off in the previous article, with our Car class example.
https://www.dinocajic.com/php-traits/
Recap : Review the following files since we’ll build on from here.
Our Car class extends the Vehicle class and uses a couple of Traits: Engine and Transmission. This article is about passing objects as arguments, so let’s get creative.
To begin, let’s create a couple of additional classes: Lamborghini and Ferrari. Each one of those classes will require the Car class.
<?php
require_once ("Car.php" );
class Lamborghini extends Car {
}
<?php
require_once ("Ferrari.php" );
class Ferrari extends Car {
}
We can now create our test class and instantiate those objects.
<?php
require_once ("Lamborghini.php" );
require_once ("Ferrari.php" );
$lamborghini_diablo = new Lamborghini (1999 , "Lamborghini" , "Diablo" );
$ferrari_f355 = new Ferrari (1996 , "Ferrari" , "F355" );
Do you think the above code will work? We haven’t added any constructor to our Lamborghini and Ferrari classes, but we’re passing arguments to the constructor. Believe it or not, it actually will work. Why? Because we’ve defined our constructor arguments within our Car parent class. If there’s no constructor within our child class, it will automatically pass arguments to our parent class. Let’s take a quick look at our Car class constructor.
<?php
require_once ('Vehicle.php' );
require_once ('Engine.php' );
require_once ('Transmission.php' );
class Car extends Vehicle
{
use Transmission , Engine {
Transmission ::check_oil_level insteadof Engine ;
Engine ::check_oil_level as check_engine_oil_level;
}
const HAS_HEADLIGHTS = true ;
const HAS_TAIL_LIGHTS = true ;
const HAS_TURN_SIGNALS = true ;
private int $fuel_type ;
private int $hp ;
private int $tq ;
private string $transmission ;
private string $vehicle_type ;
private bool $car_on ;
public function __construct (int $year ,
string $make ,
string $model ,
string $color = "" ,
int $fuel_type = 93 ,
int $hp = -1 ,
int $tq = -1 ,
string $transmission = "5 Speed Manual" ,
string $vehicle_type = "" ,
float $exterior_height = -1 ,
float $exterior_width = -1 ,
float $exterior_length = -1 ,
string $exterior_um = "in" ,
float $weight = -1 ,
string $weight_um = "lbs" ,
bool $car_on = false
)
{
$this ->year = $year ;
$this ->make = $make ;
$this ->model = $model ;
$this ->color = $color ;
$this ->fuel_type = $fuel_type ;
$this ->hp = $hp ;
$this ->tq = $tq ;
$this ->transmission = $transmission ;
$this ->vehicle_type = $vehicle_type ;
$this ->exterior_height = $exterior_height ;
$this ->exterior_width = $exterior_width ;
$this ->exterior_length = $exterior_length ;
$this ->exterior_um = $exterior_um ;
$this ->weight = $weight ;
$this ->weight_um = $weight_um ;
$this ->car_on = $car_on ;
}
}
What can we do now? Remember inheritance and traits that we just looked at? We have access to all of those properties and methods in our parent class (as long as they’re not private).
<?php
require_once ("Lamborghini.php" );
require_once ("Ferrari.php" );
$lamborghini_diablo = new Lamborghini (1999 , "Lamborghini" , "Diablo" );
echo $lamborghini_diablo ->check_engine_oil_level ();
$ferrari_f355 = new Ferrari (1996 , "Ferrari" , "F355" );
echo $ferrari_f355 ->check_oil_level ();
Great. We still haven’t done any passing of objects as arguments. Hold your horses. Wasn’t that an interesting introduction? No? Move on? Okay.
What we’re going to do is create a Driver class. The first method inside of our Driver class will be the drive() method since the Driver should be able to drive() a car. We can pass the Car object to it as an argument and then we can use various different Car methods to help us with driving the car. The two methods that we’ll focus on from our Car class is the turnOn() method and the drive() method.
<?php
class Driver {
public function drive (Car $car ) {
echo $car ->turnOn ();
echo $car ->drive ();
}
}
We can now go back to our test class and instantiate the object.
<?php
require_once ("Lamborghini.php" );
require_once ("Ferrari.php" );
$lamborghini_diablo = new Lamborghini (1999 , "Lamborghini" , "Diablo" );
$ferrari_f355 = new Ferrari (1996 , "Ferrari" , "F355" );
$dino = new Driver ();
$dino ->drive ( $lamborghini_diablo );
Woah. What just happened? I thought we specified that we have to pass a Car type object as an argument to the drive() method. It’s in the drive() declaration.
public function drive ( Car $car )
And here’s your lesson on Polymorphism. Since both our Lamborghini and Ferrari classes extend the Car class, technically they are Cars! Guess what? Since the Car extends the Vehicle class, technically the Lamborghini and Ferrari classes are Vehicles as well! You’re wondering if we can modify our declaration to say the following:
public function drive ( Vehicle $vehicle )
The answer is yes! You absolutely can. And you would still pass the $lamborghini_diablo or the $ferrari_f355 as an argument to the drive() method. If we pass the $ferrari_f355 to our drive() method, we get the following result: Ferrari F355 has been turned on. I’m driving.
Let’s do this a little differently now. Do you remember when we created class properties of int, string, etc. type? Just look at the Car class and you’ll see all of the various different properties. How did we initialize those properties? From the constructor. When instantiating the object, we would pass arguments to the constructor and then the constructor would initialize those properties with the values that were passed through as arguments. Same concept with what we’ll do here in our Driver class. We’ll specify that we’re going to have a Car based property and we’re going to pass a Car argument through our constructor and assign it to our Car property. Remember, a Car is just a data type like int or string .
<?php
class Driver {
private Car $car ;
public function __construct (Car $car )
{
$this ->car = $car ;
}
}
I don’t want to lose you here now. Remember that once the $car property has been initialized through our constructor, we’re going to have access to all of our Car properties and methods, such as turnOn() and drive() . Since we know that we must pass a Car type of object to our constructor and it must be assigned to our $car property, we can create methods in anticipation that we’re going to have access to those Car methods. So, let’s recreate our drive() method inside of our Driver class.
<?php
class Driver {
private Car $car ;
public function __construct (Car $car )
{
$this ->car = $car ;
}
public function drive () {
echo $this ->car->turnOn ();
echo $this ->car->drive ();
echo $this ->car->turnOff ();
}
}
Do you see how this works? Let’s run an example in our test file and then walk through the code.
Time for a thorough code walk through.
PHP enters the test class and requires the Lamborghini, Ferrari, and Driver classes.
A new Lamborghini object is instantiated, which is of type Car.
Since the Lamborghini class does not contain a constructor, the parent constructor is called and the three arguments are passed to the parent constructor: 1999, Lamborghini, Diablo.
A new Ferrari object is instantiated, which is also of type Car.
Since the Ferrari class does not contain a constructor, the parent constructor is called and the three arguments are passed to the parent constructor: 1996, Ferrari, F355.
A new Driver is created and the $ferrari_f355 object is passed to the constructor as an argument.
The Driver class initialized the $car property inside itself with the $ferrari_f355 object that was just passed to it. The $car property now has access to all of the methods inside of $ferrari_f355.
The drive() method is invoked.
PHP enters the drive() method. It sees that it’s calling 3 different methods all located within the $car property, which is technically the $ferrari_355 object.
PHP executes the turnOn(), drive(), and turnOff() methods.
A new Driver is created inside of the test file and the $lamborghini_diablo object is passed to the constructor as an argument.
The Driver class initialized the $car property inside itself with the $lamborghini_diablo object that was just passed to it. The $car property now has access to all of the methods inside of $lamborghini_diablo.
The drive() method is invoked.
PHP enters the drive() method. It sees that it’s calling 3 different methods all located within the $car property, which is technically the $lamborghini_diablo object.
PHP executes the turnOn(), drive(), and turnOff() methods.
This passing of an argument through the constructor and assigning it to a property is what’s called Dependency Injection.
As long as you visualize the flow of how the argument moves through the code, I’m confident that you’ll understand dependency injection. Complicated name for a simple topic.