Purpose of the entry
With PHP5.3.3 recently released I really feel it is time that php developers are taking namespaces seriously. If you don’t I guarantee you will be out of a job within five years. Namespaces are a fundamental part of the future of PHP.
The big frameworks like Symfony and Zend Framework have understood this and build their latest versions (2.0) with namespace support.
You all have namespaced before using the filesystem and long class names. This is still a valid way to go, but as time evolves you will see this less and less. Probably the filesystem structure will remain but not the long tedious names. We will start to use aliasing or namespace scopes for that.
Overview
- Getting started with namespaces
- resources
Getting started with namespaces
So how does one begin with namespaces? Well simply by doing it. And what better way do to it than by example.
In this blog post I will explain how to use namespaces in a project and how to import two external libraries into it.
One your own and the other a real third party.
It is a small project and solely for demonstration purposes, kept intentionally very small but explanatory by nature.
So do not heckle me for design decisions and such, those would only cloud the goal of this small presentation.
You can download a working copy of the project so you can play with it and experience namespaces first hand.
namespaces.zip
I will wait a minute or so.
Done and loaded in the browser? Great, let’s get on with it.
As you see I first put all code outside of my public folder (httpdocs). I have put everything regarding setting include_paths
and constants in Application.php.
// Application.php
<?php
namespace {
error_reporting(E_ALL);
ini_set('display_errors', true);
function __autoload($class) {
$class = str_replace('\\', '/', $class);
$class = preg_replace('/^Application/', 'application', $class);
require_once $class.'.php';
}
set_include_path(
realpath(dirname(__FILE__).'/../').PATH_SEPARATOR.
realpath(dirname(__FILE__).'/../library')
);
}
namespace Application {
const PROJECT_PATH = '../';
$bootstrap = new Bootstrap(new \Nick\PlayBowl());
$bootstrap->run();
}
The file itself contains procedural code with two namespaces. You can attach as many namespaces as you want inside a file by encapsulating them between brackets. The namespace operator must be the first thing defined in a file.
In Application.php we have a global namespace in which we will register the autoload and setup the include_path. Autoloading namespaces
has to be done in the global scope. It also makes sense to set the include path in the global scope. Everything related to our application
itself goes into the Application namespace.
We will load the Bootstrap class from the namespace Application. Class names that do not contain a backslash like Bootstrap can be resolved in 2 different ways. If there is an import statement that aliases another Bootstrap to Bootsrap, then the import alias is applied.
Otherwise, the current namespace name is prepended to Bootsrap. Because we did not use the use operator no aliasing or importing is done, and Bootstrap will resolve to Application\Bootstrap().
Because no leading backslash is used when we defined the “namespace Application” we have specified an unqualified name. PHP will resolve this by adding the namespace to the current used namespace. In this case it is the \ (global) namespace.
// Bootstrap.php
<?php
namespace Application;
class Bootstrap
{
protected $_game;
public function __construct(\Nick\PlayBowl $game)
{
$this->_game = $game;
$this->_setupSomeUglyNamedNameSpace();
}
public function _setupSomeUglyNamedNameSpace()
{
\SomeUglyNamedNameSpace\Exception::setLogFile(namespace\PROJECT_PATH.'/logs/application.log');
}
public function run()
{
$this->_game->aim();
$this->_game->roll();
$this->_game->getHits();
}
}
Inside the Bootstrap.php file we have used the namespace operator to register the class Bootstrap under the Application namespace. We
did this by opening Application as the current namespace and then adding constants, functions and or classes to it. In our example the class Bootstrap.
Making it available by calling it from outside the current namespace using \Application\Bootstrap(); Just as we did with \Nick\PlayBowl in Application.php. As you can see we can also force the type in the constructor using namespaces just as we did with long class names.
In \Application\Bootstrap we use already 3 namespaces: Application, Nick and SomeUglyNamedNameSpace to target specific functions, constants and classes.
we have two different use cases for the namespace operator. Outside a namespace we switch the scope to a new namespace using namespace nameOfScope and inside a namespaced scope we can use the operator to target the current scope. This making the namespace operator the equivalent of the class self:: operator.
We have registered an instance of \Nick\Playbowl to $this->_game and we call its methods in the run() method. Let us take a look at that.
Because here you will see aliasing.
//PlayBowl.php
<?php
namespace Nick;
use \SomeUglyNamedNameSpace as Lib;
class PlayBowl
{
public function aim()
{
echo 'you are aiming at the pins<br />';
}
public function roll()
{
echo 'you release the ball and wait in anticipation<br />';
}
public function getHits()
{
$this->_hit();
$this->_hit2();
$this->_getPinsHit();
$this->_getPinsHit2();
$this->_getPinsHit3();
$this->_getPinsUnexisting();
}
protected function _hit()
{
$calculator = new Calculator();
if ($calculator->isGood()) {
echo 'you hit all pins<br />';
}
}
protected function _hit2()
{
$calculator = new namespace\Calculator();
if ($calculator->isGood()) {
echo 'you hit all pins<br />';
}
}
protected function _getPinsHit()
{
$someCalculator = \SomeUglyNamedNameSpace\Calculator::factory(\SomeUglyNamedNameSpace\Calculator::SCIENTIFIC);
$num = $someCalculator->add(2, rand(2,4));
echo sprintf('%d pins hit', $num),'<br />';
}
protected function _getPinsHit2()
{
$someCalculator = Lib\Calculator::factory(Lib\Calculator::SCIENTIFIC);
$num = $someCalculator->add(2, rand(2,4));
echo sprintf('%d pins hit', $num),'<br />';
}
protected function _getPinsHit3()
{
$someCalculator = new Lib\Math\Addition\Scientific();
$num = $someCalculator->add(2, rand(2,4));
echo sprintf('%d pins hit', $num), '<br />';
}
protected function _getPinsUnexisting()
{
try {
$someCalculator = Lib\Calculator::factory('science');
} catch (Lib\Exception $e) {
$e->log();
}
}
}
Ok a lot is going on in here. Or so it seems… What I actually have done is included a lot of duplicate code for your pleasure.
let us first take a look at hit() and hit2(). Those two are identical. My preference is the hit() method. I will explain.
This method is identical to hit2, but this function doesn’t use the namespace operator, because by default PHP will prepend it with the current namespace. In hit two we explicitely use the namespace operator and so we manually prepend the current namespace to the classname. In hit2 the namespace operator is the namespace equivalent of the self operator for classes.
Time to take a look at the _getPinsHit methods. Again these are example methods. Were in _getPinsHit we use the third party library SomeUglyNamedNameSpace and use the fully qualified namespace (leading backslash in the namespace).
_getPinsHit2() does exactly the same thing but here we use the namespace aliasing functionality. We have at the top of the file defined the
namespace alias with the use of the use operator. use \SomeUglyNamedNameSpace as Lib; Thereby effectively shortening that uglynamespace or preventing namespace clashes.
_getPinsHit3() goes further into that example by showing that you can easily use that aliased name as the base for nested namespaces.
Note that you can not have real nested namespaces like you can have nested ifs. You can only have nested namespaces like you can have nested classnames. Thus by appending the namespace names, extending the scope. Like in this example. The class Scientific is a part of the namespace Lib\Math\Addition\ which in his turn is a part of \Lib\Math\ and so on…
_getPinsUnexisting Doesn’t really demonstrate something in this class but I needed to show you the difference between global namespace functions and other namespaced functions.
But first let us take a look at the other usage of the use operator. The import usage. This will be used a lot to shorten names.
//Calculator.php
<?php
namespace SomeUglyNamedNameSpace;
use SomeUglyNamedNameSpace\Math\Addition;
class Calculator
{
const SCIENTIFIC = 'scientific';
static public function factory($type = 'scientific')
{
if (self::SCIENTIFIC === $type) {
return new Addition\Scientific();
}
throw new Exception('you must specify a valid Calculator type');
}
}
We used to do “return new SomeUglyNamedNameSpace_Math_Addition_Scientific();” , now with namespaces we can finally shorten it with:
use SomeUglyNamedNameSpace\Math\Addition; and from then of on you can call in the current scope simply “return new Addition\Scientific();” Which makes your code much easier to read.
Ok of to our final piece of example code:
//Exception.php
<?php
namespace SomeUglyNamedNameSpace;
class Exception extends \Exception
{
static protected $_logFile;
public function log()
{
file_put_contents(self::$_logFile, 'this will call the namespaced function');
\file_put_contents(self::$_logFile, date('H:i:s')."\t".'this will call the global function, which we all know');
}
static public function setLogFile($path)
{
self::$_logFile = $path;
}
}
function file_put_contents($filename, $string) {
echo 'this is '.__NAMESPACE__.' function';
}
I know in good design the last function should not have been in that same file, but this example if for the understanding of namespaces and nothing else. It isn’t a real Bowling game either 😉
Ok what is going on?
We first Extend the global Exception class which we all use everyday and implemented a log() method.
This log method does two things. It first calls the file_put_contents as an unqualified namespace (no backslash operator in the name found).
As before this will have the effect that it will first try to prepend the current namespace to the function and thus will find the function at the bottom of the file. And secondly we call explicitily the good file_put_contents, the global scope version! Which will nicely log the error message to the log file setted in our Bootstrap.php
I hope this small presentation on namespaces made a lot of things clear.
resources
See you soon,
Nick Belhomme