From 7db754debea116c613331a627cb25e3a6ee8fd4b Mon Sep 17 00:00:00 2001 From: Kevin Decherf Date: Thu, 18 Jun 2026 17:58:34 +0200 Subject: [PATCH] Fix "Undefined array key 0" error If a matching element isn't the first child (e.g. preceded by a whitespace text node), it may sit at a key other than 0 after some cleanup, leading to a "Undefined array key 0" error. This fix is now covered by 6 tests. Fixes #96 Signed-off-by: Kevin Decherf --- src/Readability.php | 9 +++++++- tests/ReadabilityTest.php | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/Readability.php b/src/Readability.php index cf823f2..e5f661a 100644 --- a/src/Readability.php +++ b/src/Readability.php @@ -1483,8 +1483,15 @@ class Readability implements LoggerAwareInterface $childNodes = iterator_to_array($node->childNodes); $children = array_filter($childNodes, static fn ($childNode) => $childNode instanceof \DOMElement); + // array_first() has been added in PHP 8.5, failing back for older versions + if (!\function_exists('array_first')) { + $firstChild = $children ? $children[array_key_first($children)] : null; + } else { + $firstChild = array_first($children); + } + // There should be exactly 1 element child with given tag - if (1 !== \count($children) || $children[0]->nodeName !== $tag) { + if (1 !== \count($children) || $firstChild->nodeName !== $tag) { return false; } diff --git a/tests/ReadabilityTest.php b/tests/ReadabilityTest.php index 2971f99..e0844a1 100644 --- a/tests/ReadabilityTest.php +++ b/tests/ReadabilityTest.php @@ -464,6 +464,49 @@ class ReadabilityTest extends \PHPUnit\Framework\TestCase $this->assertTrue($res); } + public function dataForHasSingleTagInsideElement(): array + { + return [ + 'single matching tag, no other content' => [ + '

Some text

', 'p', true, + ], + 'single matching tag with comment' => [ + '

Some text

', 'p', true, + ], + 'whitespace-only text around single matching tag' => [ + '

Some text

', 'p', true, + ], + 'two matching tags' => [ + '

One

Two

', 'p', false, + ], + 'non-whitespace text alongside the single tag' => [ + '
Some text

One

', 'p', false, + ], + 'single tag with a different name' => [ + '
One
', 'p', false, + ], + ]; + } + + /** + * @dataProvider dataForHasSingleTagInsideElement + */ + public function testHasSingleTagInsideElement(string $html, string $tag, bool $expected): void + { + $dom = new \DOMDocument(); + $dom->loadHTML('' . $html . ''); + $node = $dom->getElementsByTagName('div')->item(0); + + $readability = $this->getReadability('', '', 'libxml', false); + + $method = new \ReflectionMethod($readability, 'hasSingleTagInsideElement'); + if (\PHP_VERSION_ID < 80100) { + $method->setAccessible(true); + } + + $this->assertSame($expected, $method->invoke($readability, $node, $tag)); + } + public function testKeepFootnotes(): void { // from https://www.schreibdichte.de/blog/feed-aggregator-und-spaeter-lesen-dienst-im-team