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