gplx/mediawiki/includes/libs/rdbms/database/resultwrapper/ResultWrapper.php

162 lines
3.8 KiB
PHP

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