Optional type-hinting & Semantic Methods
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.
I need to show you something - so start another battle between some Jedi Star Fighters. It works... but if I refresh enough times... come on... yes! It blows up!
Argument 2 passed to BattleResult::__construct() must be an instance
of Ship, null given.
In BattleResult
- because we're good programmers - we type-hinted the two
Ship
arguments. Buuuuut, if you look at the battle()
function, there's
a case where the ships can destroy each other. And when that happens, there
is no winning or losing ship - they're both null. Since - news flash null
is not a Ship
object, PHP gets angry and casts down this big error.
When you type-hint an argument, the value must be that class - not even
null
is ok. But sometimes you do have a spot where an argument might
be a specific object, or it might be null. To support this, make the argument
optional - add an = null
after it:
// ... lines 1 - 2 | |
class BattleResult | |
{ | |
// ... lines 5 - 13 | |
public function __construct($usedJediPowers, Ship $winningShip = null, Ship $losingShip = null) | |
{ | |
$this->usedJediPowers = $usedJediPowers; | |
$this->winningShip = $winningShip; | |
$this->losingShip = $losingShip; | |
} | |
// ... lines 20 - 53 | |
} |
I don't have to, but I'll update @return
on the methods to be Ship|null
:
// ... lines 1 - 2 | |
class BattleResult | |
{ | |
// ... lines 5 - 36 | |
/** | |
* @return Ship|null | |
*/ | |
public function getLosingShip() | |
{ | |
return $this->losingShip; | |
} | |
/** | |
* Was there a winner? Or did everybody die :( | |
* | |
* @return bool | |
*/ | |
public function isThereAWinner() | |
{ | |
return $this->getWinningShip() !== null; | |
} | |
} |
PhpStorm will still give me auto-completion - but this is a signal to other
developers not to blindly call this method and always assume it will return
a Ship
object. We're already coding safely in battle.php
: we check to
make sure getWinningShip()
returns something before calling a method on
it. Cool.
Adding a Semantic isThereAWinner Method
To check if a BattleResult
has a winner, you can see if getWinningShip()
returns null. But we can do even better. Go to BattleResult
and make a
new public method called isThereAWinner()
. Here, return
$this->getWinningShip != null
:
// ... lines 1 - 2 | |
class BattleResult | |
{ | |
// ... lines 5 - 43 | |
/** | |
* Was there a winner? Or did everybody die :( | |
* | |
* @return bool | |
*/ | |
public function isThereAWinner() | |
{ | |
return $this->getWinningShip() !== null; | |
} | |
} |
There's at least two great things about this. First, code outside of this class doesn't need to know how to figure out whether or not there was a winner: that code can be dumb and just call this method. Second, if something happens in the future and the logic used to figure out if there is a winner changes, we only need to update the code in this one spot: no need to run around the code base trying to figure out where we have the old logic for seeing if there was a winner.
Update battle.php
to use this. The first if
statement is really trying
to figure out whether or not there was a winner. Update this to
$battleResult->isThereAWinner()
. Use that again right below:
// ... lines 1 - 70 | |
<?php if ($battleResult->isThereAWinner()): ?> | |
// ... line 72 | |
<?php else: ?> | |
// ... line 74 | |
<?php endif; ?> | |
// ... lines 76 - 77 | |
<?php if (!$battleResult->isThereAWinner()): ?> | |
// ... line 79 | |
<?php else: ?> | |
// ... lines 81 - 86 | |
<?php endif; ?> | |
// ... lines 88 - 99 |
Go back and refresh! You'll have to trust me that if we refresh this 1000
times, it'll always work - our bug is gone - and we have a nifty new helper
method in BattleResult
.
I know that this is a older course, but nowdays as i speak in PHP you can actually put null as ? (question mark):