From c418b07ee0890badcc0436ff2ebff3133029ecbf Mon Sep 17 00:00:00 2001 From: jerodev Date: Fri, 7 Jun 2024 12:02:27 +0200 Subject: [PATCH 1/3] Implement ArrayAccess --- src/NodeFilter/NodeCollection.php | 26 ++++++++++++++++++++++++++ src/NodeFilter/NodeFilter.php | 2 +- src/NodeFilter/NullNode.php | 20 ++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/NodeFilter/NodeCollection.php b/src/NodeFilter/NodeCollection.php index 1776a07..890214e 100644 --- a/src/NodeFilter/NodeCollection.php +++ b/src/NodeFilter/NodeCollection.php @@ -200,4 +200,30 @@ public function xPath(string $selector): NodeFilter { return $this->internalXpath($this->nodes, $selector); } + + public function offsetExists(mixed $offset): bool + { + return match (\gettype($offset)) { + 'integer' => $this->nth($offset) instanceof NullNode, + default => false, + }; + } + + public function offsetGet(mixed $offset): ?NodeFilter + { + return match (\gettype($offset)) { + 'integer' => $this->nth($offset), + default => null, + }; + } + + public function offsetSet(mixed $offset, mixed $value): void + { + // Can't set nodes on NodeCollection + } + + public function offsetUnset(mixed $offset): void + { + // Can't set nodes on NodeCollection + } } diff --git a/src/NodeFilter/NodeFilter.php b/src/NodeFilter/NodeFilter.php index dc114a1..abd5028 100644 --- a/src/NodeFilter/NodeFilter.php +++ b/src/NodeFilter/NodeFilter.php @@ -4,7 +4,7 @@ use Closure; -interface NodeFilter +interface NodeFilter extends \ArrayAccess { /** * Get the value for the requested attribute of the first node in the current collection. diff --git a/src/NodeFilter/NullNode.php b/src/NodeFilter/NullNode.php index dd13e8f..ce08f05 100644 --- a/src/NodeFilter/NullNode.php +++ b/src/NodeFilter/NullNode.php @@ -97,4 +97,24 @@ public function xPath(string $selector): NodeFilter { return $this; } + + public function offsetExists(mixed $offset): bool + { + return false; + } + + public function offsetGet(mixed $offset): mixed + { + return null; + } + + public function offsetSet(mixed $offset, mixed $value): void + { + // Can't set nodes on NullNode + } + + public function offsetUnset(mixed $offset): void + { + // Can't set nodes on NullNode + } } From c1e285ff5cfa9b275e70e3cccb69b66fb7aa2405 Mon Sep 17 00:00:00 2001 From: jerodev Date: Fri, 7 Jun 2024 12:32:20 +0200 Subject: [PATCH 2/3] Uhm actually, an iterator is what you are looking for --- src/NodeFilter/NodeCollection.php | 28 ++++++++++++++-------------- src/NodeFilter/NodeFilter.php | 3 ++- src/NodeFilter/NullNode.php | 19 +++++++++++-------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/NodeFilter/NodeCollection.php b/src/NodeFilter/NodeCollection.php index 890214e..60c42dd 100644 --- a/src/NodeFilter/NodeCollection.php +++ b/src/NodeFilter/NodeCollection.php @@ -13,6 +13,7 @@ final class NodeCollection implements NodeFilter use Traits\EnumeratesValues; private DOMNodeList $nodes; + private int $iteratorPosition = 0; /** * @throws Exception When node could not be imported into a new document. @@ -201,29 +202,28 @@ public function xPath(string $selector): NodeFilter return $this->internalXpath($this->nodes, $selector); } - public function offsetExists(mixed $offset): bool + public function current(): NodeFilter { - return match (\gettype($offset)) { - 'integer' => $this->nth($offset) instanceof NullNode, - default => false, - }; + return $this->nth($this->iteratorPosition); } - public function offsetGet(mixed $offset): ?NodeFilter + public function next(): void { - return match (\gettype($offset)) { - 'integer' => $this->nth($offset), - default => null, - }; + $this->iteratorPosition++; } - public function offsetSet(mixed $offset, mixed $value): void + public function key(): int { - // Can't set nodes on NodeCollection + return $this->iteratorPosition; } - public function offsetUnset(mixed $offset): void + public function valid(): bool { - // Can't set nodes on NodeCollection + return $this->current()->exists(); + } + + public function rewind(): void + { + $this->iteratorPosition = 0; } } diff --git a/src/NodeFilter/NodeFilter.php b/src/NodeFilter/NodeFilter.php index abd5028..1653aa0 100644 --- a/src/NodeFilter/NodeFilter.php +++ b/src/NodeFilter/NodeFilter.php @@ -4,7 +4,8 @@ use Closure; -interface NodeFilter extends \ArrayAccess +/** @extends \Iterator */ +interface NodeFilter extends \Iterator { /** * Get the value for the requested attribute of the first node in the current collection. diff --git a/src/NodeFilter/NullNode.php b/src/NodeFilter/NullNode.php index ce08f05..c6a6bfb 100644 --- a/src/NodeFilter/NullNode.php +++ b/src/NodeFilter/NullNode.php @@ -98,23 +98,26 @@ public function xPath(string $selector): NodeFilter return $this; } - public function offsetExists(mixed $offset): bool + public function current(): mixed { - return false; + return null; } - public function offsetGet(mixed $offset): mixed + public function next(): void { - return null; } - public function offsetSet(mixed $offset, mixed $value): void + public function key(): int + { + return 0; + } + + public function valid(): bool { - // Can't set nodes on NullNode + return false; } - public function offsetUnset(mixed $offset): void + public function rewind(): void { - // Can't set nodes on NullNode } } From 5560701ebc7e7110eabaa6305c81888eb1196e20 Mon Sep 17 00:00:00 2001 From: jerodev Date: Fri, 7 Jun 2024 13:12:44 +0200 Subject: [PATCH 3/3] Test iterating node collections --- src/NodeFilter/NullNode.php | 4 ++-- tests/NodeFilter/NodeCollectionTest.php | 11 +++++++++++ tests/NodeFilter/NullNodeTest.php | 11 +++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/NodeFilter/NullNode.php b/src/NodeFilter/NullNode.php index c6a6bfb..e591974 100644 --- a/src/NodeFilter/NullNode.php +++ b/src/NodeFilter/NullNode.php @@ -98,9 +98,9 @@ public function xPath(string $selector): NodeFilter return $this; } - public function current(): mixed + public function current(): NodeFilter { - return null; + return $this; } public function next(): void diff --git a/tests/NodeFilter/NodeCollectionTest.php b/tests/NodeFilter/NodeCollectionTest.php index e7de584..565e7d4 100644 --- a/tests/NodeFilter/NodeCollectionTest.php +++ b/tests/NodeFilter/NodeCollectionTest.php @@ -224,4 +224,15 @@ private function createDOMNodes(): DOMNodeList return $doc->childNodes; } + + /** @test */ + public function it_should_iterate_zero_times(): void + { + $text = ''; + foreach ($this->node->querySelector('li') as $t) { + $text .= $t->text(); + } + + $this->assertEquals('onetwothreefour', $text); + } } diff --git a/tests/NodeFilter/NullNodeTest.php b/tests/NodeFilter/NullNodeTest.php index 61753ad..4399a41 100644 --- a/tests/NodeFilter/NullNodeTest.php +++ b/tests/NodeFilter/NullNodeTest.php @@ -16,6 +16,17 @@ protected function setUp(): void $this->node = new NullNode(); } + /** @test */ + public function it_should_iterate_zero_times(): void + { + $count = 0; + foreach ($this->node as $n) { + $count += $n->count() + 1; + } + + $this->assertEquals(0, $count); + } + /** @test */ public function it_should_return_empty_array_on_each(): void {