Buy Access to Course
07.

Namespaces and Core PHP Classes

Share this awesome video!

|

Keep on Learning!

Let's close all our tabs and open up Container. In the last course, we created two different ways to load Ship objects: one that reads a JSON file - JsonFileShipStorage and another that reads from a database - PdoShipStorage:

74 lines | lib/Service/Container.php
// ... lines 1 - 4
class Container
{
// ... lines 7 - 51
public function getShipStorage()
{
if ($this->shipStorage === null) {
//$this->shipStorage = new PdoShipStorage($this->getPDO());
$this->shipStorage = new JsonFileShipStorage(__DIR__.'/../../resources/ships.json');
}
// ... lines 58 - 59
}
// ... lines 61 - 72
}

And you could switch back and forth between these without breaking anything, thanks to our cool ShipStorageInterface. Change it to use the PDO version and refresh.

Woh, new error:

Class Service\PDO not found in Container.php on line 28.

Let's check that out:

74 lines | lib/Service/Container.php
// ... lines 1 - 4
class Container
{
// ... lines 7 - 24
public function getPDO()
{
if ($this->pdo === null) {
$this->pdo = new PDO(
// ... lines 29 - 31
);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
// ... lines 36 - 37
}
// ... lines 39 - 72
}

use Statements for core PHP Classes?

Here, we see the exact same error as before: "Undefined Class PDO". So far, the answer to this has always been:

Oh, I must have forgotten a use statement. I referenced a class, so I probably need to add a use statement for it.

But here's the kicker: PDO is a core PHP class that happens to not live in a namespace. In other words, it's like a file that lives at the root of you file system: not in any directory.

So when PHP sees PDO mentioned, it looks at the top of the class for a use statement that ends in PDO, it doesn't find one, and it assumes that PDO lives in the Service namespace. But in fact, PDO lives at the root namespace.

The fix is easy: add a \ at the front of PDO:

74 lines | lib/Service/Container.php
// ... lines 1 - 4
class Container
{
// ... lines 7 - 24
public function getPDO()
{
if ($this->pdo === null) {
$this->pdo = new \PDO(
// ... lines 29 - 31
);
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}
// ... lines 36 - 37
}
// ... lines 39 - 72
}

This makes sense: if you think of namespaces like a directory structure, This is like saying ls /PDO . It doesn't matter what directory, or namespace, we're in, adding the \ tells PHP that this class lives at the root namespace. Update the other places where we reference this class.

The Opening Slash is Portable

This is true for all core PHP classes: none of them live in namespaces. So, always include that beginning \. Now, technically, if you were inside of a file that did not have a namespace - like index.php - then you don't need the opening \. But it's always safe to say new \PDO: it'll work in all files, regardless of whether or not they have a namespace.

When Type-Hints Fail

If you refresh now, you'll see another error that's caused by this same problem. But this one is less clear:

Argument 1 passed to PDOShipStorage::__construct() must be an instance of Service\PDO, instance of PDO given.

This should jump out at you: "Instance of Service\PDO". PHP thinks that argument 1 to PDOShipStorage should be this, nonsense class. There is no class Service\PDO!

Check out PDOShipStorage: the __construct() argument is type-hinted with PDO:

35 lines | lib/Service/PdoShipStorage.php
// ... lines 1 - 4
class PdoShipStorage implements ShipStorageInterface
{
// ... lines 7 - 8
public function __construct(PDO $pdo)
{
// ... line 11
}
public function fetchAllShipsData()
{
// ... lines 16 - 18
return $statement->fetchAll(PDO::FETCH_ASSOC);
}
public function fetchSingleShipData($id)
{
// ... lines 24 - 25
$shipArray = $statement->fetch(PDO::FETCH_ASSOC);
// ... lines 27 - 32
}
}

But of course, this looks like Service\PDO to PHP, and that causes problems. Add the \ there as well:

35 lines | lib/Service/PdoShipStorage.php
// ... lines 1 - 4
class PdoShipStorage implements ShipStorageInterface
{
// ... lines 7 - 8
public function __construct(\PDO $pdo)
{
// ... line 11
}
// ... lines 13 - 33
}

Phew! We spent time on these because these are the mistakes and errors that we all make when starting with namespaces. They're annoying, unless you can debug them quickly. If you're ever not sure about a "Class Not Found" error, the problem is almost always a missing use statement.

Update the other spots that reference PDO:

35 lines | lib/Service/PdoShipStorage.php
// ... lines 1 - 4
class PdoShipStorage implements ShipStorageInterface
{
// ... lines 7 - 13
public function fetchAllShipsData()
{
// ... lines 16 - 18
return $statement->fetchAll(\PDO::FETCH_ASSOC);
}
public function fetchSingleShipData($id)
{
// ... lines 24 - 25
$shipArray = $statement->fetch(\PDO::FETCH_ASSOC);
// ... lines 27 - 32
}
}

Finally, refresh! Life is good. You just saw the ugliest parts of namespaces.