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...

  1. Clean install of D7 (drush dl drupal)
  2. Minimal Install + enable Comment module
  3. Create a content type with commenting enabled. Create a node and add a comment. This works fine.
  4. Now create a custom module with the following:
    url_test.info
    name = 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?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

nicholasThompson’s picture

Version: 7.2 » 7.x-dev

Hmm.. 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...

nicholasThompson’s picture

The bug is present in Drupal-7.x-dev.

I have enabled E_ALL | E_STRICT and there are no relevant entries in watchdog.

Damien Tournoud’s picture

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...

Why 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.

nicholasThompson’s picture

I 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:

  1. That's node/59 and comment/[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.
  2. "External" links on a page... Although they're pointing to the same domain, it means on this example page, I have 155 links pointing away from the page.

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.

stewsnooze’s picture

subscribe

catch’s picture

Version: 7.x-dev » 8.x-dev
Issue tags: +Needs backport to D7

@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.

pounard’s picture

Version: 7.x-dev » 8.x-dev
Component: comment.module » entity system
Status: Needs review » Active
FileSize
714 bytes

This 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 the l() 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 be current_path() instead by the way) is the same that the $uri variable in the l() function, which makes it looking like random, but in real life can only happen when the comment path is set to the node path./EDIT

I 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.

pounard’s picture

Version: 8.x-dev » 7.x-dev
Priority: Normal » Critical
Status: Active » Needs review

Also 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.

pounard’s picture

Component: entity system » comment.module

Bumping this to the right component too.

pounard’s picture

Title: Changing the 'uri callback' can break the page theme » Changing the comment path to the node path triggers a PHP fatal error in l() due to wrongly structured options
Version: 8.x-dev » 7.x-dev
Component: entity system » comment.module
Status: Active » Needs review

Re-titling.

pounard’s picture

Status: Needs review » Reviewed & tested by the community

RTBC.

mitron’s picture

Status: Reviewed & tested by the community » Needs review

Change status back to needs review. No "review" postings on this issue yet.

mitron’s picture

The 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()?

mitron’s picture

Priority: Critical » Major

Changing priority to major per priority levels of issues.

pounard’s picture

@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.

pounard’s picture

Core internals:

  • authorize.inc, install.inc, locale.inc, menu.inc, pager.inc, tablesort.inc, theme.maintainance.inc; : always uses arrays
  • bootstrap.inc (lines 2064, 2065) : documents class should be an array for link options
  • common.inc : documents class on elements should be array too
  • form.inc : always uses an array for class, except for: radios (theme_radios()) , form_required (theme_form_required_marker()), I consider those two occurences as a bug
  • theme.inc : both uses only arrays and document to use arrays

Core modules:

  • aggregator, block, blog, book, color, contact, contextual, dblog, field (and submodules), field_ui, file, filter, image, locale, menu, openid, overlay, path, poll, profile, rdf, search, shortcut, simpletest, statistics, taxonomy, tracker, translation, trigger, update, : always uses arrays
  • node : always uses arrays except in theme_node_recent_block() function for a theme_table() call
  • system : always uses arrays except in theme_status_report()
  • toolbar : always uses arrays except one occurence in toolbar_view() which I suspect to become the same bug as this issue in some very rare case (links building)
  • user : always uses array except one occurence in theme_user_admin_roles()
  • comment (doing this one in the end) : always uses arrays except for the one I fix in the latest patch

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?

mitron’s picture

In 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.

mitron’s picture

Correction of wrong test_only patch included in #17.

Please ignore attached patches. See #19.

mitron’s picture

Third try. Yikes. Notice to self: Work on ONE patch at a time. Use git features to separate multiple in progress patches.

Status: Needs review » Needs work
Issue tags: -Needs backport to D7

The last submitted patch, 1200478-17-comment_preprocess_wrong_uri_options.patch, failed testing.

mitron’s picture

Status: Needs work » Needs review
mitron’s picture

marcingy’s picture

Priority: Normal » Major

@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.

mitron’s picture

Status: Needs review » Needs work

@marcingy Did you mean to say we should NOT be adding in checks? Do you think that the check will degrade performance?

mitron’s picture

Priority: Major » Normal

Changing priority to normal based on opinion of more senior contributor.

marcingy’s picture

Priority: Major » Normal

@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

If element 'class' is included, it must be an array

So the patch http://drupal.org/node/1200478#comment-7033406 is correct in my opinion and that patch I would rtbc.

mitron’s picture

Here 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.

mitron’s picture

Version: 8.x-dev » 7.x-dev
FileSize
714 bytes

Change 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.

mitron’s picture

Status: Needs work » Needs review

Looks like this patch has consensus assuming it passes test.

marcingy’s picture

Status: Needs review » Reviewed & tested by the community
pounard’s picture

Whow! 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.

pounard’s picture

Please, maintainers, commit this.

David_Rothstein’s picture

Version: 7.x-dev » 8.x-dev
Status: Reviewed & tested by the community » Needs work

This 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.)

David_Rothstein’s picture

Status: Needs work » Needs review
FileSize
1.37 KB

Quick reroll for Drupal 8, fixing both of them.

marcingy’s picture

Status: Needs review » Reviewed & tested by the community

Looks good

pounard’s picture

Indeed, RTBC.

webchick’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

No 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.

pounard’s picture

I'm not even sure a test case would be revelant for this. FYI I'm using the D7 patch on two sites already.

pounard’s picture

Status: Needs work » Reviewed & tested by the community

Please commit this at least to 7.x.

xjm’s picture

xjm’s picture

Status: Reviewed & tested by the community » Needs work
David_Rothstein’s picture

Status: Needs work » Needs review

It'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.

pounard’s picture

I 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.

xjm’s picture

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).

Yeah, 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?

I 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.

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.

pounard’s picture

I 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.

David_Rothstein’s picture

May 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.

webflo’s picture

Found two other places in core where this is wrong. #2177703: Fix link attribute structure

pounard’s picture

Please make this happen.

pounard’s picture

Status: Needs review » Needs work

The last submitted patch, 34: comment-preprocess_fix_class-1200478-34.patch, failed testing.

pounard’s picture

Version: 8.x-dev » 7.x-dev
Status: Needs work » Needs review
Issue tags: -Needs backport to D7
FileSize
714 bytes
pounard’s picture

This somehow needs to be commited at some point?

marcingy’s picture

Status: Needs review » Reviewed & tested by the community

Looks good

drumm’s picture

sun’s picture

Issue tags: -Needs tests

Patch 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.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 51: comment-preprocess_fix_class-1200478-51.patch, failed testing.

drumm’s picture

Status: Needs work » Needs review
drumm’s picture

Status: Needs review » Reviewed & tested by the community
David_Rothstein’s picture

Status: Reviewed & tested by the community » Fixed

Committed 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.

  • Commit 56670b3 on 7.x by David_Rothstein:
    Issue #1200478 by mitron, pounard, David_Rothstein | nicholasThompson:...
David_Rothstein’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.