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.
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!
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!
Hey Patrick L.
Seems like you forgot to add the PDO class import. Can you double-check the imports at the top of your file?
Cheers!
The top part of my first comment is missing this:
namespace Service;
class Container
{
private $configuration;
private $pdo;
private $shipLoader;
private $battleManager;
private $shipStorage;
public function __construct(array $configuration)
{
not enteriely sure why it got removed
Okay I haven't a clue how I fixed it, but I did my code went from this:
configuration = $configuration;
}
/**
* @return PDO
*/
public function getPDO()
{
if ($this->pdo === null) {
$this->pdo = new PDO(
$this->configuration['db_dsn'],
$this->configuration['db_user'],
$this->configuration['db_pass']
);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return $this->pdo;
}
/**
* @return ShipLoader
*/
public function getShipLoader()
{
if ($this->shipLoader === null) {
$this->shipLoader = new ShipLoader($this->getShipStorage());
}
return $this->shipLoader;
}
public function getShipStorage()
{
if ($this->shipStorage === null) {
//$this->shipStorage = new PdoShipStorage($this->getPDO());
$this->shipStorage = new JsonFileShipStorage(__DIR__.'/../../resources/ships.json');
}
return $this->shipStorage;
}
/**
* @return BattleManager
*/
public function getBattleManager()
{
if ($this->battleManager === null) {
$this->battleManager = new BattleManager();
}
return $this->battleManager;
}
}
to this:
configuration = $configuration;
}
/**
* @return PDO
*/
public function getPDO()
{
if ($this->pdo === null) {
$this->pdo = new PDO(
$this->configuration['db_dsn'],
$this->configuration['db_user'],
$this->configuration['db_pass']
);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return $this->pdo;
}
/**
* @return ShipLoader
*/
public function getShipLoader()
{
if ($this->shipLoader === null) {
$this->shipLoader = new ShipLoader($this->getShipStorage());
}
return $this->shipLoader;
}
public function getShipStorage()
{
if ($this->shipStorage === null) {
//$this->shipStorage = new PdoShipStorage($this->getPDO());
$this->shipStorage = new JsonFileShipStorage(__DIR__.'/../../resources/ships.json');
}
return $this->shipStorage;
}
/**
* @return BattleManager
*/
public function getBattleManager()
{
if ($this->battleManager === null) {
$this->battleManager = new BattleManager();
}
return $this->battleManager;
}
}
I'm getting errors around mcrypt_module_self_test()::TYPE_NO_JEDI in BattleManager.php. They've only just started, I'm on PHP v 7.2 and this function is deprecated. I'm not really sure as to why it's being used, could someone please enlighten me? Many thanks . Si.
Hey Si Y.
This tutorial was coded on PHP5 that's why you will probably find some deprecated functions being used (but it should be nothing to worry about). Can you show me what error you got?
Cheers!
Yo Hakim Ch! Try it again - I saw that (once) the server you were using shutdown (it does that for security after about 20 minutes). Or there may have been some other connection problem. Anyways, sorry about any issues - I can see the problem in the logs, but it looks temporary.
Cheers!
What is the purpose of the coloring of the files in the document tree on the left side? It changes during the process of adding namespaces and in my own code I saw at some point also red coloring for instance.
Yo Max!
You're playing close attention :). PHPstorm highlights the files in different colors based on their storage status in git. Behind the scenes. this project is being stored in git, and between the chapters, I actually commit all of my changes. You'll notice that the tree at the end of one chapter has different colors than the start of the next chapter because of this (nobody has ever noticed that before!). I believe blue is a modified file and red is a new file.
Cheers!
And you are right again... :D Actually a quite useful feature, if you know about it :) Thanks!
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?