Monday, February 14, 2011

Php Namespace

PHP 5.3 introduces a much requested feature for object-oriented programmers: namespaces. At the time of this writing, version 5.3 of PHP was in development, but is planned on being released in the near future.
One of the purposes object-oriented programming is to remove ambiguous development and data access items. This basically means identifying common functionality and creating the most reusable framework possible, typically in the form of classes. When creating this functionality, you will begin to have issues with naming conventions and narrowing down functionality even further. To resolve this scoping issue, namespaces allow you to contain those bits of code even more.
UPDATE
Since the writing of this article, PHP has changed the namespace separator to a new character. Please view my update to this article.
Getting Your Environment Setup
Before going any further, you will need to download a release of PHP greater than or equal to 5.3. Since 5.3 is not currently production ready, you can download one of the development releases at snaps.php.net. Once you have that installed, you can use the examples described in this article.
Should I Use Namespaces?
That is a question that can cause controversy among developers. Namespaces are a solution for grouping large sets of libraries and common functionality. The keyword being “large“. You need to be realistic about this. While not definitive, here are some good signs that namespaces are right for you:
  1. Your application is not on shared hosting.
  2. You have so many libraries, or your framework requires less work by adding class paths to the include_path php.ini directive.
  3. More than one developer is working on the application (debatable)
  4. You have an existing MVC framework (Code Igniter, Symfony, Zend Framework) that your application is developed on.
Namespaces really can be handy, but they can also cause confusion. Just as you group methods and properties in a class for common, relational functionality, you may group classes and functions even further. However, there are some rules that you must follow.
Now for the Rules
Here are the rules from php.net:
  1. All qualified names are translated during compilation according to current import rules. In example, if the namespace A::B::C is imported, a call to C::D::e() is translated to A::B::C::D::e().
  2. Unqualified class names are translated during compilation according to current import rules (full name substituted for short imported name). In example, if the namespace A::B::C is imported, new C() is translated to new A::B::C().
  3. Inside namespace, calls to unqualified functions that are defined in the current namespace (and are known at the time the call is parsed) are interpreted as calls to these namespace functions, at compile time.
  4. Inside namespace (say A::B), calls to unqualified functions that are not defined in current namespace are resolved at run-time. Here is how a call to function foo() is resolved:
    1. It looks for a function from the current namespace: A::B::foo().
    2. It tries to find and call the internal function foo().
    To call a user defined function in the global namespace, ::foo() has to be used.
  5. Inside namespace (say A::B), calls to unqualified class names are resolved at run-time. Here is how a call to new C() is resolved:
    1. It looks for a class from the current namespace: A::B::C.
    2. It tries to find and call the internal class C.
    3. It attemts to autoload A::B::C.
    To reference a user defined class in the global namespace, new ::C() has to be used.
  6. Calls to qualified functions are resolved at run-time. Here is how a call to A::B::foo() is resolved:
    1. It looks for a function foo() in the namespace A::B.
    2. It looks for a class A::B and call its static method foo(). It will autoload the class if necessary.
  7. Qualified class names are resolved in compile-time as class from corresponding namespace. For example, new A::B::C() refers to class C from namespace >A::B.
Okay, so How Do I Use Them?
It seems that PHP will be going with a similar namespace setup as C++. In order to declare a namespace, you will use the “namespace” keyword.
<?php
namespace MyNamespace;
 
class Test {
    public function hello() {
        echo 'Hello';
    }
}
?>
The declaration above simply states that all elements contained in this script will be referenced with the “MyNamespace” namespace. You will need to place the “namespace” declaration at the top of the script.
In order to use a portion of functionality within this script, we will use the Scope Resolution Operator and instantiate the “Test” class.
<?php
// Requiring the namespace file is a good indication that you don't need to use namespaces, but this is only an example!
require('the_file_above.php');
$test = new MyNamespace::Test();
$test->hello();
// Prints 'hello'
?>
Pretty nifty, huh? Okay, so let’s get a little deeper into this. What if you have a complicated set of namespaces that are three or four levels deep? Is this a viable solution?
<?php
$object = new Level1::Level2::Level3::SomeClass();
$object->doSomething();
 
$object2 = new Level1::Level2::Level3::AnotherClass();
$object2->imTiredOfTyping();
?>
In case you’re wondering, the answer is NO. Instead, we can alias a namespace to save some keystrokes and future arthritis pains:
<?php
use Level1::Level2::Level3 as SomeAlias;
 
$object = new SomeAlias::SomeClass();
$object2 = new SomeAlias::AnotherClass();
?>
That saved some time! Now, how do we get that many levels of namespaces and why? Well, the more complicated your framework and libraries become, the more creative naming and depth of namespaces become. For instance, let’s consider a namespace that handles database actions. You may have a MySQL, Postgres, and MS SQL set of drivers. You could setup your namespace levels like this:
  • Database
    • Mysql
      • ActiveRecord
    • Pgsql
      • ActiveRecord
    • Mssql
      • ActiveRecord
In this example, albeit not the best, your namespaces could be setup in a relational manner:
Database::Mysql::ActiveRecord;
Database::Pgsql::ActiveRecord;
Database::Mssql::ActiveRecord;
Within the ActiveRecord namespace, you could have a class that handles common database transactions:
<?php
namespace Database::Mysql::ActiveRecord;
 
class Manager  {
    public function select($sql) {
        // do a select
     }
 
    public function update($table,$params) {
        // do an update
    }
}
?>
You could then have a directory outside of the web root called “database” which contains each of these DB namespaces, and add that to your INI “include_path” directive. This makes a large system a little better organized and maintainable. Just remember, namespaces add another layer of complexity to your codebase, so make sure your organization creates standards of developing namespaces.
You can also alias a class within a namespace for use:
<?php
use Database::Mysql::ActiveRecord::Manager as RecordManager;
$manager = new RecordManager();
?>
This scenario is handy if you find yourself instantiating a class multiple times. Declare the use of the namespace all the way to the class level, alias the class, then use it as you would as a normal instantiation.
Global Space
You can also redeclare PHP functionality that is available in the global space. Normally you can’t redeclare a function name without producing a fatal error. In the case of namespaces, it is possible!
Let’s say you want to recreate the function print_r. You might want to add a check to see if the script running is web or CLI (Command Line Interface). You can do so by prefixing the PHP function with the Scope Resolution Operator:
<?php
namespace String;
 
function print_r($string) {
    // Assume CLI
    if (isset($argv)) {
        ::print_r($string);
    } else {
        // Add <pre> tags for better output to a browser
        printf('<pre>%s< /pre>', ::print_r($string,1));
    }
}
?>
You could then call this function:
<?php
require('String.php');
String::print_r('Test text');
?>
This allows you to “redeclare” print_r.
Conclusion
As I took a glance at the upcoming namespace functionality in PHP 5.3, I was intrigued, but a little disappointed. I was really hoping that PHP would implement similar functionality to C#, but PHP is a very well-developed language that is constantly being improved, and the OOP implementation between versions 4 and 5 can account for that. I have high hopes that PHP will increase the functionality of namespaces, and satisfy users that have been using them for years with other tools. There is no doubt in my mind that PHP is heading in the right direction!