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:
- Your application is not on shared hosting.
- You have so many libraries, or your framework requires less work by adding class paths to the include_path php.ini directive.
- More than one developer is working on the application (debatable)
- 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:
- 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().
- 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().
- 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.
- 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:
- It looks for a function from the current namespace: A::B::foo().
- It tries to find and call the internal function foo().
- 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:
- It looks for a class from the current namespace: A::B::C.
- It tries to find and call the internal class C.
- It attemts to autoload A::B::C.
-
Calls to qualified functions are resolved at run-time. Here is how a call to A::B::foo() is resolved:
- It looks for a function foo() in the namespace A::B.
- It looks for a class A::B and call its static method foo(). It will autoload the class if necessary.
- 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;
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!
No comments:
Post a Comment