Implement LimitIterator

Implements LimitIterator from SPL.

Closes #788
Esse commit está contido em:
Eric Caruso
2013-06-24 17:35:41 -07:00
commit de Sara Golemon
commit 533ed5d493
21 arquivos alterados com 262 adições e 4 exclusões
+1
Ver Arquivo
@@ -65,5 +65,6 @@ hphp/system/php/spl/datastructures/SplObjectStorage.php
hphp/system/php/spl/datastructures/SplPriorityQueue.php
hphp/system/php/spl/iterators/AppendIterator.php
hphp/system/php/spl/iterators/ArrayIterator.php
hphp/system/php/spl/iterators/LimitIterator.php
hphp/system/php/spl/iterators/RecursiveIteratorIterator.php
hphp/system/php/spl/miscellaneous/ArrayObject.php
@@ -13,6 +13,10 @@
*/
class IteratorIterator implements OuterIterator {
private $iterator;
private $current;
private $key;
private $valid;
private $position;
// This doc comment block generated by idl/sysdoc.php
/**
@@ -72,7 +76,7 @@ class IteratorIterator implements OuterIterator {
* @return mixed The key of the current element.
*/
public function key() {
return $this->iterator->key();
return $this->key;
}
// This doc comment block generated by idl/sysdoc.php
@@ -84,7 +88,7 @@ class IteratorIterator implements OuterIterator {
* @return mixed The value of the current element.
*/
public function current() {
return $this->iterator->current();
return $this->current;
}
// This doc comment block generated by idl/sysdoc.php
@@ -96,7 +100,10 @@ class IteratorIterator implements OuterIterator {
* @return mixed No value is returned.
*/
public function next() {
return $this->iterator->next();
$this->iterator->next();
$this->_position++;
$this->_fetch(true);
return;
}
// This doc comment block generated by idl/sysdoc.php
@@ -108,10 +115,42 @@ class IteratorIterator implements OuterIterator {
* @return mixed No value is returned.
*/
public function rewind() {
return $this->iterator->rewind();
$this->iterator->rewind();
$this->_position = 0;
$this->_fetch(true);
return;
}
public function __call($func, $params) {
return call_user_func_array(array($this->iterator, $func), $params);
}
/**
* This function appears in the php source in spl_iterators.c as
* spl_dual_it_fetch. Apparently, all iterators that store other
* iterators are forced to do this layer of caching. If you call
* next(), these "dual" iterators will need to get the key and
* current value out of the underlying iterator and store it.
*
* Basically, if you see a call to spl_dual_it_fetch in the
* PHP source, it's very likely that you should call this.
*/
protected function _fetch($check) {
if (!$check || $this->iterator->valid()) {
$this->current = $this->iterator->current();
$key = $this->iterator->key();
$this->key = is_null($key) ? $this->_position : $key;
return true;
}
return false;
}
protected function _getPosition() {
return $this->position;
}
protected function _setPosition($position) {
$this->position = $position;
}
}
@@ -0,0 +1,164 @@
<?php
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from http://php.net/manual/en/class.limititerator.php )
*
* The LimitIterator class allows iteration over a limited subset of items
* in an Iterator.
*
*/
class LimitIterator extends IteratorIterator implements OuterIterator {
private $offset;
private $count;
private $valid;
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from http://php.net/manual/en/limititerator.construct.php )
*
* Constructs a new LimitIterator from an iterator with a given starting
* offset and maximum count.
*
* @iterator mixed The Iterator to limit.
* @offset mixed Optional offset of the limit.
* @count mixed Optional count of the limit.
*
* @return mixed The new LimitIterator.
*/
public function __construct($iterator, $offset = 0, $count = -1) {
if ($offset < 0) {
throw new OutOfRangeException(
"Parameter offset must be >= 0");
} else if ($count < -1) {
throw new OutOfRangeException(
"Parameter count must either be -1 or ".
"a value greater than or equal 0");
}
parent::__construct($iterator);
$this->offset = $offset;
$this->count = $count;
}
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from http://php.net/manual/en/limititerator.getposition.php )
*
* Gets the current zero-based position of the inner Iterator.
*
* @return mixed The current position.
*/
public function getPosition() {
return $this->_getPosition();
}
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from http://php.net/manual/en/limititerator.rewind.php )
*
* Rewinds the iterator to the starting offset specified in
* LimitIterator::__construct().
*
* @return mixed No value is returned.
*/
public function rewind() {
$this->getInnerIterator()->rewind();
$this->_setPosition(0);
$this->seek($this->offset);
}
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from http://php.net/manual/en/limititerator.next.php )
*
* Moves the iterator forward.
*
* @return mixed No value is returned.
*/
public function next() {
$this->getInnerIterator()->next();
$this->_setPosition($this->_getPosition() + 1);
if ($this->count == -1 ||
$this->_getPosition() < $this->offset + $this->count) {
$this->valid = $this->_fetch(true);
}
}
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from http://php.net/manual/en/limititerator.seek.php )
*
* Moves the iterator to the offset specified by position.
*
* @position mixed The position to seek to.
*
* @return mixed Returns the offset position after seeking.
*/
public function seek($position) {
// Bounds checking
if ($position < $this->offset) {
throw new OutOfBoundsException(
"Cannot seek to ".$position.
" which is below the offset ".$this->offset);
} else if ($this->count != -1 &&
$position >= $this->offset + $this->count) {
throw new OutOfBoundsException(
"Cannot seek to ".$position.
" which is behind offset ".$this->offset.
" plus count ".$this->count);
}
$iter = $this->getInnerIterator();
if ($this->_getPosition() != $position &&
$iter instanceof SeekableIterator) {
// We can use the underlying seek implementation.
$iter->seek($position);
$this->_setPosition($position);
if (($this->count == -1 ||
$this->_getPosition() < $this->offset + $this->count) &&
$iter->valid()) {
$this->valid = $this->_fetch(false);
}
} else {
// We need to simulate the seek using rewind() and next().
// Backwards seeking is done using rewind() first.
if ($this->_getPosition() > $position) {
$iter->rewind();
$this->_setPosition(0);
}
// We can seek forwards using next().
while ($this->_getPosition() < $position && $iter->valid()) {
/*
* Note that we're basically reimplementing next() here.
* This is because the SPL implementation does not fetch
* when it's walking the iterator - it only fetches when
* it reaches the right position. This also accounts for
* why we update _position manually (and similarly, why
* we set it manually to 0 in the block above).
*/
$iter->next();
$this->_setPosition($this->_getPosition() + 1);
}
// Fetch.
if ($iter->valid()) {
$this->valid = $this->_fetch(true);
}
}
return $this->_getPosition();
}
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from http://php.net/manual/en/limititerator.valid.php )
*
* Checks whether the current element is valid.
*
* @return mixed Returns TRUE on success or FALSE on failure.
*/
public function valid() {
return ($this->count == -1 ||
$this->_getPosition() < $this->offset + $this->count) &&
$this->valid;
}
}
@@ -0,0 +1,24 @@
<?php
// Create an iterator to be limited
$fruits = new ArrayIterator(array(
'apple',
'banana',
'cherry',
'damson',
'elderberry'
));
// Loop over first three fruits only
foreach (new LimitIterator($fruits, 0, 3) as $fruit) {
var_dump($fruit);
}
echo "\n";
// Loop from third fruit until the end
// Note: offset starts from zero for apple
foreach (new LimitIterator($fruits, 2) as $fruit) {
var_dump($fruit);
}
@@ -0,0 +1,7 @@
string(5) "apple"
string(6) "banana"
string(6) "cherry"
string(6) "cherry"
string(6) "damson"
string(10) "elderberry"
@@ -0,0 +1,21 @@
<?php
$arr = new ArrayIterator(array('a','b','c'));
function check_seek($lim, $seek) {
try {
$lim->seek($seek);
} catch (OutOfBoundsException $e) {
var_dump($e->getMessage());
}
}
$lim1 = new LimitIterator($arr, 0, 2);
check_seek($lim1, 0);
check_seek($lim1, 1);
check_seek($lim1, 2);
$lim2 = new LimitIterator($arr, 1);
check_seek($lim2, 0);
check_seek($lim2, 1);
check_seek($lim2, 2);
@@ -0,0 +1,2 @@
string(54) "Cannot seek to 2 which is behind offset 0 plus count 2"
string(44) "Cannot seek to 0 which is below the offset 1"