Class Autoloading, Namespaces, PSR-0 and Acting Like Java

One of the surprises of ZF2 is that the preamble to the code looks like this:

namespace AlbumModel;

use ZendInputFilterFactory as InputFactory;
use ZendInputFilterInputFilter;
use ZendInputFilterInputFilterAwareInterface;
use ZendInputFilterInputFilterInterface;

That’s an awful lot like Java:

package illustration;

import java.awt.*;

The slight difference is that “namespace” and “use” are more like Perl lingo. “Package” and “import” hint at the existence of a compiler and linker.

ZF doesn’t use include(). It uses the PHP class autoloading feature.

Class Autoloading works via a hook in PHP that is used when you try to instantiate a class, and the code for it hasn’t been included. That normally throws an error, but before the error happens, you’re allowed to run your autoloader function and try to find the file which defines the class, and include() it.

Imagine that the code for ClassDoesntExist hasn’t been loaded, and you do this:

$c = new ClassDoesntExist();

That’s an error, and your autoloader function is called with that class name, “ClassDoesntExist”. You can then read through your file system looking for the file that contains the definition for “class ClassDoesntExist”, and include that file.

Obviously, that’s terribly slow to read inside each file, so you want to require that class names match the file names, and there’s only one class per file (just like in Java) so you only have to search for “ClassDoesntExist.php” and then include that file.

Better yet, if you had an array that contained the mapping like this, you could just search your array:

$autoload_classmap = array(
  'ClassDoesntExist' => 'classes/ClassDoesntExist.php'
);

That would be pretty fast. You just need tools to compile that ahead of time.

ZF2 uses the classloader like that. If you don’t have the array predefined, it’ll go into a search of the file system, but if you have the array, it’ll use the array.

If you have hundreds of classes, it’s difficult to keep them all in one directory. Additionally, you’re going to want to separate out the classes by vendor, so each library can be updated separately. You want to maintain a hierarchy of classes, so related classes are stored in the same directory. To address this, PHP namespaces are used.

Some History

Now, things get a little weird here, because, before namespaces were available in PHP, people would map classnames to a directory hierarchy by using the underscore (_) character. You can see this in the PEAR. Take a look at this repository for PEAR_PackageFileManager_Frontend.

The name of the class is that very long string. The sources are stored in a hierarchy that looks like this: PEAR/PackageFileManager/Frontend/.

(You might be wondering “how did they come up with that?” They copied C. Before C++, people did object oriented programming in C by using naming conventions. C uses _ to separate words.)

Inventing Namespaces

Well, that is a pain, so they invented namespaces, which would call that class PEARPackageFileManagerFrontend. At the top of the class’ source code, you’d have the namespace declaration:

namespace PEARPackageFileManager

(You don’t include the class name in the namespace. The class exists within the namespace.)

So with that, you can now have a 1:1 mapping of namespaces to the file hierarchy.

Imagine what that means for the classloader. It means it’s really easy to find the class definition.

All these naming conventions got subsumed under a PHP standard called PSR-0.

Life is good. The only problem is, the full name of the class is PEARPackageFileManagerFrontend. You instantiate it like this:

$c = new PEARPackageFileManagerFrontend();

Generally, people would rather write code like this:

$c = new Frontend();

This is where the “use” keyword comes into play. When you use the “use” keyword, it means “I want to use this class within my local namespace, and alias it to another classname”. The first part makes sense, but that last part is weird – why would you do that aliasing?

Well, here’s the magic. The full syntax for use is:

use PathToNamespaceMyClass as AliasName

If you don’t include the alias, it defaults to the classname:

use PathToNamespaceMyClass

is identical to

use PathToNamespaceMyClass as MyClass

That means you can now do “$c = new MyClass()” and it will be just like “$c = new PathToNamespaceMyClass()”.

So, now you can omit the namespace part of the class name.

However, when we compile the code, it’ll be an error.

When the class definition doesn’t exist, it triggers the autoloader function.

If the autoloader configuration has an entry for that full class name, it’ll include() the file.

If the configuration doesn’t have an entry, the autoloader function breaks up the class name on “”, flip them into “/”, and include()s the class from the file system.

Magic.

And now the code looks more like Java, even though it works nothing like Java.