Skip to content

Commit 8b2a2a5

Browse files
committed
Add BufferState enum replacing magic buffer values (-1, -2, null, array)
Add EmptyResult, ArrayResult, IteratorResult implementing ResultInterface Narrow AbstractResultSet::$dataSource type to just ResultInterface Remove null checks using Null Object pattern with EmptyResult Add generic type annotations throughout ResultSet classes Update tests to use intersection types for mocks Signed-off-by: Simon Mundy <simon.mundy@peptolab.com>
1 parent 481c94f commit 8b2a2a5

14 files changed

Lines changed: 543 additions & 131 deletions

phpstan.neon.dist

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
includes:
22
- phpstan-baseline.neon
33
parameters:
4-
level: 4
4+
level: 8
55
paths:
66
- src
77
- test
@@ -16,4 +16,4 @@ parameters:
1616
- stubs/Laminas/ServiceManager/Factory/InvokableFactory.stub
1717
- stubs/Laminas/ServiceManager/Initializer/InitializerInterface.stub
1818
- stubs/Psr/Container/ContainerInterface.stub
19-
treatPhpDocTypesAsCertain: false
19+
treatPhpDocTypesAsCertain: false

src/Adapter/Driver/ArrayResult.php

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpDb\Adapter\Driver;
6+
7+
use ArrayIterator;
8+
use Override;
9+
10+
use function count;
11+
12+
/**
13+
* ResultInterface implementation that wraps an array
14+
*
15+
* Used when AbstractResultSet is initialized with an array data source,
16+
* allowing the dataSource property to be typed as just ResultInterface.
17+
*
18+
* @template TValue of array<string, mixed>|object
19+
*/
20+
final class ArrayResult implements ResultInterface
21+
{
22+
/** @var ArrayIterator<int, TValue> */
23+
private ArrayIterator $iterator;
24+
25+
/** @var int<0, max> */
26+
private int $count;
27+
28+
private int $fieldCount;
29+
30+
/**
31+
* @param array<int, TValue> $data
32+
*/
33+
public function __construct(array $data, int $fieldCount = 0)
34+
{
35+
$this->iterator = new ArrayIterator($data);
36+
$this->count = count($data);
37+
$this->fieldCount = $fieldCount;
38+
}
39+
40+
#[Override]
41+
public function buffer(): void
42+
{
43+
// Arrays are naturally buffered, nothing to do
44+
}
45+
46+
#[Override]
47+
public function isBuffered(): ?bool
48+
{
49+
return true;
50+
}
51+
52+
#[Override]
53+
public function isQueryResult(): bool
54+
{
55+
return true;
56+
}
57+
58+
#[Override]
59+
public function getAffectedRows(): int
60+
{
61+
return 0;
62+
}
63+
64+
#[Override]
65+
public function getGeneratedValue(): mixed
66+
{
67+
return null;
68+
}
69+
70+
#[Override]
71+
public function getResource(): mixed
72+
{
73+
return $this->iterator->getArrayCopy();
74+
}
75+
76+
#[Override]
77+
public function getFieldCount(): int
78+
{
79+
return $this->fieldCount;
80+
}
81+
82+
/** @return TValue */
83+
#[Override]
84+
public function current(): mixed
85+
{
86+
return $this->iterator->current();
87+
}
88+
89+
#[Override]
90+
public function key(): int
91+
{
92+
return $this->iterator->key();
93+
}
94+
95+
#[Override]
96+
public function next(): void
97+
{
98+
$this->iterator->next();
99+
}
100+
101+
#[Override]
102+
public function rewind(): void
103+
{
104+
$this->iterator->rewind();
105+
}
106+
107+
#[Override]
108+
public function valid(): bool
109+
{
110+
return $this->iterator->valid();
111+
}
112+
113+
/** @return int<0, max> */
114+
#[Override]
115+
public function count(): int
116+
{
117+
return $this->count;
118+
}
119+
}

src/Adapter/Driver/EmptyResult.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpDb\Adapter\Driver;
6+
7+
use Override;
8+
9+
/**
10+
* Null Object implementation of ResultInterface
11+
*
12+
* Used as the default data source in AbstractResultSet before initialization,
13+
* eliminating the need for null checks throughout the codebase.
14+
*/
15+
final class EmptyResult implements ResultInterface
16+
{
17+
#[Override]
18+
public function buffer(): void
19+
{
20+
}
21+
22+
#[Override]
23+
public function isBuffered(): ?bool
24+
{
25+
return true;
26+
}
27+
28+
#[Override]
29+
public function isQueryResult(): bool
30+
{
31+
return false;
32+
}
33+
34+
#[Override]
35+
public function getAffectedRows(): int
36+
{
37+
return 0;
38+
}
39+
40+
#[Override]
41+
public function getGeneratedValue(): mixed
42+
{
43+
return null;
44+
}
45+
46+
#[Override]
47+
public function getResource(): mixed
48+
{
49+
return null;
50+
}
51+
52+
#[Override]
53+
public function getFieldCount(): int
54+
{
55+
return 0;
56+
}
57+
58+
#[Override]
59+
public function current(): mixed
60+
{
61+
return null;
62+
}
63+
64+
#[Override]
65+
public function key(): mixed
66+
{
67+
return null;
68+
}
69+
70+
#[Override]
71+
public function next(): void
72+
{
73+
}
74+
75+
#[Override]
76+
public function rewind(): void
77+
{
78+
}
79+
80+
#[Override]
81+
public function valid(): bool
82+
{
83+
return false;
84+
}
85+
86+
#[Override]
87+
public function count(): int
88+
{
89+
return 0;
90+
}
91+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpDb\Adapter\Driver;
6+
7+
use Countable;
8+
use Iterator;
9+
use IteratorAggregate;
10+
use Override;
11+
12+
/**
13+
* ResultInterface implementation that wraps an Iterator or IteratorAggregate
14+
*
15+
* Used when AbstractResultSet is initialized with an iterator data source,
16+
* allowing the dataSource property to be typed as just ResultInterface.
17+
*
18+
* @template TKey
19+
* @template TValue of array<string, mixed>|object
20+
*/
21+
final class IteratorResult implements ResultInterface
22+
{
23+
/** @var Iterator<TKey, TValue> */
24+
private Iterator $iterator;
25+
26+
/**
27+
* @param Iterator<TKey, TValue>|IteratorAggregate<TKey, TValue> $iterator
28+
*/
29+
public function __construct(Iterator|IteratorAggregate $iterator)
30+
{
31+
if ($iterator instanceof IteratorAggregate) {
32+
/** @var Iterator<TKey, TValue> $innerIterator */
33+
$innerIterator = $iterator->getIterator();
34+
$this->iterator = $innerIterator;
35+
} else {
36+
$this->iterator = $iterator;
37+
}
38+
}
39+
40+
#[Override]
41+
public function buffer(): void
42+
{
43+
// Cannot buffer a generic iterator
44+
}
45+
46+
#[Override]
47+
public function isBuffered(): ?bool
48+
{
49+
return false;
50+
}
51+
52+
#[Override]
53+
public function isQueryResult(): bool
54+
{
55+
return true;
56+
}
57+
58+
#[Override]
59+
public function getAffectedRows(): int
60+
{
61+
return 0;
62+
}
63+
64+
#[Override]
65+
public function getGeneratedValue(): mixed
66+
{
67+
return null;
68+
}
69+
70+
#[Override]
71+
public function getResource(): mixed
72+
{
73+
return $this->iterator;
74+
}
75+
76+
#[Override]
77+
public function getFieldCount(): int
78+
{
79+
return 0;
80+
}
81+
82+
/** @return TValue */
83+
#[Override]
84+
public function current(): mixed
85+
{
86+
return $this->iterator->current();
87+
}
88+
89+
#[Override]
90+
public function key(): mixed
91+
{
92+
return $this->iterator->key();
93+
}
94+
95+
#[Override]
96+
public function next(): void
97+
{
98+
$this->iterator->next();
99+
}
100+
101+
#[Override]
102+
public function rewind(): void
103+
{
104+
$this->iterator->rewind();
105+
}
106+
107+
#[Override]
108+
public function valid(): bool
109+
{
110+
return $this->iterator->valid();
111+
}
112+
113+
/**
114+
* @return int<0, max>
115+
*/
116+
#[Override]
117+
public function count(): int
118+
{
119+
if ($this->iterator instanceof Countable) {
120+
return $this->iterator->count();
121+
}
122+
123+
return 0;
124+
}
125+
}

0 commit comments

Comments
 (0)