The Constructor!
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
Here's the next challenge, sometimes I want my ships to be broken. So when
a ship comes in it might be able to fight or it might be under repair. This
is a property on a Ship
, a Ship
could be under repair or not. So I'll
add this as a new private property that has a boolean which can be true or
false:
// ... lines 1 - 2 | |
class Ship | |
{ | |
// ... lines 5 - 12 | |
private $underRepair; | |
// ... lines 14 - 108 | |
} |
The challenge is that whenever we create our objects inside of functions.php
,
I want to randomly set a Ship
to be under repair or not under repair, and
I want it to happen automatically. So just by creating a Ship
, I want it
to internally figure out if it is under repair or not.
public function __construct
This is where the idea of a constructor comes in. Whenever you create an
object, you can actually hook into that process and say: "Hey whenever a
Ship
is created, call this function so I can set some stuff up." The way
you do this is by creating a very special public function inside of your
class called __construct()
:
// ... lines 1 - 2 | |
class Ship | |
{ | |
// ... lines 5 - 14 | |
public function __construct() | |
{ | |
echo 'Automatically called!'; | |
} | |
// ... lines 19 - 108 | |
} |
The magic here is that name: it must be __construct
. And just by having
this it should be called everytime we say new Ship()
.
Let's try it, refresh! And that's it: it's called four times, once for each
of our ships. And it's called right when you say new Ship()
, so if I throw
in a die()
statement right after creating a Ship
, we're still going to
see one of those called.
Setting up Data in __construct
Now we have a really powerful way to set up our data. Internally we can determine
whether or not the Ship
is under repair. We'll use $this- underRepair = mt_rand(1, 100) < 30;
:
// ... lines 1 - 2 | |
class Ship | |
{ | |
// ... lines 5 - 14 | |
public function __construct() | |
{ | |
// randomly put this ship under repair | |
$this->underRepair = mt_rand(1, 100) < 30; | |
} | |
// ... lines 20 - 114 | |
} |
This gives each ship a 30% chance of being broken...maybe a wing fell off!
To see this in action, let's cheat real quick and var_dump
the $ships
array. When we refresh we can see the first two ships are ready for action
but the third one isn't. Refresh again and they're all ready to fly. And
a third time shows that the first two are busted and the other two are
battle worthy. So that's working already!
Don't Create a Getter Function
Next, let's go into index.php
and up top we have our table information.
Let's include status which will tell us if our ship is under repair or not:
// ... lines 2 - 56 | |
<thead> | |
<tr> | |
<th>Ship</th> | |
// ... lines 60 - 62 | |
<th>Status</th> | |
</tr> | |
</thead> | |
// ... lines 66 - 118 |
Now so far, we don't have a way to access that new property. It's private
and we don't have a getter or a setter and you don't necessarily need to
create these. In fact, we don't want a setter: it's being set automatically
inside of the class itself. But I do want to figure out if this Ship
is
functional or not. So what I'll do is create a new public
function and I'll call it isFunctional()
:
// ... lines 1 - 2 | |
class Ship | |
{ | |
// ... lines 5 - 20 | |
public function isFunctional() | |
{ | |
return !$this->underRepair; | |
} | |
// ... lines 25 - 114 | |
} |
This will be the opposite of the underRepair
value. If it is underRepair
,
then it is not functional and if it is functional then it is not underRepair
.
For the outsider whose going to be calling this function, they don't care
what we're doing internally to figure that out.
Let's go back to index.php
and create a nice if statement. If $ship
is
functional, else
, and we'll put some adorable icons:
// ... lines 1 - 66 | |
<?php foreach ($ships as $ship): ?> | |
<tr> | |
// ... lines 69 - 72 | |
<td> | |
<?php if ($ship->isFunctional()): ?> | |
<i class="fa fa-sun-o"></i> | |
<?php else: ?> | |
<i class="fa fa-cloud"></i> | |
<?php endif; ?> | |
</td> | |
</tr> | |
<?php endforeach; ?> | |
// ... lines 82 - 118 |
A sunshine for functional and a sad cloud for not functional.
Refresh and try it out, four sunshines and one cloud. Awesome!
Leveraging isFunctional() Like a Boss
Now it's really easy to do the next step. If a Ship
is under repair, I
don't want it to show up in this select menu. It's easy because we can just
call isFunctional
and it will take care of all the internal stuff for us.
Down here we only want to print this out if the ship is in working order.
And the same thing down here:
// ... lines 1 - 91 | |
<?php foreach ($ships as $key => $ship): ?> | |
<?php if ($ship->isFunctional()): ?> | |
<option value="<?php echo $key; ?>"><?php echo $ship->getNameAndSpecs(); ?></option> | |
<?php endif; ?> | |
<?php endforeach; ?> | |
// ... lines 97 - 103 | |
<?php foreach ($ships as $key => $ship): ?> | |
<?php if ($ship->isFunctional()): ?> | |
<option value="<?php echo $key; ?>"><?php echo $ship->getNameAndSpecs(); ?></option> | |
<?php endif; ?> | |
<?php endforeach; ?> | |
// ... lines 109 - 118 |
Cool! Refresh, all sunshines. Refresh again -- there's a cloud. It looks like we're missing the Cloakshape Fighter due to repairs. And when you check the list, it isn't there! Perfect!
Adding Arguments to __construct
The __construct()
function is something you are going to see a lot but
it's a really easy idea. It just says if you have a function called __construct()
,
then it's automatically going to be called when you instantiate your object.
There is one other thing you can do: like most functions, it can have an
argument. Let's put a $name
argument here:
// ... lines 1 - 14 | |
public function __construct($name) | |
{ | |
// ... line 17 | |
// randomly put this ship under repair | |
$this->underRepair = mt_rand(1, 100) < 30; | |
} | |
// ... lines 21 - 117 |
I'm not going to use it yet because I'm going to show you what happens when we do that.
Go back to functions.php
. You can see that my editor is angry because it
says required parameter $name
missing:
// ... lines 1 - 8 | |
$ship = new Ship(); | |
// ... lines 10 - 129 |
So, you notice whenever we create a new Ship
object, it's always Ship()
,
but you never pass anything in there. When you create an object, the stuff
that goes in between the parenthesis are arguments that are passed to your
__construct()
function, if you have one. Because we have a $name
argument
here, now we need to pass a name there, just like that:
// ... lines 1 - 8 | |
$ship = new Ship('Jedi Starfighter'); | |
// ... lines 10 - 126 |
Now you can see that it is happy.
And what we can do inside of Ship.php
is say, ok whatever $name
they
pass in, let's just set that to the name
property:
// ... lines 1 - 14 | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
// randomly put this ship under repair | |
$this->underRepair = mt_rand(1, 100) < 30; | |
} | |
// ... lines 21 - 117 |
In functions.php
, we don't have to call setName()
anymore: we're passing
it into the constructor and the name is being set that way. Let's update
the other ones as well:
// ... lines 1 - 8 | |
$ship = new Ship('Jedi Starfighter'); | |
//$ship->setName('Jedi Starfighter'); | |
// ... lines 11 - 15 | |
$ship2 = new Ship('CloakShape Fighter'); | |
// ... lines 17 - 21 | |
$ship3 = new Ship('Super Star Destroyer'); | |
// ... lines 23 - 27 | |
$ship4 = new Ship('RZ-1 A-wing interceptor'); | |
// ... lines 29 - 126 |
And we're good to go!
When to Pass Values to __construct
So, why would you do this? Why would you add a $name
argument to the Ship's
constructor and force it to be passed in versus the setter? It's really up
to you. In our case, it doesn't make sense to have a Ship
without a name.
And before, that would have been possible had we just instantiated a new
Ship
and forgotten to call setName()
. Then we would have been running
around with a Ship
object that had absolutely no name. How embarrassing.
Sometimes, when you have required information, you might choose to set them
up as arguments to your constructor. It says "Hey, when you create a Ship
,
you have to pass me a name." We're not forcing the user to pass a weaponPower
,
jediFactor
or strength
, because those all have a nice default value of
0. So it makes sense not to force those, but we do force the name.
When you back up I just want you to realize that the __construct
function
is just like any other function. But if you give it that special name, it
is automatically called and the arguments are passed to it.
And guess what! You just learned the fundamentals of object-oriented programming. Classes, check! Objects, super check! Methods, privacy, type-hinting, constructor and other stuff - all old news now. And there's even more great stuff to learn, like service classes, dependency injection, inheritance and interfaces. These will make you even more dangerous, and will also help you understand the outside libraries you use everyday. So keep going, and join us for episode 2.
Seeya next time!
I wonder if it would be possible that some tutorials around OOP design patterns could be considered (angled at PHP)? There is lots on the web, but most of it is written in hard to understand language with very poor examples. It would be awesome to have something written in KNP-speak that everyone can understand. Just an idea. :)