Unverified Commit 7f79d049 authored by Marco Pivetta's avatar Marco Pivetta Committed by GitHub

Merge pull request #3442 from morozov/phpstan-lvl6

PHPStan Level 6
parents 94cec711 000cd9dd
...@@ -22,7 +22,6 @@ use Doctrine\DBAL\Types\Type; ...@@ -22,7 +22,6 @@ use Doctrine\DBAL\Types\Type;
use Exception; use Exception;
use Throwable; use Throwable;
use function array_key_exists; use function array_key_exists;
use function array_merge;
use function assert; use function assert;
use function func_get_args; use function func_get_args;
use function implode; use function implode;
...@@ -599,24 +598,26 @@ class Connection implements DriverConnection ...@@ -599,24 +598,26 @@ class Connection implements DriverConnection
} }
/** /**
* Gathers conditions for an update or delete call. * Adds identifier condition to the query components
* *
* @param mixed[] $identifiers Input array of columns to values * @param mixed[] $identifier Map of key columns to their values
* @param string[] $columns Column names
* @param mixed[] $values Column values
* @param string[] $conditions Key conditions
* *
* @return string[][] a triplet with: * @throws DBALException
* - the first key being the columns
* - the second key being the values
* - the third key being the conditions
*/ */
private function gatherConditions(array $identifiers) private function addIdentifierCondition(
{ array $identifier,
$columns = []; array &$columns,
$values = []; array &$values,
$conditions = []; array &$conditions
) : void {
$platform = $this->getDatabasePlatform();
foreach ($identifiers as $columnName => $value) { foreach ($identifier as $columnName => $value) {
if ($value === null) { if ($value === null) {
$conditions[] = $this->getDatabasePlatform()->getIsNullExpression($columnName); $conditions[] = $platform->getIsNullExpression($columnName);
continue; continue;
} }
...@@ -624,8 +625,6 @@ class Connection implements DriverConnection ...@@ -624,8 +625,6 @@ class Connection implements DriverConnection
$values[] = $value; $values[] = $value;
$conditions[] = $columnName . ' = ?'; $conditions[] = $columnName . ' = ?';
} }
return [$columns, $values, $conditions];
} }
/** /**
...@@ -648,7 +647,9 @@ class Connection implements DriverConnection ...@@ -648,7 +647,9 @@ class Connection implements DriverConnection
throw InvalidArgumentException::fromEmptyCriteria(); throw InvalidArgumentException::fromEmptyCriteria();
} }
[$columns, $values, $conditions] = $this->gatherConditions($identifier); $columns = $values = $conditions = [];
$this->addIdentifierCondition($identifier, $columns, $values, $conditions);
return $this->executeUpdate( return $this->executeUpdate(
'DELETE FROM ' . $tableExpression . ' WHERE ' . implode(' AND ', $conditions), 'DELETE FROM ' . $tableExpression . ' WHERE ' . implode(' AND ', $conditions),
...@@ -713,19 +714,15 @@ class Connection implements DriverConnection ...@@ -713,19 +714,15 @@ class Connection implements DriverConnection
*/ */
public function update($tableExpression, array $data, array $identifier, array $types = []) public function update($tableExpression, array $data, array $identifier, array $types = [])
{ {
$setColumns = []; $columns = $values = $conditions = $set = [];
$setValues = [];
$set = [];
foreach ($data as $columnName => $value) { foreach ($data as $columnName => $value) {
$setColumns[] = $columnName; $columns[] = $columnName;
$setValues[] = $value; $values[] = $value;
$set[] = $columnName . ' = ?'; $set[] = $columnName . ' = ?';
} }
[$conditionColumns, $conditionValues, $conditions] = $this->gatherConditions($identifier); $this->addIdentifierCondition($identifier, $columns, $values, $conditions);
$columns = array_merge($setColumns, $conditionColumns);
$values = array_merge($setValues, $conditionValues);
if (is_string(key($types))) { if (is_string(key($types))) {
$types = $this->extractTypeValues($columns, $types); $types = $this->extractTypeValues($columns, $types);
...@@ -777,7 +774,7 @@ class Connection implements DriverConnection ...@@ -777,7 +774,7 @@ class Connection implements DriverConnection
/** /**
* Extract ordered type list from an ordered column list and type map. * Extract ordered type list from an ordered column list and type map.
* *
* @param string[] $columnList * @param int[]|string[] $columnList
* @param int[]|string[] $types * @param int[]|string[] $types
* *
* @return int[]|string[] * @return int[]|string[]
......
...@@ -42,13 +42,16 @@ class DB2Connection implements Connection, ServerInfoAwareConnection ...@@ -42,13 +42,16 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
$isPersistent = (isset($params['persistent']) && $params['persistent'] === true); $isPersistent = (isset($params['persistent']) && $params['persistent'] === true);
if ($isPersistent) { if ($isPersistent) {
$this->conn = db2_pconnect($params['dbname'], $username, $password, $driverOptions); $conn = db2_pconnect($params['dbname'], $username, $password, $driverOptions);
} else { } else {
$this->conn = db2_connect($params['dbname'], $username, $password, $driverOptions); $conn = db2_connect($params['dbname'], $username, $password, $driverOptions);
} }
if (! $this->conn) {
if ($conn === false) {
throw new DB2Exception(db2_conn_errormsg()); throw new DB2Exception(db2_conn_errormsg());
} }
$this->conn = $conn;
} }
/** /**
......
...@@ -127,16 +127,16 @@ class DB2Statement implements IteratorAggregate, Statement ...@@ -127,16 +127,16 @@ class DB2Statement implements IteratorAggregate, Statement
} }
/** /**
* @param int|string $parameter Parameter position or name * @param int $position Parameter position
* @param mixed $variable * @param mixed $variable
* *
* @throws DB2Exception * @throws DB2Exception
*/ */
private function bind($parameter, &$variable, int $parameterType, int $dataType) : void private function bind($position, &$variable, int $parameterType, int $dataType) : void
{ {
$this->bindParam[$parameter] =& $variable; $this->bindParam[$position] =& $variable;
if (! db2_bind_param($this->stmt, $parameter, 'variable', $parameterType, $dataType)) { if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) {
throw new DB2Exception(db2_stmt_errormsg()); throw new DB2Exception(db2_stmt_errormsg());
} }
} }
......
...@@ -19,6 +19,7 @@ use function feof; ...@@ -19,6 +19,7 @@ use function feof;
use function fread; use function fread;
use function get_resource_type; use function get_resource_type;
use function is_array; use function is_array;
use function is_int;
use function is_resource; use function is_resource;
use function sprintf; use function sprintf;
use function str_repeat; use function str_repeat;
...@@ -41,7 +42,7 @@ class MysqliStatement implements IteratorAggregate, Statement ...@@ -41,7 +42,7 @@ class MysqliStatement implements IteratorAggregate, Statement
/** @var mysqli_stmt */ /** @var mysqli_stmt */
protected $_stmt; protected $_stmt;
/** @var string[]|bool|null */ /** @var string[]|false|null */
protected $_columnNames; protected $_columnNames;
/** @var mixed[]|null */ /** @var mixed[]|null */
...@@ -78,11 +79,15 @@ class MysqliStatement implements IteratorAggregate, Statement ...@@ -78,11 +79,15 @@ class MysqliStatement implements IteratorAggregate, Statement
public function __construct(mysqli $conn, $prepareString) public function __construct(mysqli $conn, $prepareString)
{ {
$this->_conn = $conn; $this->_conn = $conn;
$this->_stmt = $conn->prepare($prepareString);
if ($this->_stmt === false) { $stmt = $conn->prepare($prepareString);
if ($stmt === false) {
throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno); throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno);
} }
$this->_stmt = $stmt;
$paramCount = $this->_stmt->param_count; $paramCount = $this->_stmt->param_count;
if (0 >= $paramCount) { if (0 >= $paramCount) {
return; return;
...@@ -97,14 +102,14 @@ class MysqliStatement implements IteratorAggregate, Statement ...@@ -97,14 +102,14 @@ class MysqliStatement implements IteratorAggregate, Statement
*/ */
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null) public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{ {
assert(is_int($column));
if (! isset(self::$_paramTypeMap[$type])) { if (! isset(self::$_paramTypeMap[$type])) {
throw new MysqliException(sprintf("Unknown type: '%s'", $type)); throw new MysqliException(sprintf("Unknown type: '%s'", $type));
} }
$type = self::$_paramTypeMap[$type];
$this->_bindedValues[$column] =& $variable; $this->_bindedValues[$column] =& $variable;
$this->types[$column - 1] = $type; $this->types[$column - 1] = self::$_paramTypeMap[$type];
return true; return true;
} }
...@@ -114,15 +119,15 @@ class MysqliStatement implements IteratorAggregate, Statement ...@@ -114,15 +119,15 @@ class MysqliStatement implements IteratorAggregate, Statement
*/ */
public function bindValue($param, $value, $type = ParameterType::STRING) public function bindValue($param, $value, $type = ParameterType::STRING)
{ {
assert(is_int($param));
if (! isset(self::$_paramTypeMap[$type])) { if (! isset(self::$_paramTypeMap[$type])) {
throw new MysqliException(sprintf("Unknown type: '%s'", $type)); throw new MysqliException(sprintf("Unknown type: '%s'", $type));
} }
$type = self::$_paramTypeMap[$type];
$this->_values[$param] = $value; $this->_values[$param] = $value;
$this->_bindedValues[$param] =& $this->_values[$param]; $this->_bindedValues[$param] =& $this->_values[$param];
$this->types[$param - 1] = $type; $this->types[$param - 1] = self::$_paramTypeMap[$type];
return true; return true;
} }
...@@ -134,15 +139,11 @@ class MysqliStatement implements IteratorAggregate, Statement ...@@ -134,15 +139,11 @@ class MysqliStatement implements IteratorAggregate, Statement
{ {
if ($this->_bindedValues !== null) { if ($this->_bindedValues !== null) {
if ($params !== null) { if ($params !== null) {
if (! $this->_bindValues($params)) { if (! $this->bindUntypedValues($params)) {
throw new MysqliException($this->_stmt->error, $this->_stmt->errno); throw new MysqliException($this->_stmt->error, $this->_stmt->errno);
} }
} else { } else {
[$types, $values, $streams] = $this->separateBoundValues(); $this->bindTypedParameters();
if (! $this->_stmt->bind_param($types, ...$values)) {
throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
}
$this->sendLongData($streams);
} }
} }
...@@ -153,10 +154,14 @@ class MysqliStatement implements IteratorAggregate, Statement ...@@ -153,10 +154,14 @@ class MysqliStatement implements IteratorAggregate, Statement
if ($this->_columnNames === null) { if ($this->_columnNames === null) {
$meta = $this->_stmt->result_metadata(); $meta = $this->_stmt->result_metadata();
if ($meta !== false) { if ($meta !== false) {
$fields = $meta->fetch_fields();
assert(is_array($fields));
$columnNames = []; $columnNames = [];
foreach ($meta->fetch_fields() as $col) { foreach ($fields as $col) {
$columnNames[] = $col->name; $columnNames[] = $col->name;
} }
$meta->free(); $meta->free();
$this->_columnNames = $columnNames; $this->_columnNames = $columnNames;
...@@ -200,12 +205,9 @@ class MysqliStatement implements IteratorAggregate, Statement ...@@ -200,12 +205,9 @@ class MysqliStatement implements IteratorAggregate, Statement
} }
/** /**
* Split $this->_bindedValues into those values that need to be sent using mysqli::send_long_data() * Binds parameters with known types previously bound to the statement
* and those that can be bound the usual way.
*
* @return array<int, array<int|string, mixed>|string>
*/ */
private function separateBoundValues() private function bindTypedParameters()
{ {
$streams = $values = []; $streams = $values = [];
$types = $this->types; $types = $this->types;
...@@ -231,7 +233,11 @@ class MysqliStatement implements IteratorAggregate, Statement ...@@ -231,7 +233,11 @@ class MysqliStatement implements IteratorAggregate, Statement
$values[$parameter] = $value; $values[$parameter] = $value;
} }
return [$types, $values, $streams]; if (! $this->_stmt->bind_param($types, ...$values)) {
throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
}
$this->sendLongData($streams);
} }
/** /**
...@@ -263,7 +269,7 @@ class MysqliStatement implements IteratorAggregate, Statement ...@@ -263,7 +269,7 @@ class MysqliStatement implements IteratorAggregate, Statement
* *
* @return bool * @return bool
*/ */
private function _bindValues($values) private function bindUntypedValues(array $values)
{ {
$params = []; $params = [];
$types = str_repeat('s', count($values)); $types = str_repeat('s', count($values));
......
...@@ -54,13 +54,15 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection ...@@ -54,13 +54,15 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection
define('OCI_NO_AUTO_COMMIT', 0); define('OCI_NO_AUTO_COMMIT', 0);
} }
$this->dbh = $persistent $dbh = $persistent
? @oci_pconnect($username, $password, $db, $charset, $sessionMode) ? @oci_pconnect($username, $password, $db, $charset, $sessionMode)
: @oci_connect($username, $password, $db, $charset, $sessionMode); : @oci_connect($username, $password, $db, $charset, $sessionMode);
if (! $this->dbh) { if ($dbh === false) {
throw OCI8Exception::fromErrorInfo(oci_error()); throw OCI8Exception::fromErrorInfo(oci_error());
} }
$this->dbh = $dbh;
} }
/** /**
...@@ -71,17 +73,23 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection ...@@ -71,17 +73,23 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection
*/ */
public function getServerVersion() public function getServerVersion()
{ {
if (! preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', oci_server_version($this->dbh), $version)) { $version = oci_server_version($this->dbh);
if ($version === false) {
throw OCI8Exception::fromErrorInfo(oci_error($this->dbh));
}
if (! preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', $version, $matches)) {
throw new UnexpectedValueException( throw new UnexpectedValueException(
sprintf( sprintf(
'Unexpected database version string "%s". Cannot parse an appropriate version number from it. ' . 'Unexpected database version string "%s". Cannot parse an appropriate version number from it. ' .
'Please report this database version string to the Doctrine team.', 'Please report this database version string to the Doctrine team.',
oci_server_version($this->dbh) $version
) )
); );
} }
return $version[1]; return $matches[1];
} }
/** /**
...@@ -222,6 +230,12 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection ...@@ -222,6 +230,12 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection
*/ */
public function errorInfo() public function errorInfo()
{ {
return oci_error($this->dbh); $error = oci_error($this->dbh);
if ($error === false) {
return [];
}
return $error;
} }
} }
...@@ -7,12 +7,16 @@ use Doctrine\DBAL\Driver\AbstractDriverException; ...@@ -7,12 +7,16 @@ use Doctrine\DBAL\Driver\AbstractDriverException;
class OCI8Exception extends AbstractDriverException class OCI8Exception extends AbstractDriverException
{ {
/** /**
* @param mixed[] $error * @param mixed[]|false $error
* *
* @return \Doctrine\DBAL\Driver\OCI8\OCI8Exception * @return \Doctrine\DBAL\Driver\OCI8\OCI8Exception
*/ */
public static function fromErrorInfo($error) public static function fromErrorInfo($error)
{ {
if ($error === false) {
return new self('Database error occurred but no error information was retrieved from the driver.');
}
return new self($error['message'], null, $error['code']); return new self($error['message'], null, $error['code']);
} }
} }
...@@ -23,9 +23,11 @@ use const OCI_TEMP_BLOB; ...@@ -23,9 +23,11 @@ use const OCI_TEMP_BLOB;
use const PREG_OFFSET_CAPTURE; use const PREG_OFFSET_CAPTURE;
use const SQLT_CHR; use const SQLT_CHR;
use function array_key_exists; use function array_key_exists;
use function assert;
use function count; use function count;
use function implode; use function implode;
use function is_numeric; use function is_int;
use function is_resource;
use function oci_bind_by_name; use function oci_bind_by_name;
use function oci_cancel; use function oci_cancel;
use function oci_error; use function oci_error;
...@@ -92,16 +94,20 @@ class OCI8Statement implements IteratorAggregate, Statement ...@@ -92,16 +94,20 @@ class OCI8Statement implements IteratorAggregate, Statement
/** /**
* Creates a new OCI8Statement that uses the given connection handle and SQL statement. * Creates a new OCI8Statement that uses the given connection handle and SQL statement.
* *
* @param resource $dbh The connection handle. * @param resource $dbh The connection handle.
* @param string $statement The SQL statement. * @param string $query The SQL query.
*/ */
public function __construct($dbh, $statement, OCI8Connection $conn) public function __construct($dbh, $query, OCI8Connection $conn)
{ {
[$statement, $paramMap] = self::convertPositionalToNamedPlaceholders($statement); [$query, $paramMap] = self::convertPositionalToNamedPlaceholders($query);
$this->_sth = oci_parse($dbh, $statement);
$this->_dbh = $dbh; $stmt = oci_parse($dbh, $query);
$this->_paramMap = $paramMap; assert(is_resource($stmt));
$this->_conn = $conn;
$this->_sth = $stmt;
$this->_dbh = $dbh;
$this->_paramMap = $paramMap;
$this->_conn = $conn;
} }
/** /**
...@@ -272,6 +278,10 @@ class OCI8Statement implements IteratorAggregate, Statement ...@@ -272,6 +278,10 @@ class OCI8Statement implements IteratorAggregate, Statement
if ($type === ParameterType::LARGE_OBJECT) { if ($type === ParameterType::LARGE_OBJECT) {
$lob = oci_new_descriptor($this->_dbh, OCI_D_LOB); $lob = oci_new_descriptor($this->_dbh, OCI_D_LOB);
$class = 'OCI-Lob';
assert($lob instanceof $class);
$lob->writeTemporary($variable, OCI_TEMP_BLOB); $lob->writeTemporary($variable, OCI_TEMP_BLOB);
$variable =& $lob; $variable =& $lob;
...@@ -327,7 +337,7 @@ class OCI8Statement implements IteratorAggregate, Statement ...@@ -327,7 +337,7 @@ class OCI8Statement implements IteratorAggregate, Statement
*/ */
public function columnCount() public function columnCount()
{ {
return oci_num_fields($this->_sth); return oci_num_fields($this->_sth) ?: 0;
} }
/** /**
...@@ -348,7 +358,13 @@ class OCI8Statement implements IteratorAggregate, Statement ...@@ -348,7 +358,13 @@ class OCI8Statement implements IteratorAggregate, Statement
*/ */
public function errorInfo() public function errorInfo()
{ {
return oci_error($this->_sth); $error = oci_error($this->_sth);
if ($error === false) {
return [];
}
return $error;
} }
/** /**
...@@ -358,8 +374,9 @@ class OCI8Statement implements IteratorAggregate, Statement ...@@ -358,8 +374,9 @@ class OCI8Statement implements IteratorAggregate, Statement
{ {
if ($params) { if ($params) {
$hasZeroIndex = array_key_exists(0, $params); $hasZeroIndex = array_key_exists(0, $params);
foreach ($params as $key => $val) { foreach ($params as $key => $val) {
if ($hasZeroIndex && is_numeric($key)) { if ($hasZeroIndex && is_int($key)) {
$this->bindValue($key + 1, $val); $this->bindValue($key + 1, $val);
} else { } else {
$this->bindValue($key, $val); $this->bindValue($key, $val);
...@@ -505,6 +522,6 @@ class OCI8Statement implements IteratorAggregate, Statement ...@@ -505,6 +522,6 @@ class OCI8Statement implements IteratorAggregate, Statement
*/ */
public function rowCount() public function rowCount()
{ {
return oci_num_rows($this->_sth); return oci_num_rows($this->_sth) ?: 0;
} }
} }
...@@ -4,7 +4,7 @@ namespace Doctrine\DBAL\Driver; ...@@ -4,7 +4,7 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
use PDO; use PDO;
use function count; use function assert;
use function func_get_args; use function func_get_args;
/** /**
...@@ -69,23 +69,13 @@ class PDOConnection extends PDO implements Connection, ServerInfoAwareConnection ...@@ -69,23 +69,13 @@ class PDOConnection extends PDO implements Connection, ServerInfoAwareConnection
*/ */
public function query() public function query()
{ {
$args = func_get_args(); $args = func_get_args();
$argsCount = count($args);
try { try {
if ($argsCount === 4) { $stmt = parent::query(...$args);
return parent::query($args[0], $args[1], $args[2], $args[3]); assert($stmt instanceof \PDOStatement);
}
if ($argsCount === 3) { return $stmt;
return parent::query($args[0], $args[1], $args[2]);
}
if ($argsCount === 2) {
return parent::query($args[0], $args[1]);
}
return parent::query($args[0]);
} catch (\PDOException $exception) { } catch (\PDOException $exception) {
throw new PDOException($exception); throw new PDOException($exception);
} }
......
...@@ -16,13 +16,21 @@ class Driver extends AbstractSQLServerDriver ...@@ -16,13 +16,21 @@ class Driver extends AbstractSQLServerDriver
*/ */
public function connect(array $params, $username = null, $password = null, array $driverOptions = []) public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{ {
[$driverOptions, $connectionOptions] = $this->splitOptions($driverOptions); $pdoOptions = $dsnOptions = [];
foreach ($driverOptions as $option => $value) {
if (is_int($option)) {
$pdoOptions[$option] = $value;
} else {
$dsnOptions[$option] = $value;
}
}
return new Connection( return new Connection(
$this->_constructPdoDsn($params, $connectionOptions), $this->_constructPdoDsn($params, $dsnOptions),
$username, $username,
$password, $password,
$driverOptions $pdoOptions
); );
} }
...@@ -57,29 +65,6 @@ class Driver extends AbstractSQLServerDriver ...@@ -57,29 +65,6 @@ class Driver extends AbstractSQLServerDriver
return $dsn . $this->getConnectionOptionsDsn($connectionOptions); return $dsn . $this->getConnectionOptionsDsn($connectionOptions);
} }
/**
* Separates a connection options from a driver options
*
* @param int[]|string[] $options
*
* @return int[][]|string[][]
*/
private function splitOptions(array $options) : array
{
$driverOptions = [];
$connectionOptions = [];
foreach ($options as $optionKey => $optionValue) {
if (is_int($optionKey)) {
$driverOptions[$optionKey] = $optionValue;
} else {
$connectionOptions[$optionKey] = $optionValue;
}
}
return [$driverOptions, $connectionOptions];
}
/** /**
* Converts a connection options array to the DSN * Converts a connection options array to the DSN
* *
......
...@@ -6,6 +6,8 @@ use Doctrine\DBAL\FetchMode; ...@@ -6,6 +6,8 @@ use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
use PDO; use PDO;
use const E_USER_DEPRECATED; use const E_USER_DEPRECATED;
use function assert;
use function is_array;
use function sprintf; use function sprintf;
use function trigger_error; use function trigger_error;
...@@ -153,20 +155,21 @@ class PDOStatement extends \PDOStatement implements Statement ...@@ -153,20 +155,21 @@ class PDOStatement extends \PDOStatement implements Statement
{ {
$fetchMode = $this->convertFetchMode($fetchMode); $fetchMode = $this->convertFetchMode($fetchMode);
try { if ($fetchMode === null && $fetchArgument === null && $ctorArgs === null) {
if ($fetchMode === null && $fetchArgument === null && $ctorArgs === null) { $args = [];
return parent::fetchAll(); } elseif ($fetchArgument === null && $ctorArgs === null) {
} $args = [$fetchMode];
} elseif ($ctorArgs === null) {
if ($fetchArgument === null && $ctorArgs === null) { $args = [$fetchMode, $fetchArgument];
return parent::fetchAll($fetchMode); } else {
} $args = [$fetchMode, $fetchArgument, $ctorArgs];
}
if ($ctorArgs === null) { try {
return parent::fetchAll($fetchMode, $fetchArgument); $data = parent::fetchAll(...$args);
} assert(is_array($data));
return parent::fetchAll($fetchMode, $fetchArgument, $ctorArgs); return $data;
} catch (\PDOException $exception) { } catch (\PDOException $exception) {
throw new PDOException($exception); throw new PDOException($exception);
} }
......
...@@ -17,7 +17,7 @@ use function func_get_args; ...@@ -17,7 +17,7 @@ use function func_get_args;
use function func_num_args; use function func_num_args;
use function gettype; use function gettype;
use function is_array; use function is_array;
use function is_numeric; use function is_int;
use function is_object; use function is_object;
use function is_resource; use function is_resource;
use function is_string; use function is_string;
...@@ -177,9 +177,11 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement ...@@ -177,9 +177,11 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement
$hasZeroIndex = array_key_exists(0, $params); $hasZeroIndex = array_key_exists(0, $params);
foreach ($params as $key => $val) { foreach ($params as $key => $val) {
$key = $hasZeroIndex && is_numeric($key) ? $key + 1 : $key; if ($hasZeroIndex && is_int($key)) {
$this->bindValue($key + 1, $val);
$this->bindValue($key, $val); } else {
$this->bindValue($key, $val);
}
} }
} }
......
...@@ -44,10 +44,13 @@ class SQLSrvConnection implements Connection, ServerInfoAwareConnection ...@@ -44,10 +44,13 @@ class SQLSrvConnection implements Connection, ServerInfoAwareConnection
throw SQLSrvException::fromSqlSrvErrors(); throw SQLSrvException::fromSqlSrvErrors();
} }
$this->conn = sqlsrv_connect($serverName, $connectionOptions); $conn = sqlsrv_connect($serverName, $connectionOptions);
if (! $this->conn) {
if ($conn === false) {
throw SQLSrvException::fromSqlSrvErrors(); throw SQLSrvException::fromSqlSrvErrors();
} }
$this->conn = $conn;
$this->lastInsertId = new LastInsertId(); $this->lastInsertId = new LastInsertId();
} }
...@@ -115,7 +118,13 @@ class SQLSrvConnection implements Connection, ServerInfoAwareConnection ...@@ -115,7 +118,13 @@ class SQLSrvConnection implements Connection, ServerInfoAwareConnection
throw SQLSrvException::fromSqlSrvErrors(); throw SQLSrvException::fromSqlSrvErrors();
} }
return sqlsrv_rows_affected($stmt); $rowsAffected = sqlsrv_rows_affected($stmt);
if ($rowsAffected === false) {
throw SQLSrvException::fromSqlSrvErrors();
}
return $rowsAffected;
} }
/** /**
......
...@@ -18,6 +18,7 @@ use function array_key_exists; ...@@ -18,6 +18,7 @@ use function array_key_exists;
use function count; use function count;
use function func_get_args; use function func_get_args;
use function in_array; use function in_array;
use function is_int;
use function is_numeric; use function is_numeric;
use function sqlsrv_errors; use function sqlsrv_errors;
use function sqlsrv_execute; use function sqlsrv_execute;
...@@ -200,7 +201,11 @@ class SQLSrvStatement implements IteratorAggregate, Statement ...@@ -200,7 +201,11 @@ class SQLSrvStatement implements IteratorAggregate, Statement
*/ */
public function columnCount() public function columnCount()
{ {
return sqlsrv_num_fields($this->stmt); if ($this->stmt === null) {
return 0;
}
return sqlsrv_num_fields($this->stmt) ?: 0;
} }
/** /**
...@@ -231,9 +236,13 @@ class SQLSrvStatement implements IteratorAggregate, Statement ...@@ -231,9 +236,13 @@ class SQLSrvStatement implements IteratorAggregate, Statement
{ {
if ($params) { if ($params) {
$hasZeroIndex = array_key_exists(0, $params); $hasZeroIndex = array_key_exists(0, $params);
foreach ($params as $key => $val) { foreach ($params as $key => $val) {
$key = $hasZeroIndex && is_numeric($key) ? $key + 1 : $key; if ($hasZeroIndex && is_int($key)) {
$this->bindValue($key, $val); $this->bindValue($key + 1, $val);
} else {
$this->bindValue($key, $val);
}
} }
} }
...@@ -406,6 +415,10 @@ class SQLSrvStatement implements IteratorAggregate, Statement ...@@ -406,6 +415,10 @@ class SQLSrvStatement implements IteratorAggregate, Statement
*/ */
public function rowCount() public function rowCount()
{ {
return sqlsrv_rows_affected($this->stmt); if ($this->stmt === null) {
return 0;
}
return sqlsrv_rows_affected($this->stmt) ?: 0;
} }
} }
...@@ -18,8 +18,10 @@ use PDO; ...@@ -18,8 +18,10 @@ use PDO;
use function array_keys; use function array_keys;
use function array_map; use function array_map;
use function array_merge; use function array_merge;
use function assert;
use function class_implements; use function class_implements;
use function in_array; use function in_array;
use function is_string;
use function is_subclass_of; use function is_subclass_of;
use function parse_str; use function parse_str;
use function parse_url; use function parse_url;
...@@ -263,6 +265,8 @@ final class DriverManager ...@@ -263,6 +265,8 @@ final class DriverManager
// (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid
$url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $params['url']); $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $params['url']);
assert(is_string($url));
$url = parse_url($url); $url = parse_url($url);
if ($url === false) { if ($url === false) {
...@@ -414,6 +418,7 @@ final class DriverManager ...@@ -414,6 +418,7 @@ final class DriverManager
// URL schemes must not contain underscores, but dashes are ok // URL schemes must not contain underscores, but dashes are ok
$driver = str_replace('-', '_', $url['scheme']); $driver = str_replace('-', '_', $url['scheme']);
assert(is_string($driver));
// The requested driver from the URL scheme takes precedence over the // The requested driver from the URL scheme takes precedence over the
// default driver from the connection parameters. If the driver is // default driver from the connection parameters. If the driver is
......
...@@ -864,9 +864,9 @@ abstract class AbstractPlatform ...@@ -864,9 +864,9 @@ abstract class AbstractPlatform
/** /**
* Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str. * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
* *
* @param string $str Literal string. * @param string $str Literal string.
* @param string $substr Literal string to find. * @param string $substr Literal string to find.
* @param int|bool $startPos Position to start at, beginning of string by default. * @param int|false $startPos Position to start at, beginning of string by default.
* *
* @return string * @return string
* *
...@@ -1832,6 +1832,10 @@ abstract class AbstractPlatform ...@@ -1832,6 +1832,10 @@ abstract class AbstractPlatform
*/ */
public function getCreatePrimaryKeySQL(Index $index, $table) public function getCreatePrimaryKeySQL(Index $index, $table)
{ {
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
}
return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
} }
...@@ -2066,11 +2070,14 @@ abstract class AbstractPlatform ...@@ -2066,11 +2070,14 @@ abstract class AbstractPlatform
*/ */
protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
{ {
$tableName = $diff->newName !== false $sql = [];
? $diff->getNewName()->getQuotedName($this) $newName = $diff->getNewName();
: $diff->getName($this)->getQuotedName($this);
$sql = []; if ($newName !== false) {
$tableName = $newName->getQuotedName($this);
} else {
$tableName = $diff->getName($this)->getQuotedName($this);
}
if ($this->supportsForeignKeyConstraints()) { if ($this->supportsForeignKeyConstraints()) {
foreach ($diff->addedForeignKeys as $foreignKey) { foreach ($diff->addedForeignKeys as $foreignKey) {
......
...@@ -599,8 +599,14 @@ class DB2Platform extends AbstractPlatform ...@@ -599,8 +599,14 @@ class DB2Platform extends AbstractPlatform
$sql = array_merge($sql, $commentsSQL); $sql = array_merge($sql, $commentsSQL);
if ($diff->newName !== false) { $newName = $diff->getNewName();
$sql[] = 'RENAME TABLE ' . $diff->getName($this)->getQuotedName($this) . ' TO ' . $diff->getNewName()->getQuotedName($this);
if ($newName !== false) {
$sql[] = sprintf(
'RENAME TABLE %s TO %s',
$diff->getName($this)->getQuotedName($this),
$newName->getQuotedName($this)
);
} }
$sql = array_merge( $sql = array_merge(
......
...@@ -480,8 +480,10 @@ class DrizzlePlatform extends AbstractPlatform ...@@ -480,8 +480,10 @@ class DrizzlePlatform extends AbstractPlatform
$columnSql = []; $columnSql = [];
$queryParts = []; $queryParts = [];
if ($diff->newName !== false) { $newName = $diff->getNewName();
$queryParts[] = 'RENAME TO ' . $diff->getNewName()->getQuotedName($this);
if ($newName !== false) {
$queryParts[] = 'RENAME TO ' . $newName->getQuotedName($this);
} }
foreach ($diff->addedColumns as $column) { foreach ($diff->addedColumns as $column) {
......
...@@ -553,8 +553,10 @@ SQL ...@@ -553,8 +553,10 @@ SQL
{ {
$columnSql = []; $columnSql = [];
$queryParts = []; $queryParts = [];
if ($diff->newName !== false) { $newName = $diff->getNewName();
$queryParts[] = 'RENAME TO ' . $diff->getNewName()->getQuotedName($this);
if ($newName !== false) {
$queryParts[] = 'RENAME TO ' . $newName->getQuotedName($this);
} }
foreach ($diff->addedColumns as $column) { foreach ($diff->addedColumns as $column) {
...@@ -855,10 +857,14 @@ SQL ...@@ -855,10 +857,14 @@ SQL
*/ */
protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
{ {
$sql = []; $sql = [];
$tableName = $diff->newName !== false $newName = $diff->getNewName();
? $diff->getNewName()->getQuotedName($this)
: $diff->getName($this)->getQuotedName($this); if ($newName !== false) {
$tableName = $newName->getQuotedName($this);
} else {
$tableName = $diff->getName($this)->getQuotedName($this);
}
foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) {
if (in_array($foreignKey, $diff->changedForeignKeys, true)) { if (in_array($foreignKey, $diff->changedForeignKeys, true)) {
......
...@@ -869,8 +869,14 @@ SQL ...@@ -869,8 +869,14 @@ SQL
if (! $this->onSchemaAlterTable($diff, $tableSql)) { if (! $this->onSchemaAlterTable($diff, $tableSql)) {
$sql = array_merge($sql, $commentsSQL); $sql = array_merge($sql, $commentsSQL);
if ($diff->newName !== false) { $newName = $diff->getNewName();
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this);
if ($newName !== false) {
$sql[] = sprintf(
'ALTER TABLE %s RENAME TO %s',
$diff->getName($this)->getQuotedName($this),
$newName->getQuotedName($this)
);
} }
$sql = array_merge( $sql = array_merge(
......
...@@ -614,8 +614,14 @@ SQL ...@@ -614,8 +614,14 @@ SQL
if (! $this->onSchemaAlterTable($diff, $tableSql)) { if (! $this->onSchemaAlterTable($diff, $tableSql)) {
$sql = array_merge($sql, $commentsSQL); $sql = array_merge($sql, $commentsSQL);
if ($diff->newName !== false) { $newName = $diff->getNewName();
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this);
if ($newName !== false) {
$sql[] = sprintf(
'ALTER TABLE %s RENAME TO %s',
$diff->getName($this)->getQuotedName($this),
$newName->getQuotedName($this)
);
} }
$sql = array_merge( $sql = array_merge(
......
...@@ -184,9 +184,11 @@ class SQLAnywherePlatform extends AbstractPlatform ...@@ -184,9 +184,11 @@ class SQLAnywherePlatform extends AbstractPlatform
$sql = array_merge($sql, $commentsSQL); $sql = array_merge($sql, $commentsSQL);
if ($diff->newName !== false) { $newName = $diff->getNewName();
if ($newName !== false) {
$sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
$this->getAlterTableRenameTableClause($diff->getNewName()); $this->getAlterTableRenameTableClause($newName);
} }
$sql = array_merge( $sql = array_merge(
......
...@@ -315,12 +315,19 @@ SQL ...@@ -315,12 +315,19 @@ SQL
*/ */
public function getCreatePrimaryKeySQL(Index $index, $table) public function getCreatePrimaryKeySQL(Index $index, $table)
{ {
$flags = ''; if ($table instanceof Table) {
$identifier = $table->getQuotedName($this);
} else {
$identifier = $table;
}
$sql = 'ALTER TABLE ' . $identifier . ' ADD PRIMARY KEY';
if ($index->hasFlag('nonclustered')) { if ($index->hasFlag('nonclustered')) {
$flags = ' NONCLUSTERED'; $sql .= ' NONCLUSTERED';
} }
return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY' . $flags . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; return $sql . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
} }
/** /**
...@@ -584,8 +591,10 @@ SQL ...@@ -584,8 +591,10 @@ SQL
$sql = array_merge($sql, $commentsSql); $sql = array_merge($sql, $commentsSql);
if ($diff->newName !== false) { $newName = $diff->getNewName();
$sql[] = "sp_RENAME '" . $diff->getName($this)->getQuotedName($this) . "', '" . $diff->getNewName()->getName() . "'";
if ($newName !== false) {
$sql[] = "sp_RENAME '" . $diff->getName($this)->getQuotedName($this) . "', '" . $newName->getName() . "'";
/** /**
* Rename table's default constraints names * Rename table's default constraints names
...@@ -598,10 +607,10 @@ SQL ...@@ -598,10 +607,10 @@ SQL
$sql[] = "DECLARE @sql NVARCHAR(MAX) = N''; " . $sql[] = "DECLARE @sql NVARCHAR(MAX) = N''; " .
"SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' " . "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' " .
"+ REPLACE(dc.name, '" . $this->generateIdentifierName($diff->name) . "', " . "+ REPLACE(dc.name, '" . $this->generateIdentifierName($diff->name) . "', " .
"'" . $this->generateIdentifierName($diff->newName) . "') + ''', ''OBJECT'';' " . "'" . $this->generateIdentifierName($newName->getName()) . "') + ''', ''OBJECT'';' " .
'FROM sys.default_constraints dc ' . 'FROM sys.default_constraints dc ' .
'JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id ' . 'JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id ' .
"WHERE tbl.name = '" . $diff->getNewName()->getName() . "';" . "WHERE tbl.name = '" . $newName->getName() . "';" .
'EXEC sp_executesql @sql'; 'EXEC sp_executesql @sql';
} }
......
...@@ -685,7 +685,12 @@ class SqlitePlatform extends AbstractPlatform ...@@ -685,7 +685,12 @@ class SqlitePlatform extends AbstractPlatform
} }
$sql = []; $sql = [];
$tableName = $diff->newName ? $diff->getNewName(): $diff->getName($this); $tableName = $diff->getNewName();
if ($tableName === false) {
$tableName = $diff->getName($this);
}
foreach ($this->getIndexesInAlteredTable($diff) as $index) { foreach ($this->getIndexesInAlteredTable($diff) as $index) {
if ($index->isPrimary()) { if ($index->isPrimary()) {
continue; continue;
...@@ -908,9 +913,14 @@ class SqlitePlatform extends AbstractPlatform ...@@ -908,9 +913,14 @@ class SqlitePlatform extends AbstractPlatform
$sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this)); $sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this));
$sql[] = $this->getDropTableSQL($dataTable); $sql[] = $this->getDropTableSQL($dataTable);
if ($diff->newName && $diff->newName !== $diff->name) { $newName = $diff->getNewName();
$renamedTable = $diff->getNewName();
$sql[] = 'ALTER TABLE ' . $newTable->getQuotedName($this) . ' RENAME TO ' . $renamedTable->getQuotedName($this); if ($newName !== false) {
$sql[] = sprintf(
'ALTER TABLE %s RENAME TO %s',
$newTable->getQuotedName($this),
$newName->getQuotedName($this)
);
} }
$sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff)); $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff));
...@@ -1028,7 +1038,8 @@ class SqlitePlatform extends AbstractPlatform ...@@ -1028,7 +1038,8 @@ class SqlitePlatform extends AbstractPlatform
$columns[strtolower($columnName)] = $columnName; $columns[strtolower($columnName)] = $columnName;
} }
foreach ($diff->addedColumns as $columnName => $column) { foreach ($diff->addedColumns as $column) {
$columnName = $column->getName();
$columns[strtolower($columnName)] = $columnName; $columns[strtolower($columnName)] = $columnName;
} }
...@@ -1127,6 +1138,10 @@ class SqlitePlatform extends AbstractPlatform ...@@ -1127,6 +1138,10 @@ class SqlitePlatform extends AbstractPlatform
} }
foreach ($diff->removedForeignKeys as $constraint) { foreach ($diff->removedForeignKeys as $constraint) {
if (! $constraint instanceof ForeignKeyConstraint) {
$constraint = new Identifier($constraint);
}
$constraintName = strtolower($constraint->getName()); $constraintName = strtolower($constraint->getName());
if (! strlen($constraintName) || ! isset($foreignKeys[$constraintName])) { if (! strlen($constraintName) || ! isset($foreignKeys[$constraintName])) {
continue; continue;
......
...@@ -24,8 +24,13 @@ use function substr; ...@@ -24,8 +24,13 @@ use function substr;
*/ */
class SQLParserUtils class SQLParserUtils
{ {
/**#@+
*
* @deprecated Will be removed as internal implementation details.
*/
public const POSITIONAL_TOKEN = '\?'; public const POSITIONAL_TOKEN = '\?';
public const NAMED_TOKEN = '(?<!:):[a-zA-Z_][a-zA-Z0-9_]*'; public const NAMED_TOKEN = '(?<!:):[a-zA-Z_][a-zA-Z0-9_]*';
/**#@-*/
// Quote characters within string literals can be preceded by a backslash. // Quote characters within string literals can be preceded by a backslash.
public const ESCAPED_SINGLE_QUOTED_TEXT = "(?:'(?:\\\\\\\\)+'|'(?:[^'\\\\]|\\\\'?|'')*')"; public const ESCAPED_SINGLE_QUOTED_TEXT = "(?:'(?:\\\\\\\\)+'|'(?:[^'\\\\]|\\\\'?|'')*')";
...@@ -39,6 +44,8 @@ class SQLParserUtils ...@@ -39,6 +44,8 @@ class SQLParserUtils
* For a statement with positional parameters, returns a zero-indexed list of placeholder position. * For a statement with positional parameters, returns a zero-indexed list of placeholder position.
* For a statement with named parameters, returns a map of placeholder positions to their parameter names. * For a statement with named parameters, returns a map of placeholder positions to their parameter names.
* *
* @deprecated Will be removed as internal implementation detail.
*
* @param string $statement * @param string $statement
* @param bool $isPositional * @param bool $isPositional
* *
...@@ -46,27 +53,64 @@ class SQLParserUtils ...@@ -46,27 +53,64 @@ class SQLParserUtils
*/ */
public static function getPlaceholderPositions($statement, $isPositional = true) public static function getPlaceholderPositions($statement, $isPositional = true)
{ {
$match = $isPositional ? '?' : ':'; return $isPositional
? self::getPositionalPlaceholderPositions($statement)
: self::getNamedPlaceholderPositions($statement);
}
/**
* Returns a zero-indexed list of placeholder position.
*
* @return int[]
*/
private static function getPositionalPlaceholderPositions(string $statement) : array
{
return self::collectPlaceholders(
$statement,
'?',
self::POSITIONAL_TOKEN,
static function (string $_, int $placeholderPosition, int $fragmentPosition, array &$carry) : void {
$carry[] = $placeholderPosition + $fragmentPosition;
}
);
}
/**
* Returns a map of placeholder positions to their parameter names.
*
* @return string[]
*/
private static function getNamedPlaceholderPositions(string $statement) : array
{
return self::collectPlaceholders(
$statement,
':',
self::NAMED_TOKEN,
static function (string $placeholder, int $placeholderPosition, int $fragmentPosition, array &$carry) : void {
$carry[$placeholderPosition + $fragmentPosition] = substr($placeholder, 1);
}
);
}
/**
* @return mixed[]
*/
private static function collectPlaceholders(string $statement, string $match, string $token, callable $collector) : array
{
if (strpos($statement, $match) === false) { if (strpos($statement, $match) === false) {
return []; return [];
} }
$token = $isPositional ? self::POSITIONAL_TOKEN : self::NAMED_TOKEN; $carry = [];
$paramMap = [];
foreach (self::getUnquotedStatementFragments($statement) as $fragment) { foreach (self::getUnquotedStatementFragments($statement) as $fragment) {
preg_match_all('/' . $token . '/', $fragment[0], $matches, PREG_OFFSET_CAPTURE); preg_match_all('/' . $token . '/', $fragment[0], $matches, PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $placeholder) { foreach ($matches[0] as $placeholder) {
if ($isPositional) { $collector($placeholder[0], $placeholder[1], $fragment[1], $carry);
$paramMap[] = $placeholder[1] + $fragment[1];
} else {
$pos = $placeholder[1] + $fragment[1];
$paramMap[$pos] = substr($placeholder[0], 1, strlen($placeholder[0]));
}
} }
} }
return $paramMap; return $carry;
} }
/** /**
...@@ -109,14 +153,14 @@ class SQLParserUtils ...@@ -109,14 +153,14 @@ class SQLParserUtils
return [$query, $params, $types]; return [$query, $params, $types];
} }
$paramPos = self::getPlaceholderPositions($query, $isPositional);
if ($isPositional) { if ($isPositional) {
$paramOffset = 0; $paramOffset = 0;
$queryOffset = 0; $queryOffset = 0;
$params = array_values($params); $params = array_values($params);
$types = array_values($types); $types = array_values($types);
$paramPos = self::getPositionalPlaceholderPositions($query);
foreach ($paramPos as $needle => $needlePos) { foreach ($paramPos as $needle => $needlePos) {
if (! isset($arrayPositions[$needle])) { if (! isset($arrayPositions[$needle])) {
continue; continue;
...@@ -156,6 +200,8 @@ class SQLParserUtils ...@@ -156,6 +200,8 @@ class SQLParserUtils
$typesOrd = []; $typesOrd = [];
$paramsOrd = []; $paramsOrd = [];
$paramPos = self::getNamedPlaceholderPositions($query);
foreach ($paramPos as $pos => $paramName) { foreach ($paramPos as $pos => $paramName) {
$paramLen = strlen($paramName) + 1; $paramLen = strlen($paramName) + 1;
$value = static::extractParam($paramName, $params, true); $value = static::extractParam($paramName, $params, true);
......
...@@ -14,10 +14,12 @@ use function array_filter; ...@@ -14,10 +14,12 @@ use function array_filter;
use function array_intersect; use function array_intersect;
use function array_map; use function array_map;
use function array_values; use function array_values;
use function assert;
use function call_user_func_array; use function call_user_func_array;
use function count; use function count;
use function func_get_args; use function func_get_args;
use function is_array; use function is_array;
use function is_callable;
use function preg_match; use function preg_match;
use function str_replace; use function str_replace;
use function strtolower; use function strtolower;
...@@ -80,8 +82,11 @@ abstract class AbstractSchemaManager ...@@ -80,8 +82,11 @@ abstract class AbstractSchemaManager
unset($args[0]); unset($args[0]);
$args = array_values($args); $args = array_values($args);
$callback = [$this, $method];
assert(is_callable($callback));
try { try {
return call_user_func_array([$this, $method], $args); return call_user_func_array($callback, $args);
} catch (Throwable $e) { } catch (Throwable $e) {
return false; return false;
} }
......
...@@ -10,6 +10,7 @@ use function array_map; ...@@ -10,6 +10,7 @@ use function array_map;
use function array_merge; use function array_merge;
use function array_shift; use function array_shift;
use function array_unique; use function array_unique;
use function assert;
use function count; use function count;
use function strtolower; use function strtolower;
...@@ -108,6 +109,8 @@ class Comparator ...@@ -108,6 +109,8 @@ class Comparator
} }
foreach ($diff->changedTables[$localTableName]->removedForeignKeys as $key => $removedForeignKey) { foreach ($diff->changedTables[$localTableName]->removedForeignKeys as $key => $removedForeignKey) {
assert($removedForeignKey instanceof ForeignKeyConstraint);
// We check if the key is from the removed table if not we skip. // We check if the key is from the removed table if not we skip.
if ($tableName !== strtolower($removedForeignKey->getForeignTableName())) { if ($tableName !== strtolower($removedForeignKey->getForeignTableName())) {
continue; continue;
......
...@@ -3,14 +3,13 @@ ...@@ -3,14 +3,13 @@
namespace Doctrine\DBAL\Schema; namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use function array_combine;
use function array_keys; use function array_keys;
use function array_map; use function array_map;
use function end;
use function explode;
use function in_array; use function in_array;
use function strrpos;
use function strtolower; use function strtolower;
use function strtoupper; use function strtoupper;
use function substr;
/** /**
* An abstraction class for a foreign key constraint. * An abstraction class for a foreign key constraint.
...@@ -66,12 +65,8 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint ...@@ -66,12 +65,8 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint
public function __construct(array $localColumnNames, $foreignTableName, array $foreignColumnNames, $name = null, array $options = []) public function __construct(array $localColumnNames, $foreignTableName, array $foreignColumnNames, $name = null, array $options = [])
{ {
$this->_setName($name); $this->_setName($name);
$identifierConstructorCallback = static function ($column) {
return new Identifier($column); $this->_localColumnNames = $this->createIdentifierMap($localColumnNames);
};
$this->_localColumnNames = $localColumnNames
? array_combine($localColumnNames, array_map($identifierConstructorCallback, $localColumnNames))
: [];
if ($foreignTableName instanceof Table) { if ($foreignTableName instanceof Table) {
$this->_foreignTableName = $foreignTableName; $this->_foreignTableName = $foreignTableName;
...@@ -79,12 +74,26 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint ...@@ -79,12 +74,26 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint
$this->_foreignTableName = new Identifier($foreignTableName); $this->_foreignTableName = new Identifier($foreignTableName);
} }
$this->_foreignColumnNames = $foreignColumnNames $this->_foreignColumnNames = $this->createIdentifierMap($foreignColumnNames);
? array_combine($foreignColumnNames, array_map($identifierConstructorCallback, $foreignColumnNames))
: [];
$this->_options = $options; $this->_options = $options;
} }
/**
* @param string[] $names
*
* @return Identifier[]
*/
private function createIdentifierMap(array $names) : array
{
$identifiers = [];
foreach ($names as $name) {
$identifiers[$name] = new Identifier($name);
}
return $identifiers;
}
/** /**
* Returns the name of the referencing table * Returns the name of the referencing table
* the foreign key constraint is associated with. * the foreign key constraint is associated with.
...@@ -218,9 +227,14 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint ...@@ -218,9 +227,14 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint
*/ */
public function getUnqualifiedForeignTableName() public function getUnqualifiedForeignTableName()
{ {
$parts = explode('.', $this->_foreignTableName->getName()); $name = $this->_foreignTableName->getName();
$position = strrpos($name, '.');
if ($position !== false) {
$name = substr($name, $position);
}
return strtolower(end($parts)); return strtolower($name);
} }
/** /**
......
...@@ -9,8 +9,10 @@ use const CASE_LOWER; ...@@ -9,8 +9,10 @@ use const CASE_LOWER;
use function array_change_key_case; use function array_change_key_case;
use function array_shift; use function array_shift;
use function array_values; use function array_values;
use function assert;
use function end; use function end;
use function explode; use function explode;
use function is_string;
use function preg_match; use function preg_match;
use function preg_replace; use function preg_replace;
use function str_replace; use function str_replace;
...@@ -101,6 +103,8 @@ class MySqlSchemaManager extends AbstractSchemaManager ...@@ -101,6 +103,8 @@ class MySqlSchemaManager extends AbstractSchemaManager
$dbType = strtolower($tableColumn['type']); $dbType = strtolower($tableColumn['type']);
$dbType = strtok($dbType, '(), '); $dbType = strtok($dbType, '(), ');
assert(is_string($dbType));
$length = $tableColumn['length'] ?? strtok('(), '); $length = $tableColumn['length'] ?? strtok('(), ');
$fixed = null; $fixed = null;
......
...@@ -5,6 +5,7 @@ namespace Doctrine\DBAL\Schema; ...@@ -5,6 +5,7 @@ namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\SQLAnywherePlatform; use Doctrine\DBAL\Platforms\SQLAnywherePlatform;
use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Type;
use function assert; use function assert;
use function is_string;
use function preg_replace; use function preg_replace;
/** /**
...@@ -222,9 +223,9 @@ class SQLAnywhereSchemaManager extends AbstractSchemaManager ...@@ -222,9 +223,9 @@ class SQLAnywhereSchemaManager extends AbstractSchemaManager
*/ */
protected function _getPortableViewDefinition($view) protected function _getPortableViewDefinition($view)
{ {
return new View( $definition = preg_replace('/^.*\s+as\s+SELECT(.*)/i', 'SELECT$1', $view['view_def']);
$view['table_name'], assert(is_string($definition));
preg_replace('/^.*\s+as\s+SELECT(.*)/i', 'SELECT$1', $view['view_def'])
); return new View($view['table_name'], $definition);
} }
} }
...@@ -6,9 +6,11 @@ use Doctrine\DBAL\DBALException; ...@@ -6,9 +6,11 @@ use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\DriverException; use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Type;
use PDOException; use PDOException;
use function assert;
use function count; use function count;
use function in_array; use function in_array;
use function preg_replace; use function is_string;
use function preg_match;
use function sprintf; use function sprintf;
use function str_replace; use function str_replace;
use function strpos; use function strpos;
...@@ -61,7 +63,9 @@ class SQLServerSchemaManager extends AbstractSchemaManager ...@@ -61,7 +63,9 @@ class SQLServerSchemaManager extends AbstractSchemaManager
*/ */
protected function _getPortableTableColumnDefinition($tableColumn) protected function _getPortableTableColumnDefinition($tableColumn)
{ {
$dbType = strtok($tableColumn['type'], '(), '); $dbType = strtok($tableColumn['type'], '(), ');
assert(is_string($dbType));
$fixed = null; $fixed = null;
$length = (int) $tableColumn['length']; $length = (int) $tableColumn['length'];
$default = $tableColumn['default']; $default = $tableColumn['default'];
...@@ -71,15 +75,7 @@ class SQLServerSchemaManager extends AbstractSchemaManager ...@@ -71,15 +75,7 @@ class SQLServerSchemaManager extends AbstractSchemaManager
} }
if ($default !== null) { if ($default !== null) {
while ($default !== ($default2 = preg_replace('/^\((.*)\)$/', '$1', $default))) { $default = $this->parseDefaultExpression($default);
$default = trim($default2, "'");
if ($default !== 'getdate()') {
continue;
}
$default = $this->_platform->getCurrentTimestampSQL();
}
} }
switch ($dbType) { switch ($dbType) {
...@@ -126,6 +122,19 @@ class SQLServerSchemaManager extends AbstractSchemaManager ...@@ -126,6 +122,19 @@ class SQLServerSchemaManager extends AbstractSchemaManager
return $column; return $column;
} }
private function parseDefaultExpression(string $value) : string
{
while (preg_match('/^\((.*)\)$/', $value, $matches)) {
$value = trim($matches[1], "'");
}
if ($value === 'getdate()') {
return $this->_platform->getCurrentTimestampSQL();
}
return $value;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
...@@ -18,9 +18,6 @@ use function strtolower; ...@@ -18,9 +18,6 @@ use function strtolower;
*/ */
class Table extends AbstractAsset class Table extends AbstractAsset
{ {
/** @var string */
protected $_name = null;
/** @var Column[] */ /** @var Column[] */
protected $_columns = []; protected $_columns = [];
...@@ -98,8 +95,8 @@ class Table extends AbstractAsset ...@@ -98,8 +95,8 @@ class Table extends AbstractAsset
/** /**
* Sets the Primary Key. * Sets the Primary Key.
* *
* @param string[] $columnNames * @param string[] $columnNames
* @param string|bool $indexName * @param string|false $indexName
* *
* @return self * @return self
*/ */
......
...@@ -13,6 +13,7 @@ use Symfony\Component\Console\Input\InputInterface; ...@@ -13,6 +13,7 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use const PHP_EOL; use const PHP_EOL;
use function assert; use function assert;
use function error_get_last;
use function file_exists; use function file_exists;
use function file_get_contents; use function file_get_contents;
use function is_readable; use function is_readable;
...@@ -79,7 +80,13 @@ EOT ...@@ -79,7 +80,13 @@ EOT
} }
$output->write(sprintf("Processing file '<info>%s</info>'... ", $filePath)); $output->write(sprintf("Processing file '<info>%s</info>'... ", $filePath));
$sql = file_get_contents($filePath); $sql = @file_get_contents($filePath);
if ($sql === false) {
throw new RuntimeException(
sprintf("Unable to read SQL file '<info>%s</info>': %s", $filePath, error_get_last()['message'])
);
}
if ($conn instanceof PDOConnection) { if ($conn instanceof PDOConnection) {
// PDO Drivers // PDO Drivers
......
...@@ -10,7 +10,9 @@ use Symfony\Component\Console\Input\InputArgument; ...@@ -10,7 +10,9 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use function assert;
use function is_numeric; use function is_numeric;
use function is_string;
use function stripos; use function stripos;
/** /**
...@@ -51,6 +53,8 @@ EOT ...@@ -51,6 +53,8 @@ EOT
throw new RuntimeException("Argument 'SQL' is required in order to execute this command correctly."); throw new RuntimeException("Argument 'SQL' is required in order to execute this command correctly.");
} }
assert(is_string($sql));
$depth = $input->getOption('depth'); $depth = $input->getOption('depth');
if (! is_numeric($depth)) { if (! is_numeric($depth)) {
......
...@@ -9,6 +9,7 @@ use Doctrine\Common\Collections\Collection; ...@@ -9,6 +9,7 @@ use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Persistence\Proxy; use Doctrine\Common\Persistence\Proxy;
use stdClass; use stdClass;
use function array_keys; use function array_keys;
use function assert;
use function class_exists; use function class_exists;
use function count; use function count;
use function end; use function end;
...@@ -20,9 +21,11 @@ use function ini_get; ...@@ -20,9 +21,11 @@ use function ini_get;
use function ini_set; use function ini_set;
use function is_array; use function is_array;
use function is_object; use function is_object;
use function is_string;
use function ob_get_clean; use function ob_get_clean;
use function ob_start; use function ob_start;
use function strip_tags; use function strip_tags;
use function strlen;
use function strrpos; use function strrpos;
use function substr; use function substr;
use function var_dump; use function var_dump;
...@@ -68,7 +71,9 @@ final class Dumper ...@@ -68,7 +71,9 @@ final class Dumper
var_dump($var); var_dump($var);
try { try {
return strip_tags(html_entity_decode(ob_get_clean())); $output = ob_get_clean();
assert(is_string($output));
return strip_tags(html_entity_decode($output));
} finally { } finally {
ini_set('html_errors', $html); ini_set('html_errors', $html);
} }
...@@ -171,6 +176,6 @@ final class Dumper ...@@ -171,6 +176,6 @@ final class Dumper
return $class; return $class;
} }
return substr($class, $pos + Proxy::MARKER_LENGTH + 2); return substr($class, $pos + strlen(Proxy::MARKER) + 2);
} }
} }
...@@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Types; ...@@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Types;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use function assert;
use function fopen; use function fopen;
use function fseek; use function fseek;
use function fwrite; use function fwrite;
...@@ -34,6 +35,7 @@ class BinaryType extends Type ...@@ -34,6 +35,7 @@ class BinaryType extends Type
if (is_string($value)) { if (is_string($value)) {
$fp = fopen('php://temp', 'rb+'); $fp = fopen('php://temp', 'rb+');
assert(is_resource($fp));
fwrite($fp, $value); fwrite($fp, $value);
fseek($fp, 0); fseek($fp, 0);
$value = $fp; $value = $fp;
......
...@@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Types; ...@@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Types;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use function assert;
use function fopen; use function fopen;
use function fseek; use function fseek;
use function fwrite; use function fwrite;
...@@ -34,6 +35,7 @@ class BlobType extends Type ...@@ -34,6 +35,7 @@ class BlobType extends Type
if (is_string($value)) { if (is_string($value)) {
$fp = fopen('php://temp', 'rb+'); $fp = fopen('php://temp', 'rb+');
assert(is_resource($fp));
fwrite($fp, $value); fwrite($fp, $value);
fseek($fp, 0); fseek($fp, 0);
$value = $fp; $value = $fp;
......
...@@ -5,9 +5,9 @@ namespace Doctrine\DBAL\Types; ...@@ -5,9 +5,9 @@ namespace Doctrine\DBAL\Types;
use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use function end;
use function explode;
use function str_replace; use function str_replace;
use function strrpos;
use function substr;
/** /**
* The base class for so-called Doctrine mapping types. * The base class for so-called Doctrine mapping types.
...@@ -255,9 +255,14 @@ abstract class Type ...@@ -255,9 +255,14 @@ abstract class Type
*/ */
public function __toString() public function __toString()
{ {
$e = explode('\\', static::class); $type = static::class;
$position = strrpos($type, '\\');
return str_replace('Type', '', end($e)); if ($position !== false) {
$type = substr($type, $position);
}
return str_replace('Type', '', $type);
} }
/** /**
......
parameters: parameters:
level: 5 level: 6
paths: paths:
- %currentWorkingDirectory%/lib - %currentWorkingDirectory%/lib
autoload_files: autoload_files:
...@@ -29,6 +29,10 @@ parameters: ...@@ -29,6 +29,10 @@ parameters:
# https://github.com/JetBrains/phpstorm-stubs/pull/488 # https://github.com/JetBrains/phpstorm-stubs/pull/488
- '~^Parameter #1 \$byteCount of function SQLSRV_SQLTYPE_VARBINARY expects int, string given\.\z~' - '~^Parameter #1 \$byteCount of function SQLSRV_SQLTYPE_VARBINARY expects int, string given\.\z~'
# https://github.com/phpstan/phpstan/issues/1847
- '~^Parameter #2 \$registeredAliases of static method Doctrine\\DBAL\\Query\\QueryException::unknownAlias\(\) expects array<string>, array<int, int|string> given\.\z~'
- '~^Parameter #2 \$registeredAliases of static method Doctrine\\DBAL\\Query\\QueryException::nonUniqueAlias\(\) expects array<string>, array<int, int|string> given\.\z~'
# legacy variadic-like signature # legacy variadic-like signature
- '~^Method Doctrine\\DBAL\\Driver\\Connection::query\(\) invoked with \d+ parameters?, 0 required\.\z~' - '~^Method Doctrine\\DBAL\\Driver\\Connection::query\(\) invoked with \d+ parameters?, 0 required\.\z~'
......
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