diff --git a/src/Iterators/CachingIterator.php b/src/Iterators/CachingIterator.php new file mode 100644 index 00000000..3866a35b --- /dev/null +++ b/src/Iterators/CachingIterator.php @@ -0,0 +1,235 @@ +iterator = $iterator; + } + + /** + * Whether a offset exists. + * + * @link http://php.net/manual/en/arrayaccess.offsetexists.php + * + * @param mixed $offset
+ * An offset to check for. + *
+ * + * @return bool true on success or false on failure. + * + *+ * The return value will be casted to boolean if non-boolean was returned + * + * @since 5.0.0 + */ + public function offsetExists($offset) + { + try { + $this->toIndex($offset); + } catch (TDBMInvalidOffsetException $e) { + return false; + } + + return true; + } + + /** + * Offset to retrieve. + * + * @link http://php.net/manual/en/arrayaccess.offsetget.php + * + * @param mixed $offset
+ * The offset to retrieve. + *
+ * + * @return mixed Can return all value types + * + * @since 5.0.0 + */ + public function offsetGet($offset) + { + $this->toIndex($offset); + + return $this->results[$offset]; + } + + /** + * @param mixed $offset + * @throws TDBMInvalidOffsetException + */ + private function toIndex($offset): void + { + if ($offset < 0 || filter_var($offset, FILTER_VALIDATE_INT) === false) { + throw new TDBMInvalidOffsetException('Trying to access result set using offset "'.$offset.'". An offset must be a positive integer.'); + } + if (!$this->fetchStarted) { + $this->rewind(); + } + while (!isset($this->results[$offset])) { + $this->next(); + if ($this->current === null) { + throw new TDBMInvalidOffsetException('Offset "'.$offset.'" does not exist in result set.'); + } + } + } + + public function next() + { + // Let's overload the next() method to store the result. + if (isset($this->results[$this->key + 1])) { + ++$this->key; + $this->current = $this->results[$this->key]; + } else { + $this->current = next($this->iterator); + $this->key = key($this->iterator); + if ($this->key !== null) { + $this->results[$this->key] = $this->current; + } + } + } + + /** + * Do not reexecute the query. + */ + public function rewind() + { + if (!$this->fetchStarted) { + reset($this->iterator); + $this->fetchStarted = true; + $this->key = key($this->iterator); + $this->current = current($this->iterator); + $this->results[$this->key] = $this->current; + } else { + $this->key = 0; + $this->current = $this->results[0]; + } + } + + /** + * Return the current element + * @link https://php.net/manual/en/iterator.current.php + * @return mixed Can return any type. + * @since 5.0.0 + */ + public function current() + { + return $this->current; + } + + /** + * Return the key of the current element + * @link https://php.net/manual/en/iterator.key.php + * @return mixed scalar on success, or null on failure. + * @since 5.0.0 + */ + public function key() + { + return $this->key; + } + + /** + * Checks if current position is valid + * @link https://php.net/manual/en/iterator.valid.php + * @return boolean The return value will be casted to boolean and then evaluated. + * Returns true on success or false on failure. + * @since 5.0.0 + */ + public function valid() + { + return $this->key !== null; + } + + /** + * Offset to set + * @link https://php.net/manual/en/arrayaccess.offsetset.php + * @param mixed $offset+ * The offset to assign the value to. + *
+ * @param mixed $value+ * The value to set. + *
+ * @return void + * @since 5.0.0 + */ + public function offsetSet($offset, $value) + { + throw new TDBMInvalidOperationException('You cannot set values in a TDBM result set.'); + } + + /** + * Offset to unset + * @link https://php.net/manual/en/arrayaccess.offsetunset.php + * @param mixed $offset+ * The offset to unset. + *
+ * @return void + * @since 5.0.0 + */ + public function offsetUnset($offset) + { + throw new TDBMInvalidOperationException('You cannot unset values in a TDBM result set.'); + } + + /** + * Count elements of an object + * @link https://php.net/manual/en/countable.count.php + * @return int The custom count as an integer. + * + *+ * The return value is cast to an integer. + * @since 5.1.0 + */ + public function count() + { + if ($this->iterator instanceof Countable) { + return $this->iterator->count(); + } + throw new TDBMInvalidOperationException('Cannot count items of iterator '.get_class($this->iterator)); + } +} diff --git a/src/ResultIterator.php b/src/ResultIterator.php index df7368c4..6a565168 100644 --- a/src/ResultIterator.php +++ b/src/ResultIterator.php @@ -5,6 +5,7 @@ use Doctrine\DBAL\Platforms\MySqlPlatform; use Psr\Log\NullLogger; +use TheCodingMachine\TDBM\Iterators\CachingIterator; use function array_map; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Statement; @@ -167,10 +168,11 @@ public function getIterator() if ($this->innerResultIterator === null) { if ($this->totalCount === 0) { $this->innerResultIterator = new EmptyInnerResultIterator(); - } elseif ($this->mode === TDBMService::MODE_CURSOR) { - $this->innerResultIterator = InnerResultIterator::createInnerResultIterator($this->queryFactory->getMagicSql(), $this->parameters, null, null, $this->queryFactory->getColumnDescriptors(), $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->logger); } else { - $this->innerResultIterator = InnerResultArray::createInnerResultIterator($this->queryFactory->getMagicSql(), $this->parameters, null, null, $this->queryFactory->getColumnDescriptors(), $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->logger); + $this->innerResultIterator = InnerResultIterator::createInnerResultIterator($this->queryFactory->getMagicSql(), $this->parameters, null, null, $this->queryFactory->getColumnDescriptors(), $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->logger); + if ($this->mode === TDBMService::MODE_ARRAY) { + $this->innerResultIterator = new CachingIterator($this->innerResultIterator); + } } } diff --git a/tests/Iterators/CachingIteratorTest.php b/tests/Iterators/CachingIteratorTest.php new file mode 100644 index 00000000..76c00fb5 --- /dev/null +++ b/tests/Iterators/CachingIteratorTest.php @@ -0,0 +1,30 @@ +assertArrayHasKey(3, $it); + $this->assertCount(5, $it); + $arr = iterator_to_array($it); + $this->assertSame([1, 2, 3, 4, 5], $arr); + } + + public function testCachingIterator2() + { + $it = new CachingIterator(new ArrayIterator(range(1,5))); + + $arr = iterator_to_array($it); + $this->assertSame([1, 2, 3, 4, 5], $arr); + } +}