File: ResultWrapper.php

package info (click to toggle)
mediawiki 1%3A1.43.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 417,464 kB
  • sloc: php: 1,062,949; javascript: 664,290; sql: 9,714; python: 5,458; xml: 3,489; sh: 1,131; makefile: 64
file content (161 lines) | stat: -rw-r--r-- 3,903 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<?php

namespace Wikimedia\Rdbms;

use OutOfBoundsException;
use stdClass;

/**
 * Result wrapper for grabbing data queried from an IDatabase object
 *
 * Only IDatabase-related classes should construct these. Other code may
 * use the FakeResultWrapper class for convenience or compatibility shims.
 *
 * Note that using the Iterator methods in combination with the non-Iterator
 * IDatabase result iteration functions may cause rows to be skipped or repeated.
 *
 * By default, this will use the iteration methods of the IDatabase handle if provided.
 * Subclasses can override methods to make it solely work on the result resource instead.
 *
 * @ingroup Database
 */
abstract class ResultWrapper implements IResultWrapper {
	/**
	 * @var int The offset of the row that would be returned by the next call
	 *   to fetchObject().
	 */
	protected $nextPos = 0;

	/**
	 * @var int The offset of the current row that would be returned by current()
	 *   and may have been previously returned by fetchObject().
	 */
	protected $currentPos = 0;

	/**
	 * @var stdClass|array|bool|null The row at $this->currentPos, or null if it has
	 *   not yet been retrieved, or false if the current row was past the end.
	 */
	protected $currentRow;

	/**
	 * @var string[]|null Cache of field names
	 */
	private $fieldNames;

	/**
	 * Get the number of rows in the result set
	 *
	 * @since 1.37
	 * @return int
	 */
	abstract protected function doNumRows();

	/**
	 * Get the next row as a stdClass object, or false if iteration has
	 * proceeded past the end. The offset within the result set is in
	 * $this->currentPos.
	 *
	 * @since 1.37
	 * @return stdClass|bool
	 */
	abstract protected function doFetchObject();

	/**
	 * Get the next row as an array containing the data duplicated, once with
	 * string keys and once with numeric keys, per the PDO::FETCH_BOTH
	 * convention. Or false if iteration has proceeded past the end.
	 *
	 * @return array|bool
	 */
	abstract protected function doFetchRow();

	/**
	 * Modify the current cursor position to the row with the specified offset.
	 * If $pos is out of bounds, the behaviour is undefined.
	 *
	 * @param int $pos
	 */
	abstract protected function doSeek( $pos );

	/**
	 * Free underlying data. It is not necessary to do anything.
	 */
	abstract protected function doFree();

	/**
	 * Get the field names in the result set.
	 *
	 * @return string[]
	 */
	abstract protected function doGetFieldNames();

	public function numRows() {
		return $this->doNumRows();
	}

	public function count(): int {
		return $this->doNumRows();
	}

	public function fetchObject() {
		$this->currentPos = $this->nextPos++;
		$this->currentRow = $this->doFetchObject();
		return $this->currentRow;
	}

	public function fetchRow() {
		$this->currentPos = $this->nextPos++;
		$this->currentRow = $this->doFetchRow();
		return $this->currentRow;
	}

	public function seek( $pos ): void {
		$numRows = $this->numRows();
		// Allow seeking to zero if there are no results
		$max = $numRows ? $numRows - 1 : 0;
		if ( $pos < 0 || $pos > $max ) {
			throw new OutOfBoundsException( __METHOD__ . ': invalid position' );
		}
		if ( $numRows ) {
			$this->doSeek( $pos );
		}
		$this->nextPos = $pos;
		$this->currentPos = $pos;
		$this->currentRow = null;
	}

	public function free() {
		$this->doFree();
		$this->currentRow = false;
	}

	public function rewind(): void {
		$this->seek( 0 );
	}

	#[\ReturnTypeWillChange]
	public function current() {
		$this->currentRow ??= $this->fetchObject();

		return $this->currentRow;
	}

	public function key(): int {
		return $this->currentPos;
	}

	public function next(): void {
		$this->fetchObject();
	}

	public function valid(): bool {
		return $this->currentPos >= 0
			&& $this->currentPos < $this->numRows();
	}

	public function getFieldNames() {
		$this->fieldNames ??= $this->doGetFieldNames();
		return $this->fieldNames;
	}
}