I've made a SPL-based class named Recordset that wraps both MySQLi_STMT and MySQLi_Result objects and allows treating either as a 3-dimensional array. It requires PHP5.3+.
The code is roughly 200 lines long, so instead of making a giant post, here's a pastebin link... http://pastebin.com/LUe4j9DM
This class allows accessing result rows by array key (echo $Recordset[295]['title'];
) or by foreach loops (foreach ($Recordsetas $rec) {echo $rec['title'];}
). It also allows you to get a row by index number without interfering with the internal result pointer. In other words, right in the middle of a foreach loop, you can do $x = $Recordset[259]['ID'];
without causing the internal result row pointer to jump out of order. It easily allows exchanging this class out for regular 3-dimensional arrays of result dumps, and avoids duplication of result binding code for every statement throughout an application. Plus it lets you still access functions and properties of the wrapped MySQLi object directly ($Recordset->attr_get(1)
).
Objects are created by passing in an sql query string. For handling prepared statements, simply pass in additional arguments as the prepared binding parameters. In those cases, there is a behind-the-scenes bind_param
going on generating the MySQLi_STMT object to be wrapped. That part isn't included here, so for the purposes of this post, pretend it's magic.
For example to automatically handle the query as a prepared statement:
$recordset = new Recordset("SELECT * FROM tblComments WHERE(parentBlogID = ?)", 2);
if (count($recordset) > 0) {
foreach ($recordset as $record)
echo $record['commentText'] . '<br>';
}
else echo "No records returned.";
Here's some speed tests iterating over 10000 records from a localhost table with 6 columns. This test was done with prepared statement results either wrapped in Recordset or not. Timing is measured starting before and ending after each entire loop call..
With Recordset...
foreach($Recordset as $rec) {$x = $rec;}
...
0.105 sfor ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset[$i];}
...
0.056 sfor ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset->fetchRow($i);}
...
0.0351 swhile ($x = $Recordset[$i]) {$i++; if (!isset($Recordset[$i])) break;}
...
0.0647 s
Without Recordset, but still using the "bind to array" technique and pre-storing results...
for ($i=0, $count=$MySQLI_STMT->num_rows; $i < $count; $i++)) {$MySQLI_STMT->fetch(); $x = $bind_row;}
...
0.0409 swhile ($MySQLI_STMT->fetch()) {$x = $bind_row;}
...
0.009 s
I'm pretty bummed about the slow foreach loop over my Recordset object, by the slower seek and fetch speeds within Recordset, and by the fact that nothing can even compare to while ($MySQLI_STMT->fetch()) {}
. I'd like for this to work better. Any suggestions or insight how to speed things up?