PHP Developers Network

A community of PHP developers offering assistance, advice, discussion, and friendship.
 
Loading
It is currently Thu May 28, 2020 6:08 am

All times are UTC - 5 hours




Post new topic Reply to topic  [ 7 posts ] 
Author Message
PostPosted: Tue May 06, 2003 1:48 pm 
Offline
Site Admin

Joined: Thu Apr 18, 2002 3:14 pm
Posts: 1767
Location: Montreal, CA
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:

Syntax: [ Download ] [ Hide ]
<?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() {}

}

?>


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:

Syntax: [ Download ] [ Hide ]
<?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');

    }

}

?>


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:

Syntax: [ Download ] [ Hide ]
<?php



for ( $it2->reset(); $it2->isValid(); $it2->next() ) {

        $row = $it2->getCurrent();

        echo 'IT2 -> ',$row[0],"\n";

}



?>


When they should be able to do this:

Syntax: [ Download ] [ Hide ]
<?php



while ( $it->next() ) {

        $row = $it->getCurrent();

        echo 'IT -> ',$row[0],"\n";

}



?>


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:

Syntax: [ Download ] [ Hide ]
<?php



        /**

        * The current iteration value

        * @type mixed

        */


        var $current_iteration;

       

        /**

        * The result of the <code>next()</code> call

        * @type bool

        */


        var $next_result = null;



?>


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.

Syntax: [ Download ] [ Hide ]
<?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;

        }

}



?>


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).

Syntax: [ Download ] [ Hide ]
<?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";

}



?>


As you can see there, both methods will work fine.

Thoughts, comments, flames?


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 07, 2003 9:42 pm 
Offline
Forum Regular
User avatar

Joined: Sun Apr 28, 2002 9:53 pm
Posts: 978
I like the way you stated the reason for doing this, to make it easier to use and without rewriting code, as thats what I think one of the main points of OOP is. Many people do not realize that the classes were meant to be altered without having other coding changes.

I, however, would probably take the simpleiterator approach, except my class would have a $element_contents variable which would store the contents of the current element. My fetch() function would grab the contents of the element, store it in the variable, and increment an internal counter so the next element would be fetched upon the next run of the loop. Other than that, it would be the same.

I do like your approach better than both of the other ones, though. And the regular iterator seems pointless to me.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 08, 2003 4:15 am 
Offline
DevNet Evangelist
User avatar

Joined: Tue May 07, 2002 9:48 am
Posts: 8391
Location: Berlin, ger


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 08, 2003 4:07 pm 
Offline
Forum Newbie

Joined: Thu May 08, 2003 4:07 pm
Posts: 2
Why even get that complicated? Why not just:
Syntax: [ Download ] [ Hide ]
<?php

$it = & new Iterator;

while($row = $it->get_next()) {

    echo $row['blah'];

}

?>


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 11, 2003 2:05 pm 
Offline
Forum Newbie

Joined: Sun May 11, 2003 2:05 pm
Posts: 1
Starting from the first last;

Syntax: [ Download ] [ Hide ]
<?php

$it = & new Iterator;

while($row = $it->get_next()) {

    echo $row['blah'];

}

?>


The problem there is (assuming the get_next() method really gets the next result), how do you get the very first result?

Jason - agree your approach is more solid. With what I'm doing, the IndexedArrayIterator in particular will be trouble if it's just an array of boolean values - the moment a value is false iteration stops. Note the intention of the size() method is not to meant to prevent this from happening but instead as a useful utility to help with rendering results - didn't really discuss that in the article. Perhaps that class should be renamed StrutIterator or something similar to imply it's only meant for use with arrays of arrays or arrays of objects, the first array always being indexed not associative.


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 12, 2003 3:27 pm 
Offline
Forum Newbie

Joined: Thu May 08, 2003 4:07 pm
Posts: 2
It's better to work off what will happen MOST of the time (IMO). Instead of get_next(), use get() and it will get the next 'expected' just like mysql_fetch_array() or something. I think the problem is that many people over-think and over-engineer a solution.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 15, 2003 2:37 pm 
Offline
DevNet Resident
User avatar

Joined: Sat Jun 08, 2002 1:24 pm
Posts: 1207
Location: Florida
It's my opinion that this is the kind of thing that should be an engine level implementation. Isn't PHP5 going to have iterators via an extension?

Cheers,
BDKR


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC - 5 hours


Who is online

Users browsing this forum: No registered users and 5 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
Powered by phpBB® Forum Software © phpBB Group