Closed (fixed)
Project:
Drupal core
Version:
9.4.x-dev
Component:
extension system
Priority:
Normal
Category:
Task
Assigned:
Unassigned
Reporter:
Created:
2 Jun 2013 at 22:00 UTC
Updated:
26 Jan 2022 at 13:34 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #1
tstoecklerI guess this is independent of the UpdateModuleHandler patch.
Oops, just saw that @chx is assigned, but I guess you're working on the parent issue?! Sorry, in case any toes were stepped on.
Comment #2
chx commentedOh that was totally unintended.
This is good but needs module_install itself converted.
Comment #3
chx commentedAnd thanks for taking this up so fast!
Comment #4
tstoecklerNo problem.
module_load_include() wasn't converted itself, and I feared that converting the two would lead into all sorts of installer/update/foo-related problems, so I didn't take that up. I'll do that no, however, and see what breaks.
Comment #5
tstoecklerAhh, in module_load_include() there's this gem.
I guess that's a feature that's specifically needed for loadInclude(). So that makes the patch above broken, IINM.
EDIT: Fix weirdo syntax highlighting
Comment #6
tstoecklerYeah, to do this cleanly we would need to convert drupal_system_listing() into a class we can inject into ModuleHandler. I'll see if I can come up with something.
Comment #7
tstoecklerOK, this is embarassing...: https://api.drupal.org/api/drupal/core!lib!Drupal!Core!SystemListing.php...
Comment #8
tstoecklerOK, here's as far as I got.
module_load_install() is currently being (ab)used for loading the install profile's .install file. I had no more steam for fixing that.
Also, this does not yet work, Drupal does not yet install. Somehow update.fetch.inc from update module is not loaded.
Comment #9
tstoecklerAt least Drush installs with this. Let's see.
A note for reviewers: I went the extra mile of splitting the 'loader' part into a dedicated interface, because not doing that would have meant tainting the ModuleHandler with a SystemListing object. I wanted to avoid that, because I think it's a very powerful feature that ModuleHandler relies solely on the module list.
Suggestions on everything (!), but also specifically on naming improvements are very much welcome.
Comment #11
panchoTitle and OP are a bit contradictory:
Is this actually about module_load_install() or about module_install()?
Anyway. Let's review what we have here:
1.
Nice. Yes, I agree that ModuleHandler should only act on our (enabled) $moduleList, making sure that code of uninstalled modules is clearly separated and never called here. We don't know yet the outcome of #1199946: Disabled modules are broken beyond repair so the "disable" functionality needs to be removed, but if it doesn't get postponed to D9, one thing is clear: there won't be a thing such as completely disabled modules anymore. Either the disabled status gets removed altogether and replaced by some convenient, or at least some code continues to run.
So at this point we only need to separate out uninstalled/never installed modules (forthcoming paradigm), but may separate out disabled modules as well (current paradigm). In any case, a ModuleHandler method should never be able to call or load any uninstalled module's code.
So what you're doing is fine. However we should take notice that #2001310: Disallow firing hooks during update brought us UpdateModuleHandler (extending ModuleHandler), and of #2004784: Move module install/uninstall implementations into ModuleInstaller wanting to bring us ModuleInstaller (implementing enable(), disable(), uninstall() and probably to be added install() in a new ModuleInstallerInterface. If the latter one lands, we could just roll loadInstall() into ModuleInstaller without tainting ModuleHandler, and that might be enough.
We probably need to further track the direction of all of these issues to keep things a bit consistent. It'still so much a moving target... :(
2.
It is very nice to have a separate loadInstall() method. ModuleHandler::loadInclude should certainly disallow the 'install' type now, because we're no longer loading the install API there:
Now I think we should push this paradigm a bit further:
First of all, we don't want loadInclude() load any file. As the name says, it should load includes (PHP files with the ending .inc) and not any '.php' or '.module' or '.whatever' file. This would also allow us getting rid of the $type parameter.
At the moment we're seeing loadInclude() being inconsistently implemented:
Drupal::moduleHandler()->loadInclude('locale', 'fetch.inc')Drupal::moduleHandler()->loadInclude('views', 'inc', 'views.theme')Drupal::moduleHandler()->loadInclude('views', 'inc', 'includes/ajax')While the first one is convenient, it slightly abuses the second parameter.
Much cleaner if we can leave out the $type parameter, so it always looks like this:
Drupal::moduleHandler()->loadInclude('locale', 'locale.fetch')Drupal::moduleHandler()->loadInclude('views', 'views.theme')Drupal::moduleHandler()->loadInclude('views', 'includes/ajax')3.
Yes, a ProfileHandler (ThemeHandler, too?) would be nice. Fine to be a followup.
4.
We don't want to load includes for disabled modules, so I believe loadInclude() should only implemented in ModuleHandler and shouldn't act on SystemListing.
The other way around, we actually don't want ModuleHandler to call any install hook or load the file containing it. This should be possible only on uninstalled modules. But for that we'd need to separate out the actual install hook from our install files, so we'd have a real install file that can only be loaded if uninstalled, an uninstall file with the uninstall hook that can only be loaded when installed, an update file that is only called by the new UpdateModuleHandler and a schema file that even could be in YAML format (leveraging the Symfony model, see #1263478-29: Identify pieces of Drupal that could be replaced by Symfony code.
5.
Deferring more nitpicks to get the general paradigm settled first... :)
Comment #12
tstoeckler1. Yeah, this was basically spawned by the UpdateModuleHandler patch (which got in already). Removing the 'disabled modules' concept is not necessarily relevant to this patch. When installing a previously uninstalled module, we still need some facility to load its install file. I disagree with "#2004784: Move module install/uninstall implementations into ModuleInstaller" basically for the same reason I mentioned here: It would taint the ModuleHandler with SystemListing information. I must admit I haven't followed that to closely so far.
2. While I agree that loadInclude()'s parameters are completely bogus, IMO changing them is out of scope of this issue. IIRC there are over 50 invocations of that in core. Also this is a complete bikeshed, which parameters to change, remove, etc.
3. Yay! :-)
4. In theory I agree with everything you said. The thing is that we need loadInstall() for both enabled and disabled/uninstalled modules. And loadInstall() currently calls loadInclude(). Personally, I think refactoring that would be out of scope for this issue, that might be something to do together with the follow-up in 2. I haven't looked into the concrete implications of splitting loadInstall() and loadInclude(), however, it might be more trivial than I imagine.
5. OK. :-)
Comment #13
pancho1.
Why would it taint the ModuleHandler? It would just taint the ModuleInstaller, and I'd say that's okay.
2. There's #507396: Change module_load_include() parameters, and I still believe we will come to a conclusion. It's all too obvious that it needs to be changed to the better. Slightly changing 50 invocations is no problem at all, given the huge amount of changes we're having anyway in D8.
4.
Yes, it's very trivial. And while it might be out of scope here, it might influence our decision, where to put all of this. If our issue here were critical or major, or very complex, then we should probably get it fixed, do the rest in followups and possibly come back and refactor. But in this case I'd rather propose doing it altogether.
Comment #14
tstoeckler1. I didn't know the direction of that issue was to *not* re-(ab)-use ModuleHandler. I totally agree with the later comments in that issue.
2. Well let's discuss that there, then. Right changing 50 invocations is no problem. It makes a patch very succeptible to re-rolls, though... So, yeah, let's leave that out for now.
4. Hmm... I'll see what that would actually change in terms of real code.
Comment #15
pancho@tstoeckler:
Regarding the disentangling, I'm preparing a patch in #507396: Change module_load_include() parameters later tonight.
We don't have much time left here either, so we should move this one into ModuleInstaller per #14.1.
But for that we need #2004784: Move module install/uninstall implementations into ModuleInstaller moving on.
Do you have time for a new patch now? Otherwise it would be on my to-do list for tonight. Will be a long night. Needs to be... :)
Comment #21
MerryHamster commentedComment #22
MerryHamster commentedtoo many changes since the last patch in the core, trying to make the patch for 8.6.x
Comment #23
MerryHamster commentedComment #26
andypostWorking on #697946: Properly deprecate module_load_include() and move it into \Drupal::moduleHandler() service it's clear this loading install should be separate method
Comment #30
sanjayk commentedComment #31
sanjayk commentedI have created patch for 9.1.x-dev.
Comment #34
tr commentedComment #35
tr commentedClarified issue summary. As stated in the original IS, this issue is a follow-up to #2001310: Disallow firing hooks during update, so I read that to determine what the intention of this issue was.
Comment #36
tr commented#31 no longer applies. It also does not properly deprecate
module_load_install()(it just removes it) and it contains many coding standards problems.I think we've gotten a little off-track here over the years. The main reason for this issue is to deprecate the procedural function
module_load_install($module)and provideModuleHandler::loadInstall($module)as a replacement for it. This is consistent with the larger effort in the meta, #3097045: [META] Provide modern replacements for and deprecate the legacy include files.By off-track I mean that instead of just moving the code the above patches are trying to change the implementation of the load install, and also change the way other pieces of code (e.g.
ModuleHander::loadInclude()) work.I think that given the fact this issue has been bouncing around for 8 years now, and that we're trying to get the procedural code removed before D10 (less than 6 months from now!), a more productive and safe strategy is to just do the straight deprecation and code move. We know that will work because it's working currently. Improvements on the ModuleHandler implementation should be deferred and handled in a separate issue.
Here is my patch to do this. It is basically written from scratch, but includes bits from #697946: Properly deprecate module_load_include() and move it into \Drupal::moduleHandler() service because that issue was expanded recently to overlap this one. I have also added a draft change record for the changes in my patch.
Comment #37
tr commentedComment #38
tr commentedI aborted the test in #36 because there was a mistake in the patch. Here is the corrected patch.
Comment #39
tr commentedBecause the app.root service is already being injected into the ModuleHandler, we should use that instead of DRUPAL_ROOT.
One line diff ...
Comment #40
andypostIt has collision with #697946: Properly deprecate module_load_include() and move it into \Drupal::moduleHandler() service
Comment #41
tr commented@andypost Yes, I'm aware. I mentioned this in #36. Regardless, I think that it would be easier to handle them separately. This issue is a lot less complicated that the other, and keeping them separate makes both easier to commit IMO.
Comment #42
andypostComment #43
tr commentedRe #42:
Surely that is an issue relevant to the
app.rootservice itself, and NOT an issue with code (including this patch) that simply uses that service.Comment #44
daffie commentedThe function module_load_install() is deprecated.
There is a deprecation message test add.
All usage of function have been replaced.
I have updated the IS.
The IS and the CR are in order.
For me it is RTBC.
Edit: for the added method Drupal\Core\Installer\Form\SelectProfileForm::__constructor(), the class is not extended in contrib. See: http://grep.xnddx.ru/search?text=SelectProfileForm&filename=
Comment #45
alexpottThis is a deprecated function and therefore should not be changed. It is okay for deprecated functions to use deprecated functions.
This makes me very unsure... we're just calling out to procedural code again. I think it might be better to move module_load_install() and module_load_include() to bootstrap.inc. That way they'd be autoloaded by composer and we could then work on removing the rest of module.inc. The usages of these method should go away as we replace them with solutions based on the autoloader.
Comment #46
longwave@alexpott this seems close to landing; if we get this in then #697946: Properly deprecate module_load_include() and move it into \Drupal::moduleHandler() service can come next to deal with
module_load_include()? That issue is kind of stalled as it's duplicating this one somewhat, but splitting the scope in two seems easier to deal with.Comment #47
alexpott@longwave as stated in #45 I'm not really convinced that adding these to ModuleHandler is really the right way to go. Loading code is our autoloaders responsibility - this is all legacy stuff. The call out to the extension list in #697946: Properly deprecate module_load_include() and move it into \Drupal::moduleHandler() service from loadInclude() so we can include modules that are not installed is a step backwards. It means the ModuleHandler and extension list service have a circular dependency. If we want to get rid of module.inc we should move these methods to bootstrap.inc and then work on deprecating the other methods here. And then we need to work on properly OO-ifying the systems that are dependent on this code.
Comment #48
tr commentedThe two points in #45 seem contradictory to me. The first says leave the procedural call in the deprecated function, the second says the procedural call should be removed from the deprecated function. I don't understand the reasoning.
To the first point, I would prefer not to leave the procedural call in
drupal_uninstall_schema(). As the deprecation says, "No direct replacement is provided" for this function, so anyone trying to deal with the deprecation in their own code is going to look in core to see how it is implemented and what they can replace it with. If the only answer is to replace the deprecated function with another deprecated function, that's not helpful at all. Core should be serving as an example here, as the source code in core is often the best or only documentation. I feel it is best to remove all uses ofmodule_load_install()from core if we're going to deprecate it.To the second point, in my view the big step we're taking here is changing the API and making the deprecation, as that's the task that is constrained to happen only for a major point release. The details of the implementation may be changed at any time. And in particular #697946: Properly deprecate module_load_include() and move it into \Drupal::moduleHandler() service will be replacing this use
module_load_include(). I think it's important to leave it in until that issue figures out how to re-writemodule_load_include()so that it covers all the current use-cases in core and so that we don't interfere with what that issue is trying to do.I don't think we should "move module_load_install() and module_load_include() to bootstrap.inc".
module_load_install()especially - since it is rarely used, we really don't want it to be loaded on every page. Moving these functions just pushes the problem down the road. I think there's an argument to keep this functionality out of the autoloader because having the autoloader do discovery of functions in randomly named include files seems like a lot of overhead to me. When we have one class per file, with a defined directory structure, finding classes can be simple and efficient. But finding functions when there are many functions per include file, and the include files can be anywhere, that seems like something that might be better left in the ModuleHandler. Contrib is always going to have to deal with explicitly loading procedural code at some level.Comment #49
andypostMaybe just decorate classloader for lookup module name in namespaces?
Comment #50
alexpottMove them into the module handler will also load the code on every page. Adding to the ModuleHandler interface gives us more problems down the road.
Comment #51
tr commentedHmm, yes I guess it will.
OK, then how about moving
module_load_install()into the ModuleInstaller? That was one of the ideas discussed above over the past 8 years. I don't really want to rehash that discussion, but if you think that's a better place then I'll re-roll the patch to do that.Regardless, if the goal is to remove and replace the procedural code in module.inc, it has to go somewhere. Is it realistic to talk about extending the autoloader for this purpose before D10? Or should it just be moved to a service like ModuleInstaller (which doesn't get loaded on every page, I hope ...).
Comment #52
alexpottI think this goal is of questionable value. Procedural code does not equal bad code. When we first embraced object orientation we perhaps went too far and didn't leverage composer's ability in to include files as much as we should have. As both of these methods are about including code I think it is fine to park them in a file loaded by composer. And then eventually we should replace all of their usages with autoload-able classes. Yes that means coming up with a plan to do that but for me thats better than moving to some place. making contrib change and then having to change again when finally work out to do the things these functions enable us to do.
Also these functions are not big functions and we already load them on every request (see \Drupal\Core\DrupalKernel::loadLegacyIncludes()) so moving them to bootstrap.inc or ModuleHandler does't save us anything.
Comment #53
andypostI see only the one related issue #1308152: Add stream wrappers to access .json files in extensions
No reason to load any includes except .install file (for schema only) because they very probably needs namespace registered in classloader, so only accessing composer's data using extension lists could be compromise.
Not sure a stream is a good wrapper to get a path to some extension to try load a file... or pick it from some map from compposer
Comment #54
andypostit prevents loading one more include
Comment #55
daffie commentedThis problem will solve itself as we have #3221051: [meta] Replace hook_schema() implementations with ::ensureTableExists(). After that the hook_schema can be removed.
Comment #57
alexpottThere's no need to change the underlying code here. We don't need to preserve the ability to load code from uninstalled modules in the new function.
This can call $this->loadInclude()
Add a string typehint to $module. I'm not sure that the minimum PHP version of Drupal 9 allows for a return typehint of
string|falsewhich is a shame.This might be the one place where we need to load code that is not installed. I think we should inline the ability to do that here. There is an issue to remove this check so this functionality might not exist much longer anyway.
Comment #58
alexpottThis is one other place where we need to load code from an uninstalled module. This function places very strict requirements on a module's hook_requirements() implementation. Given the module's code is not autoloadable at this point (because the container doesn't have the module namespace added). Therefore I think it is acceptable to inline the functionality here too. If we change the functions in install.inc to methods on some service we can consider a public API for this but I'd be an advocate for not doing this. Eventually it'd be great if all this requirement checking prior to module installation was replaced by a module's composer.json.
Don't change deprecated code. There's no need to.
Comment #59
alexpottLooking at the current implementation of ModuleHandler::loadInclude() - I think we should move to that instead of adding a new method.
Comment #60
alexpottAddressing #57. #58 and #59. No interdiff because no longer adding ModuleHandler::loadInstall() so the interdiff would be twice the size of the patch. Plus the last patch is over 6 months old.
Comment #61
alexpottlolz - this is install.inc... plus given we're using \Drupal::service below - checking for the existence of the extension.list.module service is unnecessary. It has to exist for the module handler to exist.
Comment #62
alexpottWhoops. Fixing this.
Comment #65
alexpottNow that #697946: Properly deprecate module_load_include() and move it into \Drupal::moduleHandler() service has landed we can move the test to the better location.
Comment #66
alexpottWe deprecated module_load_include() for Drupal 11 because of the amount contrib usage. This method only has 3 pages - http://grep.xnddx.ru/search?text=module_load_install&filename= - I think that that means we can remove in Drupal 10. Will ping a release manager.
Comment #67
daffie commentedI have updated the IS and the CR.
All the code changes look good to me.
For me is it RTBC.
Comment #69
catchCommitted/pushed to 10.0.x and cherry-picked to 9.4.x, thanks!