Blog post: Proposal for fixing PHP namespacing in Drupal 8

Collisions with non-Drupal PHP code

In theory, although I have never see this happen, a PHP library that Drupal is utilising could declare a function or class that is already declared in Drupal. This would cause PHP to abort.

PHP 5.3 has a solution - namespaces (http://php.net/manual/en/language.namespaces.php).

Drupal 8 should require PHP 5.3 - it was released 2009-06-30. Drupal 7 requires 5.2 and may just get released this year. When will D8 be released? Probably 2012 or 2013 - by then PHP 5.3 will be 3 or 4 years old.

Every PHP file that is part of Drupal would be in the 'drupal' namespace. This would be declared at the top of every PHP file as follows:

<?php
namespace drupal;

Because all of Drupal is in its own namespace, there is no possibility of function or class name collisions.

We are already prefixing many functions with drupal_ - this can simply be dropped from such function declarations.

The new Database API in D7 is not really part of Drupal - it can be used without the rest of Drupal with only a few minor changes. It is a standalone library, and as such it should be in its own namespace:

namespace drupaldb;

the db_ prefix could then be dropped from all function declarations in the library.

Modules and themes

Modules and themes (and engines) would have a namespace such as

namespace drupal\taxonomy;
or
namespace drupal\bartik;

Function prefixes, such as 'taxonomy_' would be dropped.

However, this makes Drupal code really ugly.

Calling functions not in a module from a module would look like this:

\drupal\set_message();

Calling a db function would look like this:

\drupaldb\select('node');

Calling an API function in another module would look like:

\drupal\taxonomy\select_nodes();

The solution? Namespace importing.

Every PHP file in a module would need to declare which namespaces it needs to access, e.g.

<?php
namespace drupal\node;
use drupal, drupaldb as db;
use drupal\taxonomy;

This means the above examples would look like:

drupal\set_message();
db\select('node');
taxonomy\select_nodes();

Which is really quite pleasant!

Calling a function that is in the current namespace would not require any prefixing. For example, calling \drupal\node\access_needs_rebuild() from inside namespace 'drupal\node' would be invoked as:

access_needs_rebuild();

Namespace importing should not be confused with module dependencies - importing is specified on a per-file basis.

Magic functions (hook implementations)

There are a couple of namespacing issues that can occur with magic functions:

  • Ambiguity of where the module name ends and the hook name begins, e.g. views_comment_load() - is the hook 'comment_load' or 'load'?
  • Modules can inadvertently implement a hook for some other module.
  • Multiple modules can 'invent' the same hook, expecting it to do different things

Solution: use __

Magic functions would take one of the following forms:

INVENTOR__HOOK()
INVENTOR__HOOK__PARAM()
INVENTOR__HOOK__PARAM1__PARAM2() etc

INVENTOR is the name of the module that 'invented' the magic function.
HOOK is the name of the hook, e.g. form_alter.
PARAM is for additional parameters, e.g. the name of the form to be altered.

e.g.

system__menu()
block__info()
system__form_alter__search_form()

Only magic functions can have __ in their name. Modules, hook names [updated] and parameters are not allowed to have a double underscore. Drupal should not permit any machine name to have a double underscore.

A further advantage of this is that the name of a magic function for a specific hook would be the same in every module that implements the hook. This helps code portability - example hook implementations could be put in place simply by cutting and pasting code.

Theme / process / preprocess implementations

These are also magic functions.

The hook name is 'theme' / 'process' / 'preprocess'.

A module that declares a theme hook in a system__theme() is considered the 'inventor' of the magic function.

theme_user_list($variables)
becomes
user__theme__list($variables)

in both the module that invented it and in themes that are overriding it.

A module / engine / theme could implement a preprocess hook like:

system__preprocess__page()
system__preprocess__links__contextual__node()

The second example is for the 'fallback' theme items described here.

Example

The attached node.module has been modified to incorporate this proposal (and a few other code style changes).

AttachmentSize
node.module.namespaced124.24 KB
Namespaces2.pdf33.4 KB