Skip to content

Commit f7634c4

Browse files
committed
Implement Countable in lazy BSON structures
1 parent 80e5b20 commit f7634c4

File tree

4 files changed

+79
-2
lines changed

4 files changed

+79
-2
lines changed

src/Model/LazyBSONArray.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,20 @@
2121
use ArrayAccess;
2222
use ArrayIterator;
2323
use CallbackFilterIterator;
24+
use Countable;
2425
use IteratorAggregate;
2526
use MongoDB\BSON\PackedArray;
2627
use MongoDB\Codec\CodecLibrary;
2728
use MongoDB\Codec\LazyBSONCodecLibrary;
2829
use MongoDB\Exception\InvalidArgumentException;
2930
use ReturnTypeWillChange;
3031

32+
use function array_filter;
3133
use function array_key_exists;
3234
use function array_keys;
3335
use function array_map;
3436
use function array_values;
37+
use function count;
3538
use function is_array;
3639
use function is_numeric;
3740
use function max;
@@ -51,7 +54,7 @@
5154
* @template-implements ArrayAccess<int, TValue>
5255
* @template-implements IteratorAggregate<int, TValue>
5356
*/
54-
final class LazyBSONArray implements ArrayAccess, IteratorAggregate
57+
final class LazyBSONArray implements ArrayAccess, Countable, IteratorAggregate
5558
{
5659
/** @var PackedArray<TValue> */
5760
private PackedArray $bson;
@@ -106,6 +109,13 @@ public function __construct($input = null, ?CodecLibrary $codecLibrary = null)
106109
$this->codecLibrary = $codecLibrary ?? new LazyBSONCodecLibrary();
107110
}
108111

112+
public function count(): int
113+
{
114+
$this->readEntirePackedArray();
115+
116+
return count(array_filter($this->exists));
117+
}
118+
109119
/** @return AsListIterator<TValue> */
110120
public function getIterator(): AsListIterator
111121
{

src/Model/LazyBSONDocument.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use ArrayAccess;
2222
use ArrayIterator;
2323
use CallbackFilterIterator;
24+
use Countable;
2425
use Iterator;
2526
use IteratorAggregate;
2627
use MongoDB\BSON\Document;
@@ -29,7 +30,9 @@
2930
use MongoDB\Exception\InvalidArgumentException;
3031
use ReturnTypeWillChange;
3132

33+
use function array_filter;
3234
use function array_key_exists;
35+
use function count;
3336
use function get_object_vars;
3437
use function is_array;
3538
use function is_object;
@@ -50,7 +53,7 @@
5053
* @template-implements ArrayAccess<string, TValue>
5154
* @template-implements IteratorAggregate<string, TValue>
5255
*/
53-
final class LazyBSONDocument implements ArrayAccess, IteratorAggregate
56+
final class LazyBSONDocument implements ArrayAccess, Countable, IteratorAggregate
5457
{
5558
/** @var Document<TValue> */
5659
private Document $bson;
@@ -67,6 +70,8 @@ final class LazyBSONDocument implements ArrayAccess, IteratorAggregate
6770
/** @var array<string, true> */
6871
private array $unset = [];
6972

73+
private bool $entireDocumentRead = false;
74+
7075
private CodecLibrary $codecLibrary;
7176

7277
/**
@@ -140,6 +145,13 @@ public function __unset(string $name): void
140145
unset($this->set[$name]);
141146
}
142147

148+
public function count(): int
149+
{
150+
$this->readEntireDocument();
151+
152+
return count(array_filter($this->exists));
153+
}
154+
143155
/** @return Iterator<string, TValue> */
144156
public function getIterator(): CallbackIterator
145157
{
@@ -228,6 +240,23 @@ public function offsetUnset($offset): void
228240
$this->__unset($offset);
229241
}
230242

243+
private function readEntireDocument(): void
244+
{
245+
if ($this->entireDocumentRead) {
246+
return;
247+
}
248+
249+
foreach ($this->bson as $offset => $value) {
250+
$this->read[$offset] = $value;
251+
252+
if (! isset($this->exists[$offset])) {
253+
$this->exists[$offset] = true;
254+
}
255+
}
256+
257+
$this->entireDocumentRead = true;
258+
}
259+
231260
private function readFromBson(string $key): void
232261
{
233262
if (array_key_exists($key, $this->read)) {

tests/Model/LazyBSONArrayTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,4 +239,23 @@ public function testIterator(LazyBSONArray $array): void
239239
$this->assertInstanceOf(LazyBSONDocument::class, $items[0]);
240240
$this->assertSame('yay!', $items[1]);
241241
}
242+
243+
public function testCount(): void
244+
{
245+
$array = new LazyBSONArray(PackedArray::fromPHP(['foo', 'bar', 'baz']));
246+
247+
$this->assertCount(3, $array);
248+
249+
// Overwrite existing item, count must not change
250+
$array[0] = 'yay';
251+
$this->assertCount(3, $array);
252+
253+
// Unset existing element, count must decrease
254+
unset($array[1]);
255+
$this->assertCount(2, $array);
256+
257+
// Append element, count must increase again
258+
$array[] = 'yay';
259+
$this->assertCount(3, $array);
260+
}
242261
}

tests/Model/LazyBSONDocumentTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,23 @@ public function testIterator(LazyBSONDocument $document): void
289289
$this->assertInstanceOf(LazyBSONDocument::class, $items['document']);
290290
$this->assertSame('yay!', $items['new']);
291291
}
292+
293+
public function testCount(): void
294+
{
295+
$document = new LazyBSONDocument(Document::fromPHP(['foo' => 'bar', 'bar' => 'baz']));
296+
297+
$this->assertCount(2, $document);
298+
299+
// Overwrite existing item, count must not change
300+
$document['foo'] = 'yay';
301+
$this->assertCount(2, $document);
302+
303+
// Unset existing element, count must decrease
304+
unset($document['bar']);
305+
$this->assertCount(1, $document);
306+
307+
// Append element, count must increase again
308+
$document['baz'] = 'yay';
309+
$this->assertCount(2, $document);
310+
}
292311
}

0 commit comments

Comments
 (0)