1

I have a problem with implementing Iterator interface Here is the code:

class User_Model_Users implements Iterator, Countable
{

    protected $_count;
    protected $_gateway;
    protected $_resultSet;

    public function __construct($results, $gateway)
    {
        $this->setGateway($gateway);
        $this->_resultSet = $results;
    }

    public function setGateway(User_Model_UserGateway $gateway)
    {
        $this->_gateway = $gateway;
        return $this;
    }

    public function getGateway()
    {
        return $this->_gateway;
    }

    public function count()
    {
        if (null === $this->_count) {
            $this->_count = count($this->_resultSet);
        }
        return $this->_count;
    }

    public function current()
    {
        if ($this->_resultSet instanceof Iterator) {
            $key = $this->_resultSet->key();
        } else {
            $key = key($this->_resultSet);
        }
        $result = $this->_resultSet [$key];
        if (!$result instanceof User_Model_User) {
            $gateway = $this->getGateway();
            $result = $gateway->createUser($result);
            $this->_resultSet [$key] = $result;
        }
        return $result;
    }

    public function key()
    {
        return key($this->_resultSet);
    }

    public function next()
    {
        return next($this->_resultSet);
    }

    public function rewind()
    {
        return reset($this->_resultSet);
    }

    public function valid()
    {
        return (bool) $this->current();
    }

}

As a $result I provide Zend_Db_Table_Rowset but it can be also other object or array. How can I fix this code so that I works in a foreach loop? I don't get any errors as it is an infinite loop.

3
  • 1
    "can be also other object or array" - in this case an example script+data and the expected result would be nice. Commented Jul 20, 2011 at 12:06
  • for now It doesn't work with Zend_Db_Table_Rowset. I'd like to solve this case Commented Jul 20, 2011 at 12:07
  • What is your question in specific? What do you think needs fixing? Which error do you get? "It doesn't work" does not say what does not work. Commented Jul 20, 2011 at 12:50

1 Answer 1

1

Wild guess (haven't really delved into the code):

Since your implementation of valid() is

public function valid()
{
    return (bool) $this->current();
}

you should make sure that current() returns false when there are no more elements

public function current()
{
    if ($this->_resultSet instanceof Iterator) {
        $key = $this->_resultSet->key();
    } else {
        $key = key($this->_resultSet);
    }

    if ( is_null($key)  ) { // could also be is_null($key)||false===$key, not sure...
        return false;
    }

    $result = $this->_resultSet[$key];
    if (!$result instanceof User_Model_User) {
        $gateway = $this->getGateway();
        $result = $gateway->createUser($result);
        $this->_resultSet[$key] = $result;
    }
    return $result;
}

and btw: your function key() doesn't implement the instanceof Iterator case like current() does.


Test script:

<?php
class User_Model_Users implements Iterator, Countable
{
    protected $_count;
    protected $_gateway;
    protected $_resultSet;

    public function __construct($results, $gateway)
    {
        $this->setGateway($gateway);
        $this->_resultSet = $results;
        $this->_count = null;
    }

    public function setGateway(User_Model_UserGateway $gateway)
    {
        $this->_gateway = $gateway;
        return $this;
    }

    public function getGateway()
    {
        return $this->_gateway;
    }

    public function count()
    {
        if ( is_null($this->_count) ) {
            $this->_count = count($this->_resultSet);
        }
        return $this->_count;
    }

    public function current()
    {
        if ($this->_resultSet instanceof Iterator) {
            $key = $this->_resultSet->key();
        } else {
            $key = key($this->_resultSet);
        }
        if ( is_null($key) ) {
            return false;
        }

        $result = $this->_resultSet[$key];
        if (!$result instanceof User_Model_User) {
            $gateway = $this->getGateway();
            $result = $gateway->createUser($result);
            $this->_resultSet[$key] = $result;
        }
        return $result;
    }

    public function key()
    {
        return key($this->_resultSet);
    }

    public function next()
    {
        return next($this->_resultSet);
    }

    public function rewind()
    {
        return reset($this->_resultSet);
    }

    public function valid()
    {
        return (bool) $this->current();
    }
}

class User_Model_User {
    protected $x, $y;

    public function __construct($x, $y) {
        $this->x = $x;
        $this->y = $y;
    }
}

class User_Model_UserGateway {
    protected $id;
    public function __construct() {
        $this->id = time();
    }

    public function createUser($x) {
        echo "User_Model_UserGateway::createUser($x)\n";
        return new User_Model_User($x, $this->id);
    }
}

$a = array('a', 'b', 'c', 'd');
$users = new User_Model_Users($a, new User_Model_UserGateway);
foreach($users as $u) {
    print_r($u);
}

prints

User_Model_UserGateway::createUser(a)
User_Model_User Object
(
    [x:protected] => a
    [y:protected] => 1311167311
)
User_Model_UserGateway::createUser(b)
User_Model_User Object
(
    [x:protected] => b
    [y:protected] => 1311167311
)
User_Model_UserGateway::createUser(c)
User_Model_User Object
(
    [x:protected] => c
    [y:protected] => 1311167311
)
User_Model_UserGateway::createUser(d)
User_Model_User Object
(
    [x:protected] => d
    [y:protected] => 1311167311
)
Sign up to request clarification or add additional context in comments.

2 Comments

@greg606 Then I really need an example script because my simple test script says otherwise ;-)
I discovered the bug and it's actually inside the Zend Framework Zend_Db_Table_Rowset_Abstract class using Arrayaccess - offsetSet and offsetGet are not implemented! Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.