Aug 4, 2014

Autoloading at module install time from module itself == problem

In my last post I described implementing hook_page_build(), which along with other hooks is defined in the .module file.

This function needs to look up paths and aliases. In our module that functionality is provided by trait LookupPath which I described in  http://optimizely-to-drupal-8.blogspot.com/2014/07/drupal-8-requires-php-54-or-higher-and.html.

Because hook_page_build() is a global-level PHP function and therefore does not provide a class context, I was not able to directly use the trait.

I took the expedient approach of defining a very thin wrapper class within the .module file. It's not pretty, but I figured it would work.

  class LookupPathWrapper {
    use Drupal\optimizely\LookupPath;
  }

A use of this wrapper would be, for example,

  $path_alias = LookupPathWrapper::lookupPathAlias($proj_path);

In fact, it did work for quite a while. During development I would edit the .module file, clear caches, and continue.

Then I uninstalled the module and re-installed. That's when things went seriously awry.

I kept getting an error about not being able to find the trait in the .module file. Every page of the site was broken. Even after emptying all the cache tables in the database and deleting all the subdirectories under sites/default/files, the site was completely broken.

The module seemed to be partly installed, partly not. Its database table had not been created, the absence of which triggered other error messages as I muddled through this mess.

I ended up re-installing Drupal repeatedly as I tried to figure out how to use the trait. The critical factor was that the trait is part of the module that is being installed.

Finally, I just wrote two ordinary functions that repeat the two lines of code in the trait's methods and placed them directly into the .module file, like the following. The module now installs fine.

function _lookup_path_alias($path) {

  $alias = \Drupal::service('path.alias_manager')

             ->getPathAlias($path);
  return (strcmp($alias, $path) == 0) ? FALSE : $alias;
}


function _lookup_system_path($alias) {

  $path = \Drupal::service('path.alias_manager')

            ->getSystemPath($alias);
  return (strcmp($path, $alias) == 0) ? FALSE : $path;
}


A different approach would be to place the functions into a file to require for re-use in different places, but I didn't want to bother with the refactoring that would have entailed.

In short: it looks like autoloading of the classes and traits of a module does not work when you are in the process of installing the module itself.

This article for Drupal 7 sounds closely related:

autoloading won't work during module install
https://www.drupal.org/node/2078587

Also,  

psr0
https://www.drupal.org/project/psr0

2 comments:

  1. For what it's worth I found this too:
    https://www.drupal.org/node/1976210

    I look forward to going over this with you again as after reading your post the point you try to tell me about at our last "playday" makes more sense. I love this blog :)

    ReplyDelete
    Replies
    1. Yep, that article on "Early-bootstrap class loading" deals with the autloading problem I ran into, and more. It's impressive what that module developer has done.

      Thanks so much for your appreciation for the blog. It's been a lot of work, but I do get a lot out of it.

      Delete