Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Namespaces and Core PHP Classes

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 $10.00

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:

... 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:

... 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:

... 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:

... 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:

... 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:

... 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!

5
Login or Register to join the conversation
Default user avatar
Default user avatar J.R. Jenkins | posted 5 years ago

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?

2 Reply
Default user avatar

never mind, just completed the challenges :-)

1 Reply

Received error: Uncaught Error: Class 'Service\Container' not found in PATH_TO_COUSE_CODE\index.php:8

If running on Windows, please note that spl_autoload_register function needs path encoded using constant DIRECTORY_SEPARATOR to enable php to find the correct paths.

Keep the great instructions comming!

Reply

Hey CISVHenriksen

Nice found! Thanks for sharing it, I bet more than one will find it useful :)

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

userVoice