With the latest PHP 5.6.3 I'm getting the following warning when posting an AJAX form in Simpletest:

DOMDocument::importNode(): ID already defined

This occurs when the AJAX actions are replacing a part of the form with a new HTML structure that contains IDs that are identical to the ones that are being replaced. This happens for example in \Drupal\field_ui\Tests\ManageDisplayTest, causing 78 exceptions.

This is probably caused by the new libxml2 version 2.9.2 being statically compiled into PHP 5.6.3. If I downgrade to PHP 5.6.2 with libxml2 version 2.9.1 the problem does not occur any more.

The bug occurs here in WebTestBase::drupalProcessAjaxResponse():

  // ajax.js adds an enclosing DIV to work around a Safari bug.
  $newDom = new \DOMDocument();
  @$newDom->loadHTML('<div>' . $command['data'] . '</div>');
  $newNode = $dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE);

When the new HTML node is imported in the existing $dom the IDs therein conflict with the existing ones, causing the warning. Apparently the previous libxml2 version did not think this was a big deal.

This occurs also in D7 in DrupalWebTestCase::drupalPostAJAX().

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers’s picture

Looking at the libxml 2.9.2 changelog, nothing stands out as being the possible culprit. However, looking at the things listed in the changelog, this looks like one *very* poorly maintained library. And the fact that we're hitting this regression suggests that its test coverage is equally poor, which is … scary.

#1333730: [Meta] PHP DOM (libxml2) misinterprets HTML5 could possibly bring salvation for Drupal 8, because libxml2 doesn't support parsing HTML5, and Drupal 8 needs to support that.

pfrenssen’s picture

Yeah I had also looked at the changelog, wouldn't expect such a change in a minor version update. I'm trying to find a good fix for it. It is simple to suppress these warnings with '@', but that doesn't seem like the right solution.

I feel like this is either a bug or a design flaw in libxml2. It seems to me that DOMNode::replaceChild() is now basically broken. It has now become impossible to replace a node with new content containing the same HTML IDs. You would think it should be possible to first delete the offending DOM element and then reinsert it again but both available methods DOMNode::appendChild() and DOMNode::insertBefore() are not guaranteeing that the element is inserted at the right spot: it's going to be inserted as either the first or the last sibling in the list.

pfrenssen’s picture

pfrenssen’s picture

I'm still having the problem with the new PHP 5.6.4 on Arch Linux.

I tried working around the problem by replacing the existing element with a temporary element with a random ID, and then replacing that again with the new element, but it resulted in a lot of very ugly code. I initially didn't want to do this but I now think it is better to simply suppress the warning until it gets sorted out in libxml2. This is acceptable since we are already suppressing similar warnings when loading HTML in DomDocument:

protected function drupalProcessAjaxResponse($content, array $ajax_response, array $ajax_settings, array $drupal_settings) {
  // ...
  // DOM can load HTML soup. But, HTML soup can throw warnings, suppress
  // them.
  $dom = new \DOMDocument();
  @$dom->loadHTML($content);
socketwench’s picture

Status: Needs review » Reviewed & tested by the community

Ran into this same problem with Flag, and the patch fixes it beautifully!

Well, #4 suppresses it beautifully, at least. Note that this bug is also making contirb 8.x simpletests go red due to the exception error. See https://www.drupal.org/node/2446311#comment-9698327

socketwench’s picture

Just to note, I'm getting this on PHP 5.6.6 also on Arch Linux.

YesCT’s picture

YesCT’s picture

Issue tags: -PHP5.6 +PHP 5.6

I wonder if should file a bug report for an upstream bugfix.

joachim’s picture

+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -1882,7 +1882,7 @@ protected function drupalProcessAjaxResponse($content, array $ajax_response, arr
-            $newNode = $dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE);
+            $newNode = @$dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE);

If we're doing something as ugly as @, then there should maybe be a comment in the code to explain why.

pfrenssen’s picture

@YesCT, thinking about it again you could argue both ways. DOMDocument was already throwing warnings for duplicate IDs when loading HTML, now it also does so when importing DOMNodes into an existing document. At this point the DOMDocument is actually invalid. It doesn't care that we are importing this DOMNode so that we can later replace another node that happens to have the same ID. It cannot know our intent, so I can understand that it throws a warning. This does mean that you cannot replace any DOMNode containing an ID without encountering a warning, which is very inconvenient for us.

@joachim, this suppression with @ is used multiple times in WebTestBase already when interfacing with DomDocument, but I do agree that an explanation wouldn't hurt. Added it in the patch. I leave it on RTBC since this only adds some documentation.

alexpott’s picture

Version: 8.0.x-dev » 7.x-dev
Status: Reviewed & tested by the community » Patch (to be ported)

If this wasn't test code it would trouble me but as it is I think this solution is acceptable. This issue is a normal bug fix, and doesn't include any disruptive changes, so it is allowed per https://www.drupal.org/core/beta-changes. Committed c5338e0 and pushed to 8.0.x. Thanks!

  • alexpott committed c5338e0 on 8.0.x
    Issue #2386903 by pfrenssen: Warning: DOMDocument::importNode() ID...
pfrenssen’s picture

@Gregory Boddin just told me that there is another way to suppress warnings in libxml: libxml_use_internal_errors(). This has been added in PHP 5.1 so that people no longer need to suppress libxml errors with @.

xjm’s picture

Issue tags: -Needs backport to 7.x +Needs backport to D7
dcam’s picture

Status: Patch (to be ported) » Needs review
FileSize
1.74 KB

Backported #11 to D7.

pfrenssen’s picture

Status: Needs review » Needs work
+++ b/.htaccess
@@ -19,6 +19,10 @@ ErrorDocument 404 /index.php
+# Add correct encoding for SVGZ.
+AddType image/svg+xml svg svgz
+AddEncoding gzip svgz
+

This is an unrelated change.

dcam’s picture

Status: Needs work » Needs review
FileSize
1.23 KB

And that's what happens when you forget to do a git reset. Sorry about that.

pfrenssen’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs backport to D7

Looking good now, thanks a lot!

I wrote the original patch, but since this has already been committed for D8 and I was not involved in the backport I think it's OK for me to RTBC this.

David_Rothstein’s picture

Status: Reviewed & tested by the community » Fixed

Committed to 7.x - thanks!

  • David_Rothstein committed a3f85f1 on 7.x
    Issue #2386903 by pfrenssen, dcam: Fixed DOMDocument::importNode()...

Status: Fixed » Closed (fixed)

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