Preparing for Drupal 8: PSR-4 Autoloading

As a developer, one of the first new concepts you're going to need to understand for Drupal 8 is PSR-4. Or, the PHP Framework Interoperability Groups (php-fig), Proposal for Standard Request number 4 (PSR-4). A lot of fancy naming, abbreviations and numbers for a pretty simple idea. Allow your PHP classes to be automatically discovered and loaded by following a convention for class namespace and file/directory naming.

Drupal 8 implements the PSR-4 standard and makes use of it to discover code, and to load the code on an as-needed basis rather requiring every single file in Drupal's code base to be loaded for every request. This has all kinds of great implications.

Pro-tip: Drupal 8 initially used the PSR-0 standard but switched to PSR-4 for most everything to help simplify the developer experience. Change record

For starters, if you follow the standard, you can just do this:


$object = new MyAwesomeClass();

Instead of:


require_once 'path/to/my/file/myawesomemodule.class.inc';
$object = new MyAwesomeClass();  

Or the more Drupal 7 appropriate:


module_load_include('inc', 'myawesomemodule', 'myawesomemodule.class');
$object = new MyAwesomeClass();  

I know it's only one line of extra code. But not having to know where a file lives and/or how to include it is absolutely glorious.

This system uses the PHP Autoloading Classes feature, which acts as a registry of sorts for classes. But it doesn't just load all the known files for every request, it's actually smart enough to defer loading of the file that contains a class until someone actually instantiates an instance of that class. This means less code loaded per-request, which reduces PHP's overall memory footprint and helps increase the speed of an application.

In Drupal 7, we have an approximation of this system in that, you can add a line to your .info file and Drupal will scan the listed files for class definitions and then add them to its database based registry —and it works. But, PSR-4 is more efficient and more like what our friends in the rest of the PHP world are doing. By using this system, the Drupal community helps pave the way for greater sharing between frameworks.

Implementing PSR-4

Okay, sold? Here's what you need to know about PSR-4 for Drupal module development.

Let's assume we've got a module named Markus. Each Drupal module has a namespace that corresponds to that module's name:

Drupal\markus

The module's namespace is mapped to the src/ directory for the module:

Drupal\markus → modules/markus/src/

Anything following the module's namespace maps directly to the directory and file structure:

Drupal\markus\Clothes\Pants → modules/markus/src/Clothes/Pants.php

Want to do something with Markus's pants in your code? Simply instantiate a copy of the class using its fully qualified namespace—anywhere in your code—and Drupal will take care of locating the file and including it.

$pants = new Drupal\markus\Clothes\Pants();
$pants->putOn();

Would use: modules/markus/src/Clothes/Pants.php

/**
 * @file
 * Pants object.
 */

namespace Drupal\markus\Clothes

class Pants extends Wearables {
  public function putOn() {
    // code goes here ...
  }
}

You can read more about PSR-4 and namespace resolution in Drupal in the handbook.

Related Topics: 

Comments

Another way to refer to a class is to use the "use" statement, after which the class name can be referred to without being fully qualified. In some cases, this may make the code a little more concise but still easy to understand, especially if there are multiple references to the class and its methods.

Following your example,

use Drupal\markus\Clothes\Pants; // Often placed at the top of the file.

$pants = new Pants();

Earl, yes. This is absolutely true. And it makes the code much easier to read in my opinion. Drupal actually makes heavy use of the <code>use</code> statement throughout the core codebase.

"But not having to know where a file lives and/or how to include it is absolutely glorious."

huh? no, I don't have to know the module is at modules/markus/src but I do have to know that the file I need is at Drupal\markus\Clothes\Pants or Drupal\markus\Clothes\Fabric\Blue\Jeans\Pants.php.

If a module properly uses files[] in .info, I only have to know $object = new Pants().

Seems we've only shifted which part of the tree we need to know not made it better. Not sure I would classify that as 'glorious'.

It's this type of 'everything about Drupal 8 is amazingly brilliant' hyperbole that has me still so concerned. Nothing is always amazingly brilliant in every aspect. It would be really really nice to see some more balanced writings about D8-- which would include the cons that inevitably accompany all such massive architectural changes along side the pros.

I have to agree with you there, @just-passin-thru. Please stay a while :)

I enjoyed this article. Very easy to digest as I come up to speed on D8 and all the new relevant standards.

"it's actually smart enough to defer loading of the file that contains a class until someone actually instantiates an instance of that class"

There may be some edge cases for when we're building customizable functionality in classes that are loaded, but won't this benefit just go away after the class is instantiated and loaded into memory once it's used the first time?

Yeah, once you instantiate an object, and use the class, the file is parsed, and it's loaded into memory. There's no way around that. The benefit with using autoloading, like PSR-4, is that files are only read into memory if the class is actually used during the request.

By comparison, non-autoloading implies that you have to load every single class into memory even if you never use it, just so you can make sure it's available when you do try and use it. Consider the following code:


if ($i == 'some condition') {
$ClassOnlyUsedHere = new SomeClass();
$ClassOnlyUsedHere->doSomething();
}
else {
$Class = new OtherClass();
$Class->doSomethingElse();
}

In this case you've got classes that are only used if the some conditional logic in the application is met. Your options here are either autoloading at the time of use, or load all classes during bootsrap just in-case you need SomeClass. In the later the code that composes SomeClass is parsed and loaded into memory even if it's not used.

Add new comment