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.

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

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:

99 lines battle.php
... 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.

Leave a comment!