If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
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!
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
:
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'
:
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.
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!
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:
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:
... 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:
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.
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:
... 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:
... 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:
... 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();
... 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:
... 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:
... 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:
... 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:
... 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.
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/sc..., 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!
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?
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: <a href="http://symfony.com/doc/current/components/var_dumper.html">Symfony VarDumper Component</a>. 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!
On Chapter 8, you tell us to "copy" the ship class...
I think you mean MOVE the ship class to the new file...
You don't want two versions of the same class - right?
Hey T. Wagner,
Ah, yes, technically you're right! We cut the class from play.php and put it into the Ship.php. We're sorry for misleading you a bit here, but yes, the Ship class is meant to moved to the new file instead of being copied. Unfortunately, we said "copy" in the video, and we do want our scripts match the scripts. Btw, did you watch the video or just read the scripts? Because on the video it's clear that we actually cut and paste instead of copy and paste.
Cheers!
PHPStorm gets angry. Can I get auto-completion for $ship->getNameandSpecs() in the foreach loop aswell?
Just hanging around and watch some basic PHP stuff
Great work :)
Update: Ok, ok I got it. In functions php we can write:
/**
* @return Ship[]
*/
function get_ships() {
...
}
Why not put the printShipSummary() into the object also?