Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
82.14% covered (warning)
82.14%
23 / 28
75.00% covered (warning)
75.00%
9 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
ResultWrapper
82.14% covered (warning)
82.14%
23 / 28
75.00% covered (warning)
75.00%
9 / 12
18.65
0.00% covered (danger)
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% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 count
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 fetchObject
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 fetchRow
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 seek
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 free
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 rewind
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 current
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 key
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 next
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 valid
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getFieldNames
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace Wikimedia\Rdbms;
4
5use OutOfBoundsException;
6use 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 */
22abstract 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    /** @inheritDoc */
94    public function numRows() {
95        return $this->doNumRows();
96    }
97
98    /** @inheritDoc */
99    public function count(): int {
100        return $this->doNumRows();
101    }
102
103    /** @inheritDoc */
104    public function fetchObject() {
105        $this->currentPos = $this->nextPos++;
106        $this->currentRow = $this->doFetchObject();
107        return $this->currentRow;
108    }
109
110    /** @inheritDoc */
111    public function fetchRow() {
112        $this->currentPos = $this->nextPos++;
113        $this->currentRow = $this->doFetchRow();
114        return $this->currentRow;
115    }
116
117    /** @inheritDoc */
118    public function seek( $pos ): void {
119        $numRows = $this->numRows();
120        // Allow seeking to zero if there are no results
121        $max = $numRows ? $numRows - 1 : 0;
122        if ( $pos < 0 || $pos > $max ) {
123            throw new OutOfBoundsException( __METHOD__ . ': invalid position' );
124        }
125        if ( $numRows ) {
126            $this->doSeek( $pos );
127        }
128        $this->nextPos = $pos;
129        $this->currentPos = $pos;
130        $this->currentRow = null;
131    }
132
133    /** @inheritDoc */
134    public function free() {
135        $this->doFree();
136        $this->currentRow = false;
137    }
138
139    public function rewind(): void {
140        $this->seek( 0 );
141    }
142
143    /** @inheritDoc */
144    #[\ReturnTypeWillChange]
145    public function current() {
146        $this->currentRow ??= $this->fetchObject();
147
148        return $this->currentRow;
149    }
150
151    /** @inheritDoc */
152    public function key(): int {
153        return $this->currentPos;
154    }
155
156    /** @inheritDoc */
157    public function next(): void {
158        $this->fetchObject();
159    }
160
161    public function valid(): bool {
162        return $this->currentPos >= 0
163            && $this->currentPos < $this->numRows();
164    }
165
166    /** @inheritDoc */
167    public function getFieldNames() {
168        $this->fieldNames ??= $this->doGetFieldNames();
169        return $this->fieldNames;
170    }
171}