Back to Blog
A
PHP Architect ·

PHP 5.3 namespaces for the rest of us

According to the official documentation, PHP namespaces have been designed to prevent name collisions between classes from different packages and to avoid the use of very long names in the code to refer to classes or functions—nobody really wants to have to deal with something called Zend_Db_Adapter_Mysqli or PHPUnit_Framework_Constraint_IsInstanceOf, after all. This means that namespaces help a developer write code that is both more concise and clearer—a direction which is always an improvement towards expressiveness. Within the PHP implementation of namespaces, these names will be ideally refactored to Zend\Db\Adapter\Mysqli and PHPUnit\Framework\Constraint\IsInstanceOf, where \ is the namespace separator. In the codebase, however, there will typically be very few references to these classes with their fully qualified name, because it is possible to import entire namespaces in a script and then use the class names directly, making the code easier to follow and unambiguous to write. In fact, the definition of a namespace class itself does not contain its fully qualified name. For example, this would be the source file of an hypothethical MyLibrary\TypeOfComponents\MyClass class:
<?PHP
namespace MyLibrary\TypeOfComponents;

class MyClass
{
    // ...
}
The convention when writing namespace-enabled code is that of creating a folder structure that reflects the individual components of a namespace (for example, MyClass would be in the MyLibrary/TypeOfComponents directory. This helps standardizing the autoloading process.

How namespaces work

Namespaced name resolution has been retrofitted in PHP 5.3 by inserting a processing mechanism at compile time. All the references to classes and functions in a namespaced source file are parsed before the actual execution of the PHP script and substituted with fully qualified names. But what rules does the name resolution process follow? You can better understand this process with a filesystem metaphor:
  • Every name that does not start with a backslash is interpreted as a relative reference (path) from the current namespace (directory). For example, in the source code above, MyClass would refer to MyLibrary\TypeOfComponent\MyOtherClass with the unqualified name MyOtherClass. It can also refer to MyLibrary\TypeOfComponents\SubNamespace\ACollaborator with the qualified name SubNamespace\ACollaborator.
  • Names that start with a backslash are always relative to the global namespace (to the root directory), and are always resolved to the literal specified value. For example, \PHPUnit\Framework\TestCase class is resolved to the fully qualified name of PHPUnit\Framework\TestCase.
Note that the name resolution process examples above are limited to classes, but the same concepts apply to methods. Pre-PHP 5.3 code will not, of course, have any namespace defined in it and will continue to work as before, residing in the global namespace because there are no namespace keywords.

Fallback

There is however an exception to the resolution process I have just described: constants and functions that are defined in the global namespace—including all built-in PHP functions like is_array() and strlen(). If we had to refer to each function using its fully-qualified name and prefix them with a backslash every time we need to use them, our code would pretty soon become an unreadable jumble of symbols. For example, you'd have to write \strlen() in all namespaced code, because the strlen() function is defined in the global namespace, and not in the current one. Luckily, function and constant name resolution falls back to the global namespace when they are not found in a more specific one that is currently available. Thus, you can call strlen() from namespaced code without using a leading backslash—as long, of course, as you do not have a strlen() function defined in the current namespace that will shadow the global one. This fallback rule does not apply to classes and interfaces: SPL's constructs such as Iterator and ArrayObject, for example, must be written as \Iterator and \ArrayObject in namespaced code, or they won't work. You can think of this rule as having these functions defined in your PATH environment variable: no one refers to the ls (dir at a Windows shell prompt) command as /bin/ls.

Importing classes

Being able to refer to classes in the current namespace without their fully qualified name is a good improvement over the long class names we had to deal with before namespaces, but we can expand the name aliasing further. Import statements that create aliases to fully qualified names can be inserted after the namespace keyword, which is always the first language construct at the beginning of the file when present. These aliases are local to the file. Consider this example:
<?PHP
namespace MyLibrary;

use ExternalLibrary\Filter\Int as IntFilter;
use ExternalLibrary\Filter\NotEmptyString;
In a source file with these use statements, you can refer to the IntFilter and NotEmptyString classes without any syntax noise. If an explicit alias is not defined, the base name (NotEmptyString) is used as a default value, and it is usually sufficient. Again, we can extend the filesystem metaphor to cover the import functionality. Aliases can be compared to symlinks from the current directory to an absolute path: you are able to refer to the symlink with a short base name instead of using a long absolute path. Importing class and function names makes namespace very efficient to employ. The fully qualified name of an external class is hidden from the source code and extracted at the top of the file: if it changes, it is easy enough to update a single instruction instead of refactoring large areas of code. Moreover, with this approach the external dependencies are explicitly listed in a single point for every source file.

Dynamic features

Name resolution happens at compile time; therefore, every time a class is referenced dynamically, such as when using a variable variable or passing a parameter to (for example) class_exists(), you must be a fully qualified class name:
$instance = new $class();
$boolean = class_exists($class);
$boolean = $instance instanceof $class;
In these use cases, by the time the code is executed, the resolution process will have already occurred and, obviously, the compiler will have no way of telling whether $class contains a class reference that must be resolved; therefore you must make sure that it contains a full-qualified class name in order for things to work properly. Despite their simplicity, some of the less-obvious aspects of namespaces can be difficult to understand when you're first starting out. If you have any other doubts, you can read the FAQ from the official manual, which contain much more code than my examples, or ask a question in the comments.
A

PHP Architect

March 29, 2010

Share

Our Partners

Collaborating with industry leaders to bring you the best PHP resources and expertise

Interested in partnering? Get in touch →