From 88db7da422777888e8feb342fbb37df0b7742d3b Mon Sep 17 00:00:00 2001 From: Dmitriy Zo Date: Thu, 7 Nov 2024 07:18:32 +0000 Subject: [PATCH] Fix error when getting parents() for HTML elements --- CHANGELOG.md | 1 + src/Helpers/QueryFilters.php | 49 ++++++++++++++++++-------------- tests/QueryPath/DOMQueryTest.php | 2 ++ 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48917bcd..f7717214 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ QueryPath Changelog - Fix for :nth-child(n+B) to select B-th and all following elements - Fix for :nth-child(-n+B) to select first B elements - Update PHPUnit Test Suite to use @dataProvider in testPseudoClassNthChild() to reduce code repetition +- Fix error when getting parents() for HTML elements # 4.0.0 diff --git a/src/Helpers/QueryFilters.php b/src/Helpers/QueryFilters.php index c242a14f..a79387f8 100644 --- a/src/Helpers/QueryFilters.php +++ b/src/Helpers/QueryFilters.php @@ -796,26 +796,7 @@ public function closest($selector): Query */ public function parent($selector = null): Query { - $found = new SplObjectStorage(); - foreach ($this->matches as $m) { - while ($m->parentNode->nodeType !== XML_DOCUMENT_NODE) { - $m = $m->parentNode; - // Is there any case where parent node is not an element? - if ($m->nodeType === XML_ELEMENT_NODE) { - if (! empty($selector)) { - if (QueryPath::with($m, null, $this->options)->is($selector) > 0) { - $found->attach($m); - break; - } - } else { - $found->attach($m); - break; - } - } - } - } - - return $this->inst($found, null); + return $this->getParentElements($selector, true); } /** @@ -835,19 +816,45 @@ public function parent($selector = null): Query * @see siblings() */ public function parents($selector = null): Query + { + return $this->getParentElements($selector, false); + } + + /** + * Get ancestor(s) of each element in the DOMQuery. + * + * If a selector is present, only matching ancestors will be retrieved. + * + * @param string|null $selector + * A valid CSS 3 Selector. + * @param bool $immediate + * If function should return only the immediate parent + * + * @return DOMQuery + * A DOMNode object containing the matching ancestors. + * @throws ParseException + * @throws Exception + */ + private function getParentElements(?string $selector, bool $immediate): Query { $found = new SplObjectStorage(); foreach ($this->matches as $m) { - while ($m->parentNode->nodeType !== XML_DOCUMENT_NODE) { + while ($m->parentNode && $m->parentNode->nodeType !== XML_DOCUMENT_NODE) { $m = $m->parentNode; // Is there any case where parent node is not an element? if ($m->nodeType === XML_ELEMENT_NODE) { if (! empty($selector)) { if (QueryPath::with($m, null, $this->options)->is($selector) > 0) { $found->attach($m); + if ($immediate) { + break; + } } } else { $found->attach($m); + if ($immediate) { + break; + } } } } diff --git a/tests/QueryPath/DOMQueryTest.php b/tests/QueryPath/DOMQueryTest.php index 39c49fda..4e4027f5 100644 --- a/tests/QueryPath/DOMQueryTest.php +++ b/tests/QueryPath/DOMQueryTest.php @@ -1676,6 +1676,8 @@ public function testParent() $this->assertEquals('root', qp($file, 'unary')->parent()->tag()); $this->assertEquals('root', qp($file, 'li')->parent('root')->tag()); $this->assertEquals(2, qp($file, 'li')->parent()->count()); + $this->assertEquals(0, qp(DATA_HTML_FILE, 'html')->parent()->count()); + $this->assertEquals(2, qp(DATA_HTML_FILE, 'table')->parents()->count()); } public function testClosest()