Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
82.14% |
23 / 28 |
|
75.00% |
9 / 12 |
CRAP | |
0.00% |
0 / 1 |
ResultWrapper | |
82.14% |
23 / 28 |
|
75.00% |
9 / 12 |
18.65 | |
0.00% |
0 / 1 |
doNumRows | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
doFetchObject | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
doFetchRow | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
doSeek | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
doFree | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
doGetFieldNames | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
numRows | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
count | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
fetchObject | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
fetchRow | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
seek | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
5 | |||
free | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
rewind | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
current | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
key | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
next | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
valid | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getFieldNames | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace Wikimedia\Rdbms; |
4 | |
5 | use OutOfBoundsException; |
6 | use stdClass; |
7 | |
8 | /** |
9 | * Result wrapper for grabbing data queried from an IDatabase object |
10 | * |
11 | * Only IDatabase-related classes should construct these. Other code may |
12 | * use the FakeResultWrapper class for convenience or compatibility shims. |
13 | * |
14 | * Note that using the Iterator methods in combination with the non-Iterator |
15 | * IDatabase result iteration functions may cause rows to be skipped or repeated. |
16 | * |
17 | * By default, this will use the iteration methods of the IDatabase handle if provided. |
18 | * Subclasses can override methods to make it solely work on the result resource instead. |
19 | * |
20 | * @ingroup Database |
21 | */ |
22 | abstract class ResultWrapper implements IResultWrapper { |
23 | /** |
24 | * @var int The offset of the row that would be returned by the next call |
25 | * to fetchObject(). |
26 | */ |
27 | protected $nextPos = 0; |
28 | |
29 | /** |
30 | * @var int The offset of the current row that would be returned by current() |
31 | * and may have been previously returned by fetchObject(). |
32 | */ |
33 | protected $currentPos = 0; |
34 | |
35 | /** |
36 | * @var stdClass|array|bool|null The row at $this->currentPos, or null if it has |
37 | * not yet been retrieved, or false if the current row was past the end. |
38 | */ |
39 | protected $currentRow; |
40 | |
41 | /** |
42 | * @var string[]|null Cache of field names |
43 | */ |
44 | private $fieldNames; |
45 | |
46 | /** |
47 | * Get the number of rows in the result set |
48 | * |
49 | * @since 1.37 |
50 | * @return int |
51 | */ |
52 | abstract protected function doNumRows(); |
53 | |
54 | /** |
55 | * Get the next row as a stdClass object, or false if iteration has |
56 | * proceeded past the end. The offset within the result set is in |
57 | * $this->currentPos. |
58 | * |
59 | * @since 1.37 |
60 | * @return stdClass|bool |
61 | */ |
62 | abstract protected function doFetchObject(); |
63 | |
64 | /** |
65 | * Get the next row as an array containing the data duplicated, once with |
66 | * string keys and once with numeric keys, per the PDO::FETCH_BOTH |
67 | * convention. Or false if iteration has proceeded past the end. |
68 | * |
69 | * @return array|bool |
70 | */ |
71 | abstract protected function doFetchRow(); |
72 | |
73 | /** |
74 | * Modify the current cursor position to the row with the specified offset. |
75 | * If $pos is out of bounds, the behaviour is undefined. |
76 | * |
77 | * @param int $pos |
78 | */ |
79 | abstract protected function doSeek( $pos ); |
80 | |
81 | /** |
82 | * Free underlying data. It is not necessary to do anything. |
83 | */ |
84 | abstract protected function doFree(); |
85 | |
86 | /** |
87 | * Get the field names in the result set. |
88 | * |
89 | * @return string[] |
90 | */ |
91 | abstract protected function doGetFieldNames(); |
92 | |
93 | public function numRows() { |
94 | return $this->doNumRows(); |
95 | } |
96 | |
97 | public function count(): int { |
98 | return $this->doNumRows(); |
99 | } |
100 | |
101 | public function fetchObject() { |
102 | $this->currentPos = $this->nextPos++; |
103 | $this->currentRow = $this->doFetchObject(); |
104 | return $this->currentRow; |
105 | } |
106 | |
107 | public function fetchRow() { |
108 | $this->currentPos = $this->nextPos++; |
109 | $this->currentRow = $this->doFetchRow(); |
110 | return $this->currentRow; |
111 | } |
112 | |
113 | public function seek( $pos ): void { |
114 | $numRows = $this->numRows(); |
115 | // Allow seeking to zero if there are no results |
116 | $max = $numRows ? $numRows - 1 : 0; |
117 | if ( $pos < 0 || $pos > $max ) { |
118 | throw new OutOfBoundsException( __METHOD__ . ': invalid position' ); |
119 | } |
120 | if ( $numRows ) { |
121 | $this->doSeek( $pos ); |
122 | } |
123 | $this->nextPos = $pos; |
124 | $this->currentPos = $pos; |
125 | $this->currentRow = null; |
126 | } |
127 | |
128 | public function free() { |
129 | $this->doFree(); |
130 | $this->currentRow = false; |
131 | } |
132 | |
133 | public function rewind(): void { |
134 | $this->seek( 0 ); |
135 | } |
136 | |
137 | #[\ReturnTypeWillChange] |
138 | public function current() { |
139 | $this->currentRow ??= $this->fetchObject(); |
140 | |
141 | return $this->currentRow; |
142 | } |
143 | |
144 | public function key(): int { |
145 | return $this->currentPos; |
146 | } |
147 | |
148 | public function next(): void { |
149 | $this->fetchObject(); |
150 | } |
151 | |
152 | public function valid(): bool { |
153 | return $this->currentPos >= 0 |
154 | && $this->currentPos < $this->numRows(); |
155 | } |
156 | |
157 | public function getFieldNames() { |
158 | $this->fieldNames ??= $this->doGetFieldNames(); |
159 | return $this->fieldNames; |
160 | } |
161 | } |