* doesn't go anywhere near an actual database.
*/
class FakeResultWrapper extends ResultWrapper {
- /** @var array */
- public $result = [];
-
- /** @var null And it's going to stay that way :D */
- protected $db = null;
-
- /** @var int */
- protected $pos = 0;
-
- /** @var array|stdClass|bool */
- protected $currentRow = null;
+ /** @var $result stdClass[] */
/**
- * @param array $array
+ * @param stdClass[] $rows
*/
- function __construct( $array ) {
- $this->result = $array;
+ function __construct( array $rows ) {
+ parent::__construct( null, $rows );
}
- /**
- * @return int
- */
function numRows() {
return count( $this->result );
}
- /**
- * @return array|bool
- */
function fetchRow() {
if ( $this->pos < count( $this->result ) ) {
$this->currentRow = $this->result[$this->pos];
function free() {
}
- /**
- * Callers want to be able to access fields with $this->fieldName
- * @return bool|stdClass
- */
function fetchObject() {
$this->fetchRow();
if ( $this->currentRow ) {
$this->currentRow = null;
}
- /**
- * @return bool|stdClass
- */
function next() {
return $this->fetchObject();
}
<?php
/**
- * Result wrapper for grabbing data queried by someone else
+ * Result wrapper for grabbing data queried from an IDatabase object
+ *
+ * Note that using the Iterator methods in combination with the non-Iterator
+ * DB 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.
+ * If no database is provided, and the subclass does not override the DB iteration methods,
+ * then a RuntimeException will be thrown when iteration is attempted.
+ *
+ * The result resource field should not be accessed from non-Database related classes.
+ * It is database class specific and is stored here to associate iterators with queries.
+ *
* @ingroup Database
*/
class ResultWrapper implements Iterator {
- /** @var resource */
+ /** @var resource|array|null Optional underlying result handle for subclass usage */
public $result;
- /** @var IDatabase */
+ /** @var IDatabase|null */
protected $db;
/** @var int */
protected $pos = 0;
-
- /** @var object|null */
+ /** @var stdClass|null */
protected $currentRow = null;
/**
- * Create a new result object from a result resource and a Database object
+ * Create a row iterator from a result resource and an optional Database object
+ *
+ * Only Database-related classes should construct ResultWrapper. Other code may
+ * use the FakeResultWrapper subclass for convenience or compatibility shims, however.
*
- * @param IDatabase $database
- * @param resource|ResultWrapper $result
+ * @param IDatabase|null $db Optional database handle
+ * @param ResultWrapper|array|resource $result Optional underlying result handle
*/
- function __construct( $database, $result ) {
- $this->db = $database;
-
+ public function __construct( IDatabase $db = null, $result ) {
+ $this->db = $db;
if ( $result instanceof ResultWrapper ) {
$this->result = $result->result;
} else {
*
* @return int
*/
- function numRows() {
- return $this->db->numRows( $this );
+ public function numRows() {
+ return $this->getDB()->numRows( $this );
}
/**
* @return stdClass|bool
* @throws DBUnexpectedError Thrown if the database returns an error
*/
- function fetchObject() {
- return $this->db->fetchObject( $this );
+ public function fetchObject() {
+ return $this->getDB()->fetchObject( $this );
}
/**
* @return array|bool
* @throws DBUnexpectedError Thrown if the database returns an error
*/
- function fetchRow() {
- return $this->db->fetchRow( $this );
+ public function fetchRow() {
+ return $this->getDB()->fetchRow( $this );
}
/**
- * Free a result object
+ * Change the position of the cursor in a result object.
+ * See mysql_data_seek()
+ *
+ * @param int $row
*/
- function free() {
- $this->db->freeResult( $this );
- unset( $this->result );
- unset( $this->db );
+ public function seek( $row ) {
+ $this->getDB()->dataSeek( $this, $row );
}
/**
- * Change the position of the cursor in a result object.
- * See mysql_data_seek()
+ * Free a result object
*
- * @param int $row
+ * This either saves memory in PHP (buffered queries) or on the server (unbuffered queries).
+ * In general, queries are not large enough in result sets for this to be worth calling.
*/
- function seek( $row ) {
- $this->db->dataSeek( $this, $row );
+ public function free() {
+ if ( $this->db ) {
+ $this->db->freeResult( $this );
+ $this->db = null;
+ }
+ $this->result = null;
}
- /*
- * ======= Iterator functions =======
- * Note that using these in combination with the non-iterator functions
- * above may cause rows to be skipped or repeated.
+ /**
+ * @return IDatabase
+ * @throws RuntimeException
*/
+ private function getDB() {
+ if ( !$this->db ) {
+ throw new RuntimeException( get_class( $this ) . ' needs a DB handle for iteration.' );
+ }
+
+ return $this->db;
+ }
function rewind() {
if ( $this->numRows() ) {
- $this->db->dataSeek( $this, 0 );
+ $this->getDB()->dataSeek( $this, 0 );
}
$this->pos = 0;
$this->currentRow = null;
return $this->currentRow;
}
- /**
- * @return bool
- */
function valid() {
return $this->current() !== false;
}