This issue was encountered on a Drupal 6 installation, but the code suggests it might apply to Drupal 7 as well:
- drupal_find_theme_templates() calls drupal_system_listing(), using the filename to index the results.
- drupal_system_listing() in turn calls file_scan_directory() which is dependent on the order in which readdir() returns files and uses depth to determine wether a match should replace a previous match.
- readdir() returns files in the order in which they have been stored in the file system.
Given the following two files:
- parent_theme/templates/global/page.tpl.php
- parent_theme/subthemes/child_theme/page.tpl.php
, page.tpl.php would no longer get picked up for the parent theme after a backup was restored. Debugging revealed that file_scan_directory would consistently return the page.tpl.php file of the child theme.
In this scenario restoring the backup changed the storage order of the involved files, thus causing the bug.
| Comment | File | Size | Author |
|---|---|---|---|
| #5 | 2704583-drupal-find-theme-templates-multiple-name-scan-D7.patch | 1.5 KB | dave reid |
| #5 | 2704583-drupal-find-theme-templates-multiple-name-scan-D8.patch | 1.24 KB | dave reid |
Comments
Comment #2
donquixote commentedI agree with this concern.
Just to clarify:
file_scan_directory() does give priority to files in the parent folder.
But it does not have a predictable way to prioritize one subfolder over another. It cannot know that "/templates/" belongs to the parent theme, but "/subthemes/child_theme/" belongs to the sub-theme.
The only way to make this distinction would be to look for *.info files in subfolders, and stop there.
Imo, file_scan_directory() is doing too many things at once.
It would be easier to fix this, if we knew that this function was only used to scan for theme templates.
Since it is used for all kinds of arbitrary and unrelated stuff (also in contrib), we need to be super careful for side effects.
Comment #3
ciss commentedComment #4
dave reidConfirmed this bug in D8 and D7. This happens because drupal_system_listing() is called to index the results by 'name', which if there are multiple templates with the same name in the directory, will only return one result. This will be a problem when the first result found is not the desired theme that is actually being searched.
Given this structure:
mytheme/ - mysubtheme/ - templates/ - page.tpl.php - mytheme.info - templates/ - page.tpl.phpThe results of
drupal_system_listing('/\.tpl\.php$', 'sites/all/themes/contrib/mytheme', 'name', 0)only returns one result, which is unexpected, there were two files that ended with .tpl.php!Now, if we change the 'name' parameter to 'uri' to ensure that results with the same filename are included, we do get two results
Note that what happened before is that since sites/all/themes/contrib/mytheme/mysubtheme/templates/page.tpl.php was found second, it overwrote the first result, since the return value was keyed by filename, and both filenames were 'page.tpl.php'. What happens originally is then since the one result returned was in a subtheme path, it was excluded as a candidate template for page.tpl.php, leaving no more results. Which then falls back to using modules/system/page.tpl.php, when we just wanted to use sites/all/themes/contrib/mytheme/templates/page.tpl.php!
Comment #5
dave reidPatches for D8 and D7 attached. Likely needs tests.
Comment #6
dave reidComment #22
jkdev commented7 years later, the issue still exist.
Only this time this is in:
\Drupal\Core\File\FileSystem::doScanDirectoryatweb/core/lib/Drupal/Core/File/FileSystem.php:707:Can we work on this, so that in
ext4filesystems, this will not mess up themes?More info about my situation are here: #3329810, #3316641 (with full easy reproduce)
Can we use scandir instead?
(
scandir(string $directory, int $sorting_order = SCANDIR_SORT_ASCENDING, ?resource $context = null): array|false)