Avoid live updates to DOMNodeList
authorAlexander Ebert <ebert@woltlab.com>
Mon, 12 Apr 2021 17:20:54 +0000 (19:20 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Mon, 12 Apr 2021 17:20:54 +0000 (19:20 +0200)
The DOMNodeList is a live collection that is updated whenever an item is removed. This can have a significant performance impact when removing a large set of nodes.

wcfsetup/install/files/lib/system/html/output/node/HtmlOutputNodeProcessor.class.php

index 86160cb32e6aa1146bfa3ebaa446445943b9f166..866ba51c1f74058aecb957d7988af8450e465f20 100644 (file)
@@ -99,10 +99,14 @@ class HtmlOutputNodeProcessor extends AbstractHtmlNodeProcessor {
                
                if ($this->outputType !== 'text/html') {
                        // convert `<p>...</p>` into `...<br><br>`
-                       $paragraphs = $this->getDocument()->getElementsByTagName('p');
-                       while ($paragraphs->length) {
-                               $paragraph = $paragraphs->item(0);
-                               
+                       $paragraphs = [];
+                       // The DOMNodeList returned by `getElementsByTagName()` is live, causing a performance
+                       // penalty when modifying it a lot.
+                       foreach ($this->getDocument()->getElementsByTagName('p') as $node) {
+                               $paragraphs[] = $node;
+                       }
+                       
+                       foreach ($paragraphs as $paragraph) {
                                $isLastNode = true;
                                $sibling = $paragraph;
                                while ($sibling = $sibling->nextSibling) {
@@ -143,6 +147,7 @@ class HtmlOutputNodeProcessor extends AbstractHtmlNodeProcessor {
                                
                                DOMUtil::removeNode($paragraph, true);
                        }
+                       unset($paragraphs);
                        
                        if ($this->outputType === 'text/plain') {
                                // remove all `\n` first