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    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}