init
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Contains a setter for marking a Statement as buffered
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait BufferResultsTrait
|
||||
{
|
||||
/**
|
||||
* Whether to buffer results in php
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_bufferResults = true;
|
||||
|
||||
/**
|
||||
* Whether to buffer results in php
|
||||
*
|
||||
* @param bool $buffer Toggle buffering
|
||||
* @return $this
|
||||
*/
|
||||
public function bufferResults(bool $buffer)
|
||||
{
|
||||
$this->_bufferResults = $buffer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Wraps a statement in a callback that allows row results
|
||||
* to be modified when being fetched.
|
||||
*
|
||||
* This is used by CakePHP to eagerly load association data.
|
||||
*/
|
||||
class CallbackStatement extends StatementDecorator
|
||||
{
|
||||
/**
|
||||
* A callback function to be applied to results.
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
protected $_callback;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Cake\Database\StatementInterface $statement The statement to decorate.
|
||||
* @param \Cake\Database\DriverInterface $driver The driver instance used by the statement.
|
||||
* @param callable $callback The callback to apply to results before they are returned.
|
||||
*/
|
||||
public function __construct(StatementInterface $statement, DriverInterface $driver, callable $callback)
|
||||
{
|
||||
parent::__construct($statement, $driver);
|
||||
$this->_callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a row from the statement.
|
||||
*
|
||||
* The result will be processed by the callback when it is not `false`.
|
||||
*
|
||||
* @param string|int $type Either 'num' or 'assoc' to indicate the result format you would like.
|
||||
* @return array|false
|
||||
*/
|
||||
public function fetch($type = parent::FETCH_TYPE_NUM)
|
||||
{
|
||||
$callback = $this->_callback;
|
||||
$row = $this->_statement->fetch($type);
|
||||
|
||||
return $row === false ? $row : $callback($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Each row in the result will be processed by the callback when it is not `false.
|
||||
*/
|
||||
public function fetchAll($type = parent::FETCH_TYPE_NUM)
|
||||
{
|
||||
$results = $this->_statement->fetchAll($type);
|
||||
|
||||
return $results !== false ? array_map($this->_callback, $results) : false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?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 PDO;
|
||||
|
||||
/**
|
||||
* Statement class meant to be used by a MySQL PDO driver
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class MysqlStatement extends PDOStatement
|
||||
{
|
||||
use BufferResultsTrait;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function execute(?array $params = null): bool
|
||||
{
|
||||
$connection = $this->_driver->getConnection();
|
||||
|
||||
try {
|
||||
$connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, $this->_bufferResults);
|
||||
$result = $this->_statement->execute($params);
|
||||
} finally {
|
||||
$connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
<?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\Core\Exception\CakeException;
|
||||
use Cake\Database\DriverInterface;
|
||||
use PDO;
|
||||
use PDOStatement as Statement;
|
||||
use function Cake\Core\getTypeName;
|
||||
|
||||
/**
|
||||
* Decorator for \PDOStatement class mainly used for converting human readable
|
||||
* fetch modes into PDO constants.
|
||||
*/
|
||||
class PDOStatement extends StatementDecorator
|
||||
{
|
||||
/**
|
||||
* PDOStatement instance
|
||||
*
|
||||
* @var \PDOStatement
|
||||
*/
|
||||
protected $_statement;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \PDOStatement $statement Original statement to be decorated.
|
||||
* @param \Cake\Database\DriverInterface $driver Driver instance.
|
||||
*/
|
||||
public function __construct(Statement $statement, DriverInterface $driver)
|
||||
{
|
||||
$this->_statement = $statement;
|
||||
$this->_driver = $driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic getter to return PDOStatement::$queryString as read-only.
|
||||
*
|
||||
* @param string $property internal property to get
|
||||
* @return string|null
|
||||
*/
|
||||
public function __get(string $property)
|
||||
{
|
||||
if ($property === 'queryString' && isset($this->_statement->queryString)) {
|
||||
return $this->_statement->queryString;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a value to a positional or named variable in prepared query. If using
|
||||
* positional variables you need to start with index one, if using named params then
|
||||
* just use the name in any order.
|
||||
*
|
||||
* You can pass PDO compatible constants for binding values with a type or optionally
|
||||
* any type name registered in the Type class. Any value will be converted to the valid type
|
||||
* representation if needed.
|
||||
*
|
||||
* It is not allowed to combine positional and named variables in the same statement
|
||||
*
|
||||
* ### Examples:
|
||||
*
|
||||
* ```
|
||||
* $statement->bindValue(1, 'a title');
|
||||
* $statement->bindValue(2, 5, PDO::INT);
|
||||
* $statement->bindValue('active', true, 'boolean');
|
||||
* $statement->bindValue(5, new \DateTime(), 'date');
|
||||
* ```
|
||||
*
|
||||
* @param string|int $column name or param position to be bound
|
||||
* @param mixed $value The value to bind to variable in query
|
||||
* @param string|int|null $type PDO type or name of configured Type class
|
||||
* @return void
|
||||
*/
|
||||
public function bindValue($column, $value, $type = 'string'): void
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = 'string';
|
||||
}
|
||||
if (!is_int($type)) {
|
||||
[$value, $type] = $this->cast($value, $type);
|
||||
}
|
||||
$this->_statement->bindValue($column, $value, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next row for the result set after executing this statement.
|
||||
* Rows can be fetched to contain columns as names or positions. If no
|
||||
* rows are left in result set, this method will return false
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```
|
||||
* $statement = $connection->prepare('SELECT id, title from articles');
|
||||
* $statement->execute();
|
||||
* print_r($statement->fetch('assoc')); // will show ['id' => 1, 'title' => 'a title']
|
||||
* ```
|
||||
*
|
||||
* @param string|int $type 'num' for positional columns, assoc for named columns
|
||||
* @return mixed Result array containing columns and values or false if no results
|
||||
* are left
|
||||
*/
|
||||
public function fetch($type = parent::FETCH_TYPE_NUM)
|
||||
{
|
||||
if ($type === static::FETCH_TYPE_NUM) {
|
||||
return $this->_statement->fetch(PDO::FETCH_NUM);
|
||||
}
|
||||
if ($type === static::FETCH_TYPE_ASSOC) {
|
||||
return $this->_statement->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
if ($type === static::FETCH_TYPE_OBJ) {
|
||||
return $this->_statement->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
if (!is_int($type)) {
|
||||
throw new CakeException(sprintf(
|
||||
'Fetch type for PDOStatement must be an integer, found `%s` instead',
|
||||
getTypeName($type)
|
||||
));
|
||||
}
|
||||
|
||||
return $this->_statement->fetch($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all rows resulting from executing this statement
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```
|
||||
* $statement = $connection->prepare('SELECT id, title from articles');
|
||||
* $statement->execute();
|
||||
* print_r($statement->fetchAll('assoc')); // will show [0 => ['id' => 1, 'title' => 'a title']]
|
||||
* ```
|
||||
*
|
||||
* @param string|int $type num for fetching columns as positional keys or assoc for column names as keys
|
||||
* @return array|false list of all results from database for this statement, false on failure
|
||||
* @psalm-assert string $type
|
||||
*/
|
||||
public function fetchAll($type = parent::FETCH_TYPE_NUM)
|
||||
{
|
||||
if ($type === static::FETCH_TYPE_NUM) {
|
||||
return $this->_statement->fetchAll(PDO::FETCH_NUM);
|
||||
}
|
||||
if ($type === static::FETCH_TYPE_ASSOC) {
|
||||
return $this->_statement->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
if ($type === static::FETCH_TYPE_OBJ) {
|
||||
return $this->_statement->fetchAll(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
if (!is_int($type)) {
|
||||
throw new CakeException(sprintf(
|
||||
'Fetch type for PDOStatement must be an integer, found `%s` instead',
|
||||
getTypeName($type)
|
||||
));
|
||||
}
|
||||
|
||||
return $this->_statement->fetchAll($type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Statement class meant to be used by an Sqlite driver
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class SqliteStatement extends StatementDecorator
|
||||
{
|
||||
use BufferResultsTrait;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function execute(?array $params = null): bool
|
||||
{
|
||||
if ($this->_statement instanceof BufferedStatement) {
|
||||
$this->_statement = $this->_statement->getInnerStatement();
|
||||
}
|
||||
|
||||
if ($this->_bufferResults) {
|
||||
$this->_statement = new BufferedStatement($this->_statement, $this->_driver);
|
||||
}
|
||||
|
||||
return $this->_statement->execute($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows returned of affected by last execution
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function rowCount(): int
|
||||
{
|
||||
/** @psalm-suppress NoInterfaceProperties */
|
||||
if (
|
||||
$this->_statement->queryString &&
|
||||
preg_match('/^(?:DELETE|UPDATE|INSERT)/i', $this->_statement->queryString)
|
||||
) {
|
||||
$changes = $this->_driver->prepare('SELECT CHANGES()');
|
||||
$changes->execute();
|
||||
$row = $changes->fetch();
|
||||
$changes->closeCursor();
|
||||
|
||||
if (!$row) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)$row[0];
|
||||
}
|
||||
|
||||
return parent::rowCount();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?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 PDO;
|
||||
|
||||
/**
|
||||
* Statement class meant to be used by an Sqlserver driver
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class SqlserverStatement extends PDOStatement
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The SQL Server PDO driver requires that binary parameters be bound with the SQLSRV_ENCODING_BINARY attribute.
|
||||
* This overrides the PDOStatement::bindValue method in order to bind binary columns using the required attribute.
|
||||
*
|
||||
* @param string|int $column name or param position to be bound
|
||||
* @param mixed $value The value to bind to variable in query
|
||||
* @param string|int|null $type PDO type or name of configured Type class
|
||||
* @return void
|
||||
*/
|
||||
public function bindValue($column, $value, $type = 'string'): void
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = 'string';
|
||||
}
|
||||
if (!is_int($type)) {
|
||||
[$value, $type] = $this->cast($value, $type);
|
||||
}
|
||||
if ($type === PDO::PARAM_LOB) {
|
||||
$this->_statement->bindParam($column, $value, $type, 0, PDO::SQLSRV_ENCODING_BINARY);
|
||||
} else {
|
||||
$this->_statement->bindValue($column, $value, $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
<?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 Countable;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* Represents a database statement. Statements contains queries that can be
|
||||
* executed multiple times by binding different values on each call. This class
|
||||
* also helps convert values to their valid representation for the corresponding
|
||||
* types.
|
||||
*
|
||||
* This class is but a decorator of an actual statement implementation, such as
|
||||
* PDOStatement.
|
||||
*
|
||||
* @property-read string $queryString
|
||||
* @template-implements \IteratorAggregate<string, \Cake\Database\StatementInterface>
|
||||
*/
|
||||
class StatementDecorator implements StatementInterface, Countable, IteratorAggregate
|
||||
{
|
||||
use TypeConverterTrait;
|
||||
|
||||
/**
|
||||
* Statement instance implementation, such as PDOStatement
|
||||
* or any other custom implementation.
|
||||
*
|
||||
* @var \Cake\Database\StatementInterface
|
||||
*/
|
||||
protected $_statement;
|
||||
|
||||
/**
|
||||
* Reference to the driver object associated to this statement.
|
||||
*
|
||||
* @var \Cake\Database\DriverInterface
|
||||
*/
|
||||
protected $_driver;
|
||||
|
||||
/**
|
||||
* Whether this statement has already been executed
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_hasExecuted = false;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a value to a positional or named variable in prepared query. If using
|
||||
* positional variables you need to start with index one, if using named params then
|
||||
* just use the name in any order.
|
||||
*
|
||||
* It is not allowed to combine positional and named variables in the same statement.
|
||||
*
|
||||
* ### Examples:
|
||||
*
|
||||
* ```
|
||||
* $statement->bindValue(1, 'a title');
|
||||
* $statement->bindValue('active', true, 'boolean');
|
||||
* $statement->bindValue(5, new \DateTime(), 'date');
|
||||
* ```
|
||||
*
|
||||
* @param string|int $column name or param position to be bound
|
||||
* @param mixed $value The value to bind to variable in query
|
||||
* @param string|int|null $type name of configured Type class
|
||||
* @return void
|
||||
*/
|
||||
public function bindValue($column, $value, $type = 'string'): void
|
||||
{
|
||||
$this->_statement->bindValue($column, $value, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a cursor in the database, freeing up any resources and memory
|
||||
* allocated to it. In most cases you don't need to call this method, as it is
|
||||
* automatically called after fetching all results from the result set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function closeCursor(): void
|
||||
{
|
||||
$this->_statement->closeCursor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of columns this statement's results will contain.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```
|
||||
* $statement = $connection->prepare('SELECT id, title from articles');
|
||||
* $statement->execute();
|
||||
* echo $statement->columnCount(); // outputs 2
|
||||
* ```
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function columnCount(): int
|
||||
{
|
||||
return $this->_statement->columnCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error code for the last error that occurred when executing this statement.
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
public function errorCode()
|
||||
{
|
||||
return $this->_statement->errorCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error information for the last error that occurred when executing
|
||||
* this statement.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function errorInfo(): array
|
||||
{
|
||||
return $this->_statement->errorInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the statement by sending the SQL query to the database. It can optionally
|
||||
* take an array or arguments to be bound to the query variables. Please note
|
||||
* that binding parameters from this method will not perform any custom type conversion
|
||||
* as it would normally happen when calling `bindValue`.
|
||||
*
|
||||
* @param array|null $params list of values to be bound to query
|
||||
* @return bool true on success, false otherwise
|
||||
*/
|
||||
public function execute(?array $params = null): bool
|
||||
{
|
||||
$this->_hasExecuted = true;
|
||||
|
||||
return $this->_statement->execute($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next row for the result set after executing this statement.
|
||||
* Rows can be fetched to contain columns as names or positions. If no
|
||||
* rows are left in result set, this method will return false.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```
|
||||
* $statement = $connection->prepare('SELECT id, title from articles');
|
||||
* $statement->execute();
|
||||
* print_r($statement->fetch('assoc')); // will show ['id' => 1, 'title' => 'a title']
|
||||
* ```
|
||||
*
|
||||
* @param string|int $type 'num' for positional columns, assoc for named columns
|
||||
* @return mixed Result array containing columns and values or false if no results
|
||||
* are left
|
||||
*/
|
||||
public function fetch($type = self::FETCH_TYPE_NUM)
|
||||
{
|
||||
return $this->_statement->fetch($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next row in a result set as an associative array. Calling this function is the same as calling
|
||||
* $statement->fetch(StatementDecorator::FETCH_TYPE_ASSOC). If no results are found an empty array is returned.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAssoc(): array
|
||||
{
|
||||
$result = $this->fetch(static::FETCH_TYPE_ASSOC);
|
||||
|
||||
return $result ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the result at position.
|
||||
*
|
||||
* @param int $position The numeric position of the column to retrieve in the result
|
||||
* @return mixed Returns the specific value of the column designated at $position
|
||||
*/
|
||||
public function fetchColumn(int $position)
|
||||
{
|
||||
$result = $this->fetch(static::FETCH_TYPE_NUM);
|
||||
if ($result && isset($result[$position])) {
|
||||
return $result[$position];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all rows resulting from executing this statement.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```
|
||||
* $statement = $connection->prepare('SELECT id, title from articles');
|
||||
* $statement->execute();
|
||||
* print_r($statement->fetchAll('assoc')); // will show [0 => ['id' => 1, 'title' => 'a title']]
|
||||
* ```
|
||||
*
|
||||
* @param string|int $type num for fetching columns as positional keys or assoc for column names as keys
|
||||
* @return array|false List of all results from database for this statement. False on failure.
|
||||
*/
|
||||
public function fetchAll($type = self::FETCH_TYPE_NUM)
|
||||
{
|
||||
return $this->_statement->fetchAll($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows affected by this SQL statement.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```
|
||||
* $statement = $connection->prepare('SELECT id, title from articles');
|
||||
* $statement->execute();
|
||||
* print_r($statement->rowCount()); // will show 1
|
||||
* ```
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function rowCount(): int
|
||||
{
|
||||
return $this->_statement->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Statements are iterable as arrays, this method will return
|
||||
* the iterator object for traversing all items in the result.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```
|
||||
* $statement = $connection->prepare('SELECT id, title from articles');
|
||||
* foreach ($statement as $row) {
|
||||
* //do stuff
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @return \Cake\Database\StatementInterface
|
||||
* @psalm-suppress ImplementedReturnTypeMismatch
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
if (!$this->_hasExecuted) {
|
||||
$this->execute();
|
||||
}
|
||||
|
||||
return $this->_statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a set of values to statement object with corresponding type.
|
||||
*
|
||||
* @param array $params list of values to be bound
|
||||
* @param array $types list of types to be used, keys should match those in $params
|
||||
* @return void
|
||||
*/
|
||||
public function bind(array $params, array $types): void
|
||||
{
|
||||
if (empty($params)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$anonymousParams = is_int(key($params));
|
||||
$offset = 1;
|
||||
foreach ($params as $index => $value) {
|
||||
$type = $types[$index] ?? null;
|
||||
if ($anonymousParams) {
|
||||
/** @psalm-suppress InvalidOperand */
|
||||
$index += $offset;
|
||||
}
|
||||
$this->bindValue($index, $value, $type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest primary inserted using this statement.
|
||||
*
|
||||
* @param string|null $table table name or sequence to get last insert value from
|
||||
* @param string|null $column the name of the column representing the primary key
|
||||
* @return string|int
|
||||
*/
|
||||
public function lastInsertId(?string $table = null, ?string $column = null)
|
||||
{
|
||||
if ($column && $this->columnCount()) {
|
||||
$row = $this->fetch(static::FETCH_TYPE_ASSOC);
|
||||
|
||||
if ($row && isset($row[$column])) {
|
||||
return $row[$column];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_driver->lastInsertId($table, $column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the statement object that was decorated by this class.
|
||||
*
|
||||
* @return \Cake\Database\StatementInterface
|
||||
*/
|
||||
public function getInnerStatement()
|
||||
{
|
||||
return $this->_statement;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user