2

I know this is a simple one, but I just can't figure out where the order is wrong.

<?php

class StringIterator implements \Iterator
{
private $_string;
private $_length;   
private $_position;


public function __construct($string)
{
    if(empty($string))
    {
        throw new \InvalidArgumentException(sprintf('The specified string is empty'));
    }//end if

        $this->_string = $string;
        $this->_length = strlen($this->_string);
        $this->rewind(); //setting the initial position instead of having it all over the place.

}//end func 

public function current()
{
 return $this->_string[$this->_position];

}//end func

public function key()
{
    return $this->_position;
}//end func



public function rewind()
{
    $this-> _position = -1;
}//end func


public function next()
{
    $this-> _position++;
}//end func


public function valid()
{   //why is it that your doing this instead of...
/*
*   return isset(this->_string[this->_position]);
*
*
*/
    return $this->_position < $this->_length;
}//end func

}//end class

TEST CLASS
<?php

require_once __DIR__. '/../src/Iterators/StringIterator.php';

class StringIteratorTest extends PHPUnit_Framework_TestCase
{
    //each method must begin with TEST
public function testInitializing()
{

    $iterator = new \Iterators\StringIterator("Hello World");

    $this->assertEquals(true,$iterator->valid()); 


}//end func

/**
*   @expectedException InvalidArgumentException
*/
public function testInitException()
{

$iterator = new \Iterators\StringIterator("");

}//end func


public function testTraverse()
{
    $string ="Hello World";
    $iterator = new \Iterators\StringIterator($string);
    $count =0;

    $iterator->rewind();
    //test to make sure the next() runs.


    //The iterator interface defines the method Key key()= $key
    //Iterator::current() = $char (gets the current value at the position)
    foreach($iterator as $key=>$char)
    {
        $this->assertEquals($count,$key);
        $this->assertEquals($string[$count],$char);
        ++$count;
         $this->next();
    }//end 4e

}//end func

//tests that the internal pointer (it) is at a valid position in that container that is being iterated
public function testValid()
{
$iterator = new \Iterators\StringIterator($string);

}//end func

//tests the rewind method back to the start of the container.

public function testRewind()
{
$string="Bye";

$iterator = new \Iterators\StringIterator($string); 


for( $i = 0; $i< strlen($string) + 1; ++$i){

$iterator->next();

}//end for
$this->assertEquals(false,$iterator->valid());
$iterator->rewind();
$this->assertEquals(true,$iterator->valid());

}


}

The problem in question: When I run the test (phpunit test) it is stating there is a error in the current() (return line) and in the foreach loop from the actual test component

foreach($iterator as $key=>$char)
{
    $this->assertEquals($count,$key);
    $this->assertEquals($string[$count],$char);
    ++$count;
    $this->next();
}//end 4e

From my research I know that it has to do with the order I'm calling next in the foreach loop, I just can figure out exactly what it needs to be..

KaosAkroma
  • 35
  • 1
  • 5
  • 3
    I am not sure what the problem is. You state "The problem in question is stated to be..." Where is your actual question? can you remove portions of code from your example that are not pertinent? – Mike Brant Jan 24 '13 at 21:27
  • 1
    You dont need to call `next` if youre using a foreach it will be called for you... – prodigitalson Jan 24 '13 at 21:28
  • 1
    Your own rewind function sets the initial position index to -1; though I'm uncertain what your issue is, you possibly need it set to 0. – JoshDM Jan 24 '13 at 21:30
  • Your coding style keeps changing. It's pretty awful by the end. Reminds me of http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454 – Lightness Races in Orbit Jan 24 '13 at 21:31
  • @JoshDM it's set to -1 to ensure that nothing goes out of bound when it's set up. – KaosAkroma Jan 24 '13 at 21:31
  • 1
    @KaosAkroma: But thats not how the iterator works... you set it to `0` on init and rewind because `current` is called before `next` internally, and `key` should always return the current position. – prodigitalson Jan 24 '13 at 21:32
  • @Non-StopTimeTravel I'm not sure what's happened to it let me try to clean it up... – KaosAkroma Jan 24 '13 at 21:33
  • @KaosAkroma - prodigitalson is correct in assessing my comment. – JoshDM Jan 24 '13 at 21:34
  • @JoshDM When setting the value to 0 it throws an error as we set the value from -1 = 0 in the test portion. – KaosAkroma Jan 24 '13 at 21:44
  • @KaosAkroma: Thats because youre calling `next` manually and you souldnt be. You simply need to implement the methods and then you can iterate over the class without any extra trouble - thats the point of the SPL iteration interfaces. See AgentConondrum's answer for details. – prodigitalson Jan 24 '13 at 22:32
  • Fixed it, thanks every one. – KaosAkroma Jan 24 '13 at 23:08

1 Answers1

4

There seems to be a lot wrong here:

  • You're calling $this->next() within StringIteratorTest::testTraverse(). This method has no such next() method. That method belongs to the StringIterator class. This should be a fatal error.
  • Even if this code were running within the StringIterator class, you still wouldn't need to call next() from within a foreach loop. foreach calls all of the Iterator-defined methods itself. That's the whole point. Calling next() within a foreach would just have the effect of jumping a position.
  • Your rewind() method is incorrect. It's setting the position to a negative value. There are no negative positions in a string. If you were to call current() on this, you would get an error because it's trying to call $_string[-1], which doesn't exist.
  • Your valid() method is also incorrect, since it's only checking that the position isn't outside the upper limit, but not the lower limit. This is why valid() is returning TRUE even though your rewind() method is setting the position to an invalid state.
  • Your testValid() method should have caught this, but that function isn't actually testing the valid() method. It's just creating a new object and doing nothing with it.
  • Your testing methodology is bad in the testRewind() method. Rather than checking for valid(), you should be calling current() and checking if it returns "B", which is the first character in your string. The main function of the rewind() method is to reset the internal pointer of the object back to the start, so you should be testing for that explicitly.
AgentConundrum
  • 20,288
  • 6
  • 64
  • 99