init
This commit is contained in:
@@ -0,0 +1,361 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Database\Statement;
|
||||
|
||||
use Cake\Database\DriverInterface;
|
||||
use Cake\Database\StatementInterface;
|
||||
use Cake\Database\TypeConverterTrait;
|
||||
use Iterator;
|
||||
|
||||
/**
|
||||
* A statement decorator that implements buffered results.
|
||||
*
|
||||
* This statement decorator will save fetched results in memory, allowing
|
||||
* the iterator to be rewound and reused.
|
||||
*
|
||||
* @template-implements \Iterator<mixed>
|
||||
*/
|
||||
class BufferedStatement implements Iterator, StatementInterface
|
||||
{
|
||||
use TypeConverterTrait;
|
||||
|
||||
/**
|
||||
* If true, all rows were fetched
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_allFetched = false;
|
||||
|
||||
/**
|
||||
* The decorated statement
|
||||
*
|
||||
* @var \Cake\Database\StatementInterface
|
||||
*/
|
||||
protected $statement;
|
||||
|
||||
/**
|
||||
* The driver for the statement
|
||||
*
|
||||
* @var \Cake\Database\DriverInterface
|
||||
*/
|
||||
protected $_driver;
|
||||
|
||||
/**
|
||||
* The in-memory cache containing results from previous iterators
|
||||
*
|
||||
* @var array<int, array>
|
||||
*/
|
||||
protected $buffer = [];
|
||||
|
||||
/**
|
||||
* Whether this statement has already been executed
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_hasExecuted = false;
|
||||
|
||||
/**
|
||||
* The current iterator index.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $index = 0;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Cake\Database\StatementInterface $statement Statement implementation such as PDOStatement
|
||||
* @param \Cake\Database\DriverInterface $driver Driver instance
|
||||
*/
|
||||
public function __construct(StatementInterface $statement, DriverInterface $driver)
|
||||
{
|
||||
$this->statement = $statement;
|
||||
$this->_driver = $driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection driver.
|
||||
*
|
||||
* @return \Cake\Database\DriverInterface
|
||||
*/
|
||||
protected function getDriver(): DriverInterface
|
||||
{
|
||||
return $this->_driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic getter to return $queryString as read-only.
|
||||
*
|
||||
* @param string $property internal property to get
|
||||
* @return string|null
|
||||
*/
|
||||
public function __get(string $property)
|
||||
{
|
||||
if ($property === 'queryString') {
|
||||
/** @psalm-suppress NoInterfaceProperties */
|
||||
return $this->statement->queryString;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function bindValue($column, $value, $type = 'string'): void
|
||||
{
|
||||
$this->statement->bindValue($column, $value, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function closeCursor(): void
|
||||
{
|
||||
$this->statement->closeCursor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function columnCount(): int
|
||||
{
|
||||
return $this->statement->columnCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function errorCode()
|
||||
{
|
||||
return $this->statement->errorCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function errorInfo(): array
|
||||
{
|
||||
return $this->statement->errorInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function execute(?array $params = null): bool
|
||||
{
|
||||
$this->_reset();
|
||||
$this->_hasExecuted = true;
|
||||
|
||||
return $this->statement->execute($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function fetchColumn(int $position)
|
||||
{
|
||||
$result = $this->fetch(static::FETCH_TYPE_NUM);
|
||||
if ($result !== false && isset($result[$position])) {
|
||||
return $result[$position];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statements can be passed as argument for count() to return the number
|
||||
* for affected rows from last execution.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return $this->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function bind(array $params, array $types): void
|
||||
{
|
||||
$this->statement->bind($params, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function lastInsertId(?string $table = null, ?string $column = null)
|
||||
{
|
||||
return $this->statement->lastInsertId($table, $column);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @param string|int $type The type to fetch.
|
||||
* @return array|false
|
||||
*/
|
||||
public function fetch($type = self::FETCH_TYPE_NUM)
|
||||
{
|
||||
if ($this->_allFetched) {
|
||||
$row = false;
|
||||
if (isset($this->buffer[$this->index])) {
|
||||
$row = $this->buffer[$this->index];
|
||||
}
|
||||
$this->index += 1;
|
||||
|
||||
if ($row && $type === static::FETCH_TYPE_NUM) {
|
||||
return array_values($row);
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
$record = $this->statement->fetch($type);
|
||||
if ($record === false) {
|
||||
$this->_allFetched = true;
|
||||
$this->statement->closeCursor();
|
||||
|
||||
return false;
|
||||
}
|
||||
$this->buffer[] = $record;
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAssoc(): array
|
||||
{
|
||||
$result = $this->fetch(static::FETCH_TYPE_ASSOC);
|
||||
|
||||
return $result ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function fetchAll($type = self::FETCH_TYPE_NUM)
|
||||
{
|
||||
if ($this->_allFetched) {
|
||||
return $this->buffer;
|
||||
}
|
||||
$results = $this->statement->fetchAll($type);
|
||||
if ($results !== false) {
|
||||
$this->buffer = array_merge($this->buffer, $results);
|
||||
}
|
||||
$this->_allFetched = true;
|
||||
$this->statement->closeCursor();
|
||||
|
||||
return $this->buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function rowCount(): int
|
||||
{
|
||||
if (!$this->_allFetched) {
|
||||
$this->fetchAll(static::FETCH_TYPE_ASSOC);
|
||||
}
|
||||
|
||||
return count($this->buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all properties
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _reset(): void
|
||||
{
|
||||
$this->buffer = [];
|
||||
$this->_allFetched = false;
|
||||
$this->index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current key in the iterator
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current record in the iterator
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->buffer[$this->index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewinds the collection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the iterator has more elements
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
$old = $this->index;
|
||||
$row = $this->fetch(self::FETCH_TYPE_ASSOC);
|
||||
|
||||
// Restore the index as fetch() increments during
|
||||
// the cache scenario.
|
||||
$this->index = $old;
|
||||
|
||||
return $row !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the iterator pointer to the next element
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function next(): void
|
||||
{
|
||||
$this->index += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped statement
|
||||
*
|
||||
* @return \Cake\Database\StatementInterface
|
||||
*/
|
||||
public function getInnerStatement(): StatementInterface
|
||||
{
|
||||
return $this->statement;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user