Buy

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.

Leave a comment!

  • 2017-02-04 weaverryan

    Haha, awesome! :p

  • 2017-02-03 J.R. Jenkins

    never mind, just completed the challenges :-)

  • 2017-02-03 J.R. Jenkins

    Loving the course so far, but could we just add a 'use \PDO;' to the top of the file instead of prefixing each instance of PDO with a leading '\'? That way if we ever wanted to override the PDO class we would only have to update the use statement instead of find and replace all references to PDO throughout the container service?