PHP Namespaces with Autoloader Example

It took a while to wrap my mind around PHP namespaces - despite the fact I've needed them for years. It's just one of those features that seems weirder in PHP than in other systems. But that's normal for PHP - quirky. Unfortunately, it's not quirky like Perl, where the quirk eventually makes you feel good. With PHP you just feel kind of odd, maybe a little inferior... like your language is slipping toward becoming the Visual Basic of the web.

Namespaces are mainly a way for vendors to keep their class names and function names from clashing with those of other vendors. There are other uses for them - but generally, one vendor will tend not to have naming clashes within its codebase - and if there is a clash, it can usually be resolved with a few meetings.

Namespaces add another layer of indirection to the naming system so that you can avoid clashes with code you can't control. It also provides an aliasing feature so if there is still a clash, there are ways around that, too.

The tradeoff, with PHP, is code ugliness. Of course, that's usually the trade off with namespaces, so it's okay.

Rather than get into the syntax, let's look at how the files are organized. We have a few PHP files in the docroot, and a classes directory. Within classes, we have a folder hierarchy based on the domain name system (DNS). DNS is what's used in Java, so we'll use it here as well. It helps to avoid namespace clashes.

johnk@tiny:~/Sites/test$ ls -R *
index.php  lib.php  ns.php

classes:
com

classes/com:
riceball

classes/com/riceball:
sw  Test.class.php

The only class we have is Test.class.php. That's using the .class.php naming convention for PHP classes. Here's the code for Test.

<?php
namespace com\riceball;

class Test {
  function hello() {
    echo "<p>Hello, World</p>";
  }
}

It just prints Hello World. At the top, the namespace statement declares that all the things below are defined in the com\riceball namespace. The namespace we use can be any string, but our convention will be that the namespace matches the file path. The file path is com/riceball/Test.class.php. The namespace is com\riceball.

This correspondence between the namespace and the file path will be key in creating an autoloader for this class.

Autoloaders

You can skip this if you know what an autoloader is.

PHP has a feature where you can write a function, named __autoload(), that will be used to call "include" statements to include class definition code. This way you can stop writing those "require_once" statements at the top of your files. (It turns out that require_once is slow, because it does a file stat each time it's called.)

The way it works is simple. When you try to use the Test class before it's defined, PHP calls __autoload('Test'); where it's the classname being passed in. The __autoload function can then prepend a path, and append '.class.php', and "require()" that. PHP then uses tries to use the class again and succeeds.

When you're using namespaces, the autoloader is prepends the namespace to the class. Here's the code for __autoload, which is defined in lib.php:

<?php
define('CLASS_PATH','/home/johnk/Sites/test/classes/');
function __autoload($cn) {
  require(CLASS_PATH.str_replace('\\','/',$cn).'.class.php');
};

What this does is construct an absolute path to the class' code. Absolute paths are the best because they load faster than relative paths.

PHP will call __autoload with 'com\riceball\Test'. That gets mangled and turned into /home/johnk/Sites/test/classes/com/riceball/Test.class.php, which is then loaded.

"Use"-ing Namespaces

Three different ways to use the namespace are demonstrated below. The first two are in index.php, and the last in ns.php. Here's the code in index.php:

<?php
require('lib.php');

// The preferred style.

use \com\riceball as RB;

$u = new RB\Test();
$u->hello();

// A nicer looking style... but one that's slightly at risk
// of name collisions.

use \com\riceball\Test as Test;

$t = new Test();
$t->hello();

While there are a other ways to use classes within namespaces, I'm going to stick with these two. They are pretty explicit without being verbose. The first form is to "use" the namespace, and then alias it to "RB". Then, when we make an instance of Test, we have to call it RB\Test.

PHP expands this to com\riceball\Test, and passes that to __autoload() which will then load the code for Test.

The second form is to create an alias for the class itself. Again, this expands to the same name, and that's passed to __autoload().

The third form is in ns.php:

<?php
// Within an application, maybe it's OK to use
// namespace on every file.

namespace com\riceball;

require('lib.php');

$t = new Test();
$t->hello();

echo 'global echo';

This form was put into a separate file because the namespace keyword can be used only as the first statement. What this code says is "I'm also in the com\riceball namespace." (So that Test will resolve to com\riceball\Test, and again that's passed into __autoload().)

When your code is in a namespace, you can still call global functions like echo().

I'm experimenting with putting all my code into a namespace. This way, the programming style, within the namespace, is identical to not using a namespace at all. When it comes time to re-use code that exists in another namespace, you will need to use one of the above two syntaxes to access the namespaced code.

Some other web pages have demonstrating using a hierarchy of namespaces in libraries, but I don't see value in that. Why use namespaces for their own sake? Instead, use them when you need a clean way to separate out your code from some other organization's code, without requiring any interaction with the other organizations.

Granted, projects require lots of libraries, but libraries usually exist within a global community namespace, and they are generally managed by some organization or by a polite group consensus (people name their libraries based on what's already out there and popular). PHP has the PEAR namespace, PHPClasses is a kind of namespace, and each framework or library comes from a vendor, and within each collection of classes, there are no name clashes.

There's no reason to add yet another organizing schema, in addition to naming conventions and classes (and I guess the way people use global arrays in PHP), when it's not necessary.

Addendum: real world example

I'm refactoring a small program and came across two issues. One was that the __autoload() function needs to be defined in the global namespace. That means either putting it in its own file, or using the namespace { } construct to hoist it out of whatever namespace is defined. I went with the former. Here's my actual namespace function:

function __autoload($cn) {
  try {
    $f = (CLASS_PATH.str_replace('\\','/',$cn).'.class.php');
    if (!file_exists($f)) {
      throw new Exception('file not found');
    }
    require($f);
  } catch (Exception $e) {
    echo '<p>'.$e->getMessage().'</p>';
    echo 'class: '.$cn;
  }
};

It's a bit less simple than I'd like, but it deals better with errors in your code. Instead of breaking in __autoload(), it'll give you a stack trace back to where your error is. The typical error is using a global constant without the \ prefix.