More Fun with use Statements
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 hate needing all these require
statements. But thanks to our autoloader, the
only thing we need to do is give every class the namespace that matches its directory.
This will be a little bit of work because we didn't do it up front - life is much
easier when you use namespaces like this from the very beginning. But, we'll learn
some other stuff along the way.
The AbstractShip
class lives in the Model
directory, so give it the namespace
Model
:
// ... lines 1 - 2 | |
namespace Model; | |
abstract class AbstractShip | |
// ... lines 6 - 125 |
Copy that and do the same thing in BattleResult
, BrokenShip
, RebelShip
, Ship
and FriendShip
-- just kidding there's none of that in epic code battles:
// ... lines 1 - 2 | |
namespace Model; | |
class BattleResult | |
// ... lines 6 - 57 |
// ... lines 1 - 2 | |
namespace Model; | |
class BrokenShip extends AbstractShip | |
// ... lines 6 - 22 |
// ... lines 1 - 2 | |
namespace Model; | |
class RebelShip extends AbstractShip | |
// ... lines 6 - 38 |
// ... lines 1 - 2 | |
namespace Model; | |
class Ship extends AbstractShip | |
// ... lines 6 - 45 |
Perfect. BattleManager
already has the correct Service
namespace:
// ... lines 1 - 2 | |
namespace Service; | |
class BattleManager | |
// ... lines 6 - 94 |
In Container
, paste that same one:
// ... lines 1 - 2 | |
namespace Service; | |
class Container | |
// ... lines 6 - 74 |
Repeat that in JsonFileShipStorage
, PdoShipStorage
, ShipLoader
and ShipStorageInterface
:
// ... lines 1 - 2 | |
namespace Service; | |
class JsonFileShipStorage implements ShipStorageInterface | |
// ... lines 6 - 34 |
// ... lines 1 - 2 | |
namespace Service; | |
class PdoShipStorage implements ShipStorageInterface | |
// ... lines 6 - 35 |
// ... lines 1 - 2 | |
namespace Service; | |
class ShipLoader | |
// ... lines 6 - 63 |
// ... lines 1 - 2 | |
namespace Service; | |
interface ShipStorageInterface | |
// ... lines 6 - 27 |
These all live in the Service
directory.
Missing use Statements = Common Error
Ok! Let's see what breaks! Go back and refresh. The first error we get is:
Class
Container
not found inindex.php
Ok, you're going to see a lot of class not found errors in your future. When you
see them, read the error very closely: it always contains a hint. This says class
Container
is not found. Well, we don't have a class called Container
: our class
is called Service\Container
. This tells me that in index.php
on line 6, we're
referencing the class name without the namespace. Sure enough, we have new Container
:
// ... lines 1 - 6 | |
$container = new Container($configuration); | |
// ... lines 8 - 143 |
To fix this, we could say Service\Container
here or we can add a use
statement
for Service\Container
. Let's do that:
// ... lines 1 - 3 | |
use Service\Container; | |
// ... lines 5 - 8 | |
$container = new Container($configuration); | |
// ... lines 10 - 145 |
And I can already see that we'll have the same problem down below with BrokenShip
:
PhpStorm is trying to warn me! Add a use Model\BrokenShip
to take care of that:
// ... lines 1 - 4 | |
use Model\BrokenShip; | |
// ... lines 6 - 13 | |
$brokenShip = new BrokenShip('I am so broken'); | |
// ... lines 15 - 145 |
We'll probably have the same problem in battle.php
- so open that up. Yep, add
use Service\Container
:
// ... line 1 | |
use Service\Container; | |
// ... lines 3 - 5 | |
$container = new Container($configuration); | |
// ... lines 7 - 112 |
Looking good!
Reading the Error Messages... Closely
Try it again! Ok:
Class
Service\RebelShip
not found inShipLoader
.
Remember what I just said about reading the error messages closely? This one has
a clue: it's looking for Service\RebelShip
. But we don't have a class called
Service\RebelShip
- our class is called Model\RebelShip
:
// ... lines 1 - 2 | |
namespace Model; | |
class RebelShip extends AbstractShip | |
// ... lines 6 - 38 |
The problem exists where we're referencing this class - so in ShipLoader
at line 43.
This is the most common mistake with namespaces: we have new RebelShip
, but we
don't have a use
statement on top for this:
// ... lines 1 - 2 | |
namespace Service; | |
class ShipLoader | |
{ | |
// ... lines 7 - 40 | |
private function createShipFromData(array $shipData) | |
{ | |
if ($shipData['team'] == 'rebel') { | |
$ship = new RebelShip($shipData['name']); | |
} else { | |
$ship = new Ship($shipData['name']); | |
// ... line 47 | |
} | |
// ... lines 49 - 54 | |
} | |
// ... lines 56 - 60 | |
} | |
This is the same problem we just solved in index.php
, but with a small difference.
Unlike index.php
and battle.php
, this file lives in a namespace called Service
.
That causes PHP to assume that RebelShip
also lives in that namespace -- you know
like roommates.
Here's how it works: when PHP parses this file, it sees the RebelShip
class on
line 43. Next, it looks up at the top of the file to see if there are any use
statements that end in RebelShip
. Since there aren't, it assumes that RebelShip
also lives in the Service
namespace, so Service\RebelShip
.
Think about it: this is just like directories on your filesystem. If you are inside
of a directory called Service
and you say ls RebelShip
, it's going to look for
RebelShip
inside of the Service
directory.
But in index.php
- since this doesn't hold a class - we didn't give this file a
namespace. If you forget a use
statement for BrokenShip
here, this is equivalent
to saying ls BrokenShip
from the root of your file system, instead of from inside
some directory.
In both cases the solution is the same: add the missing use
statement: use Model\RebelShip
:
// ... lines 1 - 2 | |
namespace Service; | |
use Model\RebelShip; | |
// ... lines 6 - 8 | |
class ShipLoader | |
// ... lines 10 - 67 |
Now PhpStorm stops highlighting this as an error. Much better.
We have the same problem below for Ship
: add use Model\Ship
:
// ... lines 1 - 2 | |
namespace Service; | |
use Model\RebelShip; | |
use Model\Ship; | |
// ... lines 7 - 8 | |
class ShipLoader | |
// ... lines 10 - 67 |
Finally, there's one more spot in the PHP documentation itself. Because we don't have
a use
statement in this file yet for AbstractShip
, PhpStorm assumes that this class
is Service\AbstractShip
. To fix that, add use Model\AbstractShip
:
// ... lines 1 - 2 | |
namespace Service; | |
use Model\RebelShip; | |
use Model\Ship; | |
use Model\AbstractShip; | |
class ShipLoader | |
{ | |
// ... lines 11 - 17 | |
/** | |
* @return AbstractShip[] | |
*/ | |
public function getShips() | |
{ | |
// ... lines 23 - 31 | |
} | |
/** | |
* @param $id | |
* @return AbstractShip | |
*/ | |
public function findOneById($id) | |
{ | |
// ... lines 40 - 42 | |
} | |
// ... lines 44 - 64 | |
} | |
Now, everything looks happy!
The moral of the story is this: whenever you reference a class, don't forget to put
a use
statement for it. Now, there is one exception to this rule. If you reference
a class that happens to be in the same namespace as the file you're in - like
ShipStorageInterface
- then you don't need a use
statement:
// ... lines 1 - 8 | |
class ShipLoader | |
{ | |
// ... lines 11 - 12 | |
public function __construct(ShipStorageInterface $shipStorage) | |
{ | |
// ... line 15 | |
} | |
// ... lines 17 - 64 | |
} | |
PHP correctly assumes that ShipStorageInterface
lives in the Service
namespace.
But you don't get lucky like this too often.
I already know we need to fix one more spot in BattleManager
. Add a use
statement
for Model\BattleResults
and another for Model\AbstractShip
:
// ... lines 1 - 2 | |
namespace Service; | |
use Model\BattleResult; | |
use Model\AbstractShip; | |
class BattleManager | |
// ... lines 9 - 97 |
Phew! I promise, this is all a lot easier if you just use namespaces from the beginning!
Let's refresh the page. Our app is back to life, and the require
statements are
gone!
Hi there I am getting a Fatal Error saying "Uncaught Error: Class 'Service\PDO' not found in C:\Users\PatrickLye\github\PHP\oiii\lib\Service\Container.php on line 28"
line 28 has this on it
"$this->pdo = new PDO(
$this->configuration['db_dsn'],
$this->configuration['db_user'],
$this->configuration['db_pass']
);"
Any suggestions?