11

I created a module through Drupal Console, unfortunately it wasn't placed in a folder of its own, rather straight into /modules/custom. Before I noticed where it had gone, I enabled the module.

In Drupal 6/7, this was easy to fix - I could literally just put the files into the correct path, clear the cache (maybe rebuild the registry, it wasn't an exact science), and all was well.

The same isn't true for Drupal 8; I moved the files into a subfolder, rebuilt cache, and all requests tell me that Drupal is failing to include a required file. The particular file is a block class, and the path Drupal is trying to find it is where it used to be, not where I've moved it to.

The bit of code objecting is in Symfony's ApcClassLoader, so I guess I need to clear something that's slightly above Drupal's head to get this path reference updated.

How can I do that?

Uninstalling and re-installing the module does work, but once there's important data stored that's not going to be an option.

I was wrong, uninstalling and re-installing is not a viable option. After uninstalling and reinstalling, Drupal can no longer pick up the custom blocks the module defines. I'm guessing that reference is cached somewhere that isn't cleared when the module is uninstalled. Just need to find out where that is and how to clear it...

3
  • So, if you create first.module and a second.module with Drupal Console, do you find both those file in /modules/custom? I will never create a module with that, then.
    – apaderno
    Jan 27, 2016 at 15:45
  • I had an old version @kiamlaluno, upgraded now and that problem isn't there. The problem in the question most definitely is though, it's actually getting worse. I've just added a brand new module, with a brand new block, and Drupal now won't pick up that block for the admin page. Something's got really screwed up inside its config but I don't know where to look :/
    – Clive
    Jan 27, 2016 at 15:51
  • Is ApcClassLoader::findFile() getting the wrong path from apc_fetch(), or is the decorated class loader returning the wrong path?
    – gapple
    Jan 27, 2016 at 17:59

6 Answers 6

12

Just restart apache. The APC cache will be rebuild then.

You can also try to hit rebuild.php or run drush cr. Those all attempt to explicitly clear the APC cache.

5
  • Yeah I did the same things (nginx+fpm but restarted both several times), plus running apc_flush_cache directly and even switching between php5 and 7 so totally different stores, still saw the problem but I'm guessing it's something with my setup specifically, logic says one of those methods would fix it. will have a closer look tomorrow
    – Clive
    Jan 27, 2016 at 19:32
  • Night time seems to have fixed it so I'm going to put a pin in this for now...
    – Clive
    Jan 28, 2016 at 16:55
  • rebuild.php (open in browser) helped, restarting apache and clearing cache did not help.
    – drupalfan
    Aug 18, 2017 at 10:20
  • I did cache clear, run rebuild.php, apache restart. Still not working getting same error. Blocks refering old module path.
    – ARUN
    Feb 22, 2018 at 9:47
  • Drush doesn’t use the same APCU as the web server so doesn’t clear it. github.com/drush-ops/drush/pull/2450 Feb 27, 2021 at 12:10
4

Moving modules to a different location without disabling it is a quite tricky process in Drupal 8. There's a lot of discussion about it but I coudn't find a proper solution so I've decided to digg a bit. In Drupal 7 it was easy, just put a module to new location, clear the cache and you're done, but D8 might be different. So, this is the quick step through without answering why you need to do this or that, I'll answer this later.

First of all, check if apcu extension is installed and enabled in your php. You should do it by visiting /admin/reports/status/php. Keep in mind there's no point to check it through the console as it might use different php.ini file. This needs to be done through browser.

With APCU extension enabled.

  1. Move your module code to desired location.
  2. Clear drupal cache.
  3. Call apcu_clear_cache() function, but it has to be triggered from browser. There's no way it'll work by calling it from console or drush, so keep in mind that drush ev "apcu_clear_cache();" will not work.
  4. That's it, now your module should be available to use and it should not complaining by missing PHP classes or plugins.

Without APCU extension enabled or installed.

  1. Move your module to different location
  2. Clear drupal cache.
  3. Done

A bit of explanation

Let's digg a bit and find the details, so we have some idea what's happning here.

In most answers you will find out that cleaning drupal cache or providing extra class_loader_auto_detect setting to FALSE will work for you. Sometimes it works, sometimes not. If you put your class_loader_auto_detect setting to FALSE it'll work but you will loose class caching mechanism. Is it really a good idea? Well, no. If it is an option and it's ready to use why would you? There's no point doing it. Instead let's use it.

So, let's assume that you do have APCU extension enabled. While drupal 8 is booting DrupalKernel.php boot method will use APCU by default unless it's disabled in you settings.php (class_loader_auto_detect settings set to FALSE) which can be found here:

https://github.com/drupal/drupal/blob/9.2.x/core/lib/Drupal/Core/DrupalKernel.php#L476

So, if the class_loader_auto_detect setting is not set just make it TRUE and use it - that's what's in code. When that happens we know that APCU extension is already used. But what exactly does it?

Well, it does walk through all of the php classes and cache its names, locations and namespaces. That's the reason your module is broken after the relocation.

Example:

Let's assume you have a custom block defined at:

/app/drupal/web/modules/custom/your_module/src/Plugin/Block/CustomBlock.php

This block plugin class, namespace and its location is cached by APCU. Let's prove it. Just call apcu_cache_info() function which is part of APCU extension. Now, move your module into new location and for testing purposes, for example to:

/app/drupal/web/modules/contrib/your_module/src/Plugin/Block/CustomBlock.php

Now, call again apcu_cache_info() function. You can see that the location of block plugin class is still registered at:

/app/drupal/web/modules/custom/your_module/src/Plugin/Block/CustomBlock.php

So this is the old location. Now, try to clear drupal cache and call it again. It's still the same. It's not going to be changed as it's not part of drupal cache. What drupal cache does in terms of relocating modules is an entry in you cache_container table. It contains information about location of your .module file and .yml file, but not about specific php classes.

So, what exactly happens when you try to display or load a block which has been relocated? Well, it'll throw a PluginException error, which happens here:

https://github.com/drupal/drupal/blob/9.2.x/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php#L96

if (!class_exists($class)) {
  throw new PluginException(sprintf('Plugin (%s) instance class "%s" does not exist.', $plugin_id, $class));
}

It fails on class_exists() function because it gets the name and namespace of class which is still registered in:

/app/drupal/web/modules/custom/your_module/src/Plugin/Block/CustomBlock.php

not

/app/drupal/web/modules/contrib/your_module/src/Plugin/Block/CustomBlock.php

location. That's what PHP gets.

Solution

So, what can we do about it? Well, it seems like pretty simple task, we need to clear the APCU cache apcu_clear_cache(). But there is a catch, if you do it from console it'll not work, in short it's not hitting the same APC segment of your webserver (more details here: https://stackoverflow.com/a/43501450/567058). It needs to be triggered from browser. But keep in mind that it needs to be called at very early stage of drupal bootstrap or on any drupal page that actually loads without any issues. So, don't try to call it on broken block page in preprocess function or similar, because it'll never gets to preprocess functions. It'll fail way before and it'll never have a chance to clear apcu cache. The best solution is to use extra contrib module which brings APCU cache clear (I'll give you a link later on) option or call it on any non broken page, might be custom controller or just a preprocess_html, up to you.

Final thoughts

As you can see drupal 8 modules relocation might be really tricky. There's several things that needs to be checked. There might be modules that can be relocated by clearing drupal cache and that's enough, just because the module constis of .module and .info files, without any php custom classes and there's nothing to be cached by apcu and all the information (cache_container table) are storred in DB, so it'll just work. There are cases where modules have custom classes and still can be relocated just becasue there's no apcu extension in your php instace or you have class_loader_auto_detect setting set to FALSE and uncommented. So, each case might be different and I think this is the reason why people are so confused about relocating modules and for some clearing the drupal cache will work and for other will not.

1
  • 1
    drush ev "apcu_clear_cache();" works for me. Seems worth a try
    – Beebee
    Jul 30, 2021 at 16:47
1

Drupal stores a cached version of the container at the cache_container table. This table contains, among other things, the list of module paths.

For most cases running drush cr should be enough but if you see that even drush cr crashes then you will need to create a database update like this in an already installed module:

/**
 * Clears all caches after moving modules from modules to modules/contrib.
 */
function mymodule_update_8100(&$sandbox) {
  drupal_flush_all_caches();
}

And then run drush updb -y.

1
  • I had this issue, drush cr raised an error (drupal cr worked and was ok) but for our case the update hook with drupal_flush_all_caches(); fixed the issue. Would be nice if it was easier. Aug 12, 2021 at 4:33
0

Another solution is put in your settings.php:

 /**
 * Class Loader.
 *
 * If the APC extension is detected, the Symfony APC class loader is used for
 * performance reasons. Detection can be prevented by setting
 * class_loader_auto_detect to false, as in the example below.
 */
$settings['class_loader_auto_detect'] = FALSE;
0

This worked for me By Admin UI Navigate to /admin/config/development/performance and ​click the button "Clear all caches".

0

None of the above solutions helped I had to visit /update.php which bypasses the drupal bootstrap. I suppose

drush updb

would do the same.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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