A primer on PHP namespaces

16th February 2012

I know that there are a lot of posts now about namespaces in PHP 5.3. This is mine which is how I learnt how they work.

What are namespaces?

From the PHP manual:

namespaces are a way of encapsulating items

Hardly the most useful of definitions, but it's a starting point! A namespace is a way of grouping code that exists across multiple files without having a naming collision. That is, you can have the same named class in two different places if they are encapsulated within namespaces.

Of course the way that ZF1 solves this problem is with Extra_Long_Classnames which are obviously unique. Namespaces allow us to reference the classname by the important bit (the last section and not have to carry the meta data (where it lives) in every use of the class.

That is, namespaces allow us to:

combine libraries with the same classnames avoid very long classnames organise our code easily

Note that namespaces do not just affect classes. They also affect functions and constants.

Defining a namespace

There is a new keyword called namespace which is used to declare a namespace for a file. This is file-wide:


    namespace Zend\Db\Statement;
    
    class Sqlsrv extends AbstractStatement
    {
    }

Note that we can have multiple sub-namespaces, separated by the backslash. When we extend the Sqlsrv class the extended class is within the namespace too, but not in the same file.

We can then use the class like this:


    $stmt = new \My\Db\Statement\Sqlsrv(); 

You can have multiple namespaces in a file, but the first namespace must be the first line of code in the file.

Working within the same namespace

When you are working within the same namespace, then any unqualified functions and classes will be resolved to the current namespace. i.e:


    namespace My\Db\Statement;
    
    function testSqlsrv()
    {
        $stmt = new Sqlsrv();
    }

In this case, the SqlSrv class is in the namespace \\My\\Db\\Statement.

Namespace importing: the use keyword

We can import a namespace into a different file using the use keyword.


    namespace My\Application;

    use My\Db\Statement;

    $stmt = new Statement\Sqlsrv();

Note that we don't use a leading \\ in the new statement as we are using a qualified namespace, not a fully-qualified one.

or you can import a specific class from a namespace;


    namespace My\Application;

    use \My\Db\Statement\Sqlsrv;
    $stmt = new Sqlsrv();

It follows you can use multiple use statements:


    use \My\Db\Statement;
    use \My\Db\Adapter;
    
    $stmt = new Statement\Sqlsrv();
    $adapter = new Adapter\Sqlsrv();

You cannot do this though:


    use \My\Db\Statement\Sqlsrv;
    use \My\Db\Adapter\Sqlsrv;
    
    $stmt = new Sqlsrv();
    $adapter = new Sqlsrv();

as clearly PHP cannot resolve which Sqlsrv class to instantiate.

You can also alias namespaces. This allows us to reference a long namespace with a shorter name or to import two namespaces having the same name and give them different names.


    use \My\Db\Statement\Sqlsrv as DbStatement;
    use \My\Db\Adapter\Sqlsrv as DbAdapter;
    
    $stmt = new DbStatement();
    $adapter = new DbAdapter();

This also allows you to write code the focusses on the functionality of the class rather than the specific type. For example, we could start using the Mysqli versions of the statement and adapter by just changing the use statements.

The __NAMESPACE__ constant

The constant __NAMESPACE__ provides the current namespace name. In the global space it will be an empty string.

Namespace resolution

This bit is really important!

An unqualified class name is resolved in this order:

If there is an import statement that aliases another name to this class name, then the alias is applied. Otherwise the current namespace is applied.

An unqualified function name has different rules:

The current namespace is prepended to the function name. If the function names doesn't exists in the current namespace, then a global function name is used if it exists.

This means that within a namespace'd file, you can do:


    $date = date('Y-m-d');

but not:


    $datetime = new DateTime();

Instead, you have to use:


    $datetime = new \DateTime();

Conclusion

That's really all you need to know to use namespaces in a PHP application. They aren't so hard really.

Using Zend\Loader\Autoloader

13th February 2012

Autoloading is the process in PHP whereby the system attempts to load a class when it is first encountered (via new or via class_exists) if it hasn't already been loaded via a require or include. Autoload works by looking for a method called __autoload or walking through any method registered with spl_autoload_register.

Zend Framework 2 provides the Zend\Loader\Autoloader component for autoloading of Zend Framework and your own classes.

What does it provide?

The ZF2 autoloader provides the following:

PSR-0 compliant include_path autoloading PSR-0 compliant per-prefix or namespace autoloading Class map autoloading, including class map generation Autoloader factory for loading several autoloader strategies at once

It provides this functionality through two classes: StandardAutoloader and ClassMapAutoloader.

Zend\Loader\StandardAutoloader

StandardAutoloader implements loading of classes by inspecting their classname and finding the file on disk. It supports three strategies:

Searching the include_path Loading from a list of specific namespace / directory pairs Loading from a list of specific vendor prefix / directory pairs

These can be combined for complete flexibility.

Example usage:


require_once ZF2_PATH . '/Loader/StandardAutoloader.php';

$autoLoader = new ZendLoaderStandardAutoloader(array(
    'prefixes' => array(
        'MyVendor' => __DIR__ . '/MyVendor',
    ),
    'namespaces' => array(
        'MyNamespace' => __DIR__ . '/MyNamespace',
    ),
    'fallback_autoloader' => true,
));

// register our StandardAutoloader with the SPL autoloader
$autoLoader->register(); 

Searching the include_path is the slowest and similar to how ZF1's autoloader works and is known as the fallback autoloader as it's the last resort :)

Having configured our StandardAutoloader and registered it, we can then use it:


$test1 = new MyNamespaceTest();
$test2 = new MyVendor_Test();

What's the difference between a prefix and a namespace?

A prefix system is used for non-namespaced classes. i.e. those classes where the directories are separated by underscores. e.g. Zend_Config_Ini. A namespace is a PHP 5.3 namespace where the directories are separated by the namespace separator or underscores. For example: the class MyNamespace\Sub_Test would be stored in /path/to/MyNamespace/Sub/Test.php.

Programmatic interface

You can also use a Programmatic interface rather than configuring in the constructor:


require_once ZF2_PATH . '/Loader/StandardAutoloader.php';

$loader = new ZendLoaderStandardAutoloader();

$loader->registerPrefix('MyVendor', __DIR__ . '/MyVendor')
       ->registerNamespace('MyNamespace', __DIR__ . '/MyNamespace')
       ->setFallbackAutoloader(true);
       
$loader->register();

If you have multiple prefixes or namespaces, you can use `registerPrefixes` and `registerNamespaces` which take arrays.

Zend\Loader\ClassMapAutoloader

The class map autoloader is a high performance autoloader. It uses class maps, which are simply associative arrays of each classname to the name of the file disk that contains that class. As a result, it is very fast as the work done is an array key lookup. In fact the entire autoload method is simply:


public function autoload($class)
{
    if (isset($this->map[$class])) {
        include $this->map[$class];
    }
}

You can't get much faster than that! It also works well with PHP opcode cache and realpath caches.

To use the class map autoloader, you need a class map file, autoload_classmap.php such as this:

<?php 
return array(
    'MyNamespaceTest' => __DIR__ .  '/MyNamespace/Test.php',
    'MyVendor_Test'    => __DIR__ .  '/MyVendor/Test.php',
);

You can the use the class map autoloader like this:


require_once ZF2_PATH . '/Loader/ClassMapAutoloader.php';
        
$autoLoader = new ZendLoaderClassMapAutoloader(
    array(__DIR__ . '/autoload_classmap.php'));
        
// register with the SPL autoloader
$autoLoader->register(); 

In this case, you can only load those classes that are listed in the supplied file(s). Note that, by convention, the class map file is called autoload_classmap.php, but can be any file that returns an array.

Note that the constructor allows you to pass in multiple map files:


$loader = new ZendLoaderClassMapAutoloader(array(
    __DIR__ . '/../library/autoload_classmap.php',
    __DIR__ . '/../application/autoload_classmap.php',
));

There's also a method called registerAutoloadMap() that does the same thing.


$loader = new ZendLoaderClassMapAutoloader();
$loader->registerAutoloadMap(array(
    __DIR__ . '/../library/autoload_classmap.php',
    __DIR__ . '/../application/autoload_classmap.php',
));

Note that new maps are merged with the maps that are already registered. The last definition for a class wins. Hence, you can override the location of class if required.

Creating class maps

As you can imagine, creating class maps manually would quickly get tiresome. To alleviate this, Zend Framework 2 provides a PHP script, classmap_generator.php in the `bin` directory that will do this for you. This tool will scan the entire directory from the current directory (or that specified via an option) and create a class map file for every class that it finds. It is used like this:

    prompt> php path/to/zf2/bin/classmap_generator.php -w
    Creating class file map for library in '/var/www/project/library'...
    Wrote classmap file to '/var/www/project/library/autoload_classmap.php'

I expect that ZF2 will be distributed with an autoload_classmap.php when it is released.

Combining autoloading strategies

ZF2 also provides an AutoloaderFactory that allows you to combine ClassMapAutoloaders and StandardAutoloaders. This is especially helpful in development where you may want to use a ClassMapAutoloader for ZF2 files, but a StandardAutoloader for your own files. Usage is like this:


require_once ZF2_PATH . '/Loader/AutoloaderFactory.php';
ZendLoaderAutoloaderFactory::factory(array(
    'ZendLoaderClassMapAutoloader' => array(
        __DIR__ . '/../library/Zend/autoload_classmap.php',
    ),
    'ZendLoaderStandardAutoloader' => array(
        'prefixes' => array(
            'MyVendor' => __DIR__ . '/MyVendor',
        ),
        'namespaces' => array(
            'MyNamespace' => __DIR__ . '/MyNamespace',
        ),
        'fallback_autoloader' => true,
    ),
));

When the factory method executes, it will load and initialise each autoloader in turn and then call the register() method to register with the spl_autoload system. As spl_autoload uses as queue, you should put the class map autoloaders first as they return really quickly on failure.

Note, that if you subsequently use the factory to add more files to the class map autoloader, for instance, then it will reuse the same ClassMapAutoloader instance that it created earlier.

One-to-many joins with Zend_Db_Table_Select

8th February 2012

Let's say that you want to set up a one-to-many relationship between two tables: Artists and Albums because you've refactored my ZF1 tutorial.

Let's assume that an artist has many albums. These are the basic table definitions:

artists table: id, artist
albums table: id, artist_id, title

When you list the albums, you obviously want to see the artist name rather than the id, so clearly you use a join!

Assuming you're using Zend_Db_Table, the easiest way is to turn off the integrity check and do a join in a mapper or table method.

Something like this:


class AlbumTable extends Zend_Db_Table_Abstract
{
    protected $_name = 'album';

    public function fetchAllWithArtistName($order = array('title ASC'))
    {
        $select = $this->select();
        $select->setIntegrityCheck(false);
        $select->from($this->_name);
        
        $select->joinLeft('artist', 'album.artist_id = artist.id', 
            array('artist_name' => 'name'));
        $select->order($order);
        
        $rows = $this->fetchAll($select);
        return $rows;
    }
    
}

The row set returned will have all the columns from the albums table and one additional column called artist_name which is an alias of the name column from the artists table.

Two new Sublime Text 2 packages for PHP

6th February 2012

Stuart Herbert has written two new Sublime Text packages for PHP:

The best way to install these is to install Package Control first and then use shift+cmd+P -> install package.

Even better, Stuart has rolled my getter/setter creation snippet into Additional PHP Snippets, so you can now have it without any hassle!

Update: Also, Ben Selby has created a package for DocBlox!

Jason Grimes: Using Doctrine 2 in Zend Framework 2

30th January 2012

Jason Grimes has posted an article showing how to use Doctrine 2 with Zend Framework 2.

He uses my tutorial as the starting point which enables him to concentrate on the Doctrine integration rather than the irrelevant details about setting a ZF2 application which is excellent.

He walks through 6 steps in order to do the integration:

This article shows how to set up and use Doctrine 2 in Zend Framework 2, by extending Rob’s Getting Started tutorial to use Doctrine instead of Zend_Db.

Start with Akrabat’s tutorial Install Doctrine modules Configure the Album module to use Doctrine Create the Album entity Update the Album controller to use Doctrine instead of Zend_Db That’s it!

I highly recommend having a read if you're at all interested in using Doctrine 2 with Zend Framework 2.


You are viewing a mobilized version of this site...
View original page here

Mobilized by Mowser Mowser