Buy

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:

110 lines lib/Ship.php
... 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():

110 lines lib/Ship.php
... 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;:

116 lines lib/Ship.php
... 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:

118 lines index.php
<?php
... 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():

116 lines lib/Ship.php
... 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:

118 lines index.php
... 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:

118 lines index.php
... 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:

117 lines lib/Ship.php
... 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:

129 lines functions.php
... 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:

126 lines functions.php
... 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:

117 lines lib/Ship.php
... 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:

126 lines functions.php
... 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 embarrasing.

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!

Leave a comment!

  • 2016-03-03 weaverryan

    Ah, that's very nice! I agree - it's a really solid idea, especially as we keep developing the OO track. We have a tracker for course ideas - I've just added your comment to it.

    Thanks!

  • 2016-03-01 DevDonkey

    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. :)

  • 2015-11-17 weaverryan

    We don't have any specific details on *what's* been updated on a tutorial yet - but I've added that to our issue tracker (I'd like to show a log of what was updated and when). In this case, we made some minor changes to the course description - so nothing too interesting :).

  • 2015-11-17 Krzysztof Wolniak

    Yes :)

  • 2015-11-16 weaverryan

    Hey Krzysztof! Do you mean what has been updated on the tutorial - because you're seeing the new "updated" label on some tutorials?

  • 2015-11-16 Krzysztof Wolniak

    How can I check what exactly has been updated?