This is a VERY strange issue... In Drupal 7.0 I noticed that Comment URL's were "comment/[cid]#comment-[cid]" (which differed from D6 which uses "node/[nid]#comment-[cid]"). I wanted the old URL back so, with some research, found you could change the "uri callback" using hook_entity_info_alter. So I wrote a little helper module which defined its own Comment URI function. I copied the existing comment_uri() function and just altered the path value. This worked PERFECTLY in Drupal 7.0.
Then, last week, I upgraded my blog (http://www.thingy-ma-jig.co.uk/) to Drupal 7.2 and noticed that, "randomly" some pages were rendering without html.tpl.php. After investigating my custom theme, I couldn't see anything wrong so I switched to Garland and had the same issue. I eventually rounded it down to urls which were "nodes with comments" and eventually to my tweak module for the comment uri.
This bug is replicatable...
- Clean install of D7 (drush dl drupal)
- Minimal Install + enable Comment module
- Create a content type with commenting enabled. Create a node and add a comment. This works fine.
- Now create a custom module with the following:
url_test.infoname = URL Test description = Test module core = 7.x
url_test.module
<?php function url_test_entity_info_alter(&$entity_info) { $entity_info['comment']['uri callback'] = 'url_test_comment_uri'; } function url_test_comment_uri($comment) { return array( 'path' => 'node/'. $comment->nid, 'options' => array('fragment' => 'comment-' . $comment->cid), ); }
When you refresh the node URL with the comment on it, you should (or at least I did/do) get a very plain page and when I examine the HTML Source, I see that there is no html.tpl.php being included.
My server stats:
- PHP
[nthompson@server1 url_test]$ php -v PHP 5.3.6 (cli) (built: Mar 18 2011 14:40:00) Copyright (c) 1997-2011 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies with XCache v1.3.2, Copyright (c) 2005-2011, by mOo
- Server (nginx):
[nthompson@server1 url_test]$ /usr/sbin/nginx -v nginx version: nginx/0.7.68
Now the *REALLY* weird thing is the path ONLY breaks if you use the path "node/"... if you switch it back to "comment/" (which essentially EXACTLY mirrors the comment_uri() function) then the page renders perfectly. In fact, if you switch the path to 'foo/'. $comment->nid
then the page renders correctly. Only 'node/' breaks it.
Any thoughts on why changing the Comment URI from 'comment/' to 'node/' stops Drupal 7.2 from rendering html.tpl.php?
Comments
Comment #1
nicholasThompsonHmm.. I just tried url_test on 7.0 and 7.1 too and they both exhibit the same issue... Setting the comment path to
'node/'. $comment->nid
seems to cause some kind of fatal theme error...Comment #2
nicholasThompsonThe bug is present in Drupal-7.x-dev.
I have enabled
E_ALL | E_STRICT
and there are no relevant entries in watchdog.Comment #3
Damien Tournoud CreditAttribution: Damien Tournoud commentedWhy would you want to do that? This URL is the permalink URL, it simply redirects the user to "node/[nid]#comment-[cid]", taking pagination into account.
Comment #4
nicholasThompsonI want to remove those links for SEO reasons. Take, for example, a "popular" link page such as Mac Book Pro Hard Disk Failure where I have 155 comments:
node/59
andcomment/[cid]
all with the same content. I know the comment/* URL's all have a Canonical URL which somewhat reduces the risk of duplicate content, however it is still something I don't want. I want my comments to link to the node they belong on.I do understand why this has been done and can see a genuine advantage, particularly on sites with paginated comments... However I don't have paginated comments.
I also guess this issue is slightly bigger than just comments. I guess the question really is:
Why does the theme break when you change an entities uri callback to 'node/[nid]'?
I'm still getting to grips with the D7 API and all the new inner workings of it, so I'm not even 100% sure where to start looking to debug it.
Comment #5
stewsnoozesubscribe
Comment #6
catch@nicholasThompson, we implement canonical for these links iirc, so it should be fine for SEO afaik.
However, this looks like a proper strange bug regardless of the use case.
Comment #7
pounardThis actually a real bug in the comment.module file.
It defines the
$options['attributes']['class'] = 'permilink'
where it should be an array of class instead of an array. This cause thel()
function to use the[]
operator over the'permalink'
string which causes a non catchable PHP fatal error.Because this URI is built in the
template_preprocess_comment()
function, it causes a fatal error while rendering is being done, leaving an ugly incomplete rendered page with a PHP stack trace at the bottom.EDIT: This case happens only when the
$_GET['q']
variable (which should becurrent_path()
instead by the way) is the same that the$uri
variable in thel()
function, which makes it looking like random, but in real life can only happen when the comment path is set to the node path./EDITI got the same bug while attempting to build some comments URI as absolute links using the node URI instead (using the fragment) in order to be able to store them in an external application. In my use case comments are stored in a Drupal site but displayed and POSTed using AJAX on another site's node.
Bumping this to critical, because doing a valid alteration we experience PHP fatal errors, and this should never happen, it's not a question of "why would you want to do that" but a question of "this will happen again and again if we don't fix it".
Patch attached.
Comment #8
pounardAlso switching back to Drupal 7.x version, because this seems to be a Drupal 7 only bug for now, and probably won't be a Drupal 8.x bug since the comment module is being fully rewritten.
Comment #9
pounardBumping this to the right component too.
Comment #10
pounardRe-titling.
Comment #11
pounardRTBC.
Comment #12
mitron CreditAttribution: mitron commentedChange status back to needs review. No "review" postings on this issue yet.
Comment #13
mitron CreditAttribution: mitron commentedThe below line is in the l function in both version 7 and version 8 and assumes any value of key "class" will be an array:
$options['attributes']['class'][] = 'active'
However, to render attributes, drupal_attributes() uses
$data = implode(' ', (array) $data);
which will work just fine if the attribute value is not an array.
In one part of the system, it can be either and array or a not, in another, it must be an array. This inconsistency is not ideal. Are there other parts of the system that assume the value of key "class" will be an array? Should the patch instead go in l() so it works like drupal_attributes()?
Comment #14
mitron CreditAttribution: mitron commentedChanging priority to major per priority levels of issues.
Comment #15
pounard@mitron Indeed, major seems better than critical. Excellent question, but all over the framework, everywhere, template process functions are awaiting for "class" to always be an array, in most cases.
It might worth the shot to introspect this further, meanwhile, I have sites crashing here, and it seems I am not the only one. I don't want this issue, opened almost 2 years ago by someone elses to stall one year more.
I'm gonna search a bit more.
Comment #16
pounardCore internals:
Core modules:
My conclusion is we should always use arrays no matter where for the 'class' attribute. Every string use is a bug.
@mitron Most usage of strings are in pure theme_* functions, which make them being safe thanks of the array cast trick in drupal_attributes(). This is also safe for table data cells. Considering this, this reduces the real bug occurence to 2 or 3 potential bugguy cases in the full core. My opinion is that we should only fix those, and not the l() function which is accuratly documented. What do you think?
Comment #17
mitron CreditAttribution: mitron commentedIn general, attributes can be passed in various ways, including strings and arrays. However, the l function currently fails if the class attribute passed to it is not an array. Since in other contexts it is possible to pass a single class as a string, it could create an expectation in a developer that this should be OK with the l function also and the failure is surprising. The l function is thus fragile to breaking due to this expectation. At least one major failure in Drupal 7 resulted from a developer passing a class as a string and not as an array.
The proposed patch, which would need to be backported to version 7, makes the l function more robust. The class can be either an array or a string.
Version change back to version 8 since it would be fixed there first.
First patch is test only and should fail. Second patch is test plus change to the l function.
Please ignore attached patches. See #19.
Comment #18
mitron CreditAttribution: mitron commentedCorrection of wrong test_only patch included in #17.
Please ignore attached patches. See #19.
Comment #19
mitron CreditAttribution: mitron commentedThird try. Yikes. Notice to self: Work on ONE patch at a time. Use git features to separate multiple in progress patches.
Comment #21
mitron CreditAttribution: mitron commented#19: 1200478-17-comment_preprocess_wrong_uri_options.patch queued for re-testing.
Comment #22
mitron CreditAttribution: mitron commented#19: 1200478-17-comment_preprocess_wrong_uri_options-test_only.patch queued for re-testing.
Comment #23
marcingy CreditAttribution: marcingy commented@pounard I agree attributes elements should be an array for classes and underlying code that calls l() should be fixed. We should not be adding in checks for if we have an array or not, l() is a critical function as such should be as optimal as possible. And to be honest to me this bug is normal not major or critical.
Comment #24
mitron CreditAttribution: mitron commented@marcingy Did you mean to say we should NOT be adding in checks? Do you think that the check will degrade performance?
Comment #25
mitron CreditAttribution: mitron commentedChanging priority to normal based on opinion of more senior contributor.
Comment #26
marcingy CreditAttribution: marcingy commented@mitron thanks for catching that yes I meant not. Well l() is called many many times on a page on certain pages it can 100 of times. More than that though I am a big fan of defined data structures when ever possible because then it makes things easier to do deal with. The real issue here is that comments should pass an array of classes as l() docs state
http://api.drupal.org/api/drupal/core%21includes%21common.inc/function/l/8
So the patch http://drupal.org/node/1200478#comment-7033406 is correct in my opinion and that patch I would rtbc.
Comment #27
mitron CreditAttribution: mitron commentedHere is some timing information for the curious:
0.0018 seconds for 1,000 calls to the original line of code
0.2753 seconds for 1,000 calls to the section of code that is new.
The patched lines take more than 150x as long to execute.
In this part of the system the difference is significant.
Comment #28
mitron CreditAttribution: mitron commentedChange version to 7.x. This bug does not exist in 8.x.
Resubmit patch from #7 written by pounard. Patch file name changed to follow Patch Naming.
Comment #29
mitron CreditAttribution: mitron commentedLooks like this patch has consensus assuming it passes test.
Comment #30
marcingy CreditAttribution: marcingy commentedComment #31
pounardWhow! Such activity within the night! Ok, read everything what happens, and I still consider fixing the comment template is better for API consistency, and now it seems for micro optimization too. Thanks for rerolling the patch.
RTBC for me too. We should also ensure that other potential buggy cases in core cannot lead to the same error as a followup and this will be a win.
Comment #32
pounardPlease, maintainers, commit this.
Comment #33
David_Rothstein CreditAttribution: David_Rothstein commentedThis code exists in Drupal 8 too, so needs to be committed there first. (For what it's worth, Drupal 8 actualy has two instances of this in the same function, whereas Drupal 7 only has one.)
Comment #34
David_Rothstein CreditAttribution: David_Rothstein commentedQuick reroll for Drupal 8, fixing both of them.
Comment #35
marcingy CreditAttribution: marcingy commentedLooks good
Comment #36
pounardIndeed, RTBC.
Comment #37
webchickNo tests? I mean, it's probably fine since it's fixing essentially a typo, but if we'd had test coverage of permalinks to begin with, this never would've happened.
Comment #38
pounardI'm not even sure a test case would be revelant for this. FYI I'm using the D7 patch on two sites already.
Comment #39
pounardPlease commit this at least to 7.x.
Comment #40
xjm#34: comment-preprocess_fix_class-1200478-34.patch queued for re-testing.
Comment #41
xjmI do agree that we still need tests here.
http://drupal.org/core-gates#testing
http://drupal.org/node/767608
Comment #42
David_Rothstein CreditAttribution: David_Rothstein commentedIt's probably possible to write tests for something like #7 but I think that might fit the category of too much of an edge case to test :) You are basically testing that if someone alters URLs in a particular unusual way and if they do it specifically for comments, that Drupal still works correctly...
I think that runs the risk of writing tests for the symptom rather than the cause, and time would be better spent addressing the cause. To me, it seems like the real cause is that throughout Drupal,
$options['attributes']['class'] = 'permalink'
and$options['attributes']['class'] = array('permalink')
both work pretty much fine (in the sense that Drupal will happily print the class to the page with no errors in both cases).If that were not the case, this bug would have been caught long ago. I think if you are going to have that kind of flexibility, though, you have to accept that edge case bugs like this will crop up sometimes.
In my opinion, this patch would be fine to commit to Drupal 7 and Drupal 8 as long as there's a followup somewhere to consider making Drupal's attribute handling more strict (e.g. it could expect 'class' to always be an array and refuse to handle it if it isn't). But that's a potentially tricky issue.
Comment #43
pounardI agree with @David_Rothstein here, this wouldn't be a useful test to write, we'd loose more time that we would gain doing that.
I have a small fix to what he said thought: 'class' is documented to be an array pretty much everywhere, so it's actually not a problem due to flexibility, but a real bug that we have here.
Comment #44
xjmYeah, this is what I think needs coverage, not the weird edgecase behavior. I guess I didn't consider that that would be a non-backportable change, though. Let's get a followup issue?
Thanks, but that's not the point. The point is that when we have bugs, we add tests for them to prevent future regressions, regardless of how long it takes. See the link above about the core gates.
Comment #45
pounardI understand you point, but right now I so many sites with so many core patches applied that I really don't care about an obvious typo error fix, I'd just like to see it commited already and remove at least this one from the list I have to maintain. I don't have time to write those tests and already lost too much time here trying to find the bug real source, debugging it, and fix it: my job is done here.
Comment #46
David_Rothstein CreditAttribution: David_Rothstein commentedMay be a duplicate of #1027936: Comment Preprocess sets a links class as a string, should be an array (which was committed to Drupal 8). Or actually, that issue fixed half of the bug in Drupal 8, but looks like the other half was already fixed somewhere else too.
Comment #47
webflo CreditAttribution: webflo commentedFound two other places in core where this is wrong. #2177703: Fix link attribute structure
Comment #48
pounardPlease make this happen.
Comment #49
pounard34: comment-preprocess_fix_class-1200478-34.patch queued for re-testing.
Comment #51
pounardComment #52
pounardThis somehow needs to be commited at some point?
Comment #53
marcingy CreditAttribution: marcingy commentedLooks good
Comment #54
drummYep, looks good. I ran into this when testing #2125397: Enable comment_fragment 7.x-1.x-dev to improve issue comment permalinks.
Comment #55
sunPatch looks good and is correct.
Duly noting: It's a bit weird that it took more than 1 year to get this simple fix committed...
This is fine to commit without test coverage.
The only possible test I could imagine (for D8) would be a functional change to add an explicit condition to the
Attribute
class that ensures that a 'class' attribute is an array (and throws an exception otherwise). But even that approach could be databable. In any case, separate issue.Comment #57
drumm51: comment-preprocess_fix_class-1200478-51.patch queued for re-testing.
Comment #58
drummComment #59
David_Rothstein CreditAttribution: David_Rothstein commentedCommitted to 7.x - thanks!
Per #42, #44, and #55 we should have a D8 followup to consider forcing 'class' to always be an array (and that's where any tests would be). I'll create a quick issue for that now.
Comment #61
David_Rothstein CreditAttribution: David_Rothstein commentedCreated #2259419: Force the 'class' attribute to always be an array, never a string.