【问题标题】:How to "refresh" DOMDocument instances of LibXML2?如何“刷新” LibXML2 的 DOMDocument 实例?
【发布时间】:2013-11-21 06:50:50
【问题描述】:

用PHP说明:normalizeDocument()方法存在BUG,或者缺少“刷新”方法,因为更改后DOM一致性丢失(甚至只是属性更改)。 .. 因此,您使用 LIBXML2 实现的任何“带有 DOM 更改”的算法在某些情况下有效,有时无效,是不可预测的! (?)

$doc->LoadXML($doc->saveXML()); 的“刷新”是一种变通方法,并且在使用 DOM 的工作流程中失去了性能...一个子问题:我需要刷新 DOM 的所有时刻?

  $XML = '
  <html>
    <h1>Hello</h1>
    <ol>
        <li>test (no id)</li>
        <li xml:id="i2">test i2</li>
    </ol>
  </html>
  ';
  $doc = new DOMDocument;
  $doc->LoadXML($XML);
  doSomeChange($doc);    // here DOM is modified
  print $doc->saveXML(); // show new DOM state

  $doc->normalizeDocument(); // NOT REFRESHING!?!
  var_dump($doc->getElementById('i2'));  //NULL!??! is a BUG!
  //CAN_NOT_doMORESomeChange($doc);

  $doc->LoadXML($doc->saveXML());        // only way to refresh?
  print $doc->getElementById('i2')->tagName;  //OK, is there

  // illustrating attribute modification:
  function doSomeChange(&$dom) {
    $max = 0;
    $xp  = new DOMXpath($dom);
    foreach(iterator_to_array($xp->query('/html/* | //li')) as $e) {
        $max++;
        $e->setAttribute('xml:id',"i$max");
    }
    print "\ncmpDOM='".($xp->document === $dom)."'\n"; // after @ThomasWeinert
  }

所以,输入是 $XML,输出是

  <html>
            <h1 xml:id="i1">Hello</h1>
            <ol xml:id="i2">
                <li xml:id="i3">test (no id)</li>
                <li xml:id="i4">test i2</li>
            </ol>
        </html>
  NULL
  ol

NULL 是错误(参见代码 cmets)。

PS:如果我将输入行 &lt;li xml:id="i2"&gt;test i2&lt;/li&gt; 更改为 &lt;li&gt;test i2&lt;/li&gt;,算法会按预期工作(!),因此是不可预测的。


相关问题:In DomDocument, reuse of DOMXpath, it is stable?PHP DomDocument, reuse of XSLTProcessor, it is stable/secure?

【问题讨论】:

  • 这可能是normalizeDocument 没有重新解析 xml:id 属性 - 这个问题是否发生在任何其他 DOM 操作上?这个具体的例子可以通过使用loadHTML($xml)id(而不是xml:id)属性来解决,不用normalizeDocument
  • 感谢您的笔记。好吧,“不重新解析 xml:id 属性”,为什么?是的,任何其他修改(removeChild、replaceChild 等)都会出现问题:DOM 行为将是不可预测的。我使用具有许多“doMORESomeChange()”函数序列的程序...我的具体问题是处理XML JATS documents,大文档,因此很难在此处复制/粘贴测试环境...xml:id 使用正确,并且是一个很好的例子——我现在没有另一个“如此短的 XML”来显示更多的插图。
  • 尽管the documentation 说“这个方法就像你保存然后加载文档一样”,normalizeDocument 实际上只适用于collapsing adjacent text nodes
  • ...谢谢(!)。嗯,是的,所以,没有“刷新 DOM”的内部方法:只有 saveXML/loadXML 的蛮力(并且需要重新配置 DOM 属性)。

标签: refresh domdocument libxml2


【解决方案1】:

更改会在您执行更改的那一刻应用到 DOM。在您的示例中,这会创建一个状态,其中两个元素具有相同的 xml:id,这似乎搞砸了索引。在设置它们之前删除 xml:id 属性并且它可以工作:

$XML = '
  <html>
    <h1>Hello</h1>
    <ol>
        <li>test (no id)</li>
        <li xml:id="i2">test i2</li>
    </ol>
  </html>
  ';
  $doc = new DOMDocument;
  $doc->loadXML($XML);
  var_dump($doc->getElementById('i2'), $doc->getElementById('i2')->tagName);
  /*
    object(DOMElement)#2 (0) { }
    string(2) "li"
  */

  doSomeChange($doc);    // here DOM is modified

  var_dump($doc->getElementById('i2'), $doc->getElementById('i2')->tagName);
  /*
    object(DOMElement)#6 (0) { }
    string(2) "ol"
  */

  print $doc->saveXML(); // show new DOM state
  /*
  <?xml version="1.0"?>
  <html>
    <h1 xml:id="i1">Hello</h1>
    <ol xml:id="i2">
      <li xml:id="i3">test (no id)</li>
      <li xml:id="i4">test i2</li>
    </ol>
  </html>
  */

  // illustrating xml:id attribute modification:
  function doSomeChange($dom) {
    $xp  = new DOMXpath($dom);
    foreach($xp->evaluate('//*') as $e) {
      $e->removeAttribute('xml:id');
    }
    $max = 0;
    foreach($xp->evaluate('/html/*|//li') as $e) {
      $max++;
      $e->setAttribute('xml:id',"i$max");
    }
  }

您的特定 dom 修改是破坏 getElementById() 调用的原因。

关于“稳定性”问题:DOMXpath 和 DOMDocument 之间的连接并不完全“稳定”。如果您在 DOMDocument 中使用 load*() 方法,则连接将丢失。您可以通过比较其文档属性来验证 DOMXpath 是否使用了正确的 DOMDocument:

var_dump($xpath->document === $doc);

这在您的情况下不会发生,因为您总是在函数中创建一个新的 DOMXpath 实例。但这意味着您应该避免重新加载文档,因为这会破坏为文档创建的 xpath 实例。

【讨论】:

  • 好线索,谢谢!我编辑了问题的代码(参见您的用户名行):您关于“如何检查是否需要刷新”的线索不起作用,即使报告了错误也会导致 cmpDOM=1
  • 这个答案是正确的。如果您在调用doSomeChange($doc) 之前添加$doc-&gt;getElementById('i2')-&gt;removeAttribute('xml:id');,它会按预期工作(并且无需使用$doc-&gt;normalizeDocument())。
  • 是的,我同意您与以前的removeAttribute 的解决方案,但对我来说,这是一种人为的解决方法,因为不能解决我必须决定使用另一个有效(也许是唯一的)的所有上下文和情况解决方案是“刷新 DOM”。我可以使用什么条件或什么“触发器”来检查 DOM 是否“损坏”(不可预测)??
  • 问题是您尝试将 xml:id 分配给已分配给另一个元素的元素。这打破了它!您误解了我对 DOMXpath 的评论,如果您“刷新”(保存和加载)文档,则与 DOMXpath 实例的连接断开。在这种情况下,您需要为文档创建一个新的 DOMXpath 实例。
  • 你说这只是我的“循环问题”(一个已经分配给另一个元素的元素),也许这是唯一的问题——我看不到,因为 saveXML 显示所有 xml:id并且 DOM 不反映显示的 XML 字符串。行。但是我在处理大文档的大型库中也有类似的不稳定性......需要一些安全和通用的“危险检查器”......所以,这是我的具体问题(需要对“DOM 已损坏”或“索引已损坏”进行通用检查",警告或错误消息)。
猜你喜欢
  • 1970-01-01
  • 2016-06-03
  • 1970-01-01
  • 1970-01-01
  • 2020-07-27
  • 2011-01-28
  • 2011-07-10
  • 1970-01-01
  • 2011-12-28
相关资源
最近更新 更多