Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

Has anyone used autoloading and/or PHP namespaces within a plugin or theme?

Thoughts on using them? Any harm? Pitfalls?

Note: namespaces are PHP 5.3+ only. Assume, for this question, that you know you'll be dealing with servers that you know have PHP 5.3 or greater.

share|improve this question
add comment

2 Answers

up vote 28 down vote accepted

Okay, I've had two big projects where I've been in control of the server enough to namespace and rely on autoloading.

First up. Autoloading is awesome. Not worrying about requires is a relatively good thing.

Here's a loader I've been using on a few projects. Checks to make sure the class is in the current namespace first, then bails if not. From there it's just some string manipulation to find the class.

<?php
spl_autoload_register(__NAMESPACE__ . '\\autoload');
function autoload($cls)
{
    $cls = ltrim($cls, '\\');
    if(strpos($cls, __NAMESPACE__) !== 0)
        return;

    $cls = str_replace(__NAMESPACE__, '', $cls);

    $path = PLUGIN_PATH_PATH . 'inc' . 
        str_replace('\\', DIRECTORY_SEPARATOR, $cls) . '.php';

    require_once($path);
}

One could easily adapt this for use without namespaces. Assuming you prefix your plugin's/theme's classes uniformly, you could just test for that prefix. Then use underscores in the class name to as placeholders for directory separators. If you're using a lot of classes, you'll likely want to use some sort of classmap autoloader.

Namespaces and Hooks

WordPress' hooks system works by using call_user_func (and call_user_func_array), which takes function names as strings and calls them at when the do_action (and, subsequently, call_user_func) function call is made.

With namespaces, that means you'll need to pass fully qualified function names that include the namespace into hooks.

<?php
namespace WPSE\SomeNameSpace;

add_filter('some_filter', 'WPSE\\SomeNameSpace\\the_function');
function the_function()
{
   return 'did stuff';
}

It would probably be better to make liberal use of the __NAMESPACE__ magic constant if you want to do this.

<?php
namespace WPSE\SomeNameSpace;

add_filter('some_filter', __NAMESPACE__ . '\\the_function');
function the_function()
{
   return 'did stuff';
}

If you always put your hooks into classes, it's easier. The standard create instance of a class and all hooks in the constructor with $this works fine.

<?php
namespace WPSE\SomeNameSpace;

new Plugin;

class Plugin
{
    function __construct()
    {
        add_action('plugins_loaded', array($this, 'loaded'));
    }

    function loaded()
    {
        // this works!
    }
}

If you use static methods like I want to do, you'll need to pass the fully qualified class name as the first argument of the array. That's a lot of work, so you can just use the magic __CLASS__ constant or get_class.

<?php
namespace WPSE\SomeNameSpace;

Plugin::init();

class Plugin
{
    public static function init()
    {
        add_action('plugins_loaded', array(__CLASS__, 'loaded'));
        // OR: add_action('plugins_loaded', array(get_class(), 'loaded'));
    }

    public static function loaded()
    {
        // this works!
    }
}

Using Core Classes

PHP's classname resolution is a bit wonky. If you're going to use core WP classes (WP_Widget in the example below) you must provide use statements.

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

Or you can use the fully qualified class name -- basically just prefixing it with a backslash.

<?php
namespace WPSE\SomeNameSpace;

class MyWidget extends \WP_Widget
{
   // ...
}

Defines

This is more general PHP, but it bit me, so here it is.

You may want to define things you'll use often, like the path to your plugin. Using the define statement puts things in the root namespace unless you explicitly pass the namespace into the first argument of define.

<?php
namespace WPSE\SomeNameSpace;

// root namespace
define('WPSE_63668_PATH', plugin_dir_path(__FILE__));

// in the current namespace
define(__NAMESPACE__ . '\\PATH', plugin_dir_path(__FILE__));

You can also use the const keyword on in the root level of a file with PHP 5.3 plus. constss are always in the current namespace, but are less flexible that a define call.

<?php
namespace WPSE\SomeNameSpace;

// in the current namespace
const MY_CONST = 1;

// this won't work!
const MY_PATH = plugin_dir_path(__FILE__);

Please feel free to add any other tips you might have!

share|improve this answer
add comment

I use autoloading (as my plugin has loads of classes - partly because it includes Twig), never had an issue brought to my attention (plugin installed > 20,000 times).

If you are confident that you will never ever need to use a php installation that doesn't support namespaces then again you are fine (~70% of current wordpress blogs do not support namespaces). A few things to note:

I seem to remember that namespaces are not case sensitive in regular php but are when using fastcgi php on iis - this causes some headaches if you test on linux and don't spot a rogue lowercase letter.

Also even if you are sure that the code you are currently developing will only be used on > 5.3.0 you will not be able to reuse any code with projects that do not have that luxury - that is the main reason why I haven't used namespaces on internal projects. I have found that namespaces really don't add that much when compared with the possible headache of having to remove the dependency on them.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.