diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index 06ebae21944..4d79fb739b6 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -22,7 +22,6 @@ 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 @@ public function isTransactionActive() } /** - * 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 @@ private function gatherConditions(array $identifiers) $values[] = $value; $conditions[] = $columnName . ' = ?'; } - - return [$columns, $values, $conditions]; } /** @@ -648,7 +647,9 @@ public function delete($tableExpression, array $identifier, array $types = []) 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 @@ public function getTransactionIsolation() */ 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 @@ public function insert($tableExpression, array $data, array $types = []) /** * 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[] diff --git a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php index bd80e6e49a7..cb1e6018f3e 100644 --- a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php @@ -42,13 +42,16 @@ public function __construct(array $params, $username, $password, $driverOptions $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; } /** diff --git a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php index 4244b256414..af6e852bc43 100644 --- a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php @@ -127,16 +127,16 @@ public function bindParam($column, &$variable, $type = ParameterType::STRING, $l } /** - * @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()); } } diff --git a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php index e7600d92296..85a88f33401 100644 --- a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php +++ b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php @@ -19,6 +19,7 @@ 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 @@ public function __construct(mysqli $conn, $prepareString) */ 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 @@ public function bindParam($column, &$variable, $type = ParameterType::STRING, $l */ 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 @@ public function execute($params = null) { 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 @@ public function execute($params = null) 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 @@ public function execute($params = null) } /** - * 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|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 @@ private function separateBoundValues() $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 @@ private function sendLongData($streams) * * @return bool */ - private function _bindValues($values) + private function bindUntypedValues(array $values) { $params = []; $types = str_repeat('s', count($values)); diff --git a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php index 5ebc877a898..35333e8d2d7 100644 --- a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php +++ b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php @@ -54,13 +54,15 @@ public function __construct($username, $password, $db, $charset = null, $session 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 @@ public function __construct($username, $password, $db, $charset = null, $session */ 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 @@ public function errorCode() */ public function errorInfo() { - return oci_error($this->dbh); + $error = oci_error($this->dbh); + + if ($error === false) { + return []; + } + + return $error; } } diff --git a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php index 9d61ad42d08..af3a9d1a7a9 100644 --- a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php +++ b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php @@ -7,12 +7,16 @@ 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']); } } diff --git a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php index 568f3713b5a..32feaef93e3 100644 --- a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php +++ b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php @@ -23,9 +23,11 @@ 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 @@ public function bindParam($column, &$variable, $type = ParameterType::STRING, $l 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 @@ public function closeCursor() */ public function columnCount() { - return oci_num_fields($this->_sth); + return oci_num_fields($this->_sth) ?: 0; } /** @@ -348,7 +358,13 @@ public function errorCode() */ public function errorInfo() { - return oci_error($this->_sth); + $error = oci_error($this->_sth); + + if ($error === false) { + return []; + } + + return $error; } /** @@ -358,8 +374,9 @@ public function execute($params = null) { 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 @@ public function fetchColumn($columnIndex = 0) */ public function rowCount() { - return oci_num_rows($this->_sth); + return oci_num_rows($this->_sth) ?: 0; } } diff --git a/lib/Doctrine/DBAL/Driver/PDOConnection.php b/lib/Doctrine/DBAL/Driver/PDOConnection.php index 2155140b2a8..11f5febc87b 100644 --- a/lib/Doctrine/DBAL/Driver/PDOConnection.php +++ b/lib/Doctrine/DBAL/Driver/PDOConnection.php @@ -4,7 +4,7 @@ use Doctrine\DBAL\ParameterType; use PDO; -use function count; +use function assert; use function func_get_args; /** @@ -69,23 +69,13 @@ public function prepare($prepareString, $driverOptions = []) */ 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); } diff --git a/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php b/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php index 6543e32392e..8bc8671620f 100644 --- a/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php +++ b/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php @@ -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 @@ private function _constructPdoDsn(array $params, array $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 * diff --git a/lib/Doctrine/DBAL/Driver/PDOStatement.php b/lib/Doctrine/DBAL/Driver/PDOStatement.php index 2aed89fbf07..f734659c2b7 100644 --- a/lib/Doctrine/DBAL/Driver/PDOStatement.php +++ b/lib/Doctrine/DBAL/Driver/PDOStatement.php @@ -6,6 +6,8 @@ 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 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n { $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); } diff --git a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php index 6354836a478..886bb245873 100644 --- a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php +++ b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php @@ -17,7 +17,7 @@ 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 @@ public function execute($params = null) $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); + } } } diff --git a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php index f1f9ddd1d79..dbda962006a 100644 --- a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php +++ b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php @@ -44,10 +44,13 @@ public function __construct($serverName, $connectionOptions) 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 @@ public function exec($statement) throw SQLSrvException::fromSqlSrvErrors(); } - return sqlsrv_rows_affected($stmt); + $rowsAffected = sqlsrv_rows_affected($stmt); + + if ($rowsAffected === false) { + throw SQLSrvException::fromSqlSrvErrors(); + } + + return $rowsAffected; } /** diff --git a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php index 147ef76e1a0..49ca2567845 100644 --- a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php +++ b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php @@ -18,6 +18,7 @@ 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 @@ public function closeCursor() */ 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 @@ public function execute($params = null) { 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 @@ public function fetchColumn($columnIndex = 0) */ public function rowCount() { - return sqlsrv_rows_affected($this->stmt); + if ($this->stmt === null) { + return 0; + } + + return sqlsrv_rows_affected($this->stmt) ?: 0; } } diff --git a/lib/Doctrine/DBAL/DriverManager.php b/lib/Doctrine/DBAL/DriverManager.php index 658123199c0..cb9db2c0221 100644 --- a/lib/Doctrine/DBAL/DriverManager.php +++ b/lib/Doctrine/DBAL/DriverManager.php @@ -18,8 +18,10 @@ 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 @@ private static function parseDatabaseUrl(array $params) : array // (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 @@ private static function parseDatabaseUrlScheme(array $url, array $params) : arra // 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 diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index 68994846864..439f6f541d2 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -864,9 +864,9 @@ public function getLowerExpression($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 $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 @@ protected function getCreateIndexSQLFlags(Index $index) */ 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 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) */ 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) { diff --git a/lib/Doctrine/DBAL/Platforms/DB2Platform.php b/lib/Doctrine/DBAL/Platforms/DB2Platform.php index 605122115d4..bfe2790e30c 100644 --- a/lib/Doctrine/DBAL/Platforms/DB2Platform.php +++ b/lib/Doctrine/DBAL/Platforms/DB2Platform.php @@ -599,8 +599,14 @@ public function getAlterTableSQL(TableDiff $diff) $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( diff --git a/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php b/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php index 27aacf42286..bb5b5cad36d 100644 --- a/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php @@ -480,8 +480,10 @@ public function getAlterTableSQL(TableDiff $diff) $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) { diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index 289dca9e7d7..9679a8447e7 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -553,8 +553,10 @@ public function getAlterTableSQL(TableDiff $diff) { $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 @@ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) */ 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)) { diff --git a/lib/Doctrine/DBAL/Platforms/OraclePlatform.php b/lib/Doctrine/DBAL/Platforms/OraclePlatform.php index 537a566bada..711fbccbd53 100644 --- a/lib/Doctrine/DBAL/Platforms/OraclePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/OraclePlatform.php @@ -869,8 +869,14 @@ public function getAlterTableSQL(TableDiff $diff) 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( diff --git a/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php b/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php index 3c9984f95e6..806d59e6c97 100644 --- a/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php @@ -614,8 +614,14 @@ public function getAlterTableSQL(TableDiff $diff) 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( diff --git a/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php b/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php index ec3ef933441..0f097de601b 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php @@ -184,9 +184,11 @@ public function getAlterTableSQL(TableDiff $diff) $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( diff --git a/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php b/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php index dc8775e61ab..5ce17aae36b 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php @@ -315,12 +315,19 @@ protected function _getCreateTableSQL($tableName, array $columns, array $options */ 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 @@ public function getAlterTableSQL(TableDiff $diff) $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 @@ public function getAlterTableSQL(TableDiff $diff) $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'; } diff --git a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php index 6eff6b88d79..009a8f23df4 100644 --- a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -685,7 +685,12 @@ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) } $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 @@ public function getAlterTableSQL(TableDiff $diff) $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 @@ private function getColumnNamesInAlteredTable(TableDiff $diff) $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 @@ private function getForeignKeysInAlteredTable(TableDiff $diff) } foreach ($diff->removedForeignKeys as $constraint) { + if (! $constraint instanceof ForeignKeyConstraint) { + $constraint = new Identifier($constraint); + } + $constraintName = strtolower($constraint->getName()); if (! strlen($constraintName) || ! isset($foreignKeys[$constraintName])) { continue; diff --git a/lib/Doctrine/DBAL/SQLParserUtils.php b/lib/Doctrine/DBAL/SQLParserUtils.php index 7dc63884a51..0ff0456a00e 100644 --- a/lib/Doctrine/DBAL/SQLParserUtils.php +++ b/lib/Doctrine/DBAL/SQLParserUtils.php @@ -24,8 +24,13 @@ */ class SQLParserUtils { + /**#@+ + * + * @deprecated Will be removed as internal implementation details. + */ public const POSITIONAL_TOKEN = '\?'; public const NAMED_TOKEN = '(? $needlePos) { if (! isset($arrayPositions[$needle])) { continue; @@ -156,6 +200,8 @@ public static function expandListParameters($query, $params, $types) $typesOrd = []; $paramsOrd = []; + $paramPos = self::getNamedPlaceholderPositions($query); + foreach ($paramPos as $pos => $paramName) { $paramLen = strlen($paramName) + 1; $value = static::extractParam($paramName, $params, true); diff --git a/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php b/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php index bf278e6e78a..d1db70c4c25 100644 --- a/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php @@ -14,10 +14,12 @@ 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 @@ public function tryMethod() 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; } diff --git a/lib/Doctrine/DBAL/Schema/Comparator.php b/lib/Doctrine/DBAL/Schema/Comparator.php index 1d8a7275d98..585732d7dff 100644 --- a/lib/Doctrine/DBAL/Schema/Comparator.php +++ b/lib/Doctrine/DBAL/Schema/Comparator.php @@ -10,6 +10,7 @@ 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 @@ public function compare(Schema $fromSchema, Schema $toSchema) } 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; diff --git a/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php b/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php index 31850d7f23e..0a0977a4027 100644 --- a/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php +++ b/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php @@ -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 @@ public function __construct(array $localColumnNames, $foreignTableName, array $f $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 @@ public function getForeignTableName() */ 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); } /** diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 9f50517e1d4..f442f54d116 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -9,8 +9,10 @@ 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 @@ protected function _getPortableTableColumnDefinition($tableColumn) $dbType = strtolower($tableColumn['type']); $dbType = strtok($dbType, '(), '); + assert(is_string($dbType)); + $length = $tableColumn['length'] ?? strtok('(), '); $fixed = null; diff --git a/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php b/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php index 62d5fa88e89..c169a6e0708 100644 --- a/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php @@ -5,6 +5,7 @@ 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 @@ protected function _getPortableTableIndexesList($tableIndexRows, $tableName = nu */ 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); } } diff --git a/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php b/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php index 763074e7d27..ce3e403ae77 100644 --- a/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php @@ -6,9 +6,11 @@ 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 @@ protected function _getPortableSequenceDefinition($sequence) */ 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 @@ protected function _getPortableTableColumnDefinition($tableColumn) } 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 @@ protected function _getPortableTableColumnDefinition($tableColumn) 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} */ diff --git a/lib/Doctrine/DBAL/Schema/Table.php b/lib/Doctrine/DBAL/Schema/Table.php index 09ae27f0590..5659e578636 100644 --- a/lib/Doctrine/DBAL/Schema/Table.php +++ b/lib/Doctrine/DBAL/Schema/Table.php @@ -18,9 +18,6 @@ */ class Table extends AbstractAsset { - /** @var string */ - protected $_name = null; - /** @var Column[] */ protected $_columns = []; @@ -98,8 +95,8 @@ protected function _getMaxIdentifierLength() /** * Sets the Primary Key. * - * @param string[] $columnNames - * @param string|bool $indexName + * @param string[] $columnNames + * @param string|false $indexName * * @return self */ diff --git a/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php b/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php index 0e815663786..22bce134061 100644 --- a/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php +++ b/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php @@ -13,6 +13,7 @@ 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 @@ protected function execute(InputInterface $input, OutputInterface $output) } $output->write(sprintf("Processing file '%s'... ", $filePath)); - $sql = file_get_contents($filePath); + $sql = @file_get_contents($filePath); + + if ($sql === false) { + throw new RuntimeException( + sprintf("Unable to read SQL file '%s': %s", $filePath, error_get_last()['message']) + ); + } if ($conn instanceof PDOConnection) { // PDO Drivers diff --git a/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php b/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php index 8361219ad08..8aeb0ea2fba 100644 --- a/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php +++ b/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php @@ -10,7 +10,9 @@ 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 @@ protected function execute(InputInterface $input, OutputInterface $output) 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)) { diff --git a/lib/Doctrine/DBAL/Tools/Dumper.php b/lib/Doctrine/DBAL/Tools/Dumper.php index d5086adee81..6174b7ad865 100644 --- a/lib/Doctrine/DBAL/Tools/Dumper.php +++ b/lib/Doctrine/DBAL/Tools/Dumper.php @@ -9,6 +9,7 @@ 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_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 @@ public static function dump($var, int $maxDepth = 2) : string 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 @@ private static function getClass($object) : string return $class; } - return substr($class, $pos + Proxy::MARKER_LENGTH + 2); + return substr($class, $pos + strlen(Proxy::MARKER) + 2); } } diff --git a/lib/Doctrine/DBAL/Types/BinaryType.php b/lib/Doctrine/DBAL/Types/BinaryType.php index 14362e840ae..2894f8c864d 100644 --- a/lib/Doctrine/DBAL/Types/BinaryType.php +++ b/lib/Doctrine/DBAL/Types/BinaryType.php @@ -4,6 +4,7 @@ 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 @@ public function convertToPHPValue($value, AbstractPlatform $platform) if (is_string($value)) { $fp = fopen('php://temp', 'rb+'); + assert(is_resource($fp)); fwrite($fp, $value); fseek($fp, 0); $value = $fp; diff --git a/lib/Doctrine/DBAL/Types/BlobType.php b/lib/Doctrine/DBAL/Types/BlobType.php index c309f0f0637..c9c8d88ec1c 100644 --- a/lib/Doctrine/DBAL/Types/BlobType.php +++ b/lib/Doctrine/DBAL/Types/BlobType.php @@ -4,6 +4,7 @@ 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 @@ public function convertToPHPValue($value, AbstractPlatform $platform) if (is_string($value)) { $fp = fopen('php://temp', 'rb+'); + assert(is_resource($fp)); fwrite($fp, $value); fseek($fp, 0); $value = $fp; diff --git a/lib/Doctrine/DBAL/Types/Type.php b/lib/Doctrine/DBAL/Types/Type.php index ec1ab681356..025eb14cbda 100644 --- a/lib/Doctrine/DBAL/Types/Type.php +++ b/lib/Doctrine/DBAL/Types/Type.php @@ -5,9 +5,9 @@ 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 @@ public static function getTypesMap() */ 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); } /** diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 4bedf0cd776..22f53630b64 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,5 +1,5 @@ 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, array given\.\z~' + - '~^Parameter #2 \$registeredAliases of static method Doctrine\\DBAL\\Query\\QueryException::nonUniqueAlias\(\) expects array, array given\.\z~' + # legacy variadic-like signature - '~^Method Doctrine\\DBAL\\Driver\\Connection::query\(\) invoked with \d+ parameters?, 0 required\.\z~'