This seems to be a side-effect of the new file registry.
Old behaviour in D6:
1) You upload and install a module, decide you don't like it, and delete the module folder.
= system is slightly unstable, but a visit to admin/build/modules will auto-scan available modules and heal itself by discarding the old module setting.
2) In a multisite, you can have different versions of the same module in sites/subsitename/modules and sites/all/modules. Maybe for local tweaks, testing patches or branching. Later on, you consolidate back from sites/subsitename/modules/custommodule and want to start using the sites/all/ version.
= Easy, just delete the local one, and visit admin/build/modules. System autodetects the change.
3) For management or deployment purposes, you have modules in a subdirectory sites/all/modules/contributions sites/all/modules/modified. You want to modify a contribution, so first shift the module from one location to another.
= Easy, visit admin/build/modules. System autodetects the change.
but
not always any more.
DRUPAL-7-dev via CVS [2009-10-20]
(OSX localhost, PHP 5.2.11 locally built)
1) Deleting an in-use module seems to be recoverable as before.
2) When trying to store alternate versions of the same module in sites/all/modules and sites/sitename/modules, death happens.
Fatal error: Cannot redeclare devel_install() (previously declared in [..]/drupal7-dev/sites/devel/modules/devel/devel.install:8) in [..]/drupal7-dev/sites/all/modules/devel/devel.install on line 25
To replicate:
- Install (eg) devel.module in
sites/all/modules. Enable it. - (Realize you need an upgrade, so want to try a installing a localized dev version before you impact the main multisite /all version)
- Trying to be careful, disable devel.module first
- Download a new devel.module into
sites/sitename/modules - Visit
/admin/config/modulesto let the system know about the change. - (Confirm the module list is now identifying the local version to use - OK)
- Attempt to enable devel.module.
- Expected result : transparent switch to using the local copy of the module
- Actual result: "Fatal error: Cannot redeclare devel_install"
Irrecoverable through the UI. Needed to delete rows from system table.
I don't believe devel is at fault, same thing may happen with any module.
Seems to happen even if module is 'uninstalled'.
I'm imagining this is because the system includes the old module before it enables the new module - which triggers the duplicate. Although the old module was even disabled and should NOT be getting loaded?
Also, 2.b) If you DO get the system to recognize the local version (From a clean install, or hacking {system}), then later delete it to try to start using the /sites/all/ version again, the old incorrect path is remembered in the system, even though the modules page lists details from the the correct info file. This produces fatal error filectime(): stat failed ... DOException: SQLSTATE[HY000]: General error: 1366 Incorrect integer value as quoted below in 3)
3) Moving a module causes irrecoverable death.
(this is different from 2) overriding a module.
To replicate:
- Download a module (devel.module) into D7, placing it in
sites/all/modules/contributions/[note custom subdirectory] - Enable it at /admin/config/modules
- Move the module files up to
sites/all/modules/ - Attempt to use the /admin/config/modules interface. It will not allow any submission, will not even allow you to disable the module, responding:
Warning: filectime(): stat failed for sites/all/modules/contributions/devel/devel.module in _registry_parse_files() (line 127 of [..]/drupal7-dev/includes/registry.inc).
Warning: filemtime(): stat failed for sites/all/modules/contributions/devel/devel.module in _registry_parse_files() (line 128 of [..]/drupal7-dev/includes/registry.inc).
Warning: file_get_contents(sites/all/modules/contributions/devel/devel.module): failed to open stream: No such file or directory in _registry_parse_files() (line 132 of [..]/drupal7-dev/includes/registry.inc).
PDOException: SQLSTATE[HY000]: General error: 1366 Incorrect integer value: '' for column 'filectime' at row 1: INSERT INTO {registry_file} (filectime, filemtime, filename) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2) ON DUPLICATE KEY UPDATE filectime=VALUES(filectime), filemtime=VALUES(filemtime); Array ( [:db_insert_placeholder_0] => [:db_insert_placeholder_1] => [:db_insert_placeholder_2] => sites/all/modules/contributions/devel/devel.module ) in _registry_parse_files() (line 144 of [..]/drupal7-dev/includes/registry.inc).
The only way I found to recover was manually deleting some rows from the {system} table.
I'm aware there may be a couple of "Well don't do that then" claims that this is not expected behaviour, but it used to work just fine.
Isn't the entire point of having sites/sitename/modules so that we can have different branches co-existing?
At the very least, some bullet-proofing to not try and update {registry_file} with broken and bogus values should probably be a patch?
Note - testing done on a recycled, but CVS-updated checkout of D7. I'll try again on a clean clean version and try to imagine a test case.
Comments
Comment #1
dman commentedTracing what's going on here is tricky, because I've never looked at this code before, but even with an exception handler in place to avoid inserting invalid data, I'm finding it impossible to correctly switch between the two module locations.
Creating a brand new install, with a module installed in the sites/sitename/modules dir before installation is fine. BUT you cannot ever remove that local version and fallback to a centralized sites/all version.
So far, I've found that in system_rebuild_module_data() the correct, up-to-date filepaths detected from introspection are being overwritten by older incorrect values pulled out of the database by system_get_files_database()
When there is a conflict between what is actually on the filesystem and what the database thinks should be there reality should win but that is not what is happening.
Comment #2
dman commentedFOUND IT
I think.
- if (!isset($files[$file->name]->key)) {+ if (!isset($files[$file->name]->$key)) {A typo?
... under investigation, I think I've broken more things by 'fixing' this.
edit: yep, applying this fix slaughters the entire system, leaving short filenames in the {system} table instead of full filepaths.
I'm sure that
->$keyis what was intended there ...->keyis meaningless ... but the results are not what is expected.Comment #3
dman commentedLooks related?
#345031: Useless condition in system_get_files_database
Comment #4
sunI think this is fixed in #625744: Duplicate modules cause epic fail
Comment #5
dman commentedEpic fail indeed. :-)
Look mostly similar, must try my test cases.
Hoping that fix does it.