Drupal 8 tip of the day: autoloaded code in a module install file

Submitted by Frederic Marand on Thu, 2015-08-27 08:24
(updated 2017-08-30, see bottom of post). Autoloading in D8 is much more convenient that in previous versions, however, it still has limitations. One such issue is with hook_requirements(), which is supposed to be present in the module install file, not the module itself: when called at runtime for the site report page, the module is loaded and the PSR/4 autoloader works fine. However, when that hook is fired during install to ensure the module can indeed be enabled, the module is not yet enabled, and the autoloader is not yet able to find code from that module, meaning the hook_requirements('install') implementation cannot use namespaced classes from the module, as they will not be autoloadable. What are the solutions ?

The bad

The first solution is obvious but painful : since the file layout within the module is known at the time of committing, it is possible to avoid autoloading using require_once for each class/interface/trait needed by the code, typically like this:

// to male \Drupal\mymodule\Some\Namespace\Class available in mymodule_requirements().
require_once __DIR__ . '/src/Some/Namespace/Class.php';
// ... for each such class

The good

But there is a better way: just make the classloader aware of the module being installed:

// Not needed at runtime.
if ($phase === 'install') {
$module = 'mymodule';
drupal_classloader_register($module, drupal_get_path('module', $module));

During module enabling, its path is already known, so drupal_get_path can be used. With this little help, the module autoloaded code will be available during the install, and allow the hook_requirements() implementation to use it.

The ugly

One obvious temptation would be to put the hook_requirements() implementation in the module file, reasoning that, since if is in the module file, it implies that the module is available when the hook is fired. And indeed, this works for hook_requirements('runtime').

However, during install, the module is still not loaded, so the implementation for hook_requirements('install') is simply not found and the requirements check is ignored. Don't do it.

UPDATE 2017-08-30

On https://www.drupal.org/node/2667588#comment-10856112, this issue is referenced, and the practice described above is discouraged, because registering classes with the autoloader MAY cause undesired side-effects if the module installation fails. If the install may fail, the officially sanctioned practice is to use direct require_once(__DIR__ . '/..(relative path to classes'; for each class needed during install.

Check http://cgit.drupalcode.org/drupal/commit/?id=4e658ef for current details.