class Recordset implements Iterator, ArrayAccess, Countable {
private $MySQLi_set; // MySQLi_STMT or MySQLi_Result object
private $MySQLi_set_type; // cache of MySQLi_STMT or MySQLi_Result object type to avoid repeat instanceof calls
private $field_metadata; // array of field info objects returned by query
private $num_rows; // cache row count to avoid repeat mysqli_x->num_rows call
private $bind_row; // binding array for MySQLi_STMT results
private $pointer; // for tracking iterations over records in query
function __construct($sql) {
$bind_params = func_get_args();
array_shift($bind_params);
try {
if (count($bind_params) === 0) {
// expecting MYSQLI_Result obj returned, replace code as necessary
$this->MySQLi_set = DB_Main::query($sql);
}
else {
// expecting MySQLi_STMT obj returned, replace code as necessary
$this->MySQLi_set = DB_Main::statement($sql, $bind_params);
}
}
catch (Exception $e) {
throw new RuntimeException(((count($bind_params)) ? 'Statement' : 'Query') . ' failed: ' . $e->getMessage(), $e->getCode(), $e);
}
// cache name of object to avoid repeat instanceof calls
$this->MySQLi_set_type = get_class($this->MySQLi_set);
if ($this->MySQLi_set_type === 'mysqli_result') {
// set $this->field_metadata to array of field info objects
$this->field_metadata = $this->MySQLi_set->fetch_fields();
}
else if ($this->MySQLi_set_type === 'mysqli_stmt') {
// Set up special handling for MySQLi_STMT objects
// set $this->field_metadata to array of field info objects used later for result bindings for MySQLi_STMT object
$result_metadata = $this->MySQLi_set->result_metadata();
$this->field_metadata = $result_metadata->fetch_fields();
$result_metadata->free_result();
unset($result_metadata);
// store results to allow object to act as a countable, seekable array
if (!$this->MySQLi_set->store_result())
throw new RuntimeException('MySQLi_STMT Result failed to store. Error: ' . $this->MySQLi_set->error);
// because bind_result() cannot bind to an object or array, use the 'call_user_func_array()' technique instead
$this->bind_row = array();
$bind_row2 = array();
foreach($this->field_metadata as $field)
$bind_row2[$field->name] =& $this->bind_row[$field->name];
if (!call_user_func_array(array($this->MySQLi_set, 'bind_result'), $this->bind_row))
throw new RuntimeException($this->MySQLi_set->error);
if ($this->MySQLi_set->error)
throw new RuntimeException($this->MySQLi_set->error);
}
else {
throw new RuntimeException('No MySQLi_STMT or MySQLi_Result object to work with. OnlyClass: ' . $this->MySQLi_set_type . ' providedattempted.');
}
// cache row count to avoid constant calls to MySQLi_x->num_rows
$this->num_rows = $this->MySQLi_set->num_rows;
// if rows were returned, set internal pointer to 0
if ($this->num_rows > 0) $this->pointer = 0;
}
function fetchRow($offset = null) {
// private function internally using regular fetch() methods to progress through records
// sets $this->bind_row to an associative array of the next record for MySQLi_STMT wrapped objects
$this->MySQLi_set->data_seek((($offset !== null) ? (int) $offset : $this->pointer));
if ($this->MySQLi_set_type === 'mysqli_result')
return $this->MySQLi_set->fetch_assoc();
// else this is a MySQLi_STMT object
$fetch_status = $this->MySQLi_set->fetch();
if ($fetch_status === true) return $this->bind_row;
else if ($fetch_status === null) return null; // no more rows
// MySQLi_STMT row fetch failed
throw new RuntimeException('Error fetching row: . ' . $this->MySQLi_set->error);
}
function count() {return $this->num_rows;}
function current() {return $this->fetchRow();}
function key() {return $this->pointer;}
function next() {$this->pointer++;}
function rewind() {$this->pointer = 0;}
function valid() {return $this->offsetExists($this->pointer);}
function offsetExists($offset) {
// Part of the ArrayAccess interface. Used to determine if the selected record in the set actually exists or is out of bounds.
function offsetExists // Example: 'isset($offset$Recordset[22])' {returns true if there were 23 or more records returned from the query.
return ((int) $offset < $this->num_rows AND (int) $offset >= 0);}
}
function offsetGet($offset) {
// Part of the ArrayAccess interface. Used to retrieve the assoc array of the chosen record in line.
// allow index referencing without altering "internal array pointer"
// Example: 'print_r($Recordset[22])' prints the 23rd record returned by the query.
if (!((int) $offset < $this->offsetExists>num_rows AND (int) $offset >= 0))
throw new OutOfBoundsException('Out of range of result set.');
return $this->fetchRow($offset);
}
}
The code is roughly 200 lines long, so instead of making a giant post, here's a pastebin link... http://pastebin.com/WPaHeguXhttp://pastebin.com/n17MuEzf
foreach($Recordset as $rec) {$x = $rec;}
...
0.1050723 s
for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset[$i];}
...
0.0560523 s
for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset->fetchRow($i);}
...
0.03510406 s
while ($x = $Recordset[$i]) {$i++; if (!isset($Recordset[$i])) break;}
...
0.06470618 s
for ($i=0, $count=$MySQLI_STMT$count=MySQLI_STMT->num_rows; $i < $count; $i++)) {$MySQLI_STMT->fetch(); $x = $bind_row;}
...
0.04090434 s
while ($MySQLI_STMT->fetch()) {$x = $bind_row;}
...
0.0090091 s