Browse Source

Fix DOM fatal error while filtering retrieved full content (#8161)

https://github.com/FreshRSS/FreshRSS/pull/8132 was not sufficient.
Fix *PHP Fatal error: Uncaught Error: Couldn't fetch DOMElement. Node no longer exists*
Alexandre Alapetite 5 months ago
parent
commit
6440f9c9a9
1 changed files with 47 additions and 23 deletions
  1. 47 23
      app/Models/Entry.php

+ 47 - 23
app/Models/Entry.php

@@ -961,25 +961,37 @@ HTML;
 			if ($nodes != false) {
 				$filter_xpath = $path_entries_filter === '' ? '' : (new Gt\CssXPath\Translator($path_entries_filter, 'descendant-or-self::'))->asXPath();
 				foreach ($nodes as $node) {
-					if (!($node instanceof DOMElement)) {
-						continue;
-					}
-					if ($filter_xpath !== '' && ($filterednodes = $xpath->query($filter_xpath, $node)) !== false) {
-						// Remove unwanted elements once before sanitizing, for CSS selectors to also match original content
-						foreach ($filterednodes as $filterednode) {
-							if ($filterednode === $node) {
-								continue 2;
-							}
-							if (!($filterednode instanceof DOMElement) || $filterednode->parentNode === null) {
-								continue;
+					try {
+						if (!($node instanceof DOMElement)) {
+							continue;
+						}
+						if ($filter_xpath !== '' && ($filterednodes = $xpath->query($filter_xpath, $node)) !== false) {
+							// Remove unwanted elements once before sanitizing, for CSS selectors to also match original content
+							foreach ($filterednodes as $filterednode) {
+								try {
+									if ($filterednode === $node) {
+										continue 2;
+									}
+									if (!($filterednode instanceof DOMElement) || $filterednode->ownerDocument !== $doc || $filterednode->parentNode === null) {
+										continue;
+									}
+									$filterednode->remove();
+								} catch (Error $e) {	// @phpstan-ignore catch.neverThrown
+									if (!str_contains($e->getMessage(), 'Node no longer exists')) {
+										throw $e;
+									}
+								}
 							}
-							$filterednode->parentNode->removeChild($filterednode);
+						}
+						if ($node->ownerDocument !== $doc || $node->parentNode === null) {
+							continue;
+						}
+						$html .= $doc->saveHTML($node) . "\n";
+					} catch (Error $e) {
+						if (!str_contains($e->getMessage(), 'Node no longer exists')) {
+							throw $e;
 						}
 					}
-					if ($node->parentNode === null) {
-						continue;
-					}
-					$html .= $doc->saveHTML($node) . "\n";
 				}
 			}
 
@@ -995,11 +1007,17 @@ HTML;
 				$xpath = new DOMXPath($doc);
 				$filterednodes = $xpath->query((new Gt\CssXPath\Translator($path_entries_filter, '//'))->asXPath()) ?: [];
 				foreach ($filterednodes as $filterednode) {
-					if (!($filterednode instanceof DOMElement) || $filterednode->parentNode === null) {
-						continue;
+					try {
+						if (!($filterednode instanceof DOMElement) || $filterednode->ownerDocument !== $doc || $filterednode->parentNode === null) {
+							continue;
+						}
+						$filterednode->remove();
+						$modified = true;
+					} catch (Error $e) {	// @phpstan-ignore catch.neverThrown
+						if (!str_contains($e->getMessage(), 'Node no longer exists')) {
+							throw $e;
+						}
 					}
-					$filterednode->parentNode->removeChild($filterednode);
-					$modified = true;
 				}
 				if ($modified) {
 					$html = $doc->saveHTML($doc->getElementsByTagName('body')->item(0) ?? $doc->firstElementChild) ?: $html;
@@ -1067,10 +1085,16 @@ HTML;
 			$xpath = new DOMXPath($doc);
 			$filterednodes = $xpath->query((new Gt\CssXPath\Translator($feed->attributeString('path_entries_filter'), '//'))->asXPath()) ?: [];
 			foreach ($filterednodes as $filterednode) {
-				if (!($filterednode instanceof DOMElement) || $filterednode->parentNode === null) {
-					continue;
+				try {
+					if (!($filterednode instanceof DOMElement) || $filterednode->ownerDocument !== $doc || $filterednode->parentNode === null) {
+						continue;
+					}
+					$filterednode->remove();
+				} catch (Error $e) {	// @phpstan-ignore catch.neverThrown
+					if (!str_contains($e->getMessage(), 'Node no longer exists')) {
+						throw $e;
+					}
 				}
-				$filterednode->parentNode->removeChild($filterednode);
 			}
 			$html = $doc->saveHTML($doc->getElementsByTagName('body')->item(0) ?? $doc->firstElementChild);
 			if (!is_string($html)) {