Using Namespaces to Avoid Class-name Collisions

In the several previous articles, we looked at ways to prevent collisions betMODX logoween functions and classes. In this article, we’ll see how to handle that using PHP namespaces.

 

 

 

Namespaces Explained

In modern versions of PHP (since Version 5.3.0), PHP allows the use of namespaces to prevent collisions between classes and methods with similar names. The usual analogy used to explain namespaces is file paths. It’s not a perfect analogy, but it can help get you started in understanding namespaces.

Imagine that you have two files with the same name in different directories:

[code language=”html”]
users
subscribers
userlist.txt
non-subscribers
userlist.txt
[/code]

When you want to load the subscribers userlist.txt file, you have to provide what’s called a “fully qualified” path to it: users/subscribers/userlist.txt. Namespaces allow you to do the same thing with class files. They also work nicely with autoloaders, as we’ll see in the next article.

In this article, we’ll use our “Pets” project from the previous article. Here’s the directory structure:

[code language=”html”]
MODX_CORE_PATH components/pets
controllers
mycontroller.class.php
model
pets
dog.class.php
cat.class.php
[/code]

 

You might wonder why we have two pets directories, one under components and one under model. This isn’t necessary, but it’s common because you might want to have other types of classes in your model directory (e.g., clients or veterinarians).

 

The Class Files

Here are our new Dog and Cat class files (dog.class.php and cat.class.php), and our new controller file (mycontroller.class.php) with the addition of namespaces

Dog class file:

[code language=”php”]
<?php
namespace Pets\Model\Pets;

class Dog {
function __construct() {

}
function speak() {
return ‘arf’;
}
}
[/code]

Cat class file:

[code language=”php”]
<?php
namespace Pets\Model\Pets;

class Cat {
function __construct() {

}
function speak() {
return ‘meow’;
}
}
[/code]

Controller class file:

[code language=”php”]
<?php
namespace pets\controllers;

class MyController {
function __construct() {
}
}

[/code]

 

Notice the location of the namespace line in the files above. It *must* be the first line of the file and should be followed by a blank line. I prefer the format above, but this is also acceptable:

[code language=”php”]
<?php namespace pets\controllers;

[/code]

The backslash between the parts of the namespace is not a directory separator. It *must* be a backslash or PHP will throw a fatal parse error before the code even executes. A good code editor like PhpStorm will flag this as an error. In fact PhpStorm will tell you when anything related to namespaces is wrong, including incorrect new statements.

By setting the namespace, we’re telling PHP that when someone uses this class, it is under the Pets namespace so it won’t be confused with other classes of the same name. Of course this means that when we refer to the class with new or class_exists(), we have to specify the namespace it belongs to. Now that we have our namespace set up for the class files, let’s look at how we can use them. Because we’re not using an autoloader, we have to load the class files the old-fashioned way (we’ll add a new, namespace-based autoloader in the next article). Even without the autoloader, though, we’ll still have the collision-avoidance that comes with using namespaces.

 

Using Namespaced Classes

Here’s some code that uses our new classes:

[code language=”php”]
<?php
namespace Pets;

include MODX_CORE_PATH . ‘components/pets/model/pets/dog.class.php’;
include MODX_CORE_PATH . ‘components/pets/model/pets/cat.class.php’;
include MODX_CORE_PATH . ‘components/pets/controllers/mycontroller.class.php’;

$dog = new Model\Pets\Dog();

if ($dog) {
echo "\nDogs say: " . $dog->speak();
}

$cat = new Model\Pets\Cat();

if ($cat) {
echo "\nCats say: " . $cat->speak();
}

$controller = new Controllers\MyController();

if ($controller) {
echo "\nController: OK";
}

[/code]

 

With this structure, we can no longer write new Dog(). PHP would consider that to be in the Pets namespace and would refuse to instantiate it because there’s no Dog class directly under Pets. The only Dog class available is under Pets]Model\Pets\.

 

When using namespaces, if you want to instantiate or check for one of PHP’s built-in classes, such as Directory, DirectoryIteratot, or Exception, you need to prefix the class name with a backslash. That tells PHP to look for the class in the global namespace.

[code language=”php”]
$di = new \DirectoryIterator($path);
[/code]

 

Another thing you might notice is that the namespace here is shorter than the ones in the class files. This is because we have classes in different locations and a file can only have one namespace line. Some people put all classes in a single classes directory to avoid this, but we’ve followed the typical organization of a MODX extra. If we used Pets\Model\Pets for the namespace, we wouldn’t be able to instantiate our controller. If we used Pets\Controllers for the namespace, we wouldn’t be able to instantiate the Cat and Dog classes. In order to instantiate all classes, we have to go up the tree (or backwards in the namespace) until we find a part that’s common to all classes. In this case it’s the upper Pets directory. All our classes can be specified by adding to the end of that namespace. You might think of it like a “base path” for locating files.

When we use new or class_exists(), we have to specify the part of the namespace that follows the namespace specified at the top of the file. Since the file’s namespace is Pets, Model\Pets\Cat resolves to Pets\Model\Pets\Cat.

 

Namespace Aliases

It’s a pain to have to type out the full namespace path every time you instantiate a class, especially if it’s deeply nested. Luckily, we don’t have to. The use statement lets us specify an alias for the fully qualified namespace. Here is our code, re-written to provide more convenient aliases:

[code language=”php”]
<?php
namespace Pets;

use Pets\Model\Pets as Classes;
use Pets\Controllers as Controllers;

include MODX_CORE_PATH . ‘components/pets/model/pets/dog.class.php’;
include MODX_CORE_PATH . ‘components/pets/model/pets/cat.class.php’;
include MODX_CORE_PATH . ‘components/pets/controllers/mycontroller.class.php’;

$dog = new Classes\Dog();

if ($dog) {
echo "\nDogs say: " . $dog->speak();
}

$cat = new Classes\Cat();
echo "\nCats say: " . $cat->speak();

$controller = new Controllers\MyController();

if ($controller) {
echo "\nController: OK";
}

[/code]

 

The use statements must be together just below the namespace statement and separated from it by a blank line. Like the namespace line, they must also be followed by a blank line.

 

While the namespace line *must* be the first line of code in the file, PHP 5 versions don’t enforce the rule about the location of the use statements (I haven’t tested PHP 7 for this). The use statements should be put in the correct place when you’re finished, but you may wish to place them a little closer to where your classes are instantiated during development until you get them right, at least for now.

 

Using namespace aliases can make your code simpler and easier once you have them correct, but they are strictly optional. You can always use a fully qualified specification even if the use statements are there.

 

Coming Up

Our code above is a little clunky, especially after seeing the autoloader-based code in the previous article. Having to include each class file separately and write a new include or require line for any new classes you write in every file that uses them can be tedious. In the next article, we’ll see how to combine namespaces with an autoloader.

 


For more information on how to use MODX to create a web site, see my web site Bob’s
Guides
, or
better yet, buy my book: MODX: The Official Guide.

Looking for quality MODX Web Hosting? Look no further than Arvixe Web Hosting!

Tags: , | Posted under MODX, MODX | RSS 2.0

Author Spotlight

Bob Ray

Bob Ray is the author of MODX: The Official Guide and over 30 MODX add-on components. He hosts Bob's Guides, a source of valuable information for MODX users, and has been very active in the MODX Forums with over 19,000 posts.

Leave a Reply

Your email address will not be published. Required fields are marked *