Iterator and SImpleIterator combined
Posted: Tue May 06, 2003 1:48 pm
The idea was based on conversations with Harry Fuecks ( http://www.phppatterns.com/index.php/ar ... ew/50/1/1/ ) and various postings on the SitePoint forums ( http://www.sitepointforums.com/showthre ... adid=93442 ).
Simply put, I believe each of the main posts, Harry included, is wrong. The concept stems from my work with the Eclipse library, and the Iterator object presented here:
What's interesting to note is that it's a complete Iterator as described in the book of four. Problem being, it doesn't take into account the fact that PHP is a very dynamic language, and doesn't need to rely on methods built for languages like C++ and Java (strongly typed languages).
Harry proposed this option:
This option is equally as bad. First, I don't want a quantifier in my object. SimpleIterator serves the same purpose as the Iterator object, except it's meant to be easier.
The SimpleIterator doesn't solve the problem, it just shifts the potential solution from the potential problem.
Really, the problem is this. Why should a programmer do this:
When they should be able to do this:
Of course, $it is considered to be the iterator object.
The problem wasn't with the interace, but with how the object worked interanally.
So I left the Iterator object the way it was, and just added:
And then, for object implementing the Iterator pattern, they could us these variables. Of course, you could assume a contract, and just create a new object and hard code these variables in rather in include the entire Iterator into your code.
Anyways, looking at QueryIterator, I made the necessary changes to work with the new Iterator.
Basically, the Iterator advances on next(), which makes sense. It also returns a boolean value of false if the next() is unsuccessful, or true if it is. isValid() simply returns that same value. This means you can use both methods of iteration, the normal, classic Iterator approach or the SimpleIterator approach, and you don't have to worry about rewriting code to use either Iterator (my main reason for doing this).
As you can see there, both methods will work fine.
Thoughts, comments, flames?
Simply put, I believe each of the main posts, Harry included, is wrong. The concept stems from my work with the Eclipse library, and the Iterator object presented here:
Code: Select all
<?php
class Iterator {
/***
* Create a new iterator that's immediately ready for use. Normally,
* the constructor calls <code>reset()</code>.
***/
function Iterator(& $container) {}
/***
* Initialize this iterator.
* @returns void
***/
function reset() {}
/***
* Advance the internal cursor to the next object. The behavior of this
* method is undefined if <code>isValid()</code> returns <code>false</code>.
* @returns void
***/
function next() {}
/***
* Check if the iterator is valid
* @returns bool
***/
function isValid() {}
/***
* Return a reference to the current object. The behavior of this method
* is undefined if <code>isValid()</code> returns <code>false</code>.
* @returns mixed
***/
function &getCurrent() {}
}
?>Harry proposed this option:
Code: Select all
<?php
/**
* Defines the interface for concrete Iterators
*
* @interface
*/
class SimpleIterator {
/**
* Returns the current element from the collection
* and moves the internal pointer forward one
*
* @return mixed
*/
function fetch() {
die ('SimpleIterator::fetch must be implemented');
}
/**
* Returns the number of elements in the collection
*
* @return int
*/
function size() {
die ('SimpleIterator::size must be implemented');
}
/**
* Resets the collection pointer to the start
*
* @return void
*/
function reset() {
die ('SimpleIterator::reset must be implemented');
}
}
?>The SimpleIterator doesn't solve the problem, it just shifts the potential solution from the potential problem.
Really, the problem is this. Why should a programmer do this:
Code: Select all
<?php
for ( $it2->reset(); $it2->isValid(); $it2->next() ) {
$row = $it2->getCurrent();
echo 'IT2 -> ',$row[0],"\n";
}
?>Code: Select all
<?php
while ( $it->next() ) {
$row = $it->getCurrent();
echo 'IT -> ',$row[0],"\n";
}
?>The problem wasn't with the interace, but with how the object worked interanally.
So I left the Iterator object the way it was, and just added:
Code: Select all
<?php
/**
* The current iteration value
* @type mixed
*/
var $current_iteration;
/**
* The result of the <code>next()</code> call
* @type bool
*/
var $next_result = null;
?>Anyways, looking at QueryIterator, I made the necessary changes to work with the new Iterator.
Code: Select all
<?php
class QueryIterator extends Iterator
{
// DATA MEMBERS
/***
* The query to iterator over (a <code>QueryResult</code> or a
* <code>Query</code>)
* @type QueryResult
***/
var $queryResult;
/***
* How each row should be retrieved. Should be <code>ECLIPSE_DB_BOTH</code>,
* <code>ECLIPSE_DB_ASSOC</code> or <code>ECLIPSE_DB_NUM</code>
* @type int
***/
var $rowType;
/***
* The index of the current row
* @type int
***/
var $index;
/***
* The total number of rows in the query
* @type int
***/
var $total;
// CREATORS
/***
* Construct a new <code>QueryIterator</code>-object
* @param $queryResult the <code>QueryResult</code> to iterate over
***/
function QueryIterator(&$queryResult, $rowType = ECLIPSE_DB_BOTH)
{
$this->queryResult =& $queryResult;
$this->rowType = $rowType;
$this->reset();
}
// MANIPULATORS
/***
* @returns void
***/
function reset()
{
$this->index = -1;
}
/***
* @returns void
***/
function next()
{
$this->index++;
$this->next_result = ( $this->current_iteration =& $this->queryResult->getRow($this->index, $this->rowType) );
return $this->next_result;
}
// ACCESSORS
/***
* @returns bool
***/
function isValid()
{
if ( $this->next_result == null ) return $this->next();
return $this->next_result;
}
/***
* Return a reference to the current row of the <code>QueryResult</code>
* @returns array
***/
function &getCurrent()
{
return $this->current_iteration;
}
}
?>Code: Select all
<?php
include 'MyDatabase.php';
include 'QueryIterator.php';
$db = &new MyDatabase('database', 'host');
$db->connect('username','password');
$result =& $db->query("SHOW TABLES");
$it = &new QueryIterator($result);
$it2 = &new QueryIterator($result);
while ( $it->next() ) {
$row = $it->getCurrent();
echo 'IT -> ',$row[0],"\n";
}
for ( $it2->reset(); $it2->isValid(); $it2->next() ) {
$row = $it2->getCurrent();
echo 'IT2 -> ',$row[0],"\n";
}
?>Thoughts, comments, flames?