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 another battle - how about 3 CloakShape fighters against 4 RZ-1 A-wing
interceptors. Behind the scenes: each ship has a strength
. The battle()
function uses this as the ship's health, and as they battle each other, that
health gets lower and lower until one hits zero.
We need to add a new feature: after the battle: display the final health of the battling ships. One will be zero or negative, but how much health did the other have left?
In battle()
, those "ship health" variables are not returned in BattleResult
.
So we don't have access to this information. We could add it to BattleResult
,
but I want to do something more interesting.
After fighting a battle, let's update the strength of each ship with their
new health: like $ship1->setStrength($ship1Health)
and the same for $ship2
:
... lines 1 - 9 | |
public function battle(Ship $ship1, $ship1Quantity, Ship $ship2, $ship2Quantity) | |
{ | |
... lines 12 - 16 | |
while ($ship1Health > 0 && $ship2Health > 0) { | |
... lines 18 - 31 | |
// now battle them normally | |
$ship1Health = $ship1Health - ($ship2->getWeaponPower() * $ship2Quantity); | |
$ship2Health = $ship2Health - ($ship1->getWeaponPower() * $ship1Quantity); | |
} | |
// update the strengths on the ships, so we can show this | |
$ship1->setStrength($ship1Health); | |
$ship2->setStrength($ship2Health); | |
... lines 40 - 56 | |
} | |
... lines 58 - 66 |
After all, in real life - if a $ship
is almost defeated, it's probably
pretty broken - so it's $strength
should reflect that.
Check this out by dumping $ship1->getStrength()
and $ship2->getStrength()
and die. Refresh! We have -14 and 116, 130 and 0 and so on.
Ok, working nicely, and that's simple. Actually, we just did something really
important. Until now, this function has only read data from our ships. But
now, we've changed those objects. In other words, in battle.php
, we start
with two Ship
objects and pass them into battle()
:
... lines 1 - 25 | |
$ship1 = $ships[$ship1Name]; | |
$ship2 = $ships[$ship2Name]; | |
$battleManager = new BattleManager(); | |
$battleResult = $battleManager->battle($ship1, $ship1Quantity, $ship2, $ship2Quantity); | |
... lines 32 - 106 |
Once that finishes running, those same two objects are different now: their data has changed.
This is totally different than how arrays work: if $ship1
were an array,
and the battle()
function changed one of its keys internally, that would
have no effect here: $ship1
would still be the same array with the same
original values.
Objects are passed by reference: it means that there is only one $ship1
object in existence and when we pass it to a function, we're passing that
one object. But when you pass an array or a string to a function, you're
actually passing a copy of the original value. If that value changes inside
the function, it has no affect on the original variable.
Some of you may be familiar with adding an &
symbol before an argument:
this does the same thing: it makes that argument pass by reference. For objects,
that's not needed, because this is always true.
The takeaway is that if you change an object, you're changing that object
everywhere. To prove this, take our $ship1
and $ship2
- which are not
returned by the battle()
function - and add a new section that prints the
finished strength. Add a dl
element to make them a little pretty:
... lines 1 - 33 | |
<html> | |
... lines 35 - 53 | |
<body> | |
<div class="container"> | |
... lines 56 - 67 | |
<div class="result-box center-block"> | |
... lines 69 - 88 | |
<h3>Remaining Strength</h3> | |
<dl class="dl-horizontal"> | |
... lines 91 - 94 | |
</dl> | |
</div> | |
... lines 97 - 102 | |
</div> | |
</body> | |
</html> |
First, echo $ship1->getName()
and then $ship1->getStrength()
:
... lines 1 - 33 | |
<html> | |
... lines 35 - 53 | |
<body> | |
<div class="container"> | |
... lines 56 - 67 | |
<div class="result-box center-block"> | |
... lines 69 - 88 | |
<h3>Remaining Strength</h3> | |
<dl class="dl-horizontal"> | |
<dt><?php echo $ship1->getName(); ?></dt> | |
<dd><?php echo $ship1->getStrength(); ?></dd> | |
... lines 93 - 94 | |
</dl> | |
</div> | |
... lines 97 - 102 | |
</div> | |
</body> | |
</html> |
Do the same thing for $ship2
:
... lines 1 - 33 | |
<html> | |
... lines 35 - 53 | |
<body> | |
<div class="container"> | |
... lines 56 - 67 | |
<div class="result-box center-block"> | |
... lines 69 - 88 | |
<h3>Remaining Strength</h3> | |
<dl class="dl-horizontal"> | |
<dt><?php echo $ship1->getName(); ?></dt> | |
<dd><?php echo $ship1->getStrength(); ?></dd> | |
<dt><?php echo $ship2->getName(); ?></dt> | |
<dd><?php echo $ship2->getStrength(); ?></dd> | |
</dl> | |
</div> | |
... lines 97 - 102 | |
</div> | |
</body> | |
</html> |
We're missing auto-complete because we have some bad PHPDoc somewhere. We'll fix that in a bit.
Time to try it! Since objects are passed by reference, we should see the new, modified strength values - not the originals. Absolutely perfect.
Now let's get really wild and start fetching our ships from a database.
I noticed that if you run this with the following parameters:
~5 jedi ships against 5 jedi ships (or any match up that both sides are equal) ~
whenever one of them is using the jedi power, the health of the other does not seem to get set to zero.... see below:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Matchup:
1 CloakShape Fighter VS. 1 CloakShape Fighter
Winner:
CloakShape Fighter
The CloakShape Fighter used its Jedi Powers for a stunning victory!
Ship Health
CloakShape Fighter 46 CloakShape Fighter 46
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
one of them should have ended with a zero health.
I am new to PHP and cannot seem to find why this is happening.
Hi there!
Ah, this is a bug with our app! Nice find! But, it's an awesome bug - because it's super relevant to this chapter - about "references". Here's what's happening:
1) In battle.php, we get an array of ships by saying $ships = $shipLoader->getShips(). This is an array of Ship objects
2) In battle.php, we use the ship name to get each Ship object from the $ships array. These are the lines:
$ship1 = $ships[$ship1Name];
$ship2 = $ships[$ship2Name];
And that is actually the bug! When we "fetch" the Ship object from the array, that object is passed to us "by reference" - it doesn't make a new copy of the Ship object. In other words, if $ship1Name and $ship2Name are the same, then $ship1 and $ship2 literally point to the exact same one Ship object in memory. When we pass these two variables in BattleManager(), weird things happen - because we're calling setStrength() on both ships, which is really changing the same one Ship objects two times. In essence, since $ship2->setStrength($ship2Health) is called last, whatever $ship2Health is set to will be the final value for both ships (since there is really only one Ship). This is why sometimes you'll see the final healths as both 46, but other times as both 0. But in all cases, they're the same.
The fix for this - which we may do later, as it is a bit more realistic - would be to have ShipLoader return us unique Ship objects - e.g. we add a method called $shipLoader->findShipByName($name), and it always returns a unique Ship object. This is actually more realistic: if we're having 2 ships fight each other, then they should always be different Ship objects. In the app right now, if you select the same type, we're kind of having one ship battle itself :).
GREAT find and question!
The last code listing is wrong - it's something about database init, while it should be (as I thing) a fragment of battle.php