Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

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.

Start your All-Access Pass
Buy just this tutorial for $6.00

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 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!

Leave a comment!

16
Login or Register to join the conversation
Default user avatar
Default user avatar DevDonkey | posted 5 years ago

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

1 Reply

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!

1 Reply

How can I check what exactly has been updated?

1 Reply

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

Reply

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

1 Reply
György C. Avatar
György C. Avatar György C. | posted 2 years ago

Hey guys!

I'm just wondering where does '$_GET['error']' come from in the index.php?

Reply

Hey György C.!

Fair question! Check out the battle.php file - you'll see it there. Basically, if a form is POST'ed to battle.php and the form is missing some data, we redirect back to /index.php?error= with the error code. We built this out in our PHP intro course (but didn't explain it here). Let me know if I an offer more explanation.

Cheers!

Reply
György C. Avatar
György C. Avatar György C. | weaverryan | posted 2 years ago | edited

Hey weaverryan !

I see the code in battle.php, thanks.
Do you have a course in that you build up this project from the ground up to the point where the oop part begins?
I've only found the projet for "pet's listing website".

Anyway, I really appriciate all the work you are doing on 'symphonycasts'.

Best!

Reply

Hey György C. !

Yea know what? I was totally wrong (bad memory!). This is the *first* course where we use this code: so there is no course where we build this app from scratch. Sorry about that! The PHP track - https://symfonycasts.com/tr... - builds a different project, but that's where we cover the concepts in the "starting" code for this course.

If you have any questions about the code along the way (like you already asked), we'd be happy to do our best to answer them :).

Cheers!

Reply

Hi there. Is it ever likely that the Ship properties are going to be 0? When would it be more beneficial to use setters for these properties rather than via the constructor? It would seem you can do both so there must be some underlying principal as to when to choose either approach?

Thank you :)

Reply

Hey ThingyBob,

Usually, if some object properties are required to be set - you can make it setting in the constructor. Because, if you do not pass required arguments into constructor - the object won't be created and you'll get a fatal error. But if some properties are optional during instantiating the object - you may use setters to be able to set them later if necessary. Basically, you can think of it this way. So, if you want sure that some fields are set during object creating - do it in its constructor. of other properties that you need to set after object was created, e.g. during its modification - sue setters.

And we set some properties to 0 just to give some reasonable defaults. We just want to make sure that those properties are "numbers" not "null".

I hope it makes sense to you.

Cheers!

Reply
Meike H. Avatar
Meike H. Avatar Meike H. | posted 3 years ago

Hi there,
I would like to know how you get these nice formatted var_dump output.
When I use var_dump($somevariable); die; and view it in firefox or chrome I get unstyled stuff in one long single line.

Reply

Hey harm2o,

Good questions! :) Yeah, it's a special PHP extension called Xdebug - you can install it and your output will be similar the same as ours. Or, you can look into a Symfony component called VarDumper: https://symfony.com/doc/cur... - but you can use it outside of Symfony projects as well, it just has a nice integration with Symfony web developer toolbar.

Cheers!

Reply
Meike H. Avatar

Hi Victor,
thanks for your reply. I installed xdebug.
Now I get these nice colorful messages :)

Reply

Hey Harm2o,

Awesome! It has a lot of good things.. but take a look at the Symfony's VarDumper as well, it really nice and helps even better in Symfony apps ;)

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

userVoice