drupal_get_filename (in bootstrap.inc) is designed to be callable when the database is not intialised or not working. But a call drupal_get_filename("theme","foo") will in those circumstances look only for a file foo.theme in certain directories. Phptemplate themes do not, of course, have a foo.theme file, so the relevant theme directory is not found. Furthermore, the wrong directories are searched. drupal_get_filename does not look in the sites/all hierarchy at all if the config file is in sites/default
It looks as though this function has not been updated since the new theme system was introduced.
I'm afraid I cannot work out what the best way to fix this is. Perhaps we need something involving drupal_system_listing?
This bug applies also to drupal 5, and to the current drupal 7 code
Comment | File | Size | Author |
---|---|---|---|
#51 | drupal_get_filename-341140-48_p0-D6.patch | 2.64 KB | webchick |
#50 | drupal_get_filename-341140-48.patch | 2.65 KB | catch |
#47 | drupal-get-filename-341140-47-D6.patch | 2.71 KB | bdragon |
#46 | drupal-get-filename-341140-45-D6.patch | 2.13 KB | catch |
#45 | drupal-get-filename-341140-45-D6.patch | 2.13 KB | catch |
Comments
Comment #1
arhak CreditAttribution: arhak commentedsubscribing
Comment #2
JohnAlbinHere’s a completely untested patch. :-p
Comment #4
JohnAlbinHmm… The patch definitely applies cleanly. Re-trying.
Comment #6
JohnAlbinWhy does the test bot hate me? I’m a nice guy.
Comment #8
JohnAlbinAha! davidstrauss explained in IRC that this means the Drupal installer couldn't run with this patch applied.
So, testbot does like me! It just has poor communication skills. "Failed to install HEAD" sounds like a bot failure; the error message should be "Drupal installer failed to run after patch applied."
Comment #9
JohnAlbinComment #11
JohnAlbinOk. I've actually run the installer with this patch.
[Edit: ignore the D6 patch; its broken because the syntax for calling drupal_system_listing() changed between D6 and D7. Masks in D7 require leading and trailing slashes.]
Comment #12
cburschkaWould it make sense to look for modules by their .info files as well?
Comment #13
JohnAlbinGood point! That actually simplifies the patch. Since drupal_get_filename is only ever passed "theme, theme_engine, module", then we just need to special-case the theme_engine and look for .info files as the default. :-)
New patch needs review!
Comment #14
JohnAlbinJust realized the $conf variable is now completely unused. New patch removes it.
Also, if you are reviewing the patch and wondering why the call to drupal_system_listing() looks the way it does, have a look at the way it is called in other places in Drupal (like in module_rebuild_cache): http://api.drupal.org/api/function/drupal_system_listing
Comment #16
JohnAlbinBot, please re-test. The patch should still be working.
Comment #17
JohnAlbinNew patch refactors drupal_get_filename() using a try/catch block per chx's suggestion in IRC. I'm not positive I see the utility over a non-catch/try version, so I've attached the alternative if anyone wants to compare. But I trust chx's code idea over my own. [Edit: …for good reason; see next comment.]
@Arancaytar: Apparently part of the system is expecting the return value of drupal_get_filename() to be pointing at directly at a module's .module file. So we can't return the path to the module's .info file. :-(
This new patch also adds some additional drupal_function_exists() calls since drupal_get_filename now depends on drupal_system_listing().
Finally, this new patch also adds a simpletest! My first. Testing bot, please be nice to me.
Comment #18
JohnAlbinOk. So the reason the try/catch block is better… Suppose the database is active, but there's a db failure because the tables aren't populated (like, if you were inside a DrupalUnitTestCase!), then the non-try/catch code would fail completely. :-p This is why I trust chx even when I don't understand him at first. :-)
This new patch gets rid of the evil
"${type}s"
string construct and shortens the code by one line (compared to previous patch).Comment #20
JohnAlbinbot was broken, afaik
Comment #21
chx CreditAttribution: chx commentedI recommend drupal_get_filename('profile', 'test') for a real test.
Comment #22
chx CreditAttribution: chx commentedEdit: the reason for this is that this way in case of any error you fail back to filesystem.
Comment #23
JohnAlbinHere's a new patch with the improvements chx mentioned in #21 and #22.
Comment #24
chx CreditAttribution: chx commentedNice that you use drupal_function_exists to make sure you are not throwing a fatal error by calling a nonexisting function but in here when we know that we do not know ;) what state the database is in I would use function_exists instead. And note this in a comment.
Comment #25
JohnAlbinRe-rolled.
Comment #26
chx CreditAttribution: chx commentedLooks good.
Comment #27
JohnAlbinFYI, this badly needs to be fixed in D6. Zen can't be used as a maintenance theme because I used
drupal_get_path('theme', 'zen')
to optimize/split up the template.php file.Also, if you didn't notice while reading between the lines, chx helped me a lot with this patch. So credit goes to him as well.
Comment #28
webchicktry/catch blocks are not typical in Drupal. It'd be good to document here why we're using it. The old code checking for db_is_active() was self explanatory.
That's great. Why? ;)
Also, why are we not doing something here and just falling through? Usually we would trigger an error. Let's explain why we're not.
This is clever, but kind of a total hack. :P It only works because we use consistent naming conventions for themeS profileS and moduleS. But I guess that's ok...
However, how does this work for things in sites/all/modules or sites/all/modules/contrib? We've lost the $conf array data with this information?
hook_boot() and hook_exit().
This review is powered by Dreditor.
Comment #29
chx CreditAttribution: chx commentedComments. That's what I write today. Lots and lots of comments.
Comment #30
webchickThis looks good, apart from the hook_boot/hook_exit comment was actually not about that hunk but the one added by the patch which, if I weren't so braindead right now, I would've realized was just flat-out wrong. :P Fixed this and committed to HEAD.
I really like this because it allows us to use drupal_get_path() consistently, rather than having to teach people tricks about when it sometimes works and other times doesn't.
Marking 6.x to be ported so Gabor can take a look.
Comment #31
JohnAlbinBah. It looks like when drupal_function_exists() was rolled back, that parts of this code weren't done properly. drupal_function_exists() did two things:
The drupal_function_exists() in the patch in #29 were simply replaced with function_exists() calls, which doesn't do #2.
Here's a D7 patch that replaces the function_exists() calls with require_once statements that actually load in the function definitions that we need for this code.
And here's a D6 patch that backports everything.
Comment #32
catchHmm, this adds two extra require_once to every bootstrap. Could we keep the function_exists() and require_once if it's not available?
Comment #33
JohnAlbin@catch Ok! Fair enough. function_exists() doesn't have to do file stats, so calling it should be faster than require_once.
Re-rolled both D7 and D6 patches.
Comment #34
catchThanks, that was my only complaint.
Comment #36
ksenzeeHEAD was broken. Resetting status.
Comment #37
JohnAlbinAdding "quick fix" tag.
Comment #38
webchickCommitted to HEAD, thanks!
Marking down to 6.x for review.
Comment #39
JohnAlbinThe D6 patch in #33 still needs review.
Comment #40
JohnAlbin/me scratches head. I could have sworn I removed the issue tags in my previous comment. :-\ That was kind of the whole point of it. Sorry for the unintentional spam. :-p
Comment #41
JohnAlbinOk, drupal_get_filename() is not something people normally use. But themes can't use module_load_include(), so they have to use its lower-level cousin drupal_get_path(). And drupal_get_path() is broken because of this issue.
Here's how to test this patch.
require './' . drupal_get_path('theme', 'garland') . '/color/color.inc';
Functionally, this line does nothing useful; it just tests drupal_get_path() which calls drupal_get_filename().
$conf = array('maintenance_theme' => 'garland');
That's the official way to set your maintenance theme in Drupal, fyi.
$db_url = 'mysqli://gumby:ohnomrbill@localhost/drupal';
Comment #42
sign CreditAttribution: sign commentedBeen able to replicate this issue following the notes in #41 in D6.
The patch in #33 solved the problem.
attached re-rolled patch
Comment #43
JohnAlbinSo… RTBC, then? :-)
I've confirmed the patch in #42 is a straight re-roll of my D6 patch in #33 and that it applies cleanly.
Comment #44
Gábor HojtsyIt does not look like this patch was extensively tested. By reading the patch, the pattern is not right when the type is not a theme or theme engine (ie. it looks for "dot then end of string, then "type" as a literal character sequence, then end of string again). Unlikely to match.
Comment #45
catchUntested re-roll. Updates for string concat style changes and fixes the single quoted variable.
Comment #46
catchJohn Albin pointed out in irc that this is only for D6 and we don't necessarily need to update the code style. Not sure what the preference is now but here's one with oldie-style concatenation (if I got it right, I think I've erased that from my memory).
Comment #47
bdragon CreditAttribution: bdragon commentedIf drupal_load() is called in hook_requirements() or similar, the first check will pass and the db_query will run, but if we're currently installing from an install profile there's a point where we don't have a system table yet despite having a db connection.
As such, I'm @ing the db_query.
Rationalization:
* If the system table is missing, more errors will be along shortly, so you won't be missing anything.
* db_table_exists('system') is just plain silly.
* This is needed for the drupalorg_testing install profile.
Comment #48
catchWe should add a comment for the @db_query(), otherwise it looks like Brandon tested this, and I don't see any other issues in the patch.
Comment #49
catchAdded to the existing comment. There is a small performance hit from using @, but while drupal_get_filename() is called very often, it's extremely rare that it needs to query the database for a file, so this is acceptable I think.
Comment #50
catchComment #51
webchickThis is a version of the patch compatible with -p0. PLEASE IGNORE THIS. This is only needed for http://drupal.org/project/drupalorg_testing.
Comment #52
Steven Jones CreditAttribution: Steven Jones commentedAdding tests for this fix in D8 here: #1334862: Test broken: drupal_get_path doesn't work for theme engines and test coverage is poor, it seems it slipped into D7 without them, and promptly regressed, sigh.
Comment #53
Steven Jones CreditAttribution: Steven Jones commentedSorry, did not mean to change the status.
Comment #54
donquixote CreditAttribution: donquixote commentedPerformance problem:
drupal_system_listing() is expensive.
If the db is down, it is likely this function will be called plenty of times, for different module and theme names.
(or did I miss something?)
Solution:
Run drupal_sytem_listing() only once (or once for each "type"), with a wildcard regex. The result can be cached, either in drupal_system_listing() itself, or in a wrapper function. Further calls can then just look into the cache.
And something else:
It looks like the old code, in case the db is down, not only has just this problem with phptemplate themes, but also totally fails with modules and themes in subdirectories. Am I right?
Comment #55
makara CreditAttribution: makara commentedOne thing,
function db_is_active()
is indatabase.inc
. With a normal site, the file is included withDRUPAL_BOOTSTRAP_DATABASE
and that stage actives the db as well. The check "if (db_is_active())
" appears useless because people still cannot usedrupal_load()
beforeDRUPAL_BOOTSTRAP_DATABASE
.FYI: I have this problem because I'm trying to use
drupal_load()
in cache inc file - #1381410: Use drupal_load() when it's possible.