Buy

This play.php file is cute, but it's not our real application. we did make this nice Ship class, so let's use. It'll clean up our code and give us more power. Sounds good to me!

Moving Ship into Ship.php

But first, the Ship class lives inside play.php, and this is just a garbage file we won't use anymore. Usually, a class will live all alone in its own file. Create a lib/ directory, and a file called Ship.php. There's no technical reason why I called the directory lib/, it just sounds nice. And the same goes for the filename - we could call it anything. But to keep my sanity, putting the Ship class inside Ship.php makes a lot more sense than putting it inside of a file called ThereIsDefinitelyNotAShipClassInHere.php. So even though nothing technical forces us to do this, put one class per file, and then go celebrate how clean things look.

Go copy the Ship class, and put it into Ship.php:

50 lines lib/Ship.php
<?php
class Ship
{
public $name;
public $weaponPower = 0;
public $jediFactor = 0;
public $strength = 0;
public function sayHello()
{
echo 'Hello!';
}
public function getName()
{
return $this->name;
}
public function getNameAndSpecs($useShortFormat)
{
if ($useShortFormat) {
return sprintf(
'%s: %s/%s/%s',
$this->name,
$this->weaponPower,
$this->jediFactor,
$this->strength
);
} else {
return sprintf(
'%s: w:%s, j:%s, s:%s',
$this->name,
$this->weaponPower,
$this->jediFactor,
$this->strength
);
}
}
public function doesGivenShipHaveMoreStrength($givenShip)
{
return $givenShip->strength > $this->strength;
}
}

Don't put a closing PHP tag, because you don't need it. PHP will reach the end of the file, and close it automatically.

I'll head back to play.php, just like when you have functions in an external file you have to require that file to have access to it. So we'll require once __DIR__'/lib/Ship.php':

40 lines play.php
<?php
require __DIR__.'/lib/Ship.php';
... lines 4 - 40

The __DIR__ is a constant that points to this directory. So this makes sure that we're requiring exactly /lib/Ship.php relative to this file.

Get rid of requires?

If you're familiar with modern apps you'll notice that they don't have this require statement, we'll talk about that in the future. There is a way called autoloading to not even need require statements. But for now we do need it.

So now that we've moved that out let's refresh. Well look at that, it still works!

Creating Ship Objects... for Real

So now that we have this Ship class inside of a Ship.php file we can start using it from within our real application. From our get_ships() function, I don't want to return this array inside of an array thing anymore. I want to do awesome things like return objects.

We'll start with adding our require statement:

107 lines functions.php
<?php
require __DIR__.'/lib/Ship.php';
... lines 5 - 107

Next, let's transform our brave starfighter into a Ship object. We do that with $ship = new Ship(); and then we'll just start setting the details: name = 'Jedi Starfighter', weaponPower of 5, jediFactor of 15 and strength of 30:

107 lines functions.php
... lines 1 - 4
function get_ships()
{
$ships = array();
$ship1 = new Ship();
$ship1->name = 'Jedi Starfighter';
$ship1->weaponPower = 5;
$ship1->jediFactor = 15;
$ship1->strength = 30;
$ships['starfighter'] = $ship1;
return $ships;
... lines 17 - 45
}
... lines 47 - 107

Perfect!

I'm commenting out this bottom array, we're not going to use that at all anymore. Instead we're going to return an array with just this one ship in it, we'll add more to our fleet later.

Remember, we're calling this back in index.php, in that file we call get_ships(); that use to return an array of ship arrays. Now it returns an array of Ship objects, of which there will only be the one starfighter. Let's var_dump this to see what it looks like:

107 lines index.php
<?php
require __DIR__.'/functions.php';
$ships = get_ships();
var_dump($ships);die;
... lines 6 - 107

Take off the file name, so we load index.php:

http://localhost:8000

And there it is! We have an array with one item in it which is our Ship object. Look at those sweet spaceship stats.

Treat that Object like an Object (->)

Let's take that var_dump off and see what that does to our app. When we refresh we see an exciting error that tells us we cannot use object of type Ship as array on line 68. This is an error that you might see, so let's see what's happening on line 68:

107 lines index.php
... lines 1 - 66
<?php foreach ($ships as $ship): ?>
<tr>
<td><?php echo $ship['name']; ?></td>
<td><?php echo $ship['weapon_power']; ?></td>
<td><?php echo $ship['jedi_factor']; ?></td>
<td><?php echo $ship['strength']; ?></td>
</tr>
<?php endforeach; ?>
... lines 75 - 107

Ok, we're using $ship['name']. Before when each ship was an array, that made sense, now we know when you reference an object you need to use an arrow. So if you do have an object and you try to use the square bracket syntax that is the error that you will see. Lucky you!

I don't want to keep seeing errors so let's fix the other ones as well:

106 lines index.php
... lines 1 - 65
<?php foreach ($ships as $ship): ?>
<tr>
<td><?php echo $ship->name; ?></td>
<td><?php echo $ship->weaponPower; ?></td>
<td><?php echo $ship->jediFactor; ?></td>
<td><?php echo $ship->strength; ?></td>
</tr>
<?php endforeach; ?>
... lines 74 - 106

Awesome!

Head back to the browser and refresh and things are looking kinda better! Sweet! Well, at least we have a different fatal error in our dropdown here, cannot use object of type Ship as array, we'll fix that in a second.

Back in our editor, because this is an object we can use our methods on it. In our Ship class we have this getName() method:

50 lines lib/Ship.php
... lines 1 - 2
class Ship
{
... lines 5 - 17
public function getName()
{
return $this->name;
}
... lines 22 - 48
}

Down here let's switch out name for getName();

106 lines index.php
... lines 1 - 67
<td><?php echo $ship->getName(); ?></td>
... lines 69 - 106

When we refresh, we see it does the exact same thing.

Now, let's fix this little error we have in the select menu. In index.php you can see it's the same thing as before, we're using the $ship like an array, so change this to use getName(); here and down there as well:

106 lines index.php
... lines 1 - 81
<select class="center-block form-control btn drp-dwn-width btn-default btn-lg dropdown-toggle" name="ship1_name">
<option value="">Choose a Ship</option>
<?php foreach ($ships as $key => $ship): ?>
<option value="<?php echo $key; ?>"><?php echo $ship->getName(); ?></option>
<?php endforeach; ?>
</select>
... lines 88 - 91
<select class="center-block form-control btn drp-dwn-width btn-default btn-lg dropdown-toggle" name="ship2_name">
<option value="">Choose a Ship</option>
<?php foreach ($ships as $key => $ship): ?>
<option value="<?php echo $key; ?>"><?php echo $ship->getName(); ?></option>
<?php endforeach; ?>
</select>
... lines 98 - 106

Refresh, and now things look just fine!

We have this getNameAndSpecs() function, so perhaps when I'm choosing a ship I might want to see its important stats since I'm going to use it to save the day:

50 lines lib/Ship.php
... lines 1 - 2
class Ship
{
... lines 5 - 22
public function getNameAndSpecs($useShortFormat)
{
... lines 25 - 41
}
... lines 43 - 48
}

So instead of getName() I'll use getNameAndSpecs(). First, I'm going to make the short format an optional argument so we don't always have to fill that in:

50 lines lib/Ship.php
... lines 1 - 22
public function getNameAndSpecs($useShortFormat = false)
{
... lines 25 - 41
}
... lines 43 - 50

Let's make these updates in index.php and now refresh the browser:

106 lines index.php
... lines 1 - 83
<?php foreach ($ships as $key => $ship): ?>
<option value="<?php echo $key; ?>"><?php echo $ship->getNameAndSpecs(); ?></option>
<?php endforeach; ?>
... lines 87 - 93
<?php foreach ($ships as $key => $ship): ?>
<option value="<?php echo $key; ?>"><?php echo $ship->getNameAndSpecs(); ?></option>
<?php endforeach; ?>
... lines 97 - 106

We see our specs format in the select menu -- cool!

And that's it, switching to an object is not that big of a deal. Next we'll talk about what these public things in Ship are doing inside of here and what else we can have.

Leave a comment!

  • 2016-09-28 Victor Bocharsky

    Hey John,

    Thanks! That's not a Chrome plugin but PHP extension which is called Xdebug. For Linux servers it's easy to install with $ sudo apt-get install php5-xdebug. BTW, there's another nice solution for dumping variables: Symfony VarDumper Component. It just a simple library you can easy install with Composer, but has even cooler output than xdebug, be sure to look at it ;)

    Cheers!

  • 2016-09-27 John

    Nice tut. Good to go over some of the basics again. Anyway I noticed that Chrome shows a nice output for you when viewing your outputted objects such as this: http://imgur.com/a/HNbA4 However on my PC it looks like this: http://imgur.com/a/k3tTE Are you using a plugin for this styling?

  • 2016-05-02 weaverryan

    Hi Oscar!

    Yea, you could totally do this - I don't see any reason *not* to based on how things are setup now. Later, in part 2 (http://knpuniversity.com/scree..., we'll talk about "service classes". The printShipSummary() might also be perfect for a service class. But either way: yes - I would eventually make *all* flat functions into object methods.

    Cheers!

  • 2016-04-29 Oscar Untied

    Why not put the printShipSummary() into the object also?