Unverified Commit e6349bab authored by Sergei Morozov's avatar Sergei Morozov Committed by GitHub

Merge pull request #3070 from morozov/stmt-fetch-mode-args-cleanup

Dropped support of fetching objects and non-zero column
parents 438befbc aa6ae1d6
# Upgrade to 3.0
## BC BREAK: Dropped support for `FetchMode::CUSTOM_OBJECT` and `::STANDARD_OBJECT`
Instead of fetching an object, fetch an array and map it to an object of the desired class.
## BC BREAK: Dropped support for the `$columnIndex` argument in `ResultStatement::fetchColumn()`, other `ResultStatement::fetch*()` methods invoked with `FetchMode::COLUMN` and `Connection::fetchColumn()`.
In order to fetch a column with an index other than `0`, use `FetchMode::NUMERIC` and the array element with the corresponding index.
## BC BREAK: Removed `EchoSQLLogger`
`EchoSQLLogger` is no longer available as part of the package.
......
......@@ -98,12 +98,6 @@ parameters:
- %currentWorkingDirectory%/src/Query/QueryBuilder.php
- %currentWorkingDirectory%/src/Schema/*SchemaManager.php
# FetchMode::CUSTOM_OBJECT requires variable property access
-
message: '~^Variable property access on object\.~'
paths:
- %currentWorkingDirectory%/src/Driver/*/*Statement.php
# Some APIs use variable method calls internally
-
message: '~^Variable method call on .*~'
......
......@@ -60,12 +60,8 @@ class ArrayStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, ...$args)
public function setFetchMode($fetchMode)
{
if (count($args) > 0) {
throw new InvalidArgumentException('Caching layer does not support 2nd/3rd argument to setFetchMode()');
}
$this->defaultFetchMode = $fetchMode;
return true;
......@@ -84,7 +80,7 @@ class ArrayStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function fetch($fetchMode = null, ...$args)
public function fetch($fetchMode = null)
{
if (! isset($this->data[$this->num])) {
return false;
......@@ -115,10 +111,10 @@ class ArrayStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, ...$args)
public function fetchAll($fetchMode = null)
{
$rows = [];
while ($row = $this->fetch($fetchMode, ...$args)) {
while ($row = $this->fetch($fetchMode)) {
$rows[] = $row;
}
......@@ -128,11 +124,11 @@ class ArrayStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn()
{
$row = $this->fetch(FetchMode::NUMERIC);
// TODO: verify that return false is the correct behavior
return $row[$columnIndex] ?? false;
return $row[0] ?? false;
}
}
......@@ -104,7 +104,7 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, ...$args)
public function setFetchMode($fetchMode)
{
$this->defaultFetchMode = $fetchMode;
......@@ -124,7 +124,7 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function fetch($fetchMode = null, ...$args)
public function fetch($fetchMode = null)
{
if ($this->data === null) {
$this->data = [];
......@@ -164,9 +164,9 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, ...$args)
public function fetchAll($fetchMode = null)
{
$data = $this->statement->fetchAll($fetchMode, ...$args);
$data = $this->statement->fetchAll($fetchMode);
if ($fetchMode === FetchMode::COLUMN) {
foreach ($data as $key => $value) {
......@@ -183,12 +183,12 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn()
{
$row = $this->fetch(FetchMode::NUMERIC);
// TODO: verify that return false is the correct behavior
return $row[$columnIndex] ?? false;
return $row[0] ?? false;
}
/**
......
......@@ -570,16 +570,15 @@ class Connection implements DriverConnection
*
* @param string $statement The SQL query to be executed.
* @param mixed[] $params The prepared statement params.
* @param int $column The 0-indexed column number to retrieve.
* @param int[]|string[] $types The query parameter types.
*
* @return mixed|false False is returned if no rows are found.
*
* @throws DBALException
*/
public function fetchColumn($statement, array $params = [], $column = 0, array $types = [])
public function fetchColumn($statement, array $params = [], array $types = [])
{
return $this->executeQuery($statement, $params, $types)->fetchColumn($column);
return $this->executeQuery($statement, $params, $types)->fetchColumn();
}
/**
......
......@@ -7,25 +7,17 @@ use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use IteratorAggregate;
use ReflectionClass;
use ReflectionObject;
use ReflectionProperty;
use stdClass;
use const CASE_LOWER;
use const DB2_BINARY;
use const DB2_CHAR;
use const DB2_LONG;
use const DB2_PARAM_FILE;
use const DB2_PARAM_IN;
use function array_change_key_case;
use function assert;
use function count;
use function db2_bind_param;
use function db2_execute;
use function db2_fetch_array;
use function db2_fetch_assoc;
use function db2_fetch_both;
use function db2_fetch_object;
use function db2_free_result;
use function db2_num_fields;
use function db2_num_rows;
......@@ -34,16 +26,11 @@ use function db2_stmt_errormsg;
use function error_get_last;
use function fclose;
use function fwrite;
use function gettype;
use function is_int;
use function is_object;
use function is_resource;
use function is_string;
use function ksort;
use function sprintf;
use function stream_copy_to_stream;
use function stream_get_meta_data;
use function strtolower;
use function tmpfile;
class DB2Statement implements IteratorAggregate, Statement
......@@ -62,12 +49,6 @@ class DB2Statement implements IteratorAggregate, Statement
*/
private $lobs = [];
/** @var string Name of the default class to instantiate when fetching class instances. */
private $defaultFetchClass = '\stdClass';
/** @var mixed[] Constructor arguments for the default class to instantiate when fetching class instances. */
private $defaultFetchClassCtorArgs = [];
/** @var int */
private $defaultFetchMode = FetchMode::MIXED;
......@@ -237,18 +218,10 @@ class DB2Statement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, ...$args)
public function setFetchMode($fetchMode)
{
$this->defaultFetchMode = $fetchMode;
if (isset($args[0])) {
$this->defaultFetchClass = $args[0];
}
if (isset($args[1])) {
$this->defaultFetchClassCtorArgs = (array) $args[1];
}
return true;
}
......@@ -263,7 +236,7 @@ class DB2Statement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetch($fetchMode = null, ...$args)
public function fetch($fetchMode = null)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
......@@ -282,29 +255,9 @@ class DB2Statement implements IteratorAggregate, Statement
case FetchMode::ASSOCIATIVE:
return db2_fetch_assoc($this->stmt);
case FetchMode::CUSTOM_OBJECT:
$className = $this->defaultFetchClass;
$ctorArgs = $this->defaultFetchClassCtorArgs;
if (count($args) > 0) {
$className = $args[0];
$ctorArgs = $args[1] ?? [];
}
$result = db2_fetch_object($this->stmt);
if ($result instanceof stdClass) {
$result = $this->castObject($result, $className, $ctorArgs);
}
return $result;
case FetchMode::NUMERIC:
return db2_fetch_array($this->stmt);
case FetchMode::STANDARD_OBJECT:
return db2_fetch_object($this->stmt);
default:
throw new DB2Exception('Given Fetch-Style ' . $fetchMode . ' is not supported.');
}
......@@ -313,16 +266,11 @@ class DB2Statement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, ...$args)
public function fetchAll($fetchMode = null)
{
$rows = [];
switch ($fetchMode) {
case FetchMode::CUSTOM_OBJECT:
while (($row = $this->fetch($fetchMode, ...$args)) !== false) {
$rows[] = $row;
}
break;
case FetchMode::COLUMN:
while (($row = $this->fetchColumn()) !== false) {
$rows[] = $row;
......@@ -340,7 +288,7 @@ class DB2Statement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn()
{
$row = $this->fetch(FetchMode::NUMERIC);
......@@ -348,7 +296,7 @@ class DB2Statement implements IteratorAggregate, Statement
return false;
}
return $row[$columnIndex] ?? null;
return $row[0] ?? null;
}
/**
......@@ -359,71 +307,6 @@ class DB2Statement implements IteratorAggregate, Statement
return @db2_num_rows($this->stmt);
}
/**
* Casts a stdClass object to the given class name mapping its' properties.
*
* @param stdClass $sourceObject Object to cast from.
* @param string|object $destinationClass Name of the class or class instance to cast to.
* @param mixed[] $ctorArgs Arguments to use for constructing the destination class instance.
*
* @return object
*
* @throws DB2Exception
*/
private function castObject(stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
{
if (! is_string($destinationClass)) {
if (! is_object($destinationClass)) {
throw new DB2Exception(sprintf(
'Destination class has to be of type string or object, %s given.',
gettype($destinationClass)
));
}
} else {
$destinationClass = new ReflectionClass($destinationClass);
$destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
}
$sourceReflection = new ReflectionObject($sourceObject);
$destinationClassReflection = new ReflectionObject($destinationClass);
/** @var ReflectionProperty[] $destinationProperties */
$destinationProperties = array_change_key_case($destinationClassReflection->getProperties(), CASE_LOWER);
foreach ($sourceReflection->getProperties() as $sourceProperty) {
$sourceProperty->setAccessible(true);
$name = $sourceProperty->getName();
$value = $sourceProperty->getValue($sourceObject);
// Try to find a case-matching property.
if ($destinationClassReflection->hasProperty($name)) {
$destinationProperty = $destinationClassReflection->getProperty($name);
$destinationProperty->setAccessible(true);
$destinationProperty->setValue($destinationClass, $value);
continue;
}
$name = strtolower($name);
// Try to find a property without matching case.
// Fallback for the driver returning either all uppercase or all lowercase column names.
if (isset($destinationProperties[$name])) {
$destinationProperty = $destinationProperties[$name];
$destinationProperty->setAccessible(true);
$destinationProperty->setValue($destinationClass, $value);
continue;
}
$destinationClass->$name = $value;
}
return $destinationClass;
}
/**
* @return resource
*
......
......@@ -306,7 +306,7 @@ class MysqliStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetch($fetchMode = null, ...$args)
public function fetch($fetchMode = null)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
......@@ -345,9 +345,6 @@ class MysqliStatement implements IteratorAggregate, Statement
case FetchMode::MIXED:
return $assoc + $values;
case FetchMode::STANDARD_OBJECT:
return (object) $assoc;
default:
throw new MysqliException(sprintf("Unknown fetch type '%s'", $fetchMode));
}
......@@ -356,7 +353,7 @@ class MysqliStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, ...$args)
public function fetchAll($fetchMode = null)
{
$fetchMode = $fetchMode ?? $this->_defaultFetchMode;
......@@ -378,7 +375,7 @@ class MysqliStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn()
{
$row = $this->fetch(FetchMode::NUMERIC);
......@@ -386,7 +383,7 @@ class MysqliStatement implements IteratorAggregate, Statement
return false;
}
return $row[$columnIndex] ?? null;
return $row[0] ?? null;
}
/**
......@@ -439,7 +436,7 @@ class MysqliStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, ...$args)
public function setFetchMode($fetchMode)
{
$this->_defaultFetchMode = $fetchMode;
......
......@@ -33,7 +33,6 @@ use function oci_error;
use function oci_execute;
use function oci_fetch_all;
use function oci_fetch_array;
use function oci_fetch_object;
use function oci_new_descriptor;
use function oci_num_fields;
use function oci_num_rows;
......@@ -411,7 +410,7 @@ class OCI8Statement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, ...$args)
public function setFetchMode($fetchMode)
{
$this->_defaultFetchMode = $fetchMode;
......@@ -429,7 +428,7 @@ class OCI8Statement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetch($fetchMode = null, ...$args)
public function fetch($fetchMode = null)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
......@@ -443,10 +442,6 @@ class OCI8Statement implements IteratorAggregate, Statement
return $this->fetchColumn();
}
if ($fetchMode === FetchMode::STANDARD_OBJECT) {
return oci_fetch_object($this->_sth);
}
if (! isset(self::$fetchModeMap[$fetchMode])) {
throw new InvalidArgumentException('Invalid fetch style: ' . $fetchMode);
}
......@@ -460,20 +455,12 @@ class OCI8Statement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, ...$args)
public function fetchAll($fetchMode = null)
{
$fetchMode = $fetchMode ?? $this->_defaultFetchMode;
$result = [];
if ($fetchMode === FetchMode::STANDARD_OBJECT) {
while ($row = $this->fetch($fetchMode)) {
$result[] = $row;
}
return $result;
}
if (! isset(self::$fetchModeMap[$fetchMode])) {
throw new InvalidArgumentException('Invalid fetch style: ' . $fetchMode);
}
......@@ -514,7 +501,7 @@ class OCI8Statement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn()
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
......@@ -528,7 +515,7 @@ class OCI8Statement implements IteratorAggregate, Statement
return false;
}
return $row[$columnIndex] ?? null;
return $row[0] ?? null;
}
/**
......
......@@ -28,12 +28,10 @@ class PDOStatement implements IteratorAggregate, Statement
];
private const FETCH_MODE_MAP = [
FetchMode::ASSOCIATIVE => PDO::FETCH_ASSOC,
FetchMode::NUMERIC => PDO::FETCH_NUM,
FetchMode::MIXED => PDO::FETCH_BOTH,
FetchMode::STANDARD_OBJECT => PDO::FETCH_OBJ,
FetchMode::COLUMN => PDO::FETCH_COLUMN,
FetchMode::CUSTOM_OBJECT => PDO::FETCH_CLASS,
FetchMode::ASSOCIATIVE => PDO::FETCH_ASSOC,
FetchMode::NUMERIC => PDO::FETCH_NUM,
FetchMode::MIXED => PDO::FETCH_BOTH,
FetchMode::COLUMN => PDO::FETCH_COLUMN,
];
/** @var \PDOStatement */
......@@ -47,12 +45,12 @@ class PDOStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, ...$args)
public function setFetchMode($fetchMode)
{
$fetchMode = $this->convertFetchMode($fetchMode);
try {
return $this->stmt->setFetchMode($fetchMode, ...$args);
return $this->stmt->setFetchMode($fetchMode);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
......@@ -141,7 +139,7 @@ class PDOStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetch($fetchMode = null, ...$args)
public function fetch($fetchMode = null)
{
try {
if ($fetchMode === null) {
......@@ -149,8 +147,7 @@ class PDOStatement implements IteratorAggregate, Statement
}
return $this->stmt->fetch(
$this->convertFetchMode($fetchMode),
...$args
$this->convertFetchMode($fetchMode)
);
} catch (\PDOException $exception) {
throw new PDOException($exception);
......@@ -160,15 +157,14 @@ class PDOStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, ...$args)
public function fetchAll($fetchMode = null)
{
try {
if ($fetchMode === null) {
$data = $this->stmt->fetchAll();
} else {
$data = $this->stmt->fetchAll(
$this->convertFetchMode($fetchMode),
...$args
$this->convertFetchMode($fetchMode)
);
}
} catch (\PDOException $exception) {
......@@ -183,10 +179,10 @@ class PDOStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn()
{
try {
return $this->stmt->fetchColumn($columnIndex);
return $this->stmt->fetchColumn();
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
......
......@@ -28,13 +28,12 @@ interface ResultStatement extends Traversable
/**
* Sets the fetch mode to use while iterating this statement.
*
* @param int $fetchMode Controls how the next row will be returned to the caller.
* The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants.
* @param mixed ...$args Optional mode-specific arguments (see {@link self::fetchAll()}).
* @param int $fetchMode Controls how the next row will be returned to the caller.
* The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants.
*
* @return bool
*/
public function setFetchMode($fetchMode, ...$args);
public function setFetchMode($fetchMode);
/**
* Returns the next row of a result set.
......@@ -42,12 +41,11 @@ interface ResultStatement extends Traversable
* @param int|null $fetchMode Controls how the next row will be returned to the caller.
* The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants,
* defaulting to {@link \Doctrine\DBAL\FetchMode::MIXED}.
* @param mixed ...$args Optional mode-specific arguments (see {@link self::fetchAll()}).
*
* @return mixed The return value of this method on success depends on the fetch mode. In all cases, FALSE is
* returned on failure.
*/
public function fetch($fetchMode = null, ...$args);
public function fetch($fetchMode = null);
/**
* Returns an array containing all of the result set rows.
......@@ -55,24 +53,15 @@ interface ResultStatement extends Traversable
* @param int|null $fetchMode Controls how the next row will be returned to the caller.
* The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants,
* defaulting to {@link \Doctrine\DBAL\FetchMode::MIXED}.
* @param mixed ...$args Optional mode-specific arguments. Supported modes:
* * {@link \Doctrine\DBAL\FetchMode::COLUMN}
* 1. The 0-indexed column to be returned.
* * {@link \Doctrine\DBAL\FetchMode::CUSTOM_OBJECT}
* 1. The class name of the object to be created,
* 2. Array of constructor arguments
*
* @return mixed[]
*/
public function fetchAll($fetchMode = null, ...$args);
public function fetchAll($fetchMode = null);
/**
* Returns a single column from the next row of a result set or FALSE if there are no more rows.
*
* @param int $columnIndex 0-indexed number of the column you wish to retrieve from the row.
* If no value is supplied, fetches the first column.
*
* @return mixed|false A single column in the next row of a result set, or FALSE if there are no more rows.
*/
public function fetchColumn($columnIndex = 0);
public function fetchColumn();
}
......@@ -7,21 +7,13 @@ use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use IteratorAggregate;
use ReflectionClass;
use ReflectionObject;
use stdClass;
use const SASQL_BOTH;
use function array_key_exists;
use function assert;
use function count;
use function gettype;
use function is_int;
use function is_object;
use function is_resource;
use function is_string;
use function sasql_fetch_array;
use function sasql_fetch_assoc;
use function sasql_fetch_object;
use function sasql_fetch_row;
use function sasql_prepare;
use function sasql_stmt_affected_rows;
......@@ -32,7 +24,6 @@ use function sasql_stmt_execute;
use function sasql_stmt_field_count;
use function sasql_stmt_reset;
use function sasql_stmt_result_metadata;
use function sprintf;
/**
* SAP SQL Anywhere implementation of the Statement interface.
......@@ -42,12 +33,6 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement
/** @var resource The connection resource. */
private $conn;
/** @var string Name of the default class to instantiate when fetching class instances. */
private $defaultFetchClass = '\stdClass';
/** @var mixed[] Constructor arguments for the default class to instantiate when fetching class instances. */
private $defaultFetchClassCtorArgs = [];
/** @var int Default fetch mode to use. */
private $defaultFetchMode = FetchMode::MIXED;
......@@ -199,7 +184,7 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement
*
* @throws SQLAnywhereException
*/
public function fetch($fetchMode = null, ...$args)
public function fetch($fetchMode = null)
{
if (! is_resource($this->result)) {
return false;
......@@ -217,29 +202,9 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement
case FetchMode::MIXED:
return sasql_fetch_array($this->result, SASQL_BOTH);
case FetchMode::CUSTOM_OBJECT:
$className = $this->defaultFetchClass;
$ctorArgs = $this->defaultFetchClassCtorArgs;
if (count($args) > 0) {
$className = $args[0];
$ctorArgs = $args[1] ?? [];
}
$result = sasql_fetch_object($this->result);
if ($result instanceof stdClass) {
$result = $this->castObject($result, $className, $ctorArgs);
}
return $result;
case FetchMode::NUMERIC:
return sasql_fetch_row($this->result);
case FetchMode::STANDARD_OBJECT:
return sasql_fetch_object($this->result);
default:
throw new SQLAnywhereException('Fetch mode is not supported: ' . $fetchMode);
}
......@@ -248,17 +213,11 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, ...$args)
public function fetchAll($fetchMode = null)
{
$rows = [];
switch ($fetchMode) {
case FetchMode::CUSTOM_OBJECT:
while (($row = $this->fetch($fetchMode, ...$args)) !== false) {
$rows[] = $row;
}
break;
case FetchMode::COLUMN:
while (($row = $this->fetchColumn()) !== false) {
$rows[] = $row;
......@@ -277,7 +236,7 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn()
{
$row = $this->fetch(FetchMode::NUMERIC);
......@@ -285,7 +244,7 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement
return false;
}
return $row[$columnIndex] ?? null;
return $row[0] ?? null;
}
/**
......@@ -307,65 +266,10 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, ...$args)
public function setFetchMode($fetchMode)
{
$this->defaultFetchMode = $fetchMode;
if (isset($args[0])) {
$this->defaultFetchClass = $args[0];
}
if (isset($args[1])) {
$this->defaultFetchClassCtorArgs = (array) $args[1];
}
return true;
}
/**
* Casts a stdClass object to the given class name mapping its' properties.
*
* @param stdClass $sourceObject Object to cast from.
* @param string|object $destinationClass Name of the class or class instance to cast to.
* @param mixed[] $ctorArgs Arguments to use for constructing the destination class instance.
*
* @return object
*
* @throws SQLAnywhereException
*/
private function castObject(stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
{
if (! is_string($destinationClass)) {
if (! is_object($destinationClass)) {
throw new SQLAnywhereException(sprintf(
'Destination class has to be of type string or object, %s given.',
gettype($destinationClass)
));
}
} else {
$destinationClass = new ReflectionClass($destinationClass);
$destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
}
$sourceReflection = new ReflectionObject($sourceObject);
$destinationClassReflection = new ReflectionObject($destinationClass);
foreach ($sourceReflection->getProperties() as $sourceProperty) {
$sourceProperty->setAccessible(true);
$name = $sourceProperty->getName();
$value = $sourceProperty->getValue($sourceObject);
if ($destinationClassReflection->hasProperty($name)) {
$destinationProperty = $destinationClassReflection->getProperty($name);
$destinationProperty->setAccessible(true);
$destinationProperty->setValue($destinationClass, $value);
} else {
$destinationClass->$name = $value;
}
}
return $destinationClass;
}
}
......@@ -14,15 +14,12 @@ use const SQLSRV_FETCH_BOTH;
use const SQLSRV_FETCH_NUMERIC;
use const SQLSRV_PARAM_IN;
use function array_key_exists;
use function count;
use function in_array;
use function is_int;
use function is_numeric;
use function sqlsrv_errors;
use function sqlsrv_execute;
use function sqlsrv_fetch;
use function sqlsrv_fetch_array;
use function sqlsrv_fetch_object;
use function sqlsrv_get_field;
use function sqlsrv_next_result;
use function sqlsrv_num_fields;
......@@ -84,20 +81,6 @@ class SQLSrvStatement implements IteratorAggregate, Statement
FetchMode::NUMERIC => SQLSRV_FETCH_NUMERIC,
];
/**
* The name of the default class to instantiate when fetching class instances.
*
* @var string
*/
private $defaultFetchClass = '\stdClass';
/**
* The constructor arguments for the default class to instantiate when fetching class instances.
*
* @var mixed[]
*/
private $defaultFetchClassCtorArgs = [];
/**
* The fetch style.
*
......@@ -322,18 +305,10 @@ class SQLSrvStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, ...$args)
public function setFetchMode($fetchMode)
{
$this->defaultFetchMode = $fetchMode;
if (isset($args[0])) {
$this->defaultFetchClass = $args[0];
}
if (isset($args[1])) {
$this->defaultFetchClassCtorArgs = (array) $args[1];
}
return true;
}
......@@ -350,7 +325,7 @@ class SQLSrvStatement implements IteratorAggregate, Statement
*
* @throws SQLSrvException
*/
public function fetch($fetchMode = null, ...$args)
public function fetch($fetchMode = null)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
......@@ -368,35 +343,17 @@ class SQLSrvStatement implements IteratorAggregate, Statement
return sqlsrv_fetch_array($this->stmt, self::$fetchMap[$fetchMode]) ?? false;
}
if (in_array($fetchMode, [FetchMode::STANDARD_OBJECT, FetchMode::CUSTOM_OBJECT], true)) {
$className = $this->defaultFetchClass;
$ctorArgs = $this->defaultFetchClassCtorArgs;
if (count($args) > 0) {
$className = $args[0];
$ctorArgs = $args[1] ?? [];
}
return sqlsrv_fetch_object($this->stmt, $className, $ctorArgs) ?? false;
}
throw new SQLSrvException('Fetch mode is not supported!');
}
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, ...$args)
public function fetchAll($fetchMode = null)
{
$rows = [];
switch ($fetchMode) {
case FetchMode::CUSTOM_OBJECT:
while (($row = $this->fetch($fetchMode, ...$args)) !== false) {
$rows[] = $row;
}
break;
case FetchMode::COLUMN:
while (($row = $this->fetchColumn()) !== false) {
$rows[] = $row;
......@@ -415,7 +372,7 @@ class SQLSrvStatement implements IteratorAggregate, Statement
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn()
{
$row = $this->fetch(FetchMode::NUMERIC);
......@@ -423,7 +380,7 @@ class SQLSrvStatement implements IteratorAggregate, Statement
return false;
}
return $row[$columnIndex] ?? null;
return $row[0] ?? null;
}
/**
......
......@@ -35,15 +35,6 @@ final class FetchMode
*/
public const MIXED = 4;
/**
* Specifies that the fetch method shall return each row as an object with
* property names that correspond to the column names returned in the result
* set.
*
* @see \PDO::FETCH_OBJ
*/
public const STANDARD_OBJECT = 5;
/**
* Specifies that the fetch method shall return only a single requested
* column from the next row in the result set.
......@@ -52,14 +43,6 @@ final class FetchMode
*/
public const COLUMN = 7;
/**
* Specifies that the fetch method shall return a new instance of the
* requested class, mapping the columns to named properties in the class.
*
* @see \PDO::FETCH_CLASS
*/
public const CUSTOM_OBJECT = 8;
/**
* This class cannot be instantiated.
*/
......
......@@ -111,11 +111,11 @@ class Statement implements IteratorAggregate, DriverStatement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, ...$args)
public function setFetchMode($fetchMode)
{
$this->defaultFetchMode = $fetchMode;
return $this->stmt->setFetchMode($fetchMode, ...$args);
return $this->stmt->setFetchMode($fetchMode);
}
/**
......@@ -129,11 +129,11 @@ class Statement implements IteratorAggregate, DriverStatement
/**
* {@inheritdoc}
*/
public function fetch($fetchMode = null, ...$args)
public function fetch($fetchMode = null)
{
$fetchMode = $fetchMode ?? $this->defaultFetchMode;
$row = $this->stmt->fetch($fetchMode, ...$args);
$row = $this->stmt->fetch($fetchMode);
$iterateRow = ($this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM)) !== 0;
$fixCase = $this->case !== null
......@@ -148,11 +148,11 @@ class Statement implements IteratorAggregate, DriverStatement
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, ...$args)
public function fetchAll($fetchMode = null)
{
$fetchMode = $fetchMode ?? $this->defaultFetchMode;
$rows = $this->stmt->fetchAll($fetchMode, ...$args);
$rows = $this->stmt->fetchAll($fetchMode);
$iterateRow = ($this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM)) !== 0;
$fixCase = $this->case !== null
......@@ -215,9 +215,9 @@ class Statement implements IteratorAggregate, DriverStatement
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn()
{
$value = $this->stmt->fetchColumn($columnIndex);
$value = $this->stmt->fetchColumn();
if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) !== 0 && $value === '') {
$value = null;
......
......@@ -211,9 +211,9 @@ class Statement implements IteratorAggregate, DriverStatement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, ...$args)
public function setFetchMode($fetchMode)
{
return $this->stmt->setFetchMode($fetchMode, ...$args);
return $this->stmt->setFetchMode($fetchMode);
}
/**
......@@ -229,25 +229,25 @@ class Statement implements IteratorAggregate, DriverStatement
/**
* {@inheritdoc}
*/
public function fetch($fetchMode = null, ...$args)
public function fetch($fetchMode = null)
{
return $this->stmt->fetch($fetchMode, ...$args);
return $this->stmt->fetch($fetchMode);
}
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, ...$args)
public function fetchAll($fetchMode = null)
{
return $this->stmt->fetchAll($fetchMode, ...$args);
return $this->stmt->fetchAll($fetchMode);
}
/**
* {@inheritDoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn()
{
return $this->stmt->fetchColumn($columnIndex);
return $this->stmt->fetchColumn();
}
/**
......
......@@ -627,7 +627,6 @@ class ConnectionTest extends TestCase
$statement = 'SELECT * FROM foo WHERE bar = ?';
$params = [666];
$types = [ParameterType::INTEGER];
$column = 0;
$result = [];
$driverMock = $this->createMock(Driver::class);
......@@ -642,7 +641,6 @@ class ConnectionTest extends TestCase
$driverStatementMock->expects(self::once())
->method('fetchColumn')
->with($column)
->will(self::returnValue($result));
/** @var Connection|MockObject $conn */
......@@ -656,7 +654,7 @@ class ConnectionTest extends TestCase
->with($statement, $params, $types)
->will(self::returnValue($driverStatementMock));
self::assertSame($result, $conn->fetchColumn($statement, $params, $column, $types));
self::assertSame($result, $conn->fetchColumn($statement, $params, $types));
}
public function testFetchAll() : void
......
......@@ -6,9 +6,6 @@ use DateTime;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\Mysqli\Driver as MySQLiDriver;
use Doctrine\DBAL\Driver\OCI8\Driver as Oci8Driver;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver;
use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
......@@ -18,7 +15,6 @@ use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Statement;
use Doctrine\DBAL\Tests\FunctionalTestCase;
use Doctrine\DBAL\Types\Types;
use PDO;
use const CASE_LOWER;
use function array_change_key_case;
use function array_filter;
......@@ -27,7 +23,6 @@ use function count;
use function date;
use function is_numeric;
use function json_encode;
use function property_exists;
use function sprintf;
use function strtotime;
......@@ -376,13 +371,13 @@ class DataAccessTest extends FunctionalTestCase
public function testFetchColumn() : void
{
$sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
$testInt = $this->connection->fetchColumn($sql, [1, 'foo'], 0);
$sql = 'SELECT test_int FROM fetch_table WHERE test_int = ? AND test_string = ?';
$testInt = $this->connection->fetchColumn($sql, [1, 'foo']);
self::assertEquals(1, $testInt);
$sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
$testString = $this->connection->fetchColumn($sql, [1, 'foo'], 1);
$sql = 'SELECT test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
$testString = $this->connection->fetchColumn($sql, [1, 'foo']);
self::assertEquals('foo', $testString);
}
......@@ -392,15 +387,14 @@ class DataAccessTest extends FunctionalTestCase
$datetimeString = '2010-01-01 10:10:10';
$datetime = new DateTime($datetimeString);
$sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
$sql = 'SELECT test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
$column = $this->connection->fetchColumn(
$sql,
[1, $datetime],
1,
[ParameterType::STRING, Types::DATETIME_MUTABLE]
);
self::assertNotFalse($column);
self::assertIsString($column);
self::assertStringStartsWith($datetimeString, $column);
}
......@@ -418,7 +412,7 @@ class DataAccessTest extends FunctionalTestCase
$this->expectException(DBALException::class);
$this->connection->fetchColumn($sql, [1, $datetime], 1);
$this->connection->fetchColumn($sql, [1, $datetime]);
}
/**
......@@ -635,7 +629,7 @@ class DataAccessTest extends FunctionalTestCase
$sql = 'SELECT COUNT(*) FROM fetch_table_date_math WHERE ';
$sql .= $platform->getDateSubDaysExpression('test_date', 'test_days') . " < '2010-05-12'";
$rowCount = $this->connection->fetchColumn($sql, [], 0);
$rowCount = $this->connection->fetchColumn($sql);
self::assertEquals(1, $rowCount);
}
......@@ -738,62 +732,6 @@ class DataAccessTest extends FunctionalTestCase
}), 'should be no non-numerical elements in the result.');
}
/**
* @group DBAL-1091
*/
public function testFetchAllStyleObject() : void
{
$this->setupFixture();
$sql = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
$stmt = $this->connection->prepare($sql);
$stmt->execute();
$results = $stmt->fetchAll(FetchMode::STANDARD_OBJECT);
self::assertCount(1, $results);
self::assertInstanceOf('stdClass', $results[0]);
self::assertEquals(
1,
property_exists($results[0], 'test_int') ? $results[0]->test_int : $results[0]->TEST_INT
);
self::assertEquals(
'foo',
property_exists($results[0], 'test_string') ? $results[0]->test_string : $results[0]->TEST_STRING
);
self::assertStringStartsWith(
'2010-01-01 10:10:10',
property_exists($results[0], 'test_datetime') ? $results[0]->test_datetime : $results[0]->TEST_DATETIME
);
}
/**
* @group DBAL-196
*/
public function testFetchAllSupportFetchClass() : void
{
$this->beforeFetchClassTest();
$this->setupFixture();
$sql = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
$stmt = $this->connection->prepare($sql);
$stmt->execute();
$results = $stmt->fetchAll(
FetchMode::CUSTOM_OBJECT,
MyFetchClass::class
);
self::assertCount(1, $results);
self::assertInstanceOf(MyFetchClass::class, $results[0]);
self::assertEquals(1, $results[0]->test_int);
self::assertEquals('foo', $results[0]->test_string);
self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
}
/**
* @group DBAL-241
*/
......@@ -811,53 +749,6 @@ class DataAccessTest extends FunctionalTestCase
self::assertEquals([1, 10], $rows);
}
/**
* @group DBAL-214
*/
public function testSetFetchModeClassFetchAll() : void
{
$this->beforeFetchClassTest();
$this->setupFixture();
$sql = 'SELECT * FROM fetch_table';
$stmt = $this->connection->query($sql);
$stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
$results = $stmt->fetchAll();
self::assertCount(1, $results);
self::assertInstanceOf(MyFetchClass::class, $results[0]);
self::assertEquals(1, $results[0]->test_int);
self::assertEquals('foo', $results[0]->test_string);
self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
}
/**
* @group DBAL-214
*/
public function testSetFetchModeClassFetch() : void
{
$this->beforeFetchClassTest();
$this->setupFixture();
$sql = 'SELECT * FROM fetch_table';
$stmt = $this->connection->query($sql);
$stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
$results = [];
while ($row = $stmt->fetch()) {
$results[] = $row;
}
self::assertCount(1, $results);
self::assertInstanceOf(MyFetchClass::class, $results[0]);
self::assertEquals(1, $results[0]->test_int);
self::assertEquals('foo', $results[0]->test_string);
self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
}
/**
* @group DBAL-257
*/
......@@ -898,21 +789,6 @@ class DataAccessTest extends FunctionalTestCase
self::assertEquals([], $rows);
}
/**
* @group DBAL-1028
*/
public function testFetchColumnNullValue() : void
{
$this->connection->executeUpdate(
'INSERT INTO fetch_table (test_int, test_string) VALUES (?, ?)',
[2, 'foo']
);
self::assertNull(
$this->connection->fetchColumn('SELECT test_datetime FROM fetch_table WHERE test_int = ?', [2])
);
}
/**
* @group DBAL-1028
*/
......@@ -922,48 +798,4 @@ class DataAccessTest extends FunctionalTestCase
$this->connection->fetchColumn('SELECT test_int FROM fetch_table WHERE test_int = ?', [-1])
);
}
private function setupFixture() : void
{
$this->connection->exec('DELETE FROM fetch_table');
$this->connection->insert('fetch_table', [
'test_int' => 1,
'test_string' => 'foo',
'test_datetime' => '2010-01-01 10:10:10',
]);
}
private function beforeFetchClassTest() : void
{
$driver = $this->connection->getDriver();
if ($driver instanceof Oci8Driver) {
self::markTestSkipped('Not supported by OCI8');
}
if ($driver instanceof MySQLiDriver) {
self::markTestSkipped('Mysqli driver dont support this feature.');
}
if (! $driver instanceof PDOOracleDriver) {
return;
}
/** @var PDOConnection $connection */
$connection = $this->connection
->getWrappedConnection();
$connection->getWrappedConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
}
}
class MyFetchClass
{
/** @var int */
public $test_int;
/** @var string */
public $test_string;
/** @var string */
public $test_datetime;
}
......@@ -78,7 +78,6 @@ class BinaryTest extends FunctionalTestCase
$value = $this->connection->fetchColumn(
'SELECT val FROM binary_table WHERE id = ?',
[$id],
0,
[ParameterType::BINARY]
);
......
......@@ -3,13 +3,11 @@
namespace Doctrine\DBAL\Tests\Portability;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Portability\Connection;
use Doctrine\DBAL\Portability\Statement;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use ReflectionProperty;
use function iterator_to_array;
class StatementTest extends TestCase
......@@ -122,25 +120,6 @@ class StatementTest extends TestCase
self::assertTrue($this->stmt->execute($params));
}
public function testSetFetchMode() : void
{
$fetchMode = FetchMode::CUSTOM_OBJECT;
$arg1 = 'MyClass';
$arg2 = [1, 2];
$this->wrappedStmt->expects(self::once())
->method('setFetchMode')
->with($fetchMode, $arg1, $arg2)
->will(self::returnValue(true));
$re = new ReflectionProperty($this->stmt, 'defaultFetchMode');
$re->setAccessible(true);
self::assertSame(FetchMode::MIXED, $re->getValue($this->stmt));
self::assertTrue($this->stmt->setFetchMode($fetchMode, $arg1, $arg2));
self::assertSame($fetchMode, $re->getValue($this->stmt));
}
public function testGetIterator() : void
{
$this->wrappedStmt->expects(self::exactly(3))
......
......@@ -8,7 +8,6 @@ use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\Logging\SQLLogger;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Statement;
......@@ -149,15 +148,4 @@ class StatementTest extends TestCase
$statement->execute();
}
public function testPDOCustomClassConstructorArgs() : void
{
$statement = new Statement('', $this->conn);
$this->driverStatement->expects(self::once())
->method('fetchAll')
->with(self::equalTo(FetchMode::CUSTOM_OBJECT), self::equalTo('Example'), self::equalTo(['arg1']));
$statement->fetchAll(FetchMode::CUSTOM_OBJECT, 'Example', ['arg1']);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment