diff --git a/composer.json b/composer.json index 03f684727..ea7c0bec3 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "cweagans/composer-patches": "^1.7", "deepdiver/zipstreamer": "2.0.0", "deepdiver1975/tarstreamer": "^2.1.0", - "doctrine/dbal": "3.3.8", + "doctrine/dbal": "^3.7.0", "egulias/email-validator": "^3.2.5", "fusonic/opengraph": "^2.2", "giggsey/libphonenumber-for-php-lite": "^8.13.12", diff --git a/composer.lock b/composer.lock index fa5a3c129..9eeb02ea1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f1da6486273a61eba7ee49a03181a4e7", + "content-hash": "0f5b8d692567d75d6111be74e10dfed1", "packages": [ { "name": "aws/aws-crt-php", @@ -567,38 +567,40 @@ }, { "name": "doctrine/dbal", - "version": "3.3.8", + "version": "3.7.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "f873a820227bc352d023791775a01f078a30dfe1" + "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/f873a820227bc352d023791775a01f078a30dfe1", - "reference": "f873a820227bc352d023791775a01f078a30dfe1", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/00d03067f07482f025d41ab55e4ba0db5eca2cdf", + "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf", "shasum": "" }, "require": { "composer-runtime-api": "^2", "doctrine/cache": "^1.11|^2.0", "doctrine/deprecations": "^0.5.3|^1", - "doctrine/event-manager": "^1.0", - "php": "^7.3 || ^8.0", + "doctrine/event-manager": "^1|^2", + "php": "^7.4 || ^8.0", "psr/cache": "^1|^2|^3", "psr/log": "^1|^2|^3" }, "require-dev": { - "doctrine/coding-standard": "9.0.0", - "jetbrains/phpstorm-stubs": "2022.1", - "phpstan/phpstan": "1.8.2", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "9.5.21", - "psalm/plugin-phpunit": "0.17.0", - "squizlabs/php_codesniffer": "3.7.1", - "symfony/cache": "^5.2|^6.0", - "symfony/console": "^2.7|^3.0|^4.0|^5.0|^6.0", - "vimeo/psalm": "4.24.0" + "doctrine/coding-standard": "12.0.0", + "fig/log-test": "^1", + "jetbrains/phpstorm-stubs": "2023.1", + "phpstan/phpstan": "1.10.35", + "phpstan/phpstan-strict-rules": "^1.5", + "phpunit/phpunit": "9.6.13", + "psalm/plugin-phpunit": "0.18.4", + "slevomat/coding-standard": "8.13.1", + "squizlabs/php_codesniffer": "3.7.2", + "symfony/cache": "^5.4|^6.0", + "symfony/console": "^4.4|^5.4|^6.0", + "vimeo/psalm": "4.30.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -658,7 +660,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.3.8" + "source": "https://github.com/doctrine/dbal/tree/3.7.0" }, "funding": [ { @@ -674,29 +676,33 @@ "type": "tidelift" } ], - "time": "2022-08-05T15:35:35+00:00" + "time": "2023-09-26T20:56:55+00:00" }, { "name": "doctrine/deprecations", - "version": "v1.0.0", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", "shasum": "" }, "require": { - "php": "^7.1|^8.0" + "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5|^8.5|^9.5", - "psr/log": "^1|^2|^3" + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" @@ -715,9 +721,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" }, - "time": "2022-05-02T15:47:09+00:00" + "time": "2023-09-27T20:04:15+00:00" }, { "name": "doctrine/event-manager", @@ -2984,20 +2990,20 @@ }, { "name": "psr/cache", - "version": "1.0.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { @@ -3017,7 +3023,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for caching libraries", @@ -3027,9 +3033,9 @@ "psr-6" ], "support": { - "source": "https://github.com/php-fig/cache/tree/master" + "source": "https://github.com/php-fig/cache/tree/3.0.0" }, - "time": "2016-08-06T20:24:11+00:00" + "time": "2021-02-03T23:26:27+00:00" }, { "name": "psr/clock", diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php index 28c913756..9dd93b22e 100644 --- a/composer/autoload_classmap.php +++ b/composer/autoload_classmap.php @@ -1055,6 +1055,7 @@ 'Doctrine\\Common\\EventManager' => $vendorDir . '/doctrine/event-manager/src/EventManager.php', 'Doctrine\\Common\\EventSubscriber' => $vendorDir . '/doctrine/event-manager/src/EventSubscriber.php', 'Doctrine\\Common\\Lexer\\AbstractLexer' => $vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php', + 'Doctrine\\DBAL\\ArrayParameterType' => $vendorDir . '/doctrine/dbal/src/ArrayParameterType.php', 'Doctrine\\DBAL\\ArrayParameters\\Exception' => $vendorDir . '/doctrine/dbal/src/ArrayParameters/Exception.php', 'Doctrine\\DBAL\\ArrayParameters\\Exception\\MissingNamedParameter' => $vendorDir . '/doctrine/dbal/src/ArrayParameters/Exception/MissingNamedParameter.php', 'Doctrine\\DBAL\\ArrayParameters\\Exception\\MissingPositionalParameter' => $vendorDir . '/doctrine/dbal/src/ArrayParameters/Exception/MissingPositionalParameter.php', @@ -1085,6 +1086,7 @@ 'Doctrine\\DBAL\\Driver\\AbstractSQLServerDriver' => $vendorDir . '/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php', 'Doctrine\\DBAL\\Driver\\AbstractSQLServerDriver\\Exception\\PortWithoutHost' => $vendorDir . '/doctrine/dbal/src/Driver/AbstractSQLServerDriver/Exception/PortWithoutHost.php', 'Doctrine\\DBAL\\Driver\\AbstractSQLiteDriver' => $vendorDir . '/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php', + 'Doctrine\\DBAL\\Driver\\AbstractSQLiteDriver\\Middleware\\EnableForeignKeys' => $vendorDir . '/doctrine/dbal/src/Driver/AbstractSQLiteDriver/Middleware/EnableForeignKeys.php', 'Doctrine\\DBAL\\Driver\\Connection' => $vendorDir . '/doctrine/dbal/src/Driver/Connection.php', 'Doctrine\\DBAL\\Driver\\Exception' => $vendorDir . '/doctrine/dbal/src/Driver/Exception.php', 'Doctrine\\DBAL\\Driver\\Exception\\UnknownParameterType' => $vendorDir . '/doctrine/dbal/src/Driver/Exception/UnknownParameterType.php', @@ -1131,12 +1133,15 @@ 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\SequenceDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/Exception/SequenceDoesNotExist.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\UnknownParameterIndex' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/Exception/UnknownParameterIndex.php', 'Doctrine\\DBAL\\Driver\\OCI8\\ExecutionMode' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/ExecutionMode.php', + 'Doctrine\\DBAL\\Driver\\OCI8\\Middleware\\InitializeSession' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/Middleware/InitializeSession.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Result' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/Result.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Statement' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/Statement.php', 'Doctrine\\DBAL\\Driver\\PDO\\Connection' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/Connection.php', 'Doctrine\\DBAL\\Driver\\PDO\\Exception' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/Exception.php', 'Doctrine\\DBAL\\Driver\\PDO\\MySQL\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php', 'Doctrine\\DBAL\\Driver\\PDO\\OCI\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/OCI/Driver.php', + 'Doctrine\\DBAL\\Driver\\PDO\\PDOException' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/PDOException.php', + 'Doctrine\\DBAL\\Driver\\PDO\\ParameterTypeMap' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/ParameterTypeMap.php', 'Doctrine\\DBAL\\Driver\\PDO\\PgSQL\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php', 'Doctrine\\DBAL\\Driver\\PDO\\Result' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/Result.php', 'Doctrine\\DBAL\\Driver\\PDO\\SQLSrv\\Connection' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php', @@ -1144,17 +1149,31 @@ 'Doctrine\\DBAL\\Driver\\PDO\\SQLSrv\\Statement' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php', 'Doctrine\\DBAL\\Driver\\PDO\\SQLite\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php', 'Doctrine\\DBAL\\Driver\\PDO\\Statement' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/Statement.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Connection' => $vendorDir . '/doctrine/dbal/src/Driver/PgSQL/Connection.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\ConvertParameters' => $vendorDir . '/doctrine/dbal/src/Driver/PgSQL/ConvertParameters.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/PgSQL/Driver.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Exception' => $vendorDir . '/doctrine/dbal/src/Driver/PgSQL/Exception.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Exception\\UnexpectedValue' => $vendorDir . '/doctrine/dbal/src/Driver/PgSQL/Exception/UnexpectedValue.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Exception\\UnknownParameter' => $vendorDir . '/doctrine/dbal/src/Driver/PgSQL/Exception/UnknownParameter.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Result' => $vendorDir . '/doctrine/dbal/src/Driver/PgSQL/Result.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Statement' => $vendorDir . '/doctrine/dbal/src/Driver/PgSQL/Statement.php', 'Doctrine\\DBAL\\Driver\\Result' => $vendorDir . '/doctrine/dbal/src/Driver/Result.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Connection' => $vendorDir . '/doctrine/dbal/src/Driver/SQLSrv/Connection.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/SQLSrv/Driver.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Exception\\Error' => $vendorDir . '/doctrine/dbal/src/Driver/SQLSrv/Exception/Error.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Result' => $vendorDir . '/doctrine/dbal/src/Driver/SQLSrv/Result.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Statement' => $vendorDir . '/doctrine/dbal/src/Driver/SQLSrv/Statement.php', + 'Doctrine\\DBAL\\Driver\\SQLite3\\Connection' => $vendorDir . '/doctrine/dbal/src/Driver/SQLite3/Connection.php', + 'Doctrine\\DBAL\\Driver\\SQLite3\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/SQLite3/Driver.php', + 'Doctrine\\DBAL\\Driver\\SQLite3\\Exception' => $vendorDir . '/doctrine/dbal/src/Driver/SQLite3/Exception.php', + 'Doctrine\\DBAL\\Driver\\SQLite3\\Result' => $vendorDir . '/doctrine/dbal/src/Driver/SQLite3/Result.php', + 'Doctrine\\DBAL\\Driver\\SQLite3\\Statement' => $vendorDir . '/doctrine/dbal/src/Driver/SQLite3/Statement.php', 'Doctrine\\DBAL\\Driver\\ServerInfoAwareConnection' => $vendorDir . '/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php', 'Doctrine\\DBAL\\Driver\\Statement' => $vendorDir . '/doctrine/dbal/src/Driver/Statement.php', 'Doctrine\\DBAL\\Event\\ConnectionEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/ConnectionEventArgs.php', 'Doctrine\\DBAL\\Event\\Listeners\\OracleSessionInit' => $vendorDir . '/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php', 'Doctrine\\DBAL\\Event\\Listeners\\SQLSessionInit' => $vendorDir . '/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php', + 'Doctrine\\DBAL\\Event\\Listeners\\SQLiteSessionInit' => $vendorDir . '/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php', 'Doctrine\\DBAL\\Event\\SchemaAlterTableAddColumnEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php', 'Doctrine\\DBAL\\Event\\SchemaAlterTableChangeColumnEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php', 'Doctrine\\DBAL\\Event\\SchemaAlterTableEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php', @@ -1178,6 +1197,7 @@ 'Doctrine\\DBAL\\Exception\\DatabaseDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Exception/DatabaseDoesNotExist.php', 'Doctrine\\DBAL\\Exception\\DatabaseObjectExistsException' => $vendorDir . '/doctrine/dbal/src/Exception/DatabaseObjectExistsException.php', 'Doctrine\\DBAL\\Exception\\DatabaseObjectNotFoundException' => $vendorDir . '/doctrine/dbal/src/Exception/DatabaseObjectNotFoundException.php', + 'Doctrine\\DBAL\\Exception\\DatabaseRequired' => $vendorDir . '/doctrine/dbal/src/Exception/DatabaseRequired.php', 'Doctrine\\DBAL\\Exception\\DeadlockException' => $vendorDir . '/doctrine/dbal/src/Exception/DeadlockException.php', 'Doctrine\\DBAL\\Exception\\DriverException' => $vendorDir . '/doctrine/dbal/src/Exception/DriverException.php', 'Doctrine\\DBAL\\Exception\\ForeignKeyConstraintViolationException' => $vendorDir . '/doctrine/dbal/src/Exception/ForeignKeyConstraintViolationException.php', @@ -1185,6 +1205,7 @@ 'Doctrine\\DBAL\\Exception\\InvalidFieldNameException' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidFieldNameException.php', 'Doctrine\\DBAL\\Exception\\InvalidLockMode' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidLockMode.php', 'Doctrine\\DBAL\\Exception\\LockWaitTimeoutException' => $vendorDir . '/doctrine/dbal/src/Exception/LockWaitTimeoutException.php', + 'Doctrine\\DBAL\\Exception\\MalformedDsnException' => $vendorDir . '/doctrine/dbal/src/Exception/MalformedDsnException.php', 'Doctrine\\DBAL\\Exception\\NoKeyValue' => $vendorDir . '/doctrine/dbal/src/Exception/NoKeyValue.php', 'Doctrine\\DBAL\\Exception\\NonUniqueFieldNameException' => $vendorDir . '/doctrine/dbal/src/Exception/NonUniqueFieldNameException.php', 'Doctrine\\DBAL\\Exception\\NotNullConstraintViolationException' => $vendorDir . '/doctrine/dbal/src/Exception/NotNullConstraintViolationException.php', @@ -1211,6 +1232,7 @@ 'Doctrine\\DBAL\\ParameterType' => $vendorDir . '/doctrine/dbal/src/ParameterType.php', 'Doctrine\\DBAL\\Platforms\\AbstractMySQLPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php', 'Doctrine\\DBAL\\Platforms\\AbstractPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/AbstractPlatform.php', + 'Doctrine\\DBAL\\Platforms\\DB2111Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/DB2111Platform.php', 'Doctrine\\DBAL\\Platforms\\DB2Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/DB2Platform.php', 'Doctrine\\DBAL\\Platforms\\DateIntervalUnit' => $vendorDir . '/doctrine/dbal/src/Platforms/DateIntervalUnit.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\DB2Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php', @@ -1230,6 +1252,8 @@ 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLiteKeywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/SQLiteKeywords.php', 'Doctrine\\DBAL\\Platforms\\MariaDBPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/MariaDBPlatform.php', 'Doctrine\\DBAL\\Platforms\\MariaDb1027Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MariaDb1027Platform.php', + 'Doctrine\\DBAL\\Platforms\\MariaDb1043Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MariaDb1043Platform.php', + 'Doctrine\\DBAL\\Platforms\\MariaDb1052Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MariaDb1052Platform.php', 'Doctrine\\DBAL\\Platforms\\MySQL57Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL57Platform.php', 'Doctrine\\DBAL\\Platforms\\MySQL80Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL80Platform.php', 'Doctrine\\DBAL\\Platforms\\MySQLPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQLPlatform.php', @@ -1260,6 +1284,8 @@ 'Doctrine\\DBAL\\Query\\QueryBuilder' => $vendorDir . '/doctrine/dbal/src/Query/QueryBuilder.php', 'Doctrine\\DBAL\\Query\\QueryException' => $vendorDir . '/doctrine/dbal/src/Query/QueryException.php', 'Doctrine\\DBAL\\Result' => $vendorDir . '/doctrine/dbal/src/Result.php', + 'Doctrine\\DBAL\\SQL\\Builder\\CreateSchemaObjectsSQLBuilder' => $vendorDir . '/doctrine/dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php', + 'Doctrine\\DBAL\\SQL\\Builder\\DropSchemaObjectsSQLBuilder' => $vendorDir . '/doctrine/dbal/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php', 'Doctrine\\DBAL\\SQL\\Parser' => $vendorDir . '/doctrine/dbal/src/SQL/Parser.php', 'Doctrine\\DBAL\\SQL\\Parser\\Exception' => $vendorDir . '/doctrine/dbal/src/SQL/Parser/Exception.php', 'Doctrine\\DBAL\\SQL\\Parser\\Exception\\RegularExpressionError' => $vendorDir . '/doctrine/dbal/src/SQL/Parser/Exception/RegularExpressionError.php', @@ -1271,11 +1297,26 @@ 'Doctrine\\DBAL\\Schema\\Comparator' => $vendorDir . '/doctrine/dbal/src/Schema/Comparator.php', 'Doctrine\\DBAL\\Schema\\Constraint' => $vendorDir . '/doctrine/dbal/src/Schema/Constraint.php', 'Doctrine\\DBAL\\Schema\\DB2SchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/DB2SchemaManager.php', + 'Doctrine\\DBAL\\Schema\\DefaultSchemaManagerFactory' => $vendorDir . '/doctrine/dbal/src/Schema/DefaultSchemaManagerFactory.php', + 'Doctrine\\DBAL\\Schema\\Exception\\ColumnAlreadyExists' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php', + 'Doctrine\\DBAL\\Schema\\Exception\\ColumnDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/ColumnDoesNotExist.php', + 'Doctrine\\DBAL\\Schema\\Exception\\ForeignKeyDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/ForeignKeyDoesNotExist.php', + 'Doctrine\\DBAL\\Schema\\Exception\\IndexAlreadyExists' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/IndexAlreadyExists.php', + 'Doctrine\\DBAL\\Schema\\Exception\\IndexDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/IndexDoesNotExist.php', + 'Doctrine\\DBAL\\Schema\\Exception\\IndexNameInvalid' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/IndexNameInvalid.php', 'Doctrine\\DBAL\\Schema\\Exception\\InvalidTableName' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/InvalidTableName.php', + 'Doctrine\\DBAL\\Schema\\Exception\\NamedForeignKeyRequired' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/NamedForeignKeyRequired.php', + 'Doctrine\\DBAL\\Schema\\Exception\\NamespaceAlreadyExists' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php', + 'Doctrine\\DBAL\\Schema\\Exception\\SequenceAlreadyExists' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/SequenceAlreadyExists.php', + 'Doctrine\\DBAL\\Schema\\Exception\\SequenceDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/SequenceDoesNotExist.php', + 'Doctrine\\DBAL\\Schema\\Exception\\TableAlreadyExists' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/TableAlreadyExists.php', + 'Doctrine\\DBAL\\Schema\\Exception\\TableDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/TableDoesNotExist.php', + 'Doctrine\\DBAL\\Schema\\Exception\\UniqueConstraintDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/UniqueConstraintDoesNotExist.php', 'Doctrine\\DBAL\\Schema\\Exception\\UnknownColumnOption' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/UnknownColumnOption.php', 'Doctrine\\DBAL\\Schema\\ForeignKeyConstraint' => $vendorDir . '/doctrine/dbal/src/Schema/ForeignKeyConstraint.php', 'Doctrine\\DBAL\\Schema\\Identifier' => $vendorDir . '/doctrine/dbal/src/Schema/Identifier.php', 'Doctrine\\DBAL\\Schema\\Index' => $vendorDir . '/doctrine/dbal/src/Schema/Index.php', + 'Doctrine\\DBAL\\Schema\\LegacySchemaManagerFactory' => $vendorDir . '/doctrine/dbal/src/Schema/LegacySchemaManagerFactory.php', 'Doctrine\\DBAL\\Schema\\MySQLSchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/MySQLSchemaManager.php', 'Doctrine\\DBAL\\Schema\\OracleSchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/OracleSchemaManager.php', 'Doctrine\\DBAL\\Schema\\PostgreSQLSchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php', @@ -1284,6 +1325,7 @@ 'Doctrine\\DBAL\\Schema\\SchemaConfig' => $vendorDir . '/doctrine/dbal/src/Schema/SchemaConfig.php', 'Doctrine\\DBAL\\Schema\\SchemaDiff' => $vendorDir . '/doctrine/dbal/src/Schema/SchemaDiff.php', 'Doctrine\\DBAL\\Schema\\SchemaException' => $vendorDir . '/doctrine/dbal/src/Schema/SchemaException.php', + 'Doctrine\\DBAL\\Schema\\SchemaManagerFactory' => $vendorDir . '/doctrine/dbal/src/Schema/SchemaManagerFactory.php', 'Doctrine\\DBAL\\Schema\\Sequence' => $vendorDir . '/doctrine/dbal/src/Schema/Sequence.php', 'Doctrine\\DBAL\\Schema\\SqliteSchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/SqliteSchemaManager.php', 'Doctrine\\DBAL\\Schema\\Table' => $vendorDir . '/doctrine/dbal/src/Schema/Table.php', @@ -1296,15 +1338,16 @@ 'Doctrine\\DBAL\\Schema\\Visitor\\Graphviz' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/Graphviz.php', 'Doctrine\\DBAL\\Schema\\Visitor\\NamespaceVisitor' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php', 'Doctrine\\DBAL\\Schema\\Visitor\\RemoveNamespacedAssets' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\SchemaDiffVisitor' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/SchemaDiffVisitor.php', 'Doctrine\\DBAL\\Schema\\Visitor\\Visitor' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/Visitor.php', 'Doctrine\\DBAL\\Statement' => $vendorDir . '/doctrine/dbal/src/Statement.php', + 'Doctrine\\DBAL\\Tools\\Console\\Command\\CommandCompatibility' => $vendorDir . '/doctrine/dbal/src/Tools/Console/Command/CommandCompatibility.php', 'Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand' => $vendorDir . '/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php', 'Doctrine\\DBAL\\Tools\\Console\\Command\\RunSqlCommand' => $vendorDir . '/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionNotFound' => $vendorDir . '/doctrine/dbal/src/Tools/Console/ConnectionNotFound.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionProvider' => $vendorDir . '/doctrine/dbal/src/Tools/Console/ConnectionProvider.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionProvider\\SingleConnectionProvider' => $vendorDir . '/doctrine/dbal/src/Tools/Console/ConnectionProvider/SingleConnectionProvider.php', 'Doctrine\\DBAL\\Tools\\Console\\ConsoleRunner' => $vendorDir . '/doctrine/dbal/src/Tools/Console/ConsoleRunner.php', + 'Doctrine\\DBAL\\Tools\\DsnParser' => $vendorDir . '/doctrine/dbal/src/Tools/DsnParser.php', 'Doctrine\\DBAL\\TransactionIsolationLevel' => $vendorDir . '/doctrine/dbal/src/TransactionIsolationLevel.php', 'Doctrine\\DBAL\\Types\\ArrayType' => $vendorDir . '/doctrine/dbal/src/Types/ArrayType.php', 'Doctrine\\DBAL\\Types\\AsciiStringType' => $vendorDir . '/doctrine/dbal/src/Types/AsciiStringType.php', diff --git a/composer/autoload_static.php b/composer/autoload_static.php index 84ba13284..9048622ad 100644 --- a/composer/autoload_static.php +++ b/composer/autoload_static.php @@ -1719,6 +1719,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\Common\\EventManager' => __DIR__ . '/..' . '/doctrine/event-manager/src/EventManager.php', 'Doctrine\\Common\\EventSubscriber' => __DIR__ . '/..' . '/doctrine/event-manager/src/EventSubscriber.php', 'Doctrine\\Common\\Lexer\\AbstractLexer' => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php', + 'Doctrine\\DBAL\\ArrayParameterType' => __DIR__ . '/..' . '/doctrine/dbal/src/ArrayParameterType.php', 'Doctrine\\DBAL\\ArrayParameters\\Exception' => __DIR__ . '/..' . '/doctrine/dbal/src/ArrayParameters/Exception.php', 'Doctrine\\DBAL\\ArrayParameters\\Exception\\MissingNamedParameter' => __DIR__ . '/..' . '/doctrine/dbal/src/ArrayParameters/Exception/MissingNamedParameter.php', 'Doctrine\\DBAL\\ArrayParameters\\Exception\\MissingPositionalParameter' => __DIR__ . '/..' . '/doctrine/dbal/src/ArrayParameters/Exception/MissingPositionalParameter.php', @@ -1749,6 +1750,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Driver\\AbstractSQLServerDriver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php', 'Doctrine\\DBAL\\Driver\\AbstractSQLServerDriver\\Exception\\PortWithoutHost' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/AbstractSQLServerDriver/Exception/PortWithoutHost.php', 'Doctrine\\DBAL\\Driver\\AbstractSQLiteDriver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php', + 'Doctrine\\DBAL\\Driver\\AbstractSQLiteDriver\\Middleware\\EnableForeignKeys' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/AbstractSQLiteDriver/Middleware/EnableForeignKeys.php', 'Doctrine\\DBAL\\Driver\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Connection.php', 'Doctrine\\DBAL\\Driver\\Exception' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Exception.php', 'Doctrine\\DBAL\\Driver\\Exception\\UnknownParameterType' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Exception/UnknownParameterType.php', @@ -1795,12 +1797,15 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\SequenceDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/Exception/SequenceDoesNotExist.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\UnknownParameterIndex' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/Exception/UnknownParameterIndex.php', 'Doctrine\\DBAL\\Driver\\OCI8\\ExecutionMode' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/ExecutionMode.php', + 'Doctrine\\DBAL\\Driver\\OCI8\\Middleware\\InitializeSession' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/Middleware/InitializeSession.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/Result.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/Statement.php', 'Doctrine\\DBAL\\Driver\\PDO\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/Connection.php', 'Doctrine\\DBAL\\Driver\\PDO\\Exception' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/Exception.php', 'Doctrine\\DBAL\\Driver\\PDO\\MySQL\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php', 'Doctrine\\DBAL\\Driver\\PDO\\OCI\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/OCI/Driver.php', + 'Doctrine\\DBAL\\Driver\\PDO\\PDOException' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/PDOException.php', + 'Doctrine\\DBAL\\Driver\\PDO\\ParameterTypeMap' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/ParameterTypeMap.php', 'Doctrine\\DBAL\\Driver\\PDO\\PgSQL\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php', 'Doctrine\\DBAL\\Driver\\PDO\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/Result.php', 'Doctrine\\DBAL\\Driver\\PDO\\SQLSrv\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php', @@ -1808,17 +1813,31 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Driver\\PDO\\SQLSrv\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php', 'Doctrine\\DBAL\\Driver\\PDO\\SQLite\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php', 'Doctrine\\DBAL\\Driver\\PDO\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/Statement.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PgSQL/Connection.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\ConvertParameters' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PgSQL/ConvertParameters.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PgSQL/Driver.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Exception' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PgSQL/Exception.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Exception\\UnexpectedValue' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PgSQL/Exception/UnexpectedValue.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Exception\\UnknownParameter' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PgSQL/Exception/UnknownParameter.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PgSQL/Result.php', + 'Doctrine\\DBAL\\Driver\\PgSQL\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PgSQL/Statement.php', 'Doctrine\\DBAL\\Driver\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Result.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLSrv/Connection.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLSrv/Driver.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Exception\\Error' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLSrv/Exception/Error.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLSrv/Result.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLSrv/Statement.php', + 'Doctrine\\DBAL\\Driver\\SQLite3\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLite3/Connection.php', + 'Doctrine\\DBAL\\Driver\\SQLite3\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLite3/Driver.php', + 'Doctrine\\DBAL\\Driver\\SQLite3\\Exception' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLite3/Exception.php', + 'Doctrine\\DBAL\\Driver\\SQLite3\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLite3/Result.php', + 'Doctrine\\DBAL\\Driver\\SQLite3\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLite3/Statement.php', 'Doctrine\\DBAL\\Driver\\ServerInfoAwareConnection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php', 'Doctrine\\DBAL\\Driver\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Statement.php', 'Doctrine\\DBAL\\Event\\ConnectionEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/ConnectionEventArgs.php', 'Doctrine\\DBAL\\Event\\Listeners\\OracleSessionInit' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php', 'Doctrine\\DBAL\\Event\\Listeners\\SQLSessionInit' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php', + 'Doctrine\\DBAL\\Event\\Listeners\\SQLiteSessionInit' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php', 'Doctrine\\DBAL\\Event\\SchemaAlterTableAddColumnEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php', 'Doctrine\\DBAL\\Event\\SchemaAlterTableChangeColumnEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php', 'Doctrine\\DBAL\\Event\\SchemaAlterTableEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php', @@ -1842,6 +1861,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Exception\\DatabaseDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/DatabaseDoesNotExist.php', 'Doctrine\\DBAL\\Exception\\DatabaseObjectExistsException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/DatabaseObjectExistsException.php', 'Doctrine\\DBAL\\Exception\\DatabaseObjectNotFoundException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/DatabaseObjectNotFoundException.php', + 'Doctrine\\DBAL\\Exception\\DatabaseRequired' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/DatabaseRequired.php', 'Doctrine\\DBAL\\Exception\\DeadlockException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/DeadlockException.php', 'Doctrine\\DBAL\\Exception\\DriverException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/DriverException.php', 'Doctrine\\DBAL\\Exception\\ForeignKeyConstraintViolationException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ForeignKeyConstraintViolationException.php', @@ -1849,6 +1869,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Exception\\InvalidFieldNameException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidFieldNameException.php', 'Doctrine\\DBAL\\Exception\\InvalidLockMode' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidLockMode.php', 'Doctrine\\DBAL\\Exception\\LockWaitTimeoutException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/LockWaitTimeoutException.php', + 'Doctrine\\DBAL\\Exception\\MalformedDsnException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/MalformedDsnException.php', 'Doctrine\\DBAL\\Exception\\NoKeyValue' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/NoKeyValue.php', 'Doctrine\\DBAL\\Exception\\NonUniqueFieldNameException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/NonUniqueFieldNameException.php', 'Doctrine\\DBAL\\Exception\\NotNullConstraintViolationException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/NotNullConstraintViolationException.php', @@ -1875,6 +1896,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\ParameterType' => __DIR__ . '/..' . '/doctrine/dbal/src/ParameterType.php', 'Doctrine\\DBAL\\Platforms\\AbstractMySQLPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php', 'Doctrine\\DBAL\\Platforms\\AbstractPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/AbstractPlatform.php', + 'Doctrine\\DBAL\\Platforms\\DB2111Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/DB2111Platform.php', 'Doctrine\\DBAL\\Platforms\\DB2Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/DB2Platform.php', 'Doctrine\\DBAL\\Platforms\\DateIntervalUnit' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/DateIntervalUnit.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\DB2Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php', @@ -1894,6 +1916,8 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLiteKeywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/SQLiteKeywords.php', 'Doctrine\\DBAL\\Platforms\\MariaDBPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MariaDBPlatform.php', 'Doctrine\\DBAL\\Platforms\\MariaDb1027Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MariaDb1027Platform.php', + 'Doctrine\\DBAL\\Platforms\\MariaDb1043Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MariaDb1043Platform.php', + 'Doctrine\\DBAL\\Platforms\\MariaDb1052Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MariaDb1052Platform.php', 'Doctrine\\DBAL\\Platforms\\MySQL57Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL57Platform.php', 'Doctrine\\DBAL\\Platforms\\MySQL80Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL80Platform.php', 'Doctrine\\DBAL\\Platforms\\MySQLPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQLPlatform.php', @@ -1924,6 +1948,8 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Query\\QueryBuilder' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/QueryBuilder.php', 'Doctrine\\DBAL\\Query\\QueryException' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/QueryException.php', 'Doctrine\\DBAL\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Result.php', + 'Doctrine\\DBAL\\SQL\\Builder\\CreateSchemaObjectsSQLBuilder' => __DIR__ . '/..' . '/doctrine/dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php', + 'Doctrine\\DBAL\\SQL\\Builder\\DropSchemaObjectsSQLBuilder' => __DIR__ . '/..' . '/doctrine/dbal/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php', 'Doctrine\\DBAL\\SQL\\Parser' => __DIR__ . '/..' . '/doctrine/dbal/src/SQL/Parser.php', 'Doctrine\\DBAL\\SQL\\Parser\\Exception' => __DIR__ . '/..' . '/doctrine/dbal/src/SQL/Parser/Exception.php', 'Doctrine\\DBAL\\SQL\\Parser\\Exception\\RegularExpressionError' => __DIR__ . '/..' . '/doctrine/dbal/src/SQL/Parser/Exception/RegularExpressionError.php', @@ -1935,11 +1961,26 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Schema\\Comparator' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Comparator.php', 'Doctrine\\DBAL\\Schema\\Constraint' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Constraint.php', 'Doctrine\\DBAL\\Schema\\DB2SchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/DB2SchemaManager.php', + 'Doctrine\\DBAL\\Schema\\DefaultSchemaManagerFactory' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/DefaultSchemaManagerFactory.php', + 'Doctrine\\DBAL\\Schema\\Exception\\ColumnAlreadyExists' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php', + 'Doctrine\\DBAL\\Schema\\Exception\\ColumnDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/ColumnDoesNotExist.php', + 'Doctrine\\DBAL\\Schema\\Exception\\ForeignKeyDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/ForeignKeyDoesNotExist.php', + 'Doctrine\\DBAL\\Schema\\Exception\\IndexAlreadyExists' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/IndexAlreadyExists.php', + 'Doctrine\\DBAL\\Schema\\Exception\\IndexDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/IndexDoesNotExist.php', + 'Doctrine\\DBAL\\Schema\\Exception\\IndexNameInvalid' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/IndexNameInvalid.php', 'Doctrine\\DBAL\\Schema\\Exception\\InvalidTableName' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/InvalidTableName.php', + 'Doctrine\\DBAL\\Schema\\Exception\\NamedForeignKeyRequired' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/NamedForeignKeyRequired.php', + 'Doctrine\\DBAL\\Schema\\Exception\\NamespaceAlreadyExists' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php', + 'Doctrine\\DBAL\\Schema\\Exception\\SequenceAlreadyExists' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/SequenceAlreadyExists.php', + 'Doctrine\\DBAL\\Schema\\Exception\\SequenceDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/SequenceDoesNotExist.php', + 'Doctrine\\DBAL\\Schema\\Exception\\TableAlreadyExists' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/TableAlreadyExists.php', + 'Doctrine\\DBAL\\Schema\\Exception\\TableDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/TableDoesNotExist.php', + 'Doctrine\\DBAL\\Schema\\Exception\\UniqueConstraintDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/UniqueConstraintDoesNotExist.php', 'Doctrine\\DBAL\\Schema\\Exception\\UnknownColumnOption' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/UnknownColumnOption.php', 'Doctrine\\DBAL\\Schema\\ForeignKeyConstraint' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/ForeignKeyConstraint.php', 'Doctrine\\DBAL\\Schema\\Identifier' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Identifier.php', 'Doctrine\\DBAL\\Schema\\Index' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Index.php', + 'Doctrine\\DBAL\\Schema\\LegacySchemaManagerFactory' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/LegacySchemaManagerFactory.php', 'Doctrine\\DBAL\\Schema\\MySQLSchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/MySQLSchemaManager.php', 'Doctrine\\DBAL\\Schema\\OracleSchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/OracleSchemaManager.php', 'Doctrine\\DBAL\\Schema\\PostgreSQLSchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php', @@ -1948,6 +1989,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Schema\\SchemaConfig' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SchemaConfig.php', 'Doctrine\\DBAL\\Schema\\SchemaDiff' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SchemaDiff.php', 'Doctrine\\DBAL\\Schema\\SchemaException' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SchemaException.php', + 'Doctrine\\DBAL\\Schema\\SchemaManagerFactory' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SchemaManagerFactory.php', 'Doctrine\\DBAL\\Schema\\Sequence' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Sequence.php', 'Doctrine\\DBAL\\Schema\\SqliteSchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SqliteSchemaManager.php', 'Doctrine\\DBAL\\Schema\\Table' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Table.php', @@ -1960,15 +2002,16 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Schema\\Visitor\\Graphviz' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/Graphviz.php', 'Doctrine\\DBAL\\Schema\\Visitor\\NamespaceVisitor' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php', 'Doctrine\\DBAL\\Schema\\Visitor\\RemoveNamespacedAssets' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\SchemaDiffVisitor' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/SchemaDiffVisitor.php', 'Doctrine\\DBAL\\Schema\\Visitor\\Visitor' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/Visitor.php', 'Doctrine\\DBAL\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Statement.php', + 'Doctrine\\DBAL\\Tools\\Console\\Command\\CommandCompatibility' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/Command/CommandCompatibility.php', 'Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php', 'Doctrine\\DBAL\\Tools\\Console\\Command\\RunSqlCommand' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionNotFound' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/ConnectionNotFound.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/ConnectionProvider.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionProvider\\SingleConnectionProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/ConnectionProvider/SingleConnectionProvider.php', 'Doctrine\\DBAL\\Tools\\Console\\ConsoleRunner' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/ConsoleRunner.php', + 'Doctrine\\DBAL\\Tools\\DsnParser' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/DsnParser.php', 'Doctrine\\DBAL\\TransactionIsolationLevel' => __DIR__ . '/..' . '/doctrine/dbal/src/TransactionIsolationLevel.php', 'Doctrine\\DBAL\\Types\\ArrayType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/ArrayType.php', 'Doctrine\\DBAL\\Types\\AsciiStringType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/AsciiStringType.php', diff --git a/composer/installed.json b/composer/installed.json index b12c53aa9..9ae004e49 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -588,44 +588,46 @@ }, { "name": "doctrine/dbal", - "version": "3.3.8", - "version_normalized": "3.3.8.0", + "version": "3.7.0", + "version_normalized": "3.7.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "f873a820227bc352d023791775a01f078a30dfe1" + "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/f873a820227bc352d023791775a01f078a30dfe1", - "reference": "f873a820227bc352d023791775a01f078a30dfe1", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/00d03067f07482f025d41ab55e4ba0db5eca2cdf", + "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf", "shasum": "" }, "require": { "composer-runtime-api": "^2", "doctrine/cache": "^1.11|^2.0", "doctrine/deprecations": "^0.5.3|^1", - "doctrine/event-manager": "^1.0", - "php": "^7.3 || ^8.0", + "doctrine/event-manager": "^1|^2", + "php": "^7.4 || ^8.0", "psr/cache": "^1|^2|^3", "psr/log": "^1|^2|^3" }, "require-dev": { - "doctrine/coding-standard": "9.0.0", - "jetbrains/phpstorm-stubs": "2022.1", - "phpstan/phpstan": "1.8.2", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "9.5.21", - "psalm/plugin-phpunit": "0.17.0", - "squizlabs/php_codesniffer": "3.7.1", - "symfony/cache": "^5.2|^6.0", - "symfony/console": "^2.7|^3.0|^4.0|^5.0|^6.0", - "vimeo/psalm": "4.24.0" + "doctrine/coding-standard": "12.0.0", + "fig/log-test": "^1", + "jetbrains/phpstorm-stubs": "2023.1", + "phpstan/phpstan": "1.10.35", + "phpstan/phpstan-strict-rules": "^1.5", + "phpunit/phpunit": "9.6.13", + "psalm/plugin-phpunit": "0.18.4", + "slevomat/coding-standard": "8.13.1", + "squizlabs/php_codesniffer": "3.7.2", + "symfony/cache": "^5.4|^6.0", + "symfony/console": "^4.4|^5.4|^6.0", + "vimeo/psalm": "4.30.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, - "time": "2022-08-05T15:35:35+00:00", + "time": "2023-09-26T20:56:55+00:00", "bin": [ "bin/doctrine-dbal" ], @@ -682,7 +684,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.3.8" + "source": "https://github.com/doctrine/dbal/tree/3.7.0" }, "funding": [ { @@ -702,31 +704,35 @@ }, { "name": "doctrine/deprecations", - "version": "v1.0.0", - "version_normalized": "1.0.0.0", + "version": "1.1.2", + "version_normalized": "1.1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", "shasum": "" }, "require": { - "php": "^7.1|^8.0" + "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5|^8.5|^9.5", - "psr/log": "^1|^2|^3" + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, - "time": "2022-05-02T15:47:09+00:00", + "time": "2023-09-27T20:04:15+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -742,7 +748,7 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" }, "install-path": "../doctrine/deprecations" }, @@ -3110,23 +3116,23 @@ }, { "name": "psr/cache", - "version": "1.0.1", - "version_normalized": "1.0.1.0", + "version": "3.0.0", + "version_normalized": "3.0.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, - "time": "2016-08-06T20:24:11+00:00", + "time": "2021-02-03T23:26:27+00:00", "type": "library", "extra": { "branch-alias": { @@ -3146,7 +3152,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for caching libraries", @@ -3156,7 +3162,7 @@ "psr-6" ], "support": { - "source": "https://github.com/php-fig/cache/tree/master" + "source": "https://github.com/php-fig/cache/tree/3.0.0" }, "install-path": "../psr/cache" }, diff --git a/composer/installed.php b/composer/installed.php index 1e57d4b54..a360901a7 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'nextcloud/3rdparty', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'ed6ea207de9e7649d5f7321497a27f5612d6e100', + 'reference' => '6e16075d122c7582527e2be7e365775f0b8e5f0c', 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), @@ -92,18 +92,18 @@ 'dev_requirement' => false, ), 'doctrine/dbal' => array( - 'pretty_version' => '3.3.8', - 'version' => '3.3.8.0', - 'reference' => 'f873a820227bc352d023791775a01f078a30dfe1', + 'pretty_version' => '3.7.0', + 'version' => '3.7.0.0', + 'reference' => '00d03067f07482f025d41ab55e4ba0db5eca2cdf', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/dbal', 'aliases' => array(), 'dev_requirement' => false, ), 'doctrine/deprecations' => array( - 'pretty_version' => 'v1.0.0', - 'version' => '1.0.0.0', - 'reference' => '0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de', + 'pretty_version' => '1.1.2', + 'version' => '1.1.2.0', + 'reference' => '4f2d4f2836e7ec4e7a8625e75c6aa916004db931', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/deprecations', 'aliases' => array(), @@ -310,7 +310,7 @@ 'nextcloud/3rdparty' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'ed6ea207de9e7649d5f7321497a27f5612d6e100', + 'reference' => '6e16075d122c7582527e2be7e365775f0b8e5f0c', 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), @@ -428,9 +428,9 @@ 'dev_requirement' => false, ), 'psr/cache' => array( - 'pretty_version' => '1.0.1', - 'version' => '1.0.1.0', - 'reference' => 'd11b50ad223250cf17b86e38383413f5a6764bf8', + 'pretty_version' => '3.0.0', + 'version' => '3.0.0.0', + 'reference' => 'aa5030cfa5405eccfdcb1083ce040c2cb8d253bf', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/cache', 'aliases' => array(), diff --git a/doctrine/dbal/src/ArrayParameterType.php b/doctrine/dbal/src/ArrayParameterType.php new file mode 100644 index 000000000..65e1a29c2 --- /dev/null +++ b/doctrine/dbal/src/ArrayParameterType.php @@ -0,0 +1,42 @@ +> */ - private $data; + private array $data; - /** @var int */ - private $columnCount = 0; + private int $columnCount = 0; + private int $num = 0; - /** @var int */ - private $num = 0; - - /** - * @param list> $data - */ + /** @param list> $data */ public function __construct(array $data) { $this->data = $data; @@ -37,7 +30,7 @@ public function __construct(array $data) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchNumeric() { @@ -51,7 +44,7 @@ public function fetchNumeric() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchAssociative() { @@ -59,7 +52,7 @@ public function fetchAssociative() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchOne() { @@ -73,7 +66,7 @@ public function fetchOne() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchAllNumeric(): array { @@ -81,7 +74,7 @@ public function fetchAllNumeric(): array } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchAllAssociative(): array { @@ -89,7 +82,7 @@ public function fetchAllAssociative(): array } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchFirstColumn(): array { @@ -111,9 +104,7 @@ public function free(): void $this->data = []; } - /** - * @return array|false - */ + /** @return array|false */ private function fetch() { if (! isset($this->data[$this->num])) { diff --git a/doctrine/dbal/src/Cache/CacheException.php b/doctrine/dbal/src/Cache/CacheException.php index 3db115bdf..18e95d6be 100644 --- a/doctrine/dbal/src/Cache/CacheException.php +++ b/doctrine/dbal/src/Cache/CacheException.php @@ -4,22 +4,16 @@ use Doctrine\DBAL\Exception; -/** - * @psalm-immutable - */ +/** @psalm-immutable */ class CacheException extends Exception { - /** - * @return CacheException - */ + /** @return CacheException */ public static function noCacheKey() { return new self('No cache key was set.'); } - /** - * @return CacheException - */ + /** @return CacheException */ public static function noResultDriverConfigured() { return new self('Trying to cache a query but no result driver is configured.'); diff --git a/doctrine/dbal/src/Cache/QueryCacheProfile.php b/doctrine/dbal/src/Cache/QueryCacheProfile.php index 7115cc720..1601a9828 100644 --- a/doctrine/dbal/src/Cache/QueryCacheProfile.php +++ b/doctrine/dbal/src/Cache/QueryCacheProfile.php @@ -23,8 +23,7 @@ */ class QueryCacheProfile { - /** @var CacheItemPoolInterface|null */ - private $resultCache; + private ?CacheItemPoolInterface $resultCache = null; /** @var int */ private $lifetime; @@ -50,7 +49,7 @@ public function __construct($lifetime = 0, $cacheKey = null, ?object $resultCach 'Passing an instance of %s to %s as $resultCache is deprecated. Pass an instance of %s instead.', Cache::class, __METHOD__, - CacheItemPoolInterface::class + CacheItemPoolInterface::class, ); $this->resultCache = CacheAdapter::wrap($resultCache); @@ -59,7 +58,7 @@ public function __construct($lifetime = 0, $cacheKey = null, ?object $resultCach '$resultCache: Expected either null or an instance of %s or %s, got %s.', CacheItemPoolInterface::class, Cache::class, - get_class($resultCache) + get_class($resultCache), )); } } @@ -80,15 +79,13 @@ public function getResultCacheDriver() 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call getResultCache() instead.', - __METHOD__ + __METHOD__, ); return $this->resultCache !== null ? DoctrineProvider::wrap($this->resultCache) : null; } - /** - * @return int - */ + /** @return int */ public function getLifetime() { return $this->lifetime; @@ -116,7 +113,7 @@ public function getCacheKey() * @param array|array $types * @param array $connectionParams * - * @return string[] + * @return array{string, string} */ public function generateCacheKeys($sql, $params, $types, array $connectionParams = []) { @@ -130,11 +127,7 @@ public function generateCacheKeys($sql, $params, $types, array $connectionParams '&connectionParams=' . hash('sha256', serialize($connectionParams)); // should the key be automatically generated using the inputs or is the cache key set? - if ($this->cacheKey === null) { - $cacheKey = sha1($realCacheKey); - } else { - $cacheKey = $this->cacheKey; - } + $cacheKey = $this->cacheKey ?? sha1($realCacheKey); return [$cacheKey, $realCacheKey]; } @@ -155,7 +148,7 @@ public function setResultCacheDriver(Cache $cache) 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call setResultCache() instead.', - __METHOD__ + __METHOD__, ); return new QueryCacheProfile($this->lifetime, $this->cacheKey, CacheAdapter::wrap($cache)); diff --git a/doctrine/dbal/src/Configuration.php b/doctrine/dbal/src/Configuration.php index 2d27d733b..5cdae8135 100644 --- a/doctrine/dbal/src/Configuration.php +++ b/doctrine/dbal/src/Configuration.php @@ -7,16 +7,19 @@ use Doctrine\Common\Cache\Psr6\DoctrineProvider; use Doctrine\DBAL\Driver\Middleware; use Doctrine\DBAL\Logging\SQLLogger; +use Doctrine\DBAL\Schema\SchemaManagerFactory; use Doctrine\Deprecations\Deprecation; use Psr\Cache\CacheItemPoolInterface; +use function func_num_args; + /** * Configuration container for the Doctrine DBAL. */ class Configuration { /** @var Middleware[] */ - private $middlewares = []; + private array $middlewares = []; /** * The SQL logger in use. If null, SQL logging is disabled. @@ -27,10 +30,8 @@ class Configuration /** * The cache driver implementation that is used for query result caching. - * - * @var CacheItemPoolInterface|null */ - private $resultCache; + private ?CacheItemPoolInterface $resultCache = null; /** * The cache driver implementation that is used for query result caching. @@ -55,19 +56,54 @@ class Configuration */ protected $autoCommit = true; + /** + * Whether type comments should be disabled to provide the same DB schema than + * will be obtained with DBAL 4.x. This is useful when relying only on the + * platform-aware schema comparison (which does not need those type comments) + * rather than the deprecated legacy tooling. + */ + private bool $disableTypeComments = false; + + private ?SchemaManagerFactory $schemaManagerFactory = null; + + public function __construct() + { + $this->schemaAssetsFilter = static function (): bool { + return true; + }; + } + /** * Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled. + * + * @deprecated Use {@see setMiddlewares()} and {@see \Doctrine\DBAL\Logging\Middleware} instead. */ public function setSQLLogger(?SQLLogger $logger = null): void { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4967', + '%s is deprecated, use setMiddlewares() and Logging\\Middleware instead.', + __METHOD__, + ); + $this->sqlLogger = $logger; } /** * Gets the SQL logger that is used. + * + * @deprecated */ public function getSQLLogger(): ?SQLLogger { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4967', + '%s is deprecated.', + __METHOD__, + ); + return $this->sqlLogger; } @@ -90,7 +126,7 @@ public function getResultCacheImpl(): ?Cache 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call getResultCache() instead.', - __METHOD__ + __METHOD__, ); return $this->resultCacheImpl; @@ -116,7 +152,7 @@ public function setResultCacheImpl(Cache $cacheImpl): void 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call setResultCache() instead.', - __METHOD__ + __METHOD__, ); $this->resultCacheImpl = $cacheImpl; @@ -128,6 +164,22 @@ public function setResultCacheImpl(Cache $cacheImpl): void */ public function setSchemaAssetsFilter(?callable $callable = null): void { + if (func_num_args() < 1) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5483', + 'Not passing an argument to %s is deprecated.', + __METHOD__, + ); + } elseif ($callable === null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5483', + 'Using NULL as a schema asset filter is deprecated.' + . ' Use a callable that always returns true instead.', + ); + } + $this->schemaAssetsFilter = $callable; } @@ -179,11 +231,35 @@ public function setMiddlewares(array $middlewares): self return $this; } - /** - * @return Middleware[] - */ + /** @return Middleware[] */ public function getMiddlewares(): array { return $this->middlewares; } + + public function getSchemaManagerFactory(): ?SchemaManagerFactory + { + return $this->schemaManagerFactory; + } + + /** @return $this */ + public function setSchemaManagerFactory(SchemaManagerFactory $schemaManagerFactory): self + { + $this->schemaManagerFactory = $schemaManagerFactory; + + return $this; + } + + public function getDisableTypeComments(): bool + { + return $this->disableTypeComments; + } + + /** @return $this */ + public function setDisableTypeComments(bool $disableTypeComments): self + { + $this->disableTypeComments = $disableTypeComments; + + return $this; + } } diff --git a/doctrine/dbal/src/Connection.php b/doctrine/dbal/src/Connection.php index 9e4b1d23b..d84dc3158 100644 --- a/doctrine/dbal/src/Connection.php +++ b/doctrine/dbal/src/Connection.php @@ -21,13 +21,18 @@ use Doctrine\DBAL\Query\Expression\ExpressionBuilder; use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; +use Doctrine\DBAL\Schema\LegacySchemaManagerFactory; +use Doctrine\DBAL\Schema\SchemaManagerFactory; use Doctrine\DBAL\SQL\Parser; use Doctrine\DBAL\Types\Type; use Doctrine\Deprecations\Deprecation; use LogicException; +use SensitiveParameter; use Throwable; use Traversable; +use function array_key_exists; use function assert; use function count; use function get_class; @@ -49,35 +54,47 @@ class Connection { /** * Represents an array of ints to be expanded by Doctrine SQL parsing. + * + * @deprecated Use {@see ArrayParameterType::INTEGER} instead. */ - public const PARAM_INT_ARRAY = ParameterType::INTEGER + self::ARRAY_PARAM_OFFSET; + public const PARAM_INT_ARRAY = ArrayParameterType::INTEGER; /** * Represents an array of strings to be expanded by Doctrine SQL parsing. + * + * @deprecated Use {@see ArrayParameterType::STRING} instead. */ - public const PARAM_STR_ARRAY = ParameterType::STRING + self::ARRAY_PARAM_OFFSET; + public const PARAM_STR_ARRAY = ArrayParameterType::STRING; /** * Represents an array of ascii strings to be expanded by Doctrine SQL parsing. + * + * @deprecated Use {@see ArrayParameterType::ASCII} instead. */ - public const PARAM_ASCII_STR_ARRAY = ParameterType::ASCII + self::ARRAY_PARAM_OFFSET; + public const PARAM_ASCII_STR_ARRAY = ArrayParameterType::ASCII; /** * Offset by which PARAM_* constants are detected as arrays of the param type. + * + * @internal Should be used only within the wrapper layer. */ public const ARRAY_PARAM_OFFSET = 100; /** * The wrapped driver connection. * - * @var \Doctrine\DBAL\Driver\Connection|null + * @var DriverConnection|null */ protected $_conn; /** @var Configuration */ protected $_config; - /** @var EventManager */ + /** + * @deprecated + * + * @var EventManager + */ protected $_eventManager; /** @@ -89,31 +106,25 @@ class Connection /** * The current auto-commit mode of this connection. - * - * @var bool */ - private $autoCommit = true; + private bool $autoCommit = true; /** * The transaction nesting level. - * - * @var int */ - private $transactionNestingLevel = 0; + private int $transactionNestingLevel = 0; /** * The currently active transaction isolation level or NULL before it has been determined. * - * @var int|null + * @var TransactionIsolationLevel::*|null */ private $transactionIsolationLevel; /** * If nested transactions should use savepoints. - * - * @var bool */ - private $nestTransactionsWithSavepoints = false; + private bool $nestTransactionsWithSavepoints = false; /** * The parameters used during creation of the Connection instance. @@ -121,20 +132,15 @@ class Connection * @var array * @psalm-var Params */ - private $params; + private array $params; /** * The database platform object used by the connection or NULL before it's initialized. - * - * @var AbstractPlatform|null */ - private $platform; - - /** @var ExceptionConverter|null */ - private $exceptionConverter; + private ?AbstractPlatform $platform = null; - /** @var Parser|null */ - private $parser; + private ?ExceptionConverter $exceptionConverter = null; + private ?Parser $parser = null; /** * The schema manager. @@ -154,10 +160,10 @@ class Connection /** * Flag that indicates whether the current transaction is marked for rollback only. - * - * @var bool */ - private $isRollbackOnly = false; + private bool $isRollbackOnly = false; + + private SchemaManagerFactory $schemaManagerFactory; /** * Initializes a new instance of the Connection class. @@ -169,11 +175,11 @@ class Connection * @param Configuration|null $config The configuration, optional. * @param EventManager|null $eventManager The event manager, optional. * @psalm-param Params $params - * @phpstan-param array $params * * @throws Exception */ public function __construct( + #[SensitiveParameter] array $params, Driver $driver, ?Configuration $config = null, @@ -183,13 +189,8 @@ public function __construct( $this->params = $params; // Create default config and event manager if none given - if ($config === null) { - $config = new Configuration(); - } - - if ($eventManager === null) { - $eventManager = new EventManager(); - } + $config ??= new Configuration(); + $eventManager ??= new EventManager(); $this->_config = $config; $this->_eventManager = $eventManager; @@ -199,13 +200,36 @@ public function __construct( throw Exception::invalidPlatformType($params['platform']); } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5699', + 'The "platform" connection parameter is deprecated.' + . ' Use a driver middleware that would instantiate the platform instead.', + ); + $this->platform = $params['platform']; $this->platform->setEventManager($this->_eventManager); + $this->platform->setDisableTypeComments($config->getDisableTypeComments()); } $this->_expr = $this->createExpressionBuilder(); $this->autoCommit = $config->getAutoCommit(); + + $schemaManagerFactory = $config->getSchemaManagerFactory(); + if ($schemaManagerFactory === null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5812', + 'Not configuring a schema manager factory is deprecated.' + . ' Use %s which is going to be the default in DBAL 4.', + DefaultSchemaManagerFactory::class, + ); + + $schemaManagerFactory = new LegacySchemaManagerFactory(); + } + + $this->schemaManagerFactory = $schemaManagerFactory; } /** @@ -264,10 +288,19 @@ public function getConfiguration() /** * Gets the EventManager used by the Connection. * + * @deprecated + * * @return EventManager */ public function getEventManager() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + '%s is deprecated.', + __METHOD__, + ); + return $this->_eventManager; } @@ -283,6 +316,7 @@ public function getDatabasePlatform() if ($this->platform === null) { $this->platform = $this->detectDatabasePlatform(); $this->platform->setEventManager($this->_eventManager); + $this->platform->setDisableTypeComments($this->_config->getDisableTypeComments()); } return $this->platform; @@ -309,7 +343,7 @@ public function getExpressionBuilder() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4515', 'Connection::getExpressionBuilder() is deprecated,' - . ' use Connection::createExpressionBuilder() instead.' + . ' use Connection::createExpressionBuilder() instead.', ); return $this->_expr; @@ -324,13 +358,15 @@ public function getExpressionBuilder() * the connection is already open. * * @throws Exception + * + * @psalm-assert !null $this->_conn */ public function connect() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4966', - 'Public access to Connection::connect() is deprecated.' + 'Public access to Connection::connect() is deprecated.', ); if ($this->_conn !== null) { @@ -348,6 +384,13 @@ public function connect() } if ($this->_eventManager->hasListeners(Events::postConnect)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated. Implement a middleware instead.', + Events::postConnect, + ); + $eventArgs = new Event\ConnectionEventArgs($this); $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); } @@ -399,6 +442,10 @@ private function getDatabasePlatformVersion() return $this->params['serverVersion']; } + if (isset($this->params['primary']) && isset($this->params['primary']['serverVersion'])) { + return $this->params['primary']['serverVersion']; + } + // If not connected, we need to connect now to determine the platform version. if ($this->_conn === null) { try { @@ -408,6 +455,15 @@ private function getDatabasePlatformVersion() throw $originalException; } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5707', + 'Relying on a fallback connection used to determine the database platform while connecting' + . ' to a non-existing database is deprecated. Either use an existing database name in' + . ' connection parameters or omit the database name if the platform' + . ' and the server configuration allow that.', + ); + // The database to connect to might not yet exist. // Retry detection without database name connection parameter. $params = $this->params; @@ -458,9 +514,9 @@ private function getServerVersion() Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4750', + 'https://github.com/doctrine/dbal/pull/4750', 'Not implementing the ServerInfoAwareConnection interface in %s is deprecated', - get_class($connection) + get_class($connection), ); // Unable to detect platform version. @@ -641,7 +697,7 @@ public function delete($table, array $criteria, array $types = []) return $this->executeStatement( 'DELETE FROM ' . $table . ' WHERE ' . implode(' AND ', $conditions), $values, - is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types + is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types, ); } @@ -659,7 +715,7 @@ public function close() /** * Sets the transaction isolation level. * - * @param int $level The level to set. + * @param TransactionIsolationLevel::* $level The level to set. * * @return int|string * @@ -675,17 +731,13 @@ public function setTransactionIsolation($level) /** * Gets the currently active transaction isolation level. * - * @return int The current transaction isolation level. + * @return TransactionIsolationLevel::* The current transaction isolation level. * * @throws Exception */ public function getTransactionIsolation() { - if ($this->transactionIsolationLevel === null) { - $this->transactionIsolationLevel = $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel(); - } - - return $this->transactionIsolationLevel; + return $this->transactionIsolationLevel ??= $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel(); } /** @@ -757,7 +809,7 @@ public function insert($table, array $data, array $types = []) 'INSERT INTO ' . $table . ' (' . implode(', ', $columns) . ')' . ' VALUES (' . implode(', ', $set) . ')', $values, - is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types + is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types, ); } @@ -1005,7 +1057,7 @@ public function prepare(string $sql): Statement } /** - * Executes an, optionally parametrized, SQL query. + * Executes an, optionally parameterized, SQL query. * * If the query is parametrized, a prepared statement is used. * If an SQLLogger is configured, the execution is logged. @@ -1040,12 +1092,10 @@ public function executeQuery( } $stmt = $connection->prepare($sql); - if (count($types) > 0) { - $this->_bindTypedValues($stmt, $params, $types); - $result = $stmt->execute(); - } else { - $result = $stmt->execute($params); - } + + $this->bindParameters($stmt, $params, $types); + + $result = $stmt->execute(); } else { $result = $connection->query($sql); } @@ -1079,7 +1129,7 @@ public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp) } $connectionParams = $this->params; - unset($connectionParams['platform']); + unset($connectionParams['platform'], $connectionParams['password'], $connectionParams['url']); [$cacheKey, $realKey] = $qcp->generateCacheKeys($sql, $params, $types, $connectionParams); @@ -1147,15 +1197,10 @@ public function executeStatement($sql, array $params = [], array $types = []) $stmt = $connection->prepare($sql); - if (count($types) > 0) { - $this->_bindTypedValues($stmt, $params, $types); + $this->bindParameters($stmt, $params, $types); - $result = $stmt->execute(); - } else { - $result = $stmt->execute($params); - } - - return $result->rowCount(); + return $stmt->execute() + ->rowCount(); } return $connection->exec($sql); @@ -1198,7 +1243,7 @@ public function lastInsertId($name = null) Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.' + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); } @@ -1217,11 +1262,13 @@ public function lastInsertId($name = null) * If an exception occurs during execution of the function or transaction commit, * the transaction is rolled back and the exception re-thrown. * - * @param Closure $func The function to execute transactionally. + * @param Closure(self):T $func The function to execute transactionally. * - * @return mixed The value returned by $func + * @return T The value returned by $func * * @throws Throwable + * + * @template T */ public function transactional(Closure $func) { @@ -1249,6 +1296,18 @@ public function transactional(Closure $func) */ public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) { + if (! $nestTransactionsWithSavepoints) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5383', + <<<'DEPRECATION' + Nesting transactions without enabling savepoints is deprecated. + Call %s::setNestTransactionsWithSavepoints(true) to enable savepoints. + DEPRECATION, + self::class, + ); + } + if ($this->transactionNestingLevel > 0) { throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction(); } @@ -1312,9 +1371,30 @@ public function beginTransaction() if ($logger !== null) { $logger->stopQuery(); } + } else { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5383', + <<<'DEPRECATION' + Nesting transactions without enabling savepoints is deprecated. + Call %s::setNestTransactionsWithSavepoints(true) to enable savepoints. + DEPRECATION, + self::class, + ); } - $this->getEventManager()->dispatchEvent(Events::onTransactionBegin, new TransactionBeginEventArgs($this)); + $eventManager = $this->getEventManager(); + + if ($eventManager->hasListeners(Events::onTransactionBegin)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated.', + Events::onTransactionBegin, + ); + + $eventManager->dispatchEvent(Events::onTransactionBegin, new TransactionBeginEventArgs($this)); + } return true; } @@ -1338,32 +1418,26 @@ public function commit() $connection = $this->getWrappedConnection(); - $logger = $this->_config->getSQLLogger(); - if ($this->transactionNestingLevel === 1) { - if ($logger !== null) { - $logger->startQuery('"COMMIT"'); - } - - $result = $connection->commit(); - - if ($logger !== null) { - $logger->stopQuery(); - } + $result = $this->doCommit($connection); } elseif ($this->nestTransactionsWithSavepoints) { - if ($logger !== null) { - $logger->startQuery('"RELEASE SAVEPOINT"'); - } - $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); - if ($logger !== null) { - $logger->stopQuery(); - } } --$this->transactionNestingLevel; - $this->getEventManager()->dispatchEvent(Events::onTransactionCommit, new TransactionCommitEventArgs($this)); + $eventManager = $this->getEventManager(); + + if ($eventManager->hasListeners(Events::onTransactionCommit)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated.', + Events::onTransactionCommit, + ); + + $eventManager->dispatchEvent(Events::onTransactionCommit, new TransactionCommitEventArgs($this)); + } if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) { return $result; @@ -1374,6 +1448,28 @@ public function commit() return $result; } + /** + * @return bool + * + * @throws DriverException + */ + private function doCommit(DriverConnection $connection) + { + $logger = $this->_config->getSQLLogger(); + + if ($logger !== null) { + $logger->startQuery('"COMMIT"'); + } + + $result = $connection->commit(); + + if ($logger !== null) { + $logger->stopQuery(); + } + + return $result; + } + /** * Commits all current nesting transactions. * @@ -1441,7 +1537,18 @@ public function rollBack() --$this->transactionNestingLevel; } - $this->getEventManager()->dispatchEvent(Events::onTransactionRollBack, new TransactionRollBackEventArgs($this)); + $eventManager = $this->getEventManager(); + + if ($eventManager->hasListeners(Events::onTransactionRollBack)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated.', + Events::onTransactionRollBack, + ); + + $eventManager->dispatchEvent(Events::onTransactionRollBack, new TransactionRollBackEventArgs($this)); + } return true; } @@ -1477,6 +1584,8 @@ public function createSavepoint($savepoint) */ public function releaseSavepoint($savepoint) { + $logger = $this->_config->getSQLLogger(); + $platform = $this->getDatabasePlatform(); if (! $platform->supportsSavepoints()) { @@ -1484,10 +1593,24 @@ public function releaseSavepoint($savepoint) } if (! $platform->supportsReleaseSavepoints()) { + if ($logger !== null) { + $logger->stopQuery(); + } + return; } + if ($logger !== null) { + $logger->startQuery('"RELEASE SAVEPOINT"'); + } + $this->executeStatement($platform->releaseSavePoint($savepoint)); + + if ($logger === null) { + return; + } + + $logger->stopQuery(); } /** @@ -1525,28 +1648,23 @@ public function getWrappedConnection() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4966', 'Connection::getWrappedConnection() is deprecated.' - . ' Use Connection::getNativeConnection() to access the native connection.' + . ' Use Connection::getNativeConnection() to access the native connection.', ); $this->connect(); - assert($this->_conn !== null); - return $this->_conn; } - /** - * @return resource|object - */ + /** @return resource|object */ public function getNativeConnection() { $this->connect(); - assert($this->_conn !== null); if (! method_exists($this->_conn, 'getNativeConnection')) { throw new LogicException(sprintf( 'The driver connection %s does not support accessing the native connection.', - get_class($this->_conn) + get_class($this->_conn), )); } @@ -1561,10 +1679,7 @@ public function getNativeConnection() */ public function createSchemaManager(): AbstractSchemaManager { - return $this->_driver->getSchemaManager( - $this, - $this->getDatabasePlatform() - ); + return $this->schemaManagerFactory->createSchemaManager($this); } /** @@ -1582,14 +1697,10 @@ public function getSchemaManager() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4515', - 'Connection::getSchemaManager() is deprecated, use Connection::createSchemaManager() instead.' + 'Connection::getSchemaManager() is deprecated, use Connection::createSchemaManager() instead.', ); - if ($this->_schemaManager === null) { - $this->_schemaManager = $this->createSchemaManager(); - } - - return $this->_schemaManager; + return $this->_schemaManager ??= $this->createSchemaManager(); } /** @@ -1667,7 +1778,7 @@ public function convertToPHPValue($value, $type) * * @throws Exception */ - private function _bindTypedValues(DriverStatement $stmt, array $params, array $types): void + private function bindParameters(DriverStatement $stmt, array $params, array $types): void { // Check whether parameters are positional or named. Mixing is not allowed. if (is_int(key($params))) { @@ -1677,11 +1788,21 @@ private function _bindTypedValues(DriverStatement $stmt, array $params, array $t if (isset($types[$key])) { $type = $types[$key]; [$value, $bindingType] = $this->getBindingInfo($value, $type); - $stmt->bindValue($bindIndex, $value, $bindingType); } else { - $stmt->bindValue($bindIndex, $value); + if (array_key_exists($key, $types)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5550', + 'Using NULL as prepared statement parameter type is deprecated.' + . 'Omit or use Parameter::STRING instead', + ); + } + + $bindingType = ParameterType::STRING; } + $stmt->bindValue($bindIndex, $value, $bindingType); + ++$bindIndex; } } else { @@ -1690,10 +1811,20 @@ private function _bindTypedValues(DriverStatement $stmt, array $params, array $t if (isset($types[$name])) { $type = $types[$name]; [$value, $bindingType] = $this->getBindingInfo($value, $type); - $stmt->bindValue($name, $value, $bindingType); } else { - $stmt->bindValue($name, $value); + if (array_key_exists($name, $types)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5550', + 'Using NULL as prepared statement parameter type is deprecated.' + . 'Omit or use Parameter::STRING instead', + ); + } + + $bindingType = ParameterType::STRING; } + + $stmt->bindValue($name, $value, $bindingType); } } } @@ -1749,9 +1880,7 @@ final public function convertExceptionDuringQuery( return $this->handleDriverException($e, new Query($sql, $params, $types)); } - /** - * @internal - */ + /** @internal */ final public function convertException(Driver\Exception $e): DriverException { return $this->handleDriverException($e, null); @@ -1765,11 +1894,8 @@ final public function convertException(Driver\Exception $e): DriverException */ private function expandArrayParameters(string $sql, array $params, array $types): array { - if ($this->parser === null) { - $this->parser = $this->getDatabasePlatform()->createSQLParser(); - } - - $visitor = new ExpandArrayParameters($params, $types); + $this->parser ??= $this->getDatabasePlatform()->createSQLParser(); + $visitor = new ExpandArrayParameters($params, $types); $this->parser->parse($sql, $visitor); @@ -1792,9 +1918,10 @@ private function needsArrayParameterConversion(array $params, array $types): boo foreach ($types as $type) { if ( - $type === self::PARAM_INT_ARRAY - || $type === self::PARAM_STR_ARRAY - || $type === self::PARAM_ASCII_STR_ARRAY + $type === ArrayParameterType::INTEGER + || $type === ArrayParameterType::STRING + || $type === ArrayParameterType::ASCII + || $type === ArrayParameterType::BINARY ) { return true; } @@ -1807,11 +1934,8 @@ private function handleDriverException( Driver\Exception $driverException, ?Query $query ): DriverException { - if ($this->exceptionConverter === null) { - $this->exceptionConverter = $this->_driver->getExceptionConverter(); - } - - $exception = $this->exceptionConverter->convert($driverException, $query); + $this->exceptionConverter ??= $this->_driver->getExceptionConverter(); + $exception = $this->exceptionConverter->convert($driverException, $query); if ($exception instanceof ConnectionLost) { $this->close(); @@ -1823,33 +1947,54 @@ private function handleDriverException( /** * BC layer for a wide-spread use-case of old DBAL APIs * - * @deprecated This API is deprecated and will be removed after 2022 + * @deprecated Use {@see executeStatement()} instead * * @param array $params The query parameters * @param array $types The parameter types */ public function executeUpdate(string $sql, array $params = [], array $types = []): int { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4163', + '%s is deprecated, please use executeStatement() instead.', + __METHOD__, + ); + return $this->executeStatement($sql, $params, $types); } /** * BC layer for a wide-spread use-case of old DBAL APIs * - * @deprecated This API is deprecated and will be removed after 2022 + * @deprecated Use {@see executeQuery()} instead */ public function query(string $sql): Result { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4163', + '%s is deprecated, please use executeQuery() instead.', + __METHOD__, + ); + return $this->executeQuery($sql); } /** * BC layer for a wide-spread use-case of old DBAL APIs * - * @deprecated This API is deprecated and will be removed after 2022 + * @deprecated please use {@see executeStatement()} instead */ public function exec(string $sql): int { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4163', + '%s is deprecated, please use executeStatement() instead.', + __METHOD__, + ); + return $this->executeStatement($sql); } } diff --git a/doctrine/dbal/src/ConnectionException.php b/doctrine/dbal/src/ConnectionException.php index 8426ca288..f1e18987b 100644 --- a/doctrine/dbal/src/ConnectionException.php +++ b/doctrine/dbal/src/ConnectionException.php @@ -2,38 +2,28 @@ namespace Doctrine\DBAL; -/** - * @psalm-immutable - */ +/** @psalm-immutable */ class ConnectionException extends Exception { - /** - * @return ConnectionException - */ + /** @return ConnectionException */ public static function commitFailedRollbackOnly() { return new self('Transaction commit failed because the transaction has been marked for rollback only.'); } - /** - * @return ConnectionException - */ + /** @return ConnectionException */ public static function noActiveTransaction() { return new self('There is no active transaction.'); } - /** - * @return ConnectionException - */ + /** @return ConnectionException */ public static function savepointsNotSupported() { return new self('Savepoints are not supported by this driver.'); } - /** - * @return ConnectionException - */ + /** @return ConnectionException */ public static function mayNotAlterNestedTransactionWithSavepointsInTransaction() { return new self('May not alter the nested transaction with savepoints behavior while a transaction is open.'); diff --git a/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php b/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php index 9d38f904b..8cffb7fe6 100644 --- a/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php +++ b/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php @@ -13,7 +13,9 @@ use Doctrine\DBAL\Events; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Statement; +use Doctrine\Deprecations\Deprecation; use InvalidArgumentException; +use SensitiveParameter; use function array_rand; use function count; @@ -97,7 +99,6 @@ class PrimaryReadReplicaConnection extends Connection * * @param array $params * @psalm-param Params $params - * @phpstan-param array $params * * @throws Exception * @throws InvalidArgumentException @@ -147,7 +148,7 @@ public function connect($connectionName = null) if ($connectionName !== null) { throw new InvalidArgumentException( 'Passing a connection name as first argument is not supported anymore.' - . ' Use ensureConnectedToPrimary()/ensureConnectedToReplica() instead.' + . ' Use ensureConnectedToPrimary()/ensureConnectedToReplica() instead.', ); } @@ -199,6 +200,13 @@ protected function performConnect(?string $connectionName = null): bool } if ($this->_eventManager->hasListeners(Events::postConnect)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated. Implement a middleware instead.', + Events::postConnect, + ); + $eventArgs = new ConnectionEventArgs($this); $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); } @@ -256,8 +264,11 @@ protected function connectTo($connectionName) * * @return mixed */ - protected function chooseConnectionConfiguration($connectionName, $params) - { + protected function chooseConnectionConfiguration( + $connectionName, + #[SensitiveParameter] + $params + ) { if ($connectionName === 'primary') { return $params['primary']; } diff --git a/doctrine/dbal/src/Driver.php b/doctrine/dbal/src/Driver.php index 50a6005f9..46e422ba9 100644 --- a/doctrine/dbal/src/Driver.php +++ b/doctrine/dbal/src/Driver.php @@ -7,23 +7,30 @@ use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager; +use SensitiveParameter; /** * Driver interface. * Interface that all DBAL drivers must implement. + * + * @psalm-import-type Params from DriverManager */ interface Driver { /** * Attempts to create a connection with the database. * - * @param mixed[] $params All connection parameters. + * @param array $params All connection parameters. + * @psalm-param Params $params All connection parameters. * * @return DriverConnection The database connection. * * @throws Exception */ - public function connect(array $params); + public function connect( + #[SensitiveParameter] + array $params + ); /** * Gets the DatabasePlatform instance that provides all the metadata about @@ -37,6 +44,8 @@ public function getDatabasePlatform(); * Gets the SchemaManager that can be used to inspect and change the underlying * database schema of the platform this driver connects to. * + * @deprecated Use {@link AbstractPlatform::createSchemaManager()} instead. + * * @return AbstractSchemaManager */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform); diff --git a/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php index c851949be..87d50aff9 100644 --- a/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php @@ -22,9 +22,7 @@ use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Query; -/** - * @internal - */ +/** @internal */ final class ExceptionConverter implements ExceptionConverterInterface { /** diff --git a/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php index 727a605d7..4703a57d5 100644 --- a/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php @@ -20,14 +20,10 @@ use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Query; -/** - * @internal - */ +/** @internal */ final class ExceptionConverter implements ExceptionConverterInterface { - /** - * @link http://www.dba-oracle.com/t_error_code_list.htm - */ + /** @link http://www.dba-oracle.com/t_error_code_list.htm */ public function convert(Exception $exception, ?Query $query): DriverException { switch ($exception->getCode()) { diff --git a/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php index df34802cf..2baca1ee2 100644 --- a/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php @@ -23,14 +23,10 @@ use function strpos; -/** - * @internal - */ +/** @internal */ final class ExceptionConverter implements ExceptionConverterInterface { - /** - * @link http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html - */ + /** @link http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html */ public function convert(Exception $exception, ?Query $query): DriverException { switch ($exception->getSQLState()) { @@ -81,7 +77,7 @@ public function convert(Exception $exception, ?Query $query): DriverException return new ConnectionException($exception, $query); } - // Prior to fixing https://bugs.php.net/bug.php?id=64705 (PHP 7.3.22 and PHP 7.4.10), + // Prior to fixing https://bugs.php.net/bug.php?id=64705 (PHP 7.4.10), // in some cases (mainly connection errors) the PDO exception wouldn't provide a SQLSTATE via its code. // We have to match against the SQLSTATE in the error message in these cases. if ($exception->getCode() === 7 && strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) { diff --git a/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php index d78b0832a..9e67155ad 100644 --- a/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php @@ -8,6 +8,7 @@ use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Exception\ConnectionException; use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; use Doctrine\DBAL\Exception\InvalidFieldNameException; use Doctrine\DBAL\Exception\LockWaitTimeoutException; use Doctrine\DBAL\Exception\NonUniqueFieldNameException; @@ -21,14 +22,10 @@ use function strpos; -/** - * @internal - */ +/** @internal */ final class ExceptionConverter implements ExceptionConverterInterface { - /** - * @link http://www.sqlite.org/c3ref/c_abort.html - */ + /** @link http://www.sqlite.org/c3ref/c_abort.html */ public function convert(Exception $exception, ?Query $query): DriverException { if (strpos($exception->getMessage(), 'database is locked') !== false) { @@ -79,6 +76,10 @@ public function convert(Exception $exception, ?Query $query): DriverException return new ConnectionException($exception, $query); } + if (strpos($exception->getMessage(), 'FOREIGN KEY constraint failed') !== false) { + return new ForeignKeyConstraintViolationException($exception, $query); + } + return new DriverException($exception, $query); } } diff --git a/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php b/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php index 29e73d1f2..3779c8bab 100644 --- a/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php +++ b/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php @@ -2,6 +2,11 @@ namespace Doctrine\DBAL\Driver\API\SQLite; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\Deprecations\Deprecation; + +use function array_merge; use function strpos; /** @@ -11,6 +16,25 @@ */ final class UserDefinedFunctions { + private const DEFAULT_FUNCTIONS = [ + 'sqrt' => ['callback' => [SqlitePlatform::class, 'udfSqrt'], 'numArgs' => 1], + 'mod' => ['callback' => [SqlitePlatform::class, 'udfMod'], 'numArgs' => 2], + 'locate' => ['callback' => [SqlitePlatform::class, 'udfLocate'], 'numArgs' => -1], + ]; + + /** + * @param callable(string, callable, int): bool $callback + * @param array $additionalFunctions + */ + public static function register(callable $callback, array $additionalFunctions = []): void + { + $userDefinedFunctions = array_merge(self::DEFAULT_FUNCTIONS, $additionalFunctions); + + foreach ($userDefinedFunctions as $function => $data) { + $callback($function, $data['callback'], $data['numArgs']); + } + } + /** * User-defined function that implements MOD(). * @@ -31,6 +55,14 @@ public static function mod($a, $b): int */ public static function locate($str, $substr, $offset = 0): int { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5749', + 'Relying on DBAL\'s emulated LOCATE() function is deprecated. ' + . 'Use INSTR() or %s::getLocateExpression() instead.', + AbstractPlatform::class, + ); + // SQL's LOCATE function works on 1-based positions, while PHP's strpos works on 0-based positions. // So we have to make them compatible if an offset is given. if ($offset > 0) { diff --git a/doctrine/dbal/src/Driver/AbstractDB2Driver.php b/doctrine/dbal/src/Driver/AbstractDB2Driver.php index 38a460861..79efb8650 100644 --- a/doctrine/dbal/src/Driver/AbstractDB2Driver.php +++ b/doctrine/dbal/src/Driver/AbstractDB2Driver.php @@ -3,22 +3,27 @@ namespace Doctrine\DBAL\Driver; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface; use Doctrine\DBAL\Driver\API\IBMDB2\ExceptionConverter; +use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\DB2111Platform; use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Schema\DB2SchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; +use Doctrine\Deprecations\Deprecation; use function assert; +use function preg_match; +use function version_compare; /** * Abstract base implementation of the {@see Driver} interface for IBM DB2 based drivers. */ -abstract class AbstractDB2Driver implements Driver +abstract class AbstractDB2Driver implements VersionAwarePlatformDriver { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getDatabasePlatform() { @@ -26,10 +31,19 @@ public function getDatabasePlatform() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@link DB2Platform::createSchemaManager()} instead. */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5458', + 'AbstractDB2Driver::getSchemaManager() is deprecated.' + . ' Use DB2Platform::createSchemaManager() instead.', + ); + assert($platform instanceof DB2Platform); return new DB2SchemaManager($conn, $platform); @@ -39,4 +53,48 @@ public function getExceptionConverter(): ExceptionConverterInterface { return new ExceptionConverter(); } + + /** + * {@inheritDoc} + */ + public function createDatabasePlatformForVersion($version) + { + if (version_compare($this->getVersionNumber($version), '11.1', '>=')) { + return new DB2111Platform(); + } + + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5156', + 'IBM DB2 < 11.1 support is deprecated and will be removed in DBAL 4.' + . ' Consider upgrading to IBM DB2 11.1 or later.', + ); + + return $this->getDatabasePlatform(); + } + + /** + * Detects IBM DB2 server version + * + * @param string $versionString Version string as returned by IBM DB2 server, i.e. 'DB2/LINUXX8664 11.5.8.0' + * + * @throws DBALException + */ + private function getVersionNumber(string $versionString): string + { + if ( + preg_match( + '/^(?:[^\s]+\s)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', + $versionString, + $versionParts, + ) === 0 + ) { + throw DBALException::invalidPlatformVersionSpecified( + $versionString, + '^(?:[^\s]+\s)?..', + ); + } + + return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch']; + } } diff --git a/doctrine/dbal/src/Driver/AbstractException.php b/doctrine/dbal/src/Driver/AbstractException.php index f14501b2f..389f82e70 100644 --- a/doctrine/dbal/src/Driver/AbstractException.php +++ b/doctrine/dbal/src/Driver/AbstractException.php @@ -18,10 +18,8 @@ abstract class AbstractException extends BaseException implements Exception { /** * The SQLSTATE of the driver. - * - * @var string|null */ - private $sqlState; + private ?string $sqlState = null; /** * @param string $message The driver error message. @@ -37,7 +35,7 @@ public function __construct($message, $sqlState = null, $code = 0, ?Throwable $p } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLState() { diff --git a/doctrine/dbal/src/Driver/AbstractMySQLDriver.php b/doctrine/dbal/src/Driver/AbstractMySQLDriver.php index 7f74e42d4..5f6ffd6f5 100644 --- a/doctrine/dbal/src/Driver/AbstractMySQLDriver.php +++ b/doctrine/dbal/src/Driver/AbstractMySQLDriver.php @@ -9,6 +9,8 @@ use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\MariaDb1027Platform; +use Doctrine\DBAL\Platforms\MariaDb1043Platform; +use Doctrine\DBAL\Platforms\MariaDb1052Platform; use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\MySQL80Platform; use Doctrine\DBAL\Platforms\MySQLPlatform; @@ -27,34 +29,69 @@ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver { /** - * {@inheritdoc} + * {@inheritDoc} * * @throws Exception */ public function createDatabasePlatformForVersion($version) { $mariadb = stripos($version, 'mariadb') !== false; - if ($mariadb && version_compare($this->getMariaDbMysqlVersionNumber($version), '10.2.7', '>=')) { - return new MariaDb1027Platform(); - } - if (! $mariadb) { + if ($mariadb) { + $mariaDbVersion = $this->getMariaDbMysqlVersionNumber($version); + if (version_compare($mariaDbVersion, '10.5.2', '>=')) { + return new MariaDb1052Platform(); + } + + if (version_compare($mariaDbVersion, '10.4.3', '>=')) { + return new MariaDb1043Platform(); + } + + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/6110', + 'Support for MariaDB < 10.4 is deprecated and will be removed in DBAL 4.' + . ' Consider upgrading to a more recent version of MariaDB.', + ); + + if (version_compare($mariaDbVersion, '10.2.7', '>=')) { + return new MariaDb1027Platform(); + } + } else { $oracleMysqlVersion = $this->getOracleMysqlVersionNumber($version); if (version_compare($oracleMysqlVersion, '8', '>=')) { + if (! version_compare($version, '8.0.0', '>=')) { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/dbal/pull/5779', + 'Version detection logic for MySQL will change in DBAL 4. ' + . 'Please specify the version as the server reports it, e.g. "8.0.31" instead of "8".', + ); + } + return new MySQL80Platform(); } if (version_compare($oracleMysqlVersion, '5.7.9', '>=')) { + if (! version_compare($version, '5.7.9', '>=')) { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/dbal/pull/5779', + 'Version detection logic for MySQL will change in DBAL 4. ' + . 'Please specify the version as the server reports it, e.g. "5.7.40" instead of "5.7".', + ); + } + return new MySQL57Platform(); } - } - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5060', - 'MySQL 5.6 support is deprecated and will be removed in DBAL 4.' - . ' Consider upgrading to MySQL 5.7 or later.' - ); + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5072', + 'MySQL 5.6 support is deprecated and will be removed in DBAL 4.' + . ' Consider upgrading to MySQL 5.7 or later.', + ); + } return $this->getDatabasePlatform(); } @@ -73,12 +110,12 @@ private function getOracleMysqlVersionNumber(string $versionString): string preg_match( '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $versionString, - $versionParts + $versionParts, ) === 0 ) { throw Exception::invalidPlatformVersionSpecified( $versionString, - '..' + '..', ); } @@ -86,8 +123,8 @@ private function getOracleMysqlVersionNumber(string $versionString): string $minorVersion = $versionParts['minor'] ?? 0; $patchVersion = $versionParts['patch'] ?? null; - if ($majorVersion === '5' && $minorVersion === '7' && $patchVersion === null) { - $patchVersion = '9'; + if ($majorVersion === '5' && $minorVersion === '7') { + $patchVersion ??= '9'; } return $majorVersion . '.' . $minorVersion . '.' . $patchVersion; @@ -103,16 +140,26 @@ private function getOracleMysqlVersionNumber(string $versionString): string */ private function getMariaDbMysqlVersionNumber(string $versionString): string { + if (stripos($versionString, 'MariaDB') === 0) { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/dbal/pull/5779', + 'Version detection logic for MySQL will change in DBAL 4. ' + . 'Please specify the version as the server reports it, ' + . 'e.g. "10.9.3-MariaDB" instead of "mariadb-10.9".', + ); + } + if ( preg_match( '/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', $versionString, - $versionParts + $versionParts, ) === 0 ) { throw Exception::invalidPlatformVersionSpecified( $versionString, - '^(?:5\.5\.5-)?(mariadb-)?..' + '^(?:5\.5\.5-)?(mariadb-)?..', ); } @@ -120,7 +167,7 @@ private function getMariaDbMysqlVersionNumber(string $versionString): string } /** - * {@inheritdoc} + * {@inheritDoc} * * @return AbstractMySQLPlatform */ @@ -130,12 +177,21 @@ public function getDatabasePlatform() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@link AbstractMySQLPlatform::createSchemaManager()} instead. * * @return MySQLSchemaManager */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5458', + 'AbstractMySQLDriver::getSchemaManager() is deprecated.' + . ' Use MySQLPlatform::createSchemaManager() instead.', + ); + assert($platform instanceof AbstractMySQLPlatform); return new MySQLSchemaManager($conn, $platform); diff --git a/doctrine/dbal/src/Driver/AbstractOracleDriver.php b/doctrine/dbal/src/Driver/AbstractOracleDriver.php index c62c87d90..b0f92453a 100644 --- a/doctrine/dbal/src/Driver/AbstractOracleDriver.php +++ b/doctrine/dbal/src/Driver/AbstractOracleDriver.php @@ -10,6 +10,7 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Schema\OracleSchemaManager; +use Doctrine\Deprecations\Deprecation; use function assert; @@ -19,7 +20,7 @@ abstract class AbstractOracleDriver implements Driver { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getDatabasePlatform() { @@ -27,10 +28,19 @@ public function getDatabasePlatform() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@link OraclePlatform::createSchemaManager()} instead. */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5458', + 'AbstractOracleDriver::getSchemaManager() is deprecated.' + . ' Use OraclePlatform::createSchemaManager() instead.', + ); + assert($platform instanceof OraclePlatform); return new OracleSchemaManager($conn, $platform); @@ -44,7 +54,7 @@ public function getExceptionConverter(): ExceptionConverter /** * Returns an appropriate Easy Connect String for the given parameters. * - * @param mixed[] $params The connection parameters to return the Easy Connect String for. + * @param array $params The connection parameters to return the Easy Connect String for. * * @return string */ diff --git a/doctrine/dbal/src/Driver/AbstractOracleDriver/EasyConnectString.php b/doctrine/dbal/src/Driver/AbstractOracleDriver/EasyConnectString.php index b94d0b1e4..91bc6a7e1 100644 --- a/doctrine/dbal/src/Driver/AbstractOracleDriver/EasyConnectString.php +++ b/doctrine/dbal/src/Driver/AbstractOracleDriver/EasyConnectString.php @@ -15,8 +15,7 @@ */ final class EasyConnectString { - /** @var string */ - private $string; + private string $string; private function __construct(string $string) { @@ -87,9 +86,7 @@ public static function fromConnectionParameters(array $params): self ]); } - /** - * @param mixed[] $params - */ + /** @param mixed[] $params */ private static function renderParams(array $params): string { $chunks = []; @@ -107,9 +104,7 @@ private static function renderParams(array $params): string return implode('', $chunks); } - /** - * @param mixed $value - */ + /** @param mixed $value */ private static function renderValue($value): string { if (is_array($value)) { diff --git a/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php b/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php index adc0be095..099630d33 100644 --- a/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php +++ b/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php @@ -24,14 +24,14 @@ abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver { /** - * {@inheritdoc} + * {@inheritDoc} */ public function createDatabasePlatformForVersion($version) { if (preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts) === 0) { throw Exception::invalidPlatformVersionSpecified( $version, - '..' + '..', ); } @@ -48,14 +48,14 @@ public function createDatabasePlatformForVersion($version) 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5060', 'PostgreSQL 9 support is deprecated and will be removed in DBAL 4.' - . ' Consider upgrading to Postgres 10 or later.' + . ' Consider upgrading to Postgres 10 or later.', ); return new PostgreSQL94Platform(); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getDatabasePlatform() { @@ -63,10 +63,19 @@ public function getDatabasePlatform() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@link PostgreSQLPlatform::createSchemaManager()} instead. */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5458', + 'AbstractPostgreSQLDriver::getSchemaManager() is deprecated.' + . ' Use PostgreSQLPlatform::createSchemaManager() instead.', + ); + assert($platform instanceof PostgreSQLPlatform); return new PostgreSQLSchemaManager($conn, $platform); diff --git a/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php b/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php index adf31f554..b9a99552e 100644 --- a/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php +++ b/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php @@ -8,7 +8,9 @@ use Doctrine\DBAL\Driver\API\SQLSrv\ExceptionConverter; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SQLServer2012Platform; +use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\DBAL\Schema\SQLServerSchemaManager; +use Doctrine\Deprecations\Deprecation; use function assert; @@ -18,7 +20,7 @@ abstract class AbstractSQLServerDriver implements Driver { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getDatabasePlatform() { @@ -26,11 +28,20 @@ public function getDatabasePlatform() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@link SQLServerPlatform::createSchemaManager()} instead. */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { - assert($platform instanceof SQLServer2012Platform); + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5458', + 'AbstractSQLServerDriver::getSchemaManager() is deprecated.' + . ' Use SQLServerPlatform::createSchemaManager() instead.', + ); + + assert($platform instanceof SQLServerPlatform); return new SQLServerSchemaManager($conn, $platform); } diff --git a/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php b/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php index b6479267b..f8808f756 100644 --- a/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php +++ b/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\SqliteSchemaManager; +use Doctrine\Deprecations\Deprecation; use function assert; @@ -18,7 +19,7 @@ abstract class AbstractSQLiteDriver implements Driver { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getDatabasePlatform() { @@ -26,10 +27,19 @@ public function getDatabasePlatform() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@link SqlitePlatform::createSchemaManager()} instead. */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5458', + 'AbstractSQLiteDriver::getSchemaManager() is deprecated.' + . ' Use SqlitePlatform::createSchemaManager() instead.', + ); + assert($platform instanceof SqlitePlatform); return new SqliteSchemaManager($conn, $platform); diff --git a/doctrine/dbal/src/Driver/AbstractSQLiteDriver/Middleware/EnableForeignKeys.php b/doctrine/dbal/src/Driver/AbstractSQLiteDriver/Middleware/EnableForeignKeys.php new file mode 100644 index 000000000..666108d50 --- /dev/null +++ b/doctrine/dbal/src/Driver/AbstractSQLiteDriver/Middleware/EnableForeignKeys.php @@ -0,0 +1,31 @@ +exec('PRAGMA foreign_keys=ON'); + + return $connection; + } + }; + } +} diff --git a/doctrine/dbal/src/Driver/Exception.php b/doctrine/dbal/src/Driver/Exception.php index 0b108963a..f963563b8 100644 --- a/doctrine/dbal/src/Driver/Exception.php +++ b/doctrine/dbal/src/Driver/Exception.php @@ -6,9 +6,7 @@ use Throwable; -/** - * @psalm-immutable - */ +/** @psalm-immutable */ interface Exception extends Throwable { /** diff --git a/doctrine/dbal/src/Driver/Exception/UnknownParameterType.php b/doctrine/dbal/src/Driver/Exception/UnknownParameterType.php index c989184d6..01a9b3d0c 100644 --- a/doctrine/dbal/src/Driver/Exception/UnknownParameterType.php +++ b/doctrine/dbal/src/Driver/Exception/UnknownParameterType.php @@ -15,9 +15,7 @@ */ final class UnknownParameterType extends AbstractException { - /** - * @param mixed $type - */ + /** @param mixed $type */ public static function new($type): self { return new self(sprintf('Unknown parameter type, %d given.', $type)); diff --git a/doctrine/dbal/src/Driver/FetchUtils.php b/doctrine/dbal/src/Driver/FetchUtils.php index 240cef4a0..50e02b1a2 100644 --- a/doctrine/dbal/src/Driver/FetchUtils.php +++ b/doctrine/dbal/src/Driver/FetchUtils.php @@ -4,9 +4,7 @@ namespace Doctrine\DBAL\Driver; -/** - * @internal - */ +/** @internal */ final class FetchUtils { /** diff --git a/doctrine/dbal/src/Driver/IBMDB2/Connection.php b/doctrine/dbal/src/Driver/IBMDB2/Connection.php index aca2a02ce..dfb11c236 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Connection.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Connection.php @@ -23,7 +23,6 @@ use function db2_rollback; use function db2_server_info; use function error_get_last; -use function is_bool; use const DB2_AUTOCOMMIT_OFF; use const DB2_AUTOCOMMIT_ON; @@ -44,7 +43,7 @@ public function __construct($connection) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getServerVersion() { @@ -71,7 +70,7 @@ public function query(string $sql): ResultInterface } /** - * {@inheritdoc} + * {@inheritDoc} */ public function quote($value, $type = ParameterType::STRING) { @@ -96,7 +95,7 @@ public function exec(string $sql): int } /** - * {@inheritdoc} + * {@inheritDoc} */ public function lastInsertId($name = null) { @@ -104,7 +103,7 @@ public function lastInsertId($name = null) Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.' + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); } @@ -113,10 +112,7 @@ public function lastInsertId($name = null) public function beginTransaction(): bool { - $result = db2_autocommit($this->connection, DB2_AUTOCOMMIT_OFF); - assert(is_bool($result)); - - return $result; + return db2_autocommit($this->connection, DB2_AUTOCOMMIT_OFF); } public function commit(): bool @@ -125,10 +121,7 @@ public function commit(): bool throw ConnectionError::new($this->connection); } - $result = db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); - assert(is_bool($result)); - - return $result; + return db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); } public function rollBack(): bool @@ -137,15 +130,10 @@ public function rollBack(): bool throw ConnectionError::new($this->connection); } - $result = db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); - assert(is_bool($result)); - - return $result; + return db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); } - /** - * @return resource - */ + /** @return resource */ public function getNativeConnection() { return $this->connection; diff --git a/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php b/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php index e1ec42f2d..124a6f6db 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php +++ b/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php @@ -4,6 +4,8 @@ namespace Doctrine\DBAL\Driver\IBMDB2; +use SensitiveParameter; + use function implode; use function sprintf; use function strpos; @@ -13,11 +15,12 @@ */ final class DataSourceName { - /** @var string */ - private $string; + private string $string; - private function __construct(string $string) - { + private function __construct( + #[SensitiveParameter] + string $string + ) { $this->string = $string; } @@ -31,8 +34,10 @@ public function toString(): string * * @param array $params */ - public static function fromArray(array $params): self - { + public static function fromArray( + #[SensitiveParameter] + array $params + ): self { $chunks = []; foreach ($params as $key => $value) { @@ -47,8 +52,10 @@ public static function fromArray(array $params): self * * @param array $params */ - public static function fromConnectionParameters(array $params): self - { + public static function fromConnectionParameters( + #[SensitiveParameter] + array $params + ): self { if (isset($params['dbname']) && strpos($params['dbname'], '=') !== false) { return new self($params['dbname']); } diff --git a/doctrine/dbal/src/Driver/IBMDB2/Driver.php b/doctrine/dbal/src/Driver/IBMDB2/Driver.php index 142b369b3..7650db5f4 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Driver.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Driver.php @@ -4,6 +4,7 @@ use Doctrine\DBAL\Driver\AbstractDB2Driver; use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed; +use SensitiveParameter; use function db2_connect; use function db2_pconnect; @@ -11,12 +12,14 @@ final class Driver extends AbstractDB2Driver { /** - * {@inheritdoc} + * {@inheritDoc} * * @return Connection */ - public function connect(array $params) - { + public function connect( + #[SensitiveParameter] + array $params + ) { $dataSourceName = DataSourceName::fromConnectionParameters($params)->toString(); $username = $params['user'] ?? ''; diff --git a/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php index 86821688c..231c9d473 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php @@ -13,9 +13,7 @@ */ final class CannotCopyStreamToStream extends AbstractException { - /** - * @psalm-param array{message: string}|null $error - */ + /** @psalm-param array{message: string}|null $error */ public static function new(?array $error): self { $message = 'Could not copy source stream to temporary file'; diff --git a/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php index 587ae65f4..63f7ca1e2 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php @@ -13,9 +13,7 @@ */ final class CannotCreateTemporaryFile extends AbstractException { - /** - * @psalm-param array{message: string}|null $error - */ + /** @psalm-param array{message: string}|null $error */ public static function new(?array $error): self { $message = 'Could not create temporary file'; diff --git a/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionError.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionError.php index a27a99a18..b7bd8be69 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionError.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionError.php @@ -16,9 +16,7 @@ */ final class ConnectionError extends AbstractException { - /** - * @param resource $connection - */ + /** @param resource $connection */ public static function new($connection): self { $message = db2_conn_errormsg($connection); diff --git a/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php index 035fd67f9..42df5e15c 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php @@ -13,9 +13,7 @@ */ final class PrepareFailed extends AbstractException { - /** - * @psalm-param array{message: string}|null $error - */ + /** @psalm-param array{message: string}|null $error */ public static function new(?array $error): self { if ($error === null) { diff --git a/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php index 5c30d1cab..6bf851131 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php @@ -16,9 +16,7 @@ */ final class StatementError extends AbstractException { - /** - * @param resource|null $statement - */ + /** @param resource|null $statement */ public static function new($statement = null): self { if ($statement !== null) { diff --git a/doctrine/dbal/src/Driver/IBMDB2/Statement.php b/doctrine/dbal/src/Driver/IBMDB2/Statement.php index e7d9bc19f..699e236d7 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Statement.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Statement.php @@ -9,12 +9,14 @@ use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use function assert; use function db2_bind_param; use function db2_execute; use function error_get_last; use function fclose; +use function func_num_args; use function is_int; use function is_resource; use function stream_copy_to_stream; @@ -33,7 +35,7 @@ final class Statement implements StatementInterface private $stmt; /** @var mixed[] */ - private $parameters = []; + private array $parameters = []; /** * Map of LOB parameter positions to the tuples containing reference to the variable bound to the driver statement @@ -41,7 +43,7 @@ final class Statement implements StatementInterface * * @var array */ - private $lobs = []; + private array $lobs = []; /** * @internal The statement can be only instantiated by its driver connection. @@ -54,22 +56,49 @@ public function __construct($stmt) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { assert(is_int($param)); + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + return $this->bindParam($param, $value, $type); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@see bindValue()} instead. */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5563', + '%s is deprecated. Use bindValue() instead.', + __METHOD__, + ); + assert(is_int($param)); + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + switch ($type) { case ParameterType::INTEGER: $this->bind($param, $variable, DB2_PARAM_IN, DB2_LONG); @@ -103,10 +132,19 @@ private function bind($position, &$variable, int $parameterType, int $dataType): } /** - * {@inheritdoc} + * {@inheritDoc} */ public function execute($params = null): ResultInterface { + if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.', + ); + } + $handles = $this->bindLobs(); $result = @db2_execute($this->stmt, $params ?? $this->parameters); @@ -144,6 +182,8 @@ private function bindLobs(): array } else { $this->bind($param, $value, DB2_PARAM_IN, DB2_CHAR); } + + unset($value); } return $handles; diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php index a0e69a5e7..f2809cd0a 100644 --- a/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php +++ b/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php @@ -16,8 +16,7 @@ abstract class AbstractConnectionMiddleware implements ServerInfoAwareConnection { - /** @var Connection */ - private $wrappedConnection; + private Connection $wrappedConnection; public function __construct(Connection $wrappedConnection) { @@ -35,7 +34,7 @@ public function query(string $sql): Result } /** - * {@inheritdoc} + * {@inheritDoc} */ public function quote($value, $type = ParameterType::STRING) { @@ -48,7 +47,7 @@ public function exec(string $sql): int } /** - * {@inheritdoc} + * {@inheritDoc} */ public function lastInsertId($name = null) { @@ -56,7 +55,7 @@ public function lastInsertId($name = null) Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.' + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); } @@ -64,7 +63,7 @@ public function lastInsertId($name = null) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function beginTransaction() { @@ -72,7 +71,7 @@ public function beginTransaction() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function commit() { @@ -80,7 +79,7 @@ public function commit() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function rollBack() { @@ -88,7 +87,7 @@ public function rollBack() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getServerVersion() { @@ -99,15 +98,13 @@ public function getServerVersion() return $this->wrappedConnection->getServerVersion(); } - /** - * @return resource|object - */ + /** @return resource|object */ public function getNativeConnection() { if (! method_exists($this->wrappedConnection, 'getNativeConnection')) { throw new LogicException(sprintf( 'The driver connection %s does not support accessing the native connection.', - get_class($this->wrappedConnection) + get_class($this->wrappedConnection), )); } diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php index ab1f508f7..1c9d43097 100644 --- a/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php +++ b/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php @@ -7,11 +7,12 @@ use Doctrine\DBAL\Driver\API\ExceptionConverter; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\VersionAwarePlatformDriver; +use Doctrine\Deprecations\Deprecation; +use SensitiveParameter; abstract class AbstractDriverMiddleware implements VersionAwarePlatformDriver { - /** @var Driver */ - private $wrappedDriver; + private Driver $wrappedDriver; public function __construct(Driver $wrappedDriver) { @@ -19,15 +20,17 @@ public function __construct(Driver $wrappedDriver) } /** - * {@inheritdoc} + * {@inheritDoc} */ - public function connect(array $params) - { + public function connect( + #[SensitiveParameter] + array $params + ) { return $this->wrappedDriver->connect($params); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getDatabasePlatform() { @@ -35,10 +38,19 @@ public function getDatabasePlatform() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@link AbstractPlatform::createSchemaManager()} instead. */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5458', + 'AbstractDriverMiddleware::getSchemaManager() is deprecated.' + . ' Use AbstractPlatform::createSchemaManager() instead.', + ); + return $this->wrappedDriver->getSchemaManager($conn, $platform); } @@ -48,7 +60,7 @@ public function getExceptionConverter(): ExceptionConverter } /** - * {@inheritdoc} + * {@inheritDoc} */ public function createDatabasePlatformForVersion($version) { diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php index ebc63c570..198d39b0d 100644 --- a/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php +++ b/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php @@ -6,8 +6,7 @@ abstract class AbstractResultMiddleware implements Result { - /** @var Result */ - private $wrappedResult; + private Result $wrappedResult; public function __construct(Result $result) { @@ -15,7 +14,7 @@ public function __construct(Result $result) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchNumeric() { @@ -23,7 +22,7 @@ public function fetchNumeric() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchAssociative() { @@ -31,7 +30,7 @@ public function fetchAssociative() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchOne() { @@ -39,7 +38,7 @@ public function fetchOne() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchAllNumeric(): array { @@ -47,7 +46,7 @@ public function fetchAllNumeric(): array } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchAllAssociative(): array { @@ -55,7 +54,7 @@ public function fetchAllAssociative(): array } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchFirstColumn(): array { diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php index a646cd30c..6cd2f8f08 100644 --- a/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php +++ b/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php @@ -5,11 +5,13 @@ use Doctrine\DBAL\Driver\Result; use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; + +use function func_num_args; abstract class AbstractStatementMiddleware implements Statement { - /** @var Statement */ - private $wrappedStatement; + private Statement $wrappedStatement; public function __construct(Statement $wrappedStatement) { @@ -17,23 +19,50 @@ public function __construct(Statement $wrappedStatement) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function bindValue($param, $value, $type = ParameterType::STRING) { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + return $this->wrappedStatement->bindValue($param, $value, $type); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@see bindValue()} instead. */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5563', + '%s is deprecated. Use bindValue() instead.', + __METHOD__, + ); + + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + return $this->wrappedStatement->bindParam($param, $variable, $type, $length); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function execute($params = null): Result { diff --git a/doctrine/dbal/src/Driver/Mysqli/Connection.php b/doctrine/dbal/src/Driver/Mysqli/Connection.php index 8453f7406..d492684cc 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Connection.php +++ b/doctrine/dbal/src/Driver/Mysqli/Connection.php @@ -18,12 +18,9 @@ final class Connection implements ServerInfoAwareConnection */ public const OPTION_FLAGS = 'flags'; - /** @var mysqli */ - private $connection; + private mysqli $connection; - /** - * @internal The connection can be only instantiated by its driver. - */ + /** @internal The connection can be only instantiated by its driver. */ public function __construct(mysqli $connection) { $this->connection = $connection; @@ -42,7 +39,7 @@ public function getWrappedResourceHandle(): mysqli 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5037', '%s is deprecated, call getNativeConnection() instead.', - __METHOD__ + __METHOD__, ); return $this->getNativeConnection(); @@ -74,7 +71,7 @@ public function query(string $sql): ResultInterface } /** - * {@inheritdoc} + * {@inheritDoc} */ public function quote($value, $type = ParameterType::STRING) { @@ -97,7 +94,7 @@ public function exec(string $sql): int } /** - * {@inheritdoc} + * {@inheritDoc} */ public function lastInsertId($name = null) { @@ -105,7 +102,7 @@ public function lastInsertId($name = null) Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.' + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); } diff --git a/doctrine/dbal/src/Driver/Mysqli/Driver.php b/doctrine/dbal/src/Driver/Mysqli/Driver.php index 41a66a732..4f5186875 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Driver.php +++ b/doctrine/dbal/src/Driver/Mysqli/Driver.php @@ -8,20 +8,22 @@ use Doctrine\DBAL\Driver\Mysqli\Initializer\Charset; use Doctrine\DBAL\Driver\Mysqli\Initializer\Options; use Doctrine\DBAL\Driver\Mysqli\Initializer\Secure; +use Generator; use mysqli; use mysqli_sql_exception; - -use function count; +use SensitiveParameter; final class Driver extends AbstractMySQLDriver { /** - * {@inheritdoc} + * {@inheritDoc} * * @return Connection */ - public function connect(array $params) - { + public function connect( + #[SensitiveParameter] + array $params + ) { if (! empty($params['persistent'])) { if (! isset($params['host'])) { throw HostRequired::forPersistentConnection(); @@ -32,27 +34,9 @@ public function connect(array $params) $host = $params['host'] ?? null; } - $flags = 0; - - $preInitializers = $postInitializers = []; - - if (isset($params['driverOptions'])) { - $driverOptions = $params['driverOptions']; - - if (isset($driverOptions[Connection::OPTION_FLAGS])) { - $flags = $driverOptions[Connection::OPTION_FLAGS]; - unset($driverOptions[Connection::OPTION_FLAGS]); - } - - $preInitializers = $this->withOptions($preInitializers, $driverOptions); - } - - $preInitializers = $this->withSecure($preInitializers, $params); - $postInitializers = $this->withCharset($postInitializers, $params); - $connection = new mysqli(); - foreach ($preInitializers as $initializer) { + foreach ($this->compilePreInitializers($params) as $initializer) { $initializer->initialize($connection); } @@ -64,7 +48,7 @@ public function connect(array $params) $params['dbname'] ?? null, $params['port'] ?? null, $params['unix_socket'] ?? null, - $flags + $params['driverOptions'][Connection::OPTION_FLAGS] ?? 0, ); } catch (mysqli_sql_exception $e) { throw ConnectionFailed::upcast($e); @@ -74,7 +58,7 @@ public function connect(array $params) throw ConnectionFailed::new($connection); } - foreach ($postInitializers as $initializer) { + foreach ($this->compilePostInitializers($params) as $initializer) { $initializer->initialize($connection); } @@ -82,59 +66,52 @@ public function connect(array $params) } /** - * @param list $initializers - * @param array $options + * @param array $params * - * @return list + * @return Generator */ - private function withOptions(array $initializers, array $options): array - { - if (count($options) !== 0) { - $initializers[] = new Options($options); + private function compilePreInitializers( + #[SensitiveParameter] + array $params + ): Generator { + unset($params['driverOptions'][Connection::OPTION_FLAGS]); + + if (isset($params['driverOptions']) && $params['driverOptions'] !== []) { + yield new Options($params['driverOptions']); } - return $initializers; - } - - /** - * @param list $initializers - * @param array $params - * - * @return list - */ - private function withSecure(array $initializers, array $params): array - { if ( - isset($params['ssl_key']) || - isset($params['ssl_cert']) || - isset($params['ssl_ca']) || - isset($params['ssl_capath']) || - isset($params['ssl_cipher']) + ! isset($params['ssl_key']) && + ! isset($params['ssl_cert']) && + ! isset($params['ssl_ca']) && + ! isset($params['ssl_capath']) && + ! isset($params['ssl_cipher']) ) { - $initializers[] = new Secure( - $params['ssl_key'] ?? '', - $params['ssl_cert'] ?? '', - $params['ssl_ca'] ?? '', - $params['ssl_capath'] ?? '', - $params['ssl_cipher'] ?? '' - ); + return; } - return $initializers; + yield new Secure( + $params['ssl_key'] ?? '', + $params['ssl_cert'] ?? '', + $params['ssl_ca'] ?? '', + $params['ssl_capath'] ?? '', + $params['ssl_cipher'] ?? '', + ); } /** - * @param list $initializers - * @param array $params + * @param array $params * - * @return list + * @return Generator */ - private function withCharset(array $initializers, array $params): array - { - if (isset($params['charset'])) { - $initializers[] = new Charset($params['charset']); + private function compilePostInitializers( + #[SensitiveParameter] + array $params + ): Generator { + if (! isset($params['charset'])) { + return; } - return $initializers; + yield new Charset($params['charset']); } } diff --git a/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php b/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php index 763b4eb77..8c6bbb476 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php +++ b/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php @@ -23,7 +23,7 @@ public static function fromCharset(mysqli $connection, string $charset): self return new self( sprintf('Failed to set charset "%s": %s', $charset, $connection->error), $connection->sqlstate, - $connection->errno + $connection->errno, ); } @@ -36,7 +36,7 @@ public static function upcast(mysqli_sql_exception $exception, string $charset): sprintf('Failed to set charset "%s": %s', $charset, $exception->getMessage()), $p->getValue($exception), (int) $exception->getCode(), - $exception + $exception, ); } } diff --git a/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php b/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php index 962175679..6fb46316e 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php +++ b/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php @@ -15,13 +15,11 @@ */ final class InvalidOption extends AbstractException { - /** - * @param mixed $value - */ + /** @param mixed $value */ public static function fromOption(int $option, $value): self { return new self( - sprintf('Failed to set option %d with value "%s"', $option, $value) + sprintf('Failed to set option %d with value "%s"', $option, $value), ); } } diff --git a/doctrine/dbal/src/Driver/Mysqli/Exception/NonStreamResourceUsedAsLargeObject.php b/doctrine/dbal/src/Driver/Mysqli/Exception/NonStreamResourceUsedAsLargeObject.php index c4884c0cd..566d63638 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Exception/NonStreamResourceUsedAsLargeObject.php +++ b/doctrine/dbal/src/Driver/Mysqli/Exception/NonStreamResourceUsedAsLargeObject.php @@ -18,7 +18,7 @@ final class NonStreamResourceUsedAsLargeObject extends AbstractException public static function new(int $parameter): self { return new self( - sprintf('The resource passed as a LARGE_OBJECT parameter #%d must be of type "stream"', $parameter) + sprintf('The resource passed as a LARGE_OBJECT parameter #%d must be of type "stream"', $parameter), ); } } diff --git a/doctrine/dbal/src/Driver/Mysqli/Initializer.php b/doctrine/dbal/src/Driver/Mysqli/Initializer.php index 9966da46b..efab67e22 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Initializer.php +++ b/doctrine/dbal/src/Driver/Mysqli/Initializer.php @@ -9,8 +9,6 @@ interface Initializer { - /** - * @throws Exception - */ + /** @throws Exception */ public function initialize(mysqli $connection): void; } diff --git a/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php b/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php index b32ecaf91..8143a265c 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php +++ b/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php @@ -11,8 +11,7 @@ final class Charset implements Initializer { - /** @var string */ - private $charset; + private string $charset; public function __construct(string $charset) { diff --git a/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php b/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php index bcf2fc2d1..2e66f8d69 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php +++ b/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php @@ -13,11 +13,9 @@ final class Options implements Initializer { /** @var array */ - private $options; + private array $options; - /** - * @param array $options - */ + /** @param array $options */ public function __construct(array $options) { $this->options = $options; diff --git a/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php b/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php index 9d6db4e0e..a25fcfc2d 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php +++ b/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php @@ -6,26 +6,24 @@ use Doctrine\DBAL\Driver\Mysqli\Initializer; use mysqli; +use SensitiveParameter; final class Secure implements Initializer { - /** @var string */ - private $key; - - /** @var string */ - private $cert; - - /** @var string */ - private $ca; - - /** @var string */ - private $capath; - - /** @var string */ - private $cipher; - - public function __construct(string $key, string $cert, string $ca, string $capath, string $cipher) - { + private string $key; + private string $cert; + private string $ca; + private string $capath; + private string $cipher; + + public function __construct( + #[SensitiveParameter] + string $key, + string $cert, + string $ca, + string $capath, + string $cipher + ) { $this->key = $key; $this->cert = $cert; $this->ca = $ca; diff --git a/doctrine/dbal/src/Driver/Mysqli/Result.php b/doctrine/dbal/src/Driver/Mysqli/Result.php index ac36df033..c7dc65d1d 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Result.php +++ b/doctrine/dbal/src/Driver/Mysqli/Result.php @@ -18,16 +18,13 @@ final class Result implements ResultInterface { - /** @var mysqli_stmt */ - private $statement; + private mysqli_stmt $statement; /** * Whether the statement result has columns. The property should be used only after the result metadata * has been fetched ({@see $metadataFetched}). Otherwise, the property value is undetermined. - * - * @var bool */ - private $hasColumns = false; + private bool $hasColumns = false; /** * Mapping of statement result column indexes to their names. The property should be used only @@ -35,10 +32,10 @@ final class Result implements ResultInterface * * @var array */ - private $columnNames = []; + private array $columnNames = []; /** @var mixed[] */ - private $boundValues = []; + private array $boundValues = []; /** * @internal The result can be only instantiated by its driver connection or statement. @@ -88,7 +85,7 @@ public function __construct(mysqli_stmt $statement) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchNumeric() { @@ -130,7 +127,7 @@ public function fetchAssociative() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchOne() { @@ -138,7 +135,7 @@ public function fetchOne() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchAllNumeric(): array { @@ -146,7 +143,7 @@ public function fetchAllNumeric(): array } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchAllAssociative(): array { @@ -154,7 +151,7 @@ public function fetchAllAssociative(): array } /** - * {@inheritdoc} + * {@inheritDoc} */ public function fetchFirstColumn(): array { diff --git a/doctrine/dbal/src/Driver/Mysqli/Statement.php b/doctrine/dbal/src/Driver/Mysqli/Statement.php index 6e493c9e5..fec7c95c3 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Statement.php +++ b/doctrine/dbal/src/Driver/Mysqli/Statement.php @@ -10,6 +10,7 @@ use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use mysqli_sql_exception; use mysqli_stmt; @@ -18,6 +19,7 @@ use function count; use function feof; use function fread; +use function func_num_args; use function get_resource_type; use function is_int; use function is_resource; @@ -25,8 +27,7 @@ final class Statement implements StatementInterface { - /** @var string[] */ - private static $paramTypeMap = [ + private const PARAM_TYPE_MAP = [ ParameterType::ASCII => 's', ParameterType::STRING => 's', ParameterType::BINARY => 's', @@ -36,25 +37,21 @@ final class Statement implements StatementInterface ParameterType::LARGE_OBJECT => 'b', ]; - /** @var mysqli_stmt */ - private $stmt; + private mysqli_stmt $stmt; /** @var mixed[] */ - private $boundValues; + private array $boundValues; - /** @var string */ - private $types; + private string $types; /** * Contains ref values for bindValue(). * * @var mixed[] */ - private $values = []; + private array $values = []; - /** - * @internal The statement can be only instantiated by its driver connection. - */ + /** @internal The statement can be only instantiated by its driver connection. */ public function __construct(mysqli_stmt $stmt) { $this->stmt = $stmt; @@ -65,45 +62,85 @@ public function __construct(mysqli_stmt $stmt) } /** - * {@inheritdoc} + * @deprecated Use {@see bindValue()} instead. + * + * {@inheritDoc} + * + * @psalm-assert ParameterType::* $type */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5563', + '%s is deprecated. Use bindValue() instead.', + __METHOD__, + ); + assert(is_int($param)); - if (! isset(self::$paramTypeMap[$type])) { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + + if (! isset(self::PARAM_TYPE_MAP[$type])) { throw UnknownParameterType::new($type); } $this->boundValues[$param] =& $variable; - $this->types[$param - 1] = self::$paramTypeMap[$type]; + $this->types[$param - 1] = self::PARAM_TYPE_MAP[$type]; return true; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @psalm-assert ParameterType::* $type */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { assert(is_int($param)); - if (! isset(self::$paramTypeMap[$type])) { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + + if (! isset(self::PARAM_TYPE_MAP[$type])) { throw UnknownParameterType::new($type); } $this->values[$param] = $value; $this->boundValues[$param] =& $this->values[$param]; - $this->types[$param - 1] = self::$paramTypeMap[$type]; + $this->types[$param - 1] = self::PARAM_TYPE_MAP[$type]; return true; } /** - * {@inheritdoc} + * {@inheritDoc} */ public function execute($params = null): ResultInterface { + if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.', + ); + } + if ($params !== null && count($params) > 0) { if (! $this->bindUntypedValues($params)) { throw StatementError::new($this->stmt); @@ -139,10 +176,10 @@ private function bindTypedParameters(): void assert(is_int($parameter)); if (! isset($types[$parameter - 1])) { - $types[$parameter - 1] = self::$paramTypeMap[ParameterType::STRING]; + $types[$parameter - 1] = self::PARAM_TYPE_MAP[ParameterType::STRING]; } - if ($types[$parameter - 1] === self::$paramTypeMap[ParameterType::LARGE_OBJECT]) { + if ($types[$parameter - 1] === self::PARAM_TYPE_MAP[ParameterType::LARGE_OBJECT]) { if (is_resource($value)) { if (get_resource_type($value) !== 'stream') { throw NonStreamResourceUsedAsLargeObject::new($parameter); @@ -153,7 +190,7 @@ private function bindTypedParameters(): void continue; } - $types[$parameter - 1] = self::$paramTypeMap[ParameterType::STRING]; + $types[$parameter - 1] = self::PARAM_TYPE_MAP[ParameterType::STRING]; } $values[$parameter] = $value; diff --git a/doctrine/dbal/src/Driver/OCI8/Connection.php b/doctrine/dbal/src/Driver/OCI8/Connection.php index 46ff41866..72353fa31 100644 --- a/doctrine/dbal/src/Driver/OCI8/Connection.php +++ b/doctrine/dbal/src/Driver/OCI8/Connection.php @@ -29,11 +29,8 @@ final class Connection implements ServerInfoAwareConnection /** @var resource */ private $connection; - /** @var Parser */ - private $parser; - - /** @var ExecutionMode */ - private $executionMode; + private Parser $parser; + private ExecutionMode $executionMode; /** * @internal The connection can be only instantiated by its driver. @@ -61,9 +58,7 @@ public function getServerVersion(): string return $matches[1]; } - /** - * @throws Parser\Exception - */ + /** @throws Parser\Exception */ public function prepare(string $sql): DriverStatement { $visitor = new ConvertPositionalToNamedPlaceholders(); @@ -86,7 +81,7 @@ public function query(string $sql): ResultInterface } /** - * {@inheritdoc} + * {@inheritDoc} */ public function quote($value, $type = ParameterType::STRING) { @@ -109,7 +104,7 @@ public function exec(string $sql): int } /** - * {@inheritdoc} + * {@inheritDoc} * * @param string|null $name * @@ -126,7 +121,7 @@ public function lastInsertId($name = null) Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.' + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); $result = $this->query('SELECT ' . $name . '.CURRVAL FROM DUAL')->fetchOne(); @@ -167,9 +162,7 @@ public function rollBack(): bool return true; } - /** - * @return resource - */ + /** @return resource */ public function getNativeConnection() { return $this->connection; diff --git a/doctrine/dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php b/doctrine/dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php index 483879d47..e2a112629 100644 --- a/doctrine/dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php +++ b/doctrine/dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php @@ -18,10 +18,10 @@ final class ConvertPositionalToNamedPlaceholders implements Visitor { /** @var list */ - private $buffer = []; + private array $buffer = []; /** @var array */ - private $parameterMap = []; + private array $parameterMap = []; public function acceptOther(string $sql): void { @@ -48,9 +48,7 @@ public function getSQL(): string return implode('', $this->buffer); } - /** - * @return array - */ + /** @return array */ public function getParameterMap(): array { return $this->parameterMap; diff --git a/doctrine/dbal/src/Driver/OCI8/Driver.php b/doctrine/dbal/src/Driver/OCI8/Driver.php index 48ef0bbe4..53e563c63 100644 --- a/doctrine/dbal/src/Driver/OCI8/Driver.php +++ b/doctrine/dbal/src/Driver/OCI8/Driver.php @@ -4,6 +4,7 @@ use Doctrine\DBAL\Driver\AbstractOracleDriver; use Doctrine\DBAL\Driver\OCI8\Exception\ConnectionFailed; +use SensitiveParameter; use function oci_connect; use function oci_pconnect; @@ -16,12 +17,14 @@ final class Driver extends AbstractOracleDriver { /** - * {@inheritdoc} + * {@inheritDoc} * * @return Connection */ - public function connect(array $params) - { + public function connect( + #[SensitiveParameter] + array $params + ) { $username = $params['user'] ?? ''; $password = $params['password'] ?? ''; $charset = $params['charset'] ?? ''; diff --git a/doctrine/dbal/src/Driver/OCI8/Exception/Error.php b/doctrine/dbal/src/Driver/OCI8/Exception/Error.php index 7a27141fe..6abdf233c 100644 --- a/doctrine/dbal/src/Driver/OCI8/Exception/Error.php +++ b/doctrine/dbal/src/Driver/OCI8/Exception/Error.php @@ -16,9 +16,7 @@ */ final class Error extends AbstractException { - /** - * @param resource $resource - */ + /** @param resource $resource */ public static function new($resource): self { $error = oci_error($resource); diff --git a/doctrine/dbal/src/Driver/OCI8/Exception/NonTerminatedStringLiteral.php b/doctrine/dbal/src/Driver/OCI8/Exception/NonTerminatedStringLiteral.php index c26fe2990..776728fbe 100644 --- a/doctrine/dbal/src/Driver/OCI8/Exception/NonTerminatedStringLiteral.php +++ b/doctrine/dbal/src/Driver/OCI8/Exception/NonTerminatedStringLiteral.php @@ -20,8 +20,8 @@ public static function new(int $offset): self return new self( sprintf( 'The statement contains non-terminated string literal starting at offset %d.', - $offset - ) + $offset, + ), ); } } diff --git a/doctrine/dbal/src/Driver/OCI8/Exception/UnknownParameterIndex.php b/doctrine/dbal/src/Driver/OCI8/Exception/UnknownParameterIndex.php index ea56c549e..2cd3fe796 100644 --- a/doctrine/dbal/src/Driver/OCI8/Exception/UnknownParameterIndex.php +++ b/doctrine/dbal/src/Driver/OCI8/Exception/UnknownParameterIndex.php @@ -18,7 +18,7 @@ final class UnknownParameterIndex extends AbstractException public static function new(int $index): self { return new self( - sprintf('Could not find variable mapping with index %d, in the SQL statement', $index) + sprintf('Could not find variable mapping with index %d, in the SQL statement', $index), ); } } diff --git a/doctrine/dbal/src/Driver/OCI8/ExecutionMode.php b/doctrine/dbal/src/Driver/OCI8/ExecutionMode.php index 88c00216f..8efb93648 100644 --- a/doctrine/dbal/src/Driver/OCI8/ExecutionMode.php +++ b/doctrine/dbal/src/Driver/OCI8/ExecutionMode.php @@ -11,8 +11,7 @@ */ final class ExecutionMode { - /** @var bool */ - private $isAutoCommitEnabled = true; + private bool $isAutoCommitEnabled = true; public function enableAutoCommit(): void { diff --git a/doctrine/dbal/src/Driver/OCI8/Middleware/InitializeSession.php b/doctrine/dbal/src/Driver/OCI8/Middleware/InitializeSession.php new file mode 100644 index 000000000..314a04a65 --- /dev/null +++ b/doctrine/dbal/src/Driver/OCI8/Middleware/InitializeSession.php @@ -0,0 +1,39 @@ +exec( + 'ALTER SESSION SET' + . " NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'" + . " NLS_TIME_FORMAT = 'HH24:MI:SS'" + . " NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'" + . " NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS'" + . " NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS TZH:TZM'" + . " NLS_NUMERIC_CHARACTERS = '.,'", + ); + + return $connection; + } + }; + } +} diff --git a/doctrine/dbal/src/Driver/OCI8/Result.php b/doctrine/dbal/src/Driver/OCI8/Result.php index 8f77da759..08add4faf 100644 --- a/doctrine/dbal/src/Driver/OCI8/Result.php +++ b/doctrine/dbal/src/Driver/OCI8/Result.php @@ -129,9 +129,7 @@ private function fetch(int $mode) return $result; } - /** - * @return array - */ + /** @return array */ private function fetchAll(int $mode, int $fetchStructure): array { oci_fetch_all( @@ -139,7 +137,7 @@ private function fetchAll(int $mode, int $fetchStructure): array $result, 0, -1, - $mode | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS + $mode | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS, ); return $result; diff --git a/doctrine/dbal/src/Driver/OCI8/Statement.php b/doctrine/dbal/src/Driver/OCI8/Statement.php index c5e3774ba..015a14b7b 100644 --- a/doctrine/dbal/src/Driver/OCI8/Statement.php +++ b/doctrine/dbal/src/Driver/OCI8/Statement.php @@ -7,7 +7,9 @@ use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; +use function func_num_args; use function is_int; use function oci_bind_by_name; use function oci_execute; @@ -30,10 +32,9 @@ final class Statement implements StatementInterface private $statement; /** @var array */ - private $parameterMap; + private array $parameterMap; - /** @var ExecutionMode */ - private $executionMode; + private ExecutionMode $executionMode; /** * @internal The statement can be only instantiated by its driver connection. @@ -51,18 +52,45 @@ public function __construct($connection, $statement, array $parameterMap, Execut } /** - * {@inheritdoc} + * {@inheritDoc} */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + return $this->bindParam($param, $value, $type); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@see bindValue()} instead. */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5563', + '%s is deprecated. Use bindValue() instead.', + __METHOD__, + ); + + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + if (is_int($param)) { if (! isset($this->parameterMap[$param])) { throw UnknownParameterIndex::new($param); @@ -87,7 +115,7 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le $param, $variable, $length ?? -1, - $this->convertParameterType($type) + $this->convertParameterType($type), ); } @@ -109,16 +137,23 @@ private function convertParameterType(int $type): int } /** - * {@inheritdoc} + * {@inheritDoc} */ public function execute($params = null): ResultInterface { if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.', + ); + foreach ($params as $key => $val) { if (is_int($key)) { - $this->bindValue($key + 1, $val); + $this->bindValue($key + 1, $val, ParameterType::STRING); } else { - $this->bindValue($key, $val); + $this->bindValue($key, $val, ParameterType::STRING); } } } diff --git a/doctrine/dbal/src/Driver/PDO/Connection.php b/doctrine/dbal/src/Driver/PDO/Connection.php index 505acba5f..290dcc2d2 100644 --- a/doctrine/dbal/src/Driver/PDO/Connection.php +++ b/doctrine/dbal/src/Driver/PDO/Connection.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Driver\PDO; +use Doctrine\DBAL\Driver\Exception\UnknownParameterType; +use Doctrine\DBAL\Driver\PDO\PDOException as DriverPDOException; use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\Statement as StatementInterface; @@ -15,12 +17,9 @@ final class Connection implements ServerInfoAwareConnection { - /** @var PDO */ - private $connection; + private PDO $connection; - /** - * @internal The connection can be only instantiated by its driver. - */ + /** @internal The connection can be only instantiated by its driver. */ public function __construct(PDO $connection) { $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); @@ -42,7 +41,7 @@ public function exec(string $sql): int } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getServerVersion() { @@ -79,15 +78,19 @@ public function query(string $sql): ResultInterface } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @throws UnknownParameterType + * + * @psalm-assert ParameterType::* $type */ public function quote($value, $type = ParameterType::STRING) { - return $this->connection->quote($value, $type); + return $this->connection->quote($value, ParameterTypeMap::convertParamType($type)); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function lastInsertId($name = null) { @@ -99,7 +102,7 @@ public function lastInsertId($name = null) Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.' + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); return $this->connection->lastInsertId($name); @@ -110,17 +113,29 @@ public function lastInsertId($name = null) public function beginTransaction(): bool { - return $this->connection->beginTransaction(); + try { + return $this->connection->beginTransaction(); + } catch (PDOException $exception) { + throw DriverPDOException::new($exception); + } } public function commit(): bool { - return $this->connection->commit(); + try { + return $this->connection->commit(); + } catch (PDOException $exception) { + throw DriverPDOException::new($exception); + } } public function rollBack(): bool { - return $this->connection->rollBack(); + try { + return $this->connection->rollBack(); + } catch (PDOException $exception) { + throw DriverPDOException::new($exception); + } } public function getNativeConnection(): PDO @@ -128,16 +143,14 @@ public function getNativeConnection(): PDO return $this->connection; } - /** - * @deprecated Call {@see getNativeConnection()} instead. - */ + /** @deprecated Call {@see getNativeConnection()} instead. */ public function getWrappedConnection(): PDO { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5037', '%s is deprecated, call getNativeConnection() instead.', - __METHOD__ + __METHOD__, ); return $this->getNativeConnection(); diff --git a/doctrine/dbal/src/Driver/PDO/Exception.php b/doctrine/dbal/src/Driver/PDO/Exception.php index 7036a0e43..fbb81253b 100644 --- a/doctrine/dbal/src/Driver/PDO/Exception.php +++ b/doctrine/dbal/src/Driver/PDO/Exception.php @@ -19,9 +19,7 @@ public static function new(PDOException $exception): self if ($exception->errorInfo !== null) { [$sqlState, $code] = $exception->errorInfo; - if ($code === null) { - $code = 0; - } + $code ??= 0; } else { $code = $exception->getCode(); $sqlState = null; diff --git a/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php b/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php index 8aeb26964..2492698b4 100644 --- a/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php @@ -7,28 +7,34 @@ use Doctrine\DBAL\Driver\PDO\Exception; use PDO; use PDOException; +use SensitiveParameter; final class Driver extends AbstractMySQLDriver { /** - * {@inheritdoc} + * {@inheritDoc} * * @return Connection */ - public function connect(array $params) - { + public function connect( + #[SensitiveParameter] + array $params + ) { $driverOptions = $params['driverOptions'] ?? []; if (! empty($params['persistent'])) { $driverOptions[PDO::ATTR_PERSISTENT] = true; } + $safeParams = $params; + unset($safeParams['password'], $safeParams['url']); + try { $pdo = new PDO( - $this->constructPdoDsn($params), + $this->constructPdoDsn($safeParams), $params['user'] ?? '', $params['password'] ?? '', - $driverOptions + $driverOptions, ); } catch (PDOException $exception) { throw Exception::new($exception); diff --git a/doctrine/dbal/src/Driver/PDO/OCI/Driver.php b/doctrine/dbal/src/Driver/PDO/OCI/Driver.php index 705db6118..10ada9f0e 100644 --- a/doctrine/dbal/src/Driver/PDO/OCI/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/OCI/Driver.php @@ -7,28 +7,34 @@ use Doctrine\DBAL\Driver\PDO\Exception; use PDO; use PDOException; +use SensitiveParameter; final class Driver extends AbstractOracleDriver { /** - * {@inheritdoc} + * {@inheritDoc} * * @return Connection */ - public function connect(array $params) - { + public function connect( + #[SensitiveParameter] + array $params + ) { $driverOptions = $params['driverOptions'] ?? []; if (! empty($params['persistent'])) { $driverOptions[PDO::ATTR_PERSISTENT] = true; } + $safeParams = $params; + unset($safeParams['password'], $safeParams['url']); + try { $pdo = new PDO( $this->constructPdoDsn($params), $params['user'] ?? '', $params['password'] ?? '', - $driverOptions + $driverOptions, ); } catch (PDOException $exception) { throw Exception::new($exception); diff --git a/doctrine/dbal/src/Driver/PDO/PDOException.php b/doctrine/dbal/src/Driver/PDO/PDOException.php new file mode 100644 index 000000000..6eefda40a --- /dev/null +++ b/doctrine/dbal/src/Driver/PDO/PDOException.php @@ -0,0 +1,33 @@ +message, 0, $previous); + + $exception->errorInfo = $previous->errorInfo; + $exception->code = $previous->code; + $exception->sqlState = $previous->errorInfo[0] ?? null; + + return $exception; + } + + public function getSQLState(): ?string + { + return $this->sqlState; + } +} diff --git a/doctrine/dbal/src/Driver/PDO/ParameterTypeMap.php b/doctrine/dbal/src/Driver/PDO/ParameterTypeMap.php new file mode 100644 index 000000000..f17b585f7 --- /dev/null +++ b/doctrine/dbal/src/Driver/PDO/ParameterTypeMap.php @@ -0,0 +1,49 @@ + PDO::PARAM_NULL, + ParameterType::INTEGER => PDO::PARAM_INT, + ParameterType::STRING => PDO::PARAM_STR, + ParameterType::ASCII => PDO::PARAM_STR, + ParameterType::BINARY => PDO::PARAM_LOB, + ParameterType::LARGE_OBJECT => PDO::PARAM_LOB, + ParameterType::BOOLEAN => PDO::PARAM_BOOL, + ]; + + /** + * Converts DBAL parameter type to PDO parameter type + * + * @psalm-return PDO::PARAM_* + * + * @throws UnknownParameterType + * + * @psalm-assert ParameterType::* $type + */ + public static function convertParamType(int $type): int + { + if (! isset(self::PARAM_TYPE_MAP[$type])) { + throw UnknownParameterType::new($type); + } + + return self::PARAM_TYPE_MAP[$type]; + } + + private function __construct() + { + } + + private function __clone() + { + } +} diff --git a/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php b/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php index b90e47262..1c586d69d 100644 --- a/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php @@ -5,30 +5,37 @@ use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver; use Doctrine\DBAL\Driver\PDO\Connection; use Doctrine\DBAL\Driver\PDO\Exception; +use Doctrine\Deprecations\Deprecation; use PDO; use PDOException; +use SensitiveParameter; final class Driver extends AbstractPostgreSQLDriver { /** - * {@inheritdoc} + * {@inheritDoc} * * @return Connection */ - public function connect(array $params) - { + public function connect( + #[SensitiveParameter] + array $params + ) { $driverOptions = $params['driverOptions'] ?? []; if (! empty($params['persistent'])) { $driverOptions[PDO::ATTR_PERSISTENT] = true; } + $safeParams = $params; + unset($safeParams['password'], $safeParams['url']); + try { $pdo = new PDO( - $this->constructPdoDsn($params), + $this->constructPdoDsn($safeParams), $params['user'] ?? '', $params['password'] ?? '', - $driverOptions + $driverOptions, ); } catch (PDOException $exception) { throw Exception::new($exception); @@ -56,7 +63,7 @@ public function connect(array $params) /** * Constructs the Postgres PDO DSN. * - * @param mixed[] $params + * @param array $params */ private function constructPdoDsn(array $params): string { @@ -73,11 +80,25 @@ private function constructPdoDsn(array $params): string if (isset($params['dbname'])) { $dsn .= 'dbname=' . $params['dbname'] . ';'; } elseif (isset($params['default_dbname'])) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5705', + 'The "default_dbname" connection parameter is deprecated. Use "dbname" instead.', + ); + $dsn .= 'dbname=' . $params['default_dbname'] . ';'; } else { + if (isset($params['user']) && $params['user'] !== 'postgres') { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5705', + 'Relying on the DBAL connecting to the "postgres" database by default is deprecated.' + . ' Unless you want to have the server determine the default database for the connection,' + . ' specify the database name explicitly.', + ); + } + // Used for temporary connections to allow operations like dropping the database currently connected to. - // Connecting without an explicit database does not work, therefore "postgres" database is used - // as it is mostly present in every server setup. $dsn .= 'dbname=postgres;'; } diff --git a/doctrine/dbal/src/Driver/PDO/Result.php b/doctrine/dbal/src/Driver/PDO/Result.php index b0efdf2e1..67970ac56 100644 --- a/doctrine/dbal/src/Driver/PDO/Result.php +++ b/doctrine/dbal/src/Driver/PDO/Result.php @@ -11,12 +11,9 @@ final class Result implements ResultInterface { - /** @var PDOStatement */ - private $statement; + private PDOStatement $statement; - /** - * @internal The result can be only instantiated by its driver connection or statement. - */ + /** @internal The result can be only instantiated by its driver connection or statement. */ public function __construct(PDOStatement $statement) { $this->statement = $statement; @@ -94,7 +91,9 @@ public function free(): void } /** - * @return mixed|false + * @psalm-param PDO::FETCH_* $mode + * + * @return mixed * * @throws Exception */ @@ -108,6 +107,8 @@ private function fetch(int $mode) } /** + * @psalm-param PDO::FETCH_* $mode + * * @return list * * @throws Exception diff --git a/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php b/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php index 0c34d4b9b..9015f5558 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php +++ b/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php @@ -10,8 +10,7 @@ final class Connection extends AbstractConnectionMiddleware { - /** @var PDOConnection */ - private $connection; + private PDOConnection $connection; public function __construct(PDOConnection $connection) { @@ -23,7 +22,7 @@ public function __construct(PDOConnection $connection) public function prepare(string $sql): StatementInterface { return new Statement( - $this->connection->prepare($sql) + $this->connection->prepare($sql), ); } @@ -39,11 +38,15 @@ public function lastInsertId($name = null) Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.' + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); - return $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') - ->execute([$name]) + $statement = $this->prepare( + 'SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?', + ); + $statement->bindValue(1, $name); + + return $statement->execute() ->fetchOne(); } @@ -52,16 +55,14 @@ public function getNativeConnection(): PDO return $this->connection->getNativeConnection(); } - /** - * @deprecated Call {@see getNativeConnection()} instead. - */ + /** @deprecated Call {@see getNativeConnection()} instead. */ public function getWrappedConnection(): PDO { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5037', '%s is deprecated, call getNativeConnection() instead.', - __METHOD__ + __METHOD__, ); return $this->connection->getWrappedConnection(); diff --git a/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php b/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php index b664950f2..63eabb71a 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php @@ -8,6 +8,7 @@ use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection; use Doctrine\DBAL\Driver\PDO\Exception as PDOException; use PDO; +use SensitiveParameter; use function is_int; use function sprintf; @@ -15,12 +16,14 @@ final class Driver extends AbstractSQLServerDriver { /** - * {@inheritdoc} + * {@inheritDoc} * * @return Connection */ - public function connect(array $params) - { + public function connect( + #[SensitiveParameter] + array $params + ) { $driverOptions = $dsnOptions = []; if (isset($params['driverOptions'])) { @@ -37,12 +40,15 @@ public function connect(array $params) $driverOptions[PDO::ATTR_PERSISTENT] = true; } + $safeParams = $params; + unset($safeParams['password'], $safeParams['url']); + try { $pdo = new PDO( - $this->constructDsn($params, $dsnOptions), + $this->constructDsn($safeParams, $dsnOptions), $params['user'] ?? '', $params['password'] ?? '', - $driverOptions + $driverOptions, ); } catch (\PDOException $exception) { throw PDOException::new($exception); diff --git a/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php b/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php index 43b07a3ab..cb2dfaedb 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php +++ b/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php @@ -2,6 +2,7 @@ namespace Doctrine\DBAL\Driver\PDO\SQLSrv; +use Doctrine\DBAL\Driver\Exception\UnknownParameterType; use Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware; use Doctrine\DBAL\Driver\PDO\Statement as PDOStatement; use Doctrine\DBAL\ParameterType; @@ -12,12 +13,9 @@ final class Statement extends AbstractStatementMiddleware { - /** @var PDOStatement */ - private $statement; + private PDOStatement $statement; - /** - * @internal The statement can be only instantiated by its driver connection. - */ + /** @internal The statement can be only instantiated by its driver connection. */ public function __construct(PDOStatement $statement) { parent::__construct($statement); @@ -26,13 +24,19 @@ public function __construct(PDOStatement $statement) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@see bindValue()} instead. * * @param string|int $param * @param mixed $variable * @param int $type * @param int|null $length * @param mixed $driverOptions The usage of the argument is deprecated. + * + * @throws UnknownParameterType + * + * @psalm-assert ParameterType::* $type */ public function bindParam( $param, @@ -41,20 +45,34 @@ public function bindParam( $length = null, $driverOptions = null ): bool { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5563', + '%s is deprecated. Use bindValue() instead.', + __METHOD__, + ); + + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + if (func_num_args() > 4) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4533', - 'The $driverOptions argument of Statement::bindParam() is deprecated.' + 'The $driverOptions argument of Statement::bindParam() is deprecated.', ); } switch ($type) { case ParameterType::LARGE_OBJECT: case ParameterType::BINARY: - if ($driverOptions === null) { - $driverOptions = PDO::SQLSRV_ENCODING_BINARY; - } + $driverOptions ??= PDO::SQLSRV_ENCODING_BINARY; break; @@ -69,10 +87,23 @@ public function bindParam( } /** - * {@inheritdoc} + * @throws UnknownParameterType + * + * {@inheritDoc} + * + * @psalm-assert ParameterType::* $type */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + return $this->bindParam($param, $value, $type); } } diff --git a/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php b/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php index 5e72de08b..2e97788e6 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php @@ -3,54 +3,57 @@ namespace Doctrine\DBAL\Driver\PDO\SQLite; use Doctrine\DBAL\Driver\AbstractSQLiteDriver; +use Doctrine\DBAL\Driver\API\SQLite\UserDefinedFunctions; use Doctrine\DBAL\Driver\PDO\Connection; use Doctrine\DBAL\Driver\PDO\Exception; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\Deprecations\Deprecation; use PDO; use PDOException; +use SensitiveParameter; -use function array_merge; +use function array_intersect_key; final class Driver extends AbstractSQLiteDriver { - /** @var mixed[] */ - private $userDefinedFunctions = [ - 'sqrt' => ['callback' => [SqlitePlatform::class, 'udfSqrt'], 'numArgs' => 1], - 'mod' => ['callback' => [SqlitePlatform::class, 'udfMod'], 'numArgs' => 2], - 'locate' => ['callback' => [SqlitePlatform::class, 'udfLocate'], 'numArgs' => -1], - ]; - /** - * {@inheritdoc} + * {@inheritDoc} * * @return Connection */ - public function connect(array $params) - { - $driverOptions = $params['driverOptions'] ?? []; + public function connect( + #[SensitiveParameter] + array $params + ) { + $driverOptions = $params['driverOptions'] ?? []; + $userDefinedFunctions = []; if (isset($driverOptions['userDefinedFunctions'])) { - $this->userDefinedFunctions = array_merge( - $this->userDefinedFunctions, - $driverOptions['userDefinedFunctions'] + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5742', + 'The SQLite-specific driver option "userDefinedFunctions" is deprecated.' + . ' Register function directly on the native connection instead.', ); + + $userDefinedFunctions = $driverOptions['userDefinedFunctions']; unset($driverOptions['userDefinedFunctions']); } try { $pdo = new PDO( - $this->constructPdoDsn($params), + $this->constructPdoDsn(array_intersect_key($params, ['path' => true, 'memory' => true])), $params['user'] ?? '', $params['password'] ?? '', - $driverOptions + $driverOptions, ); } catch (PDOException $exception) { throw Exception::new($exception); } - foreach ($this->userDefinedFunctions as $fn => $data) { - $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); - } + UserDefinedFunctions::register( + [$pdo, 'sqliteCreateFunction'], + $userDefinedFunctions, + ); return new Connection($pdo); } @@ -58,7 +61,7 @@ public function connect(array $params) /** * Constructs the Sqlite PDO DSN. * - * @param mixed[] $params + * @param array $params */ private function constructPdoDsn(array $params): string { diff --git a/doctrine/dbal/src/Driver/PDO/Statement.php b/doctrine/dbal/src/Driver/PDO/Statement.php index bb137708a..64f318d2c 100644 --- a/doctrine/dbal/src/Driver/PDO/Statement.php +++ b/doctrine/dbal/src/Driver/PDO/Statement.php @@ -2,13 +2,11 @@ namespace Doctrine\DBAL\Driver\PDO; -use Doctrine\DBAL\Driver\Exception as ExceptionInterface; use Doctrine\DBAL\Driver\Exception\UnknownParameterType; use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; use Doctrine\Deprecations\Deprecation; -use PDO; use PDOException; use PDOStatement; @@ -18,36 +16,36 @@ final class Statement implements StatementInterface { - private const PARAM_TYPE_MAP = [ - ParameterType::NULL => PDO::PARAM_NULL, - ParameterType::INTEGER => PDO::PARAM_INT, - ParameterType::STRING => PDO::PARAM_STR, - ParameterType::ASCII => PDO::PARAM_STR, - ParameterType::BINARY => PDO::PARAM_LOB, - ParameterType::LARGE_OBJECT => PDO::PARAM_LOB, - ParameterType::BOOLEAN => PDO::PARAM_BOOL, - ]; - - /** @var PDOStatement */ - private $stmt; + private PDOStatement $stmt; - /** - * @internal The statement can be only instantiated by its driver connection. - */ + /** @internal The statement can be only instantiated by its driver connection. */ public function __construct(PDOStatement $stmt) { $this->stmt = $stmt; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @throws UnknownParameterType + * + * @psalm-assert ParameterType::* $type */ public function bindValue($param, $value, $type = ParameterType::STRING) { - $type = $this->convertParamType($type); + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + + $pdoType = ParameterTypeMap::convertParamType($type); try { - return $this->stmt->bindValue($param, $value, $type); + return $this->stmt->bindValue($param, $value, $pdoType); } catch (PDOException $exception) { throw Exception::new($exception); } @@ -56,11 +54,17 @@ public function bindValue($param, $value, $type = ParameterType::STRING) /** * {@inheritDoc} * + * @deprecated Use {@see bindValue()} instead. + * * @param mixed $param * @param mixed $variable * @param int $type * @param int|null $length * @param mixed $driverOptions The usage of the argument is deprecated. + * + * @throws UnknownParameterType + * + * @psalm-assert ParameterType::* $type */ public function bindParam( $param, @@ -69,23 +73,39 @@ public function bindParam( $length = null, $driverOptions = null ): bool { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5563', + '%s is deprecated. Use bindValue() instead.', + __METHOD__, + ); + + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + if (func_num_args() > 4) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4533', - 'The $driverOptions argument of Statement::bindParam() is deprecated.' + 'The $driverOptions argument of Statement::bindParam() is deprecated.', ); } - $type = $this->convertParamType($type); + $pdoType = ParameterTypeMap::convertParamType($type); try { return $this->stmt->bindParam( $param, $variable, - $type, + $pdoType, $length ?? 0, - ...array_slice(func_get_args(), 4) + ...array_slice(func_get_args(), 4), ); } catch (PDOException $exception) { throw Exception::new($exception); @@ -93,10 +113,19 @@ public function bindParam( } /** - * {@inheritdoc} + * {@inheritDoc} */ public function execute($params = null): ResultInterface { + if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.', + ); + } + try { $this->stmt->execute($params); } catch (PDOException $exception) { @@ -105,20 +134,4 @@ public function execute($params = null): ResultInterface return new Result($this->stmt); } - - /** - * Converts DBAL parameter type to PDO parameter type - * - * @param int $type Parameter type - * - * @throws ExceptionInterface - */ - private function convertParamType(int $type): int - { - if (! isset(self::PARAM_TYPE_MAP[$type])) { - throw UnknownParameterType::new($type); - } - - return self::PARAM_TYPE_MAP[$type]; - } } diff --git a/doctrine/dbal/src/Driver/PgSQL/Connection.php b/doctrine/dbal/src/Driver/PgSQL/Connection.php new file mode 100644 index 000000000..378e8ed7a --- /dev/null +++ b/doctrine/dbal/src/Driver/PgSQL/Connection.php @@ -0,0 +1,161 @@ +connection = $connection; + $this->parser = new Parser(false); + } + + public function __destruct() + { + if (! isset($this->connection)) { + return; + } + + @pg_close($this->connection); + } + + public function prepare(string $sql): Statement + { + $visitor = new ConvertParameters(); + $this->parser->parse($sql, $visitor); + + $statementName = uniqid('dbal', true); + if (@pg_send_prepare($this->connection, $statementName, $visitor->getSQL()) !== true) { + throw new Exception(pg_last_error($this->connection)); + } + + $result = @pg_get_result($this->connection); + assert($result !== false); + + if ((bool) pg_result_error($result)) { + throw Exception::fromResult($result); + } + + return new Statement($this->connection, $statementName, $visitor->getParameterMap()); + } + + public function query(string $sql): Result + { + if (@pg_send_query($this->connection, $sql) !== true) { + throw new Exception(pg_last_error($this->connection)); + } + + $result = @pg_get_result($this->connection); + assert($result !== false); + + if ((bool) pg_result_error($result)) { + throw Exception::fromResult($result); + } + + return new Result($result); + } + + /** {@inheritDoc} */ + public function quote($value, $type = ParameterType::STRING) + { + if ($type === ParameterType::BINARY || $type === ParameterType::LARGE_OBJECT) { + return sprintf("'%s'", pg_escape_bytea($this->connection, $value)); + } + + return pg_escape_literal($this->connection, $value); + } + + public function exec(string $sql): int + { + return $this->query($sql)->rowCount(); + } + + /** {@inheritDoc} */ + public function lastInsertId($name = null) + { + if ($name !== null) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4687', + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', + ); + + return $this->query(sprintf('SELECT CURRVAL(%s)', $this->quote($name)))->fetchOne(); + } + + return $this->query('SELECT LASTVAL()')->fetchOne(); + } + + /** @return true */ + public function beginTransaction(): bool + { + $this->exec('BEGIN'); + + return true; + } + + /** @return true */ + public function commit(): bool + { + $this->exec('COMMIT'); + + return true; + } + + /** @return true */ + public function rollBack(): bool + { + $this->exec('ROLLBACK'); + + return true; + } + + public function getServerVersion(): string + { + return (string) pg_version($this->connection)['server']; + } + + /** @return PgSqlConnection|resource */ + public function getNativeConnection() + { + return $this->connection; + } +} diff --git a/doctrine/dbal/src/Driver/PgSQL/ConvertParameters.php b/doctrine/dbal/src/Driver/PgSQL/ConvertParameters.php new file mode 100644 index 000000000..795f12d2f --- /dev/null +++ b/doctrine/dbal/src/Driver/PgSQL/ConvertParameters.php @@ -0,0 +1,49 @@ + */ + private array $buffer = []; + + /** @var array */ + private array $parameterMap = []; + + public function acceptPositionalParameter(string $sql): void + { + $position = count($this->parameterMap) + 1; + $this->parameterMap[$position] = $position; + $this->buffer[] = '$' . $position; + } + + public function acceptNamedParameter(string $sql): void + { + $position = count($this->parameterMap) + 1; + $this->parameterMap[$sql] = $position; + $this->buffer[] = '$' . $position; + } + + public function acceptOther(string $sql): void + { + $this->buffer[] = $sql; + } + + public function getSQL(): string + { + return implode('', $this->buffer); + } + + /** @return array */ + public function getParameterMap(): array + { + return $this->parameterMap; + } +} diff --git a/doctrine/dbal/src/Driver/PgSQL/Driver.php b/doctrine/dbal/src/Driver/PgSQL/Driver.php new file mode 100644 index 000000000..73c97cd81 --- /dev/null +++ b/doctrine/dbal/src/Driver/PgSQL/Driver.php @@ -0,0 +1,85 @@ +constructConnectionString($params), PGSQL_CONNECT_FORCE_NEW); + } catch (ErrorException $e) { + throw new Exception($e->getMessage(), '08006', 0, $e); + } finally { + restore_error_handler(); + } + + if ($connection === false) { + throw new Exception('Unable to connect to Postgres server.'); + } + + $driverConnection = new Connection($connection); + + if (isset($params['application_name'])) { + $driverConnection->exec('SET application_name = ' . $driverConnection->quote($params['application_name'])); + } + + return $driverConnection; + } + + /** + * Constructs the Postgres connection string + * + * @param array $params + */ + private function constructConnectionString( + #[SensitiveParameter] + array $params + ): string { + $components = array_filter( + [ + 'host' => $params['host'] ?? null, + 'port' => $params['port'] ?? null, + 'dbname' => $params['dbname'] ?? 'postgres', + 'user' => $params['user'] ?? null, + 'password' => $params['password'] ?? null, + 'sslmode' => $params['sslmode'] ?? null, + ], + static fn ($value) => $value !== '' && $value !== null, + ); + + return implode(' ', array_map( + static fn ($value, string $key) => sprintf("%s='%s'", $key, addslashes($value)), + array_values($components), + array_keys($components), + )); + } +} diff --git a/doctrine/dbal/src/Driver/PgSQL/Exception.php b/doctrine/dbal/src/Driver/PgSQL/Exception.php new file mode 100644 index 000000000..41e0dff19 --- /dev/null +++ b/doctrine/dbal/src/Driver/PgSQL/Exception.php @@ -0,0 +1,30 @@ +result = $result; + } + + public function __destruct() + { + if (! isset($this->result)) { + return; + } + + $this->free(); + } + + /** {@inheritDoc} */ + public function fetchNumeric() + { + if ($this->result === null) { + return false; + } + + $row = pg_fetch_row($this->result); + if ($row === false) { + return false; + } + + return $this->mapNumericRow($row, $this->fetchNumericColumnTypes()); + } + + /** {@inheritDoc} */ + public function fetchAssociative() + { + if ($this->result === null) { + return false; + } + + $row = pg_fetch_assoc($this->result); + if ($row === false) { + return false; + } + + return $this->mapAssociativeRow($row, $this->fetchAssociativeColumnTypes()); + } + + /** {@inheritDoc} */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** {@inheritDoc} */ + public function fetchAllNumeric(): array + { + if ($this->result === null) { + return []; + } + + $resultSet = pg_fetch_all($this->result, PGSQL_NUM); + // On PHP 7.4, pg_fetch_all() might return false for empty result sets. + if ($resultSet === false) { + return []; + } + + $types = $this->fetchNumericColumnTypes(); + + return array_map( + fn (array $row) => $this->mapNumericRow($row, $types), + $resultSet, + ); + } + + /** {@inheritDoc} */ + public function fetchAllAssociative(): array + { + if ($this->result === null) { + return []; + } + + $resultSet = pg_fetch_all($this->result, PGSQL_ASSOC); + // On PHP 7.4, pg_fetch_all() might return false for empty result sets. + if ($resultSet === false) { + return []; + } + + $types = $this->fetchAssociativeColumnTypes(); + + return array_map( + fn (array $row) => $this->mapAssociativeRow($row, $types), + $resultSet, + ); + } + + /** {@inheritDoc} */ + public function fetchFirstColumn(): array + { + if ($this->result === null) { + return []; + } + + $postgresType = pg_field_type($this->result, 0); + + return array_map( + fn ($value) => $this->mapType($postgresType, $value), + pg_fetch_all_columns($this->result), + ); + } + + public function rowCount(): int + { + if ($this->result === null) { + return 0; + } + + return pg_affected_rows($this->result); + } + + public function columnCount(): int + { + if ($this->result === null) { + return 0; + } + + return pg_num_fields($this->result); + } + + public function free(): void + { + if ($this->result === null) { + return; + } + + pg_free_result($this->result); + $this->result = null; + } + + /** @return array */ + private function fetchNumericColumnTypes(): array + { + assert($this->result !== null); + + $types = []; + $numFields = pg_num_fields($this->result); + for ($i = 0; $i < $numFields; ++$i) { + $types[$i] = pg_field_type($this->result, $i); + } + + return $types; + } + + /** @return array */ + private function fetchAssociativeColumnTypes(): array + { + assert($this->result !== null); + + $types = []; + $numFields = pg_num_fields($this->result); + for ($i = 0; $i < $numFields; ++$i) { + $types[pg_field_name($this->result, $i)] = pg_field_type($this->result, $i); + } + + return $types; + } + + /** + * @param list $row + * @param array $types + * + * @return list + */ + private function mapNumericRow(array $row, array $types): array + { + assert($this->result !== null); + + return array_map( + fn ($value, $field) => $this->mapType($types[$field], $value), + $row, + array_keys($row), + ); + } + + /** + * @param array $row + * @param array $types + * + * @return array + */ + private function mapAssociativeRow(array $row, array $types): array + { + assert($this->result !== null); + + $mappedRow = []; + foreach ($row as $field => $value) { + $mappedRow[$field] = $this->mapType($types[$field], $value); + } + + return $mappedRow; + } + + /** @return string|int|float|bool|null */ + private function mapType(string $postgresType, ?string $value) + { + if ($value === null) { + return null; + } + + switch ($postgresType) { + case 'bool': + switch ($value) { + case 't': + return true; + case 'f': + return false; + } + + throw UnexpectedValue::new($value, $postgresType); + + case 'bytea': + return hex2bin(substr($value, 2)); + + case 'float4': + case 'float8': + return (float) $value; + + case 'int2': + case 'int4': + return (int) $value; + + case 'int8': + return PHP_INT_SIZE >= 8 ? (int) $value : $value; + } + + return $value; + } +} diff --git a/doctrine/dbal/src/Driver/PgSQL/Statement.php b/doctrine/dbal/src/Driver/PgSQL/Statement.php new file mode 100644 index 000000000..75af66f30 --- /dev/null +++ b/doctrine/dbal/src/Driver/PgSQL/Statement.php @@ -0,0 +1,177 @@ + */ + private array $parameterMap; + + /** @var array */ + private array $parameters = []; + + /** @psalm-var array */ + private array $parameterTypes = []; + + /** + * @param PgSqlConnection|resource $connection + * @param array $parameterMap + */ + public function __construct($connection, string $name, array $parameterMap) + { + if (! is_resource($connection) && ! $connection instanceof PgSqlConnection) { + throw new TypeError(sprintf( + 'Expected connection to be a resource or an instance of %s, got %s.', + PgSqlConnection::class, + is_object($connection) ? get_class($connection) : gettype($connection), + )); + } + + $this->connection = $connection; + $this->name = $name; + $this->parameterMap = $parameterMap; + } + + public function __destruct() + { + if (! isset($this->connection)) { + return; + } + + @pg_query( + $this->connection, + 'DEALLOCATE ' . pg_escape_identifier($this->connection, $this->name), + ); + } + + /** {@inheritDoc} */ + public function bindValue($param, $value, $type = ParameterType::STRING): bool + { + if (! isset($this->parameterMap[$param])) { + throw UnknownParameter::new((string) $param); + } + + $this->parameters[$this->parameterMap[$param]] = $value; + $this->parameterTypes[$this->parameterMap[$param]] = $type; + + return true; + } + + /** {@inheritDoc} */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5563', + '%s is deprecated. Use bindValue() instead.', + __METHOD__, + ); + + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + + if (func_num_args() > 4) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4533', + 'The $driverOptions argument of Statement::bindParam() is deprecated.', + ); + } + + if (! isset($this->parameterMap[$param])) { + throw UnknownParameter::new((string) $param); + } + + $this->parameters[$this->parameterMap[$param]] = &$variable; + $this->parameterTypes[$this->parameterMap[$param]] = $type; + + return true; + } + + /** {@inheritDoc} */ + public function execute($params = null): Result + { + if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.', + ); + + foreach ($params as $param => $value) { + if (is_int($param)) { + $this->bindValue($param + 1, $value, ParameterType::STRING); + } else { + $this->bindValue($param, $value, ParameterType::STRING); + } + } + } + + ksort($this->parameters); + + $escapedParameters = []; + foreach ($this->parameters as $parameter => $value) { + switch ($this->parameterTypes[$parameter]) { + case ParameterType::BINARY: + case ParameterType::LARGE_OBJECT: + $escapedParameters[] = $value === null ? null : pg_escape_bytea( + $this->connection, + is_resource($value) ? stream_get_contents($value) : $value, + ); + break; + default: + $escapedParameters[] = $value; + } + } + + if (@pg_send_execute($this->connection, $this->name, $escapedParameters) !== true) { + throw new Exception(pg_last_error($this->connection)); + } + + $result = @pg_get_result($this->connection); + assert($result !== false); + + if ((bool) pg_result_error($result)) { + throw Exception::fromResult($result); + } + + return new Result($result); + } +} diff --git a/doctrine/dbal/src/Driver/SQLSrv/Connection.php b/doctrine/dbal/src/Driver/SQLSrv/Connection.php index 0295db7e5..16e45d110 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Connection.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Connection.php @@ -36,7 +36,7 @@ public function __construct($connection) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getServerVersion() { @@ -97,7 +97,7 @@ public function lastInsertId($name = null) Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.' + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); $result = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') @@ -136,9 +136,7 @@ public function rollBack(): bool return true; } - /** - * @return resource - */ + /** @return resource */ public function getNativeConnection() { return $this->connection; diff --git a/doctrine/dbal/src/Driver/SQLSrv/Driver.php b/doctrine/dbal/src/Driver/SQLSrv/Driver.php index 085b22412..fcbdb7734 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Driver.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Driver.php @@ -5,6 +5,7 @@ use Doctrine\DBAL\Driver\AbstractSQLServerDriver; use Doctrine\DBAL\Driver\AbstractSQLServerDriver\Exception\PortWithoutHost; use Doctrine\DBAL\Driver\SQLSrv\Exception\Error; +use SensitiveParameter; use function sqlsrv_configure; use function sqlsrv_connect; @@ -15,12 +16,14 @@ final class Driver extends AbstractSQLServerDriver { /** - * {@inheritdoc} + * {@inheritDoc} * * @return Connection */ - public function connect(array $params) - { + public function connect( + #[SensitiveParameter] + array $params + ) { $serverName = ''; if (isset($params['host'])) { diff --git a/doctrine/dbal/src/Driver/SQLSrv/Exception/Error.php b/doctrine/dbal/src/Driver/SQLSrv/Exception/Error.php index d3e633b5d..f39d5fc4c 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Exception/Error.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Exception/Error.php @@ -25,11 +25,8 @@ public static function new(): self $code = 0; foreach ((array) sqlsrv_errors(SQLSRV_ERR_ERRORS) as $error) { - $message .= 'SQLSTATE [' . $error['SQLSTATE'] . ', ' . $error['code'] . ']: ' . $error['message'] . "\n"; - - if ($sqlState === null) { - $sqlState = $error['SQLSTATE']; - } + $message .= 'SQLSTATE [' . $error['SQLSTATE'] . ', ' . $error['code'] . ']: ' . $error['message'] . "\n"; + $sqlState ??= $error['SQLSTATE']; if ($code !== 0) { continue; diff --git a/doctrine/dbal/src/Driver/SQLSrv/Result.php b/doctrine/dbal/src/Driver/SQLSrv/Result.php index 0e24002cc..db17e928d 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Result.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Result.php @@ -110,9 +110,7 @@ public function free(): void } } - /** - * @return mixed|false - */ + /** @return mixed|false */ private function fetch(int $fetchType) { return sqlsrv_fetch_array($this->statement, $fetchType) ?? false; diff --git a/doctrine/dbal/src/Driver/SQLSrv/Statement.php b/doctrine/dbal/src/Driver/SQLSrv/Statement.php index 035567a35..227c33456 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Statement.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Statement.php @@ -7,8 +7,10 @@ use Doctrine\DBAL\Driver\SQLSrv\Exception\Error; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use function assert; +use function func_num_args; use function is_int; use function sqlsrv_execute; use function SQLSRV_PHPTYPE_STREAM; @@ -32,10 +34,8 @@ final class Statement implements StatementInterface /** * The SQL statement to execute. - * - * @var string */ - private $sql; + private string $sql; /** * The SQLSRV statement resource. @@ -49,14 +49,14 @@ final class Statement implements StatementInterface * * @var array */ - private $variables = []; + private array $variables = []; /** * Bound parameter types. * * @var array */ - private $types = []; + private array $types = []; /** * Append to any INSERT query to retrieve the last insert id. @@ -82,12 +82,21 @@ public function __construct($conn, $sql) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { assert(is_int($param)); + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + $this->variables[$param] = $value; $this->types[$param] = $type; @@ -95,12 +104,30 @@ public function bindValue($param, $value, $type = ParameterType::STRING): bool } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@see bindValue()} instead. */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5563', + '%s is deprecated. Use bindValue() instead.', + __METHOD__, + ); + assert(is_int($param)); + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + $this->variables[$param] =& $variable; $this->types[$param] = $type; @@ -111,23 +138,28 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le } /** - * {@inheritdoc} + * {@inheritDoc} */ public function execute($params = null): ResultInterface { if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.', + ); + foreach ($params as $key => $val) { if (is_int($key)) { - $this->bindValue($key + 1, $val); + $this->bindValue($key + 1, $val, ParameterType::STRING); } else { - $this->bindValue($key, $val); + $this->bindValue($key, $val, ParameterType::STRING); } } } - if ($this->stmt === null) { - $this->stmt = $this->prepare(); - } + $this->stmt ??= $this->prepare(); if (! sqlsrv_execute($this->stmt)) { throw Error::new(); diff --git a/doctrine/dbal/src/Driver/SQLite3/Connection.php b/doctrine/dbal/src/Driver/SQLite3/Connection.php new file mode 100644 index 000000000..91b9b5ffe --- /dev/null +++ b/doctrine/dbal/src/Driver/SQLite3/Connection.php @@ -0,0 +1,107 @@ +connection = $connection; + } + + public function prepare(string $sql): Statement + { + try { + $statement = $this->connection->prepare($sql); + } catch (\Exception $e) { + throw Exception::new($e); + } + + assert($statement !== false); + + return new Statement($this->connection, $statement); + } + + public function query(string $sql): Result + { + try { + $result = $this->connection->query($sql); + } catch (\Exception $e) { + throw Exception::new($e); + } + + assert($result !== false); + + return new Result($result, $this->connection->changes()); + } + + /** @inheritDoc */ + public function quote($value, $type = ParameterType::STRING): string + { + return sprintf('\'%s\'', SQLite3::escapeString($value)); + } + + public function exec(string $sql): int + { + try { + $this->connection->exec($sql); + } catch (\Exception $e) { + throw Exception::new($e); + } + + return $this->connection->changes(); + } + + /** @inheritDoc */ + public function lastInsertId($name = null): int + { + return $this->connection->lastInsertRowID(); + } + + public function beginTransaction(): bool + { + try { + return $this->connection->exec('BEGIN'); + } catch (\Exception $e) { + return false; + } + } + + public function commit(): bool + { + try { + return $this->connection->exec('COMMIT'); + } catch (\Exception $e) { + return false; + } + } + + public function rollBack(): bool + { + try { + return $this->connection->exec('ROLLBACK'); + } catch (\Exception $e) { + return false; + } + } + + public function getNativeConnection(): SQLite3 + { + return $this->connection; + } + + public function getServerVersion(): string + { + return SQLite3::version()['versionString']; + } +} diff --git a/doctrine/dbal/src/Driver/SQLite3/Driver.php b/doctrine/dbal/src/Driver/SQLite3/Driver.php new file mode 100644 index 000000000..fecc4819d --- /dev/null +++ b/doctrine/dbal/src/Driver/SQLite3/Driver.php @@ -0,0 +1,49 @@ +enableExceptions(true); + + UserDefinedFunctions::register([$connection, 'createFunction']); + + return new Connection($connection); + } +} diff --git a/doctrine/dbal/src/Driver/SQLite3/Exception.php b/doctrine/dbal/src/Driver/SQLite3/Exception.php new file mode 100644 index 000000000..3ca1190bc --- /dev/null +++ b/doctrine/dbal/src/Driver/SQLite3/Exception.php @@ -0,0 +1,18 @@ +getMessage(), null, (int) $exception->getCode(), $exception); + } +} diff --git a/doctrine/dbal/src/Driver/SQLite3/Result.php b/doctrine/dbal/src/Driver/SQLite3/Result.php new file mode 100644 index 000000000..3881e189a --- /dev/null +++ b/doctrine/dbal/src/Driver/SQLite3/Result.php @@ -0,0 +1,91 @@ +result = $result; + $this->changes = $changes; + } + + /** @inheritDoc */ + public function fetchNumeric() + { + if ($this->result === null) { + return false; + } + + return $this->result->fetchArray(SQLITE3_NUM); + } + + /** @inheritDoc */ + public function fetchAssociative() + { + if ($this->result === null) { + return false; + } + + return $this->result->fetchArray(SQLITE3_ASSOC); + } + + /** @inheritDoc */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** @inheritDoc */ + public function fetchAllNumeric(): array + { + return FetchUtils::fetchAllNumeric($this); + } + + /** @inheritDoc */ + public function fetchAllAssociative(): array + { + return FetchUtils::fetchAllAssociative($this); + } + + /** @inheritDoc */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + + public function rowCount(): int + { + return $this->changes; + } + + public function columnCount(): int + { + if ($this->result === null) { + return 0; + } + + return $this->result->numColumns(); + } + + public function free(): void + { + if ($this->result === null) { + return; + } + + $this->result->finalize(); + $this->result = null; + } +} diff --git a/doctrine/dbal/src/Driver/SQLite3/Statement.php b/doctrine/dbal/src/Driver/SQLite3/Statement.php new file mode 100644 index 000000000..a4166aa61 --- /dev/null +++ b/doctrine/dbal/src/Driver/SQLite3/Statement.php @@ -0,0 +1,136 @@ + SQLITE3_NULL, + ParameterType::INTEGER => SQLITE3_INTEGER, + ParameterType::STRING => SQLITE3_TEXT, + ParameterType::ASCII => SQLITE3_TEXT, + ParameterType::BINARY => SQLITE3_BLOB, + ParameterType::LARGE_OBJECT => SQLITE3_BLOB, + ParameterType::BOOLEAN => SQLITE3_INTEGER, + ]; + + private SQLite3 $connection; + private SQLite3Stmt $statement; + + /** @internal The statement can be only instantiated by its driver connection. */ + public function __construct(SQLite3 $connection, SQLite3Stmt $statement) + { + $this->connection = $connection; + $this->statement = $statement; + } + + /** + * @throws UnknownParameterType + * + * {@inheritDoc} + * + * @psalm-assert ParameterType::* $type + */ + public function bindValue($param, $value, $type = ParameterType::STRING): bool + { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + + return $this->statement->bindValue($param, $value, $this->convertParamType($type)); + } + + /** + * @throws UnknownParameterType + * + * {@inheritDoc} + * + * @psalm-assert ParameterType::* $type + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5563', + '%s is deprecated. Use bindValue() instead.', + __METHOD__, + ); + + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + + return $this->statement->bindParam($param, $variable, $this->convertParamType($type)); + } + + /** @inheritDoc */ + public function execute($params = null): Result + { + if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.', + ); + + foreach ($params as $param => $value) { + if (is_int($param)) { + $this->bindValue($param + 1, $value, ParameterType::STRING); + } else { + $this->bindValue($param, $value, ParameterType::STRING); + } + } + } + + try { + $result = $this->statement->execute(); + } catch (\Exception $e) { + throw Exception::new($e); + } + + assert($result !== false); + + return new Result($result, $this->connection->changes()); + } + + /** + * @psalm-return value-of + * + * @psalm-assert ParameterType::* $type + */ + private function convertParamType(int $type): int + { + if (! isset(self::PARAM_TYPE_MAP[$type])) { + throw UnknownParameterType::new($type); + } + + return self::PARAM_TYPE_MAP[$type]; + } +} diff --git a/doctrine/dbal/src/Driver/Statement.php b/doctrine/dbal/src/Driver/Statement.php index 50fac842d..b273128f1 100644 --- a/doctrine/dbal/src/Driver/Statement.php +++ b/doctrine/dbal/src/Driver/Statement.php @@ -43,6 +43,8 @@ public function bindValue($param, $value, $type = ParameterType::STRING); * of stored procedures that return data as output parameters, and some also as input/output * parameters that both send in data and are updated to receive it. * + * @deprecated Use {@see bindValue()} instead. + * * @param string|int $param Parameter identifier. For a prepared statement using named placeholders, * this will be a parameter name of the form :name. For a prepared statement using * question mark placeholders, this will be the 1-indexed position of the parameter. diff --git a/doctrine/dbal/src/DriverManager.php b/doctrine/dbal/src/DriverManager.php index a2ed23611..056f42084 100644 --- a/doctrine/dbal/src/DriverManager.php +++ b/doctrine/dbal/src/DriverManager.php @@ -7,26 +7,23 @@ use Doctrine\DBAL\Driver\Mysqli; use Doctrine\DBAL\Driver\OCI8; use Doctrine\DBAL\Driver\PDO; +use Doctrine\DBAL\Driver\PgSQL; +use Doctrine\DBAL\Driver\SQLite3; use Doctrine\DBAL\Driver\SQLSrv; +use Doctrine\DBAL\Exception\MalformedDsnException; +use Doctrine\DBAL\Tools\DsnParser; +use Doctrine\Deprecations\Deprecation; +use SensitiveParameter; use function array_keys; use function array_merge; -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; -use function preg_replace; -use function rawurldecode; -use function str_replace; -use function strpos; -use function substr; +use function is_a; /** * Factory for creating {@see Connection} instances. * * @psalm-type OverrideParams = array{ + * application_name?: string, * charset?: string, * dbname?: string, * default_dbname?: string, @@ -36,13 +33,16 @@ * host?: string, * password?: string, * path?: string, - * pdo?: \PDO, + * persistent?: bool, * platform?: Platforms\AbstractPlatform, * port?: int, + * serverVersion?: string, + * url?: string, * user?: string, * unix_socket?: string, * } * @psalm-type Params = array{ + * application_name?: string, * charset?: string, * dbname?: string, * defaultTableOptions?: array, @@ -57,7 +57,7 @@ * memory?: bool, * password?: string, * path?: string, - * pdo?: \PDO, + * persistent?: bool, * platform?: Platforms\AbstractPlatform, * port?: int, * primary?: OverrideParams, @@ -65,6 +65,7 @@ * serverVersion?: string, * sharding?: array, * slaves?: array, + * url?: string, * user?: string, * wrapperClass?: class-string, * unix_socket?: string, @@ -86,15 +87,20 @@ final class DriverManager 'ibm_db2' => IBMDB2\Driver::class, 'pdo_sqlsrv' => PDO\SQLSrv\Driver::class, 'mysqli' => Mysqli\Driver::class, + 'pgsql' => PgSQL\Driver::class, 'sqlsrv' => SQLSrv\Driver::class, + 'sqlite3' => SQLite3\Driver::class, ]; /** * List of URL schemes from a database URL and their mappings to driver. * - * @var string[] + * @deprecated Use actual driver names instead. + * + * @var array + * @psalm-var array> */ - private static $driverSchemeAliases = [ + private static array $driverSchemeAliases = [ 'db2' => 'ibm_db2', 'mssql' => 'pdo_sqlsrv', 'mysql' => 'pdo_mysql', @@ -147,52 +153,24 @@ private function __construct() * * @param Configuration|null $config The configuration to use. * @param EventManager|null $eventManager The event manager to use. - * @psalm-param array{ - * charset?: string, - * dbname?: string, - * default_dbname?: string, - * driver?: key-of, - * driverClass?: class-string, - * driverOptions?: array, - * host?: string, - * keepSlave?: bool, - * keepReplica?: bool, - * master?: OverrideParams, - * memory?: bool, - * password?: string, - * path?: string, - * pdo?: \PDO, - * platform?: Platforms\AbstractPlatform, - * port?: int, - * primary?: OverrideParams, - * replica?: array, - * sharding?: array, - * slaves?: array, - * user?: string, - * wrapperClass?: class-string, - * } $params + * @psalm-param Params $params * - * @psalm-return ($params is array{wrapperClass:mixed} ? T : Connection) + * @psalm-return ($params is array{wrapperClass: class-string} ? T : Connection) * * @throws Exception * * @template T of Connection */ public static function getConnection( + #[SensitiveParameter] array $params, ?Configuration $config = null, ?EventManager $eventManager = null ): Connection { // create default config and event manager, if not set - if ($config === null) { - $config = new Configuration(); - } - - if ($eventManager === null) { - $eventManager = new EventManager(); - } - - $params = self::parseDatabaseUrl($params); + $config ??= new Configuration(); + $eventManager ??= new EventManager(); + $params = self::parseDatabaseUrl($params); // URL support for PrimaryReplicaConnection if (isset($params['primary'])) { @@ -205,20 +183,15 @@ public static function getConnection( } } - $driver = self::createDriver($params); + $driver = self::createDriver($params['driver'] ?? null, $params['driverClass'] ?? null); foreach ($config->getMiddlewares() as $middleware) { $driver = $middleware->wrap($driver); } - $wrapperClass = Connection::class; - if (isset($params['wrapperClass'])) { - if (! is_subclass_of($params['wrapperClass'], $wrapperClass)) { - throw Exception::invalidWrapperClass($params['wrapperClass']); - } - - /** @var class-string $wrapperClass */ - $wrapperClass = $params['wrapperClass']; + $wrapperClass = $params['wrapperClass'] ?? Connection::class; + if (! is_a($wrapperClass, Connection::class, true)) { + throw Exception::invalidWrapperClass($wrapperClass); } return new $wrapperClass($params, $driver, $config, $eventManager); @@ -228,6 +201,7 @@ public static function getConnection( * Returns the list of supported drivers. * * @return string[] + * @psalm-return list> */ public static function getAvailableDrivers(): array { @@ -235,46 +209,28 @@ public static function getAvailableDrivers(): array } /** - * @param array $params - * @psalm-param Params $params - * @phpstan-param array $params - * * @throws Exception + * + * @psalm-assert key-of|null $driver + * @psalm-assert class-string|null $driverClass */ - private static function createDriver(array $params): Driver + private static function createDriver(?string $driver, ?string $driverClass): Driver { - if (isset($params['driverClass'])) { - $interfaces = class_implements($params['driverClass']); - - if ($interfaces === false || ! in_array(Driver::class, $interfaces, true)) { - throw Exception::invalidDriverClass($params['driverClass']); + if ($driverClass === null) { + if ($driver === null) { + throw Exception::driverRequired(); } - return new $params['driverClass'](); - } - - if (isset($params['driver'])) { - if (! isset(self::DRIVER_MAP[$params['driver']])) { - throw Exception::unknownDriver($params['driver'], array_keys(self::DRIVER_MAP)); + if (! isset(self::DRIVER_MAP[$driver])) { + throw Exception::unknownDriver($driver, array_keys(self::DRIVER_MAP)); } - $class = self::DRIVER_MAP[$params['driver']]; - - return new $class(); + $driverClass = self::DRIVER_MAP[$driver]; + } elseif (! is_a($driverClass, Driver::class, true)) { + throw Exception::invalidDriverClass($driverClass); } - throw Exception::driverRequired(); - } - - /** - * Normalizes the given connection URL path. - * - * @return string The normalized connection URL path - */ - private static function normalizeDatabaseUrlPath(string $urlPath): string - { - // Trim leading slash from URL path. - return substr($urlPath, 1); + return new $driverClass(); } /** @@ -283,189 +239,44 @@ private static function normalizeDatabaseUrlPath(string $urlPath): string * * @param mixed[] $params The list of parameters. * @psalm-param Params $params - * @phpstan-param array $params * * @return mixed[] A modified list of parameters with info from a database * URL extracted into indidivual parameter parts. * @psalm-return Params - * @phpstan-return array * * @throws Exception */ - private static function parseDatabaseUrl(array $params): array - { + private static function parseDatabaseUrl( + #[SensitiveParameter] + array $params + ): array { if (! isset($params['url'])) { return $params; } - // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid - $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $params['url']); - $url = parse_url($url); - - if ($url === false) { - throw new Exception('Malformed parameter "url".'); - } - - foreach ($url as $param => $value) { - if (! is_string($value)) { - continue; - } - - $url[$param] = rawurldecode($value); - } - - $params = self::parseDatabaseUrlScheme($url['scheme'] ?? null, $params); - - if (isset($url['host'])) { - $params['host'] = $url['host']; - } - - if (isset($url['port'])) { - $params['port'] = $url['port']; - } - - if (isset($url['user'])) { - $params['user'] = $url['user']; - } - - if (isset($url['pass'])) { - $params['password'] = $url['pass']; - } - - $params = self::parseDatabaseUrlPath($url, $params); - $params = self::parseDatabaseUrlQuery($url, $params); - - return $params; - } - - /** - * Parses the given connection URL and resolves the given connection parameters. - * - * Assumes that the connection URL scheme is already parsed and resolved into the given connection parameters - * via {@see parseDatabaseUrlScheme}. - * - * @see parseDatabaseUrlScheme - * - * @param mixed[] $url The URL parts to evaluate. - * @param mixed[] $params The connection parameters to resolve. - * - * @return mixed[] The resolved connection parameters. - */ - private static function parseDatabaseUrlPath(array $url, array $params): array - { - if (! isset($url['path'])) { - return $params; - } - - $url['path'] = self::normalizeDatabaseUrlPath($url['path']); - - // If we do not have a known DBAL driver, we do not know any connection URL path semantics to evaluate - // and therefore treat the path as regular DBAL connection URL path. - if (! isset($params['driver'])) { - return self::parseRegularDatabaseUrlPath($url, $params); + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5843', + 'The "url" connection parameter is deprecated. Please use %s to parse a database url before calling %s.', + DsnParser::class, + self::class, + ); + + $parser = new DsnParser(self::$driverSchemeAliases); + try { + $parsedParams = $parser->parse($params['url']); + } catch (MalformedDsnException $e) { + throw new Exception('Malformed parameter "url".', 0, $e); } - if (strpos($params['driver'], 'sqlite') !== false) { - return self::parseSqliteDatabaseUrlPath($url, $params); - } - - return self::parseRegularDatabaseUrlPath($url, $params); - } - - /** - * Parses the query part of the given connection URL and resolves the given connection parameters. - * - * @param mixed[] $url The connection URL parts to evaluate. - * @param mixed[] $params The connection parameters to resolve. - * - * @return mixed[] The resolved connection parameters. - */ - private static function parseDatabaseUrlQuery(array $url, array $params): array - { - if (! isset($url['query'])) { - return $params; - } - - $query = []; - - parse_str($url['query'], $query); // simply ingest query as extra params, e.g. charset or sslmode - - return array_merge($params, $query); // parse_str wipes existing array elements - } - - /** - * Parses the given regular connection URL and resolves the given connection parameters. - * - * Assumes that the "path" URL part is already normalized via {@see normalizeDatabaseUrlPath}. - * - * @see normalizeDatabaseUrlPath - * - * @param mixed[] $url The regular connection URL parts to evaluate. - * @param mixed[] $params The connection parameters to resolve. - * - * @return mixed[] The resolved connection parameters. - */ - private static function parseRegularDatabaseUrlPath(array $url, array $params): array - { - $params['dbname'] = $url['path']; - - return $params; - } - - /** - * Parses the given SQLite connection URL and resolves the given connection parameters. - * - * Assumes that the "path" URL part is already normalized via {@see normalizeDatabaseUrlPath}. - * - * @see normalizeDatabaseUrlPath - * - * @param mixed[] $url The SQLite connection URL parts to evaluate. - * @param mixed[] $params The connection parameters to resolve. - * - * @return mixed[] The resolved connection parameters. - */ - private static function parseSqliteDatabaseUrlPath(array $url, array $params): array - { - if ($url['path'] === ':memory:') { - $params['memory'] = true; - - return $params; - } - - $params['path'] = $url['path']; // pdo_sqlite driver uses 'path' instead of 'dbname' key - - return $params; - } - - /** - * Parses the scheme part from given connection URL and resolves the given connection parameters. - * - * @param string|null $scheme The connection URL scheme, if available - * @param mixed[] $params The connection parameters to resolve. - * - * @return mixed[] The resolved connection parameters. - * - * @throws Exception If parsing failed or resolution is not possible. - */ - private static function parseDatabaseUrlScheme(?string $scheme, array $params): array - { - if ($scheme !== null) { + if (isset($parsedParams['driver'])) { // The requested driver from the URL scheme takes precedence // over the default custom driver from the connection parameters (if any). unset($params['driverClass']); - - // URL schemes must not contain underscores, but dashes are ok - $driver = str_replace('-', '_', $scheme); - - // The requested driver from the URL scheme takes precedence over the - // default driver from the connection parameters. If the driver is - // an alias (e.g. "postgres"), map it to the actual name ("pdo-pgsql"). - // Otherwise, let checkParams decide later if the driver exists. - $params['driver'] = self::$driverSchemeAliases[$driver] ?? $driver; - - return $params; } + $params = array_merge($params, $parsedParams); + // If a schemeless connection URL is given, we require a default driver or default custom driver // as connection parameter. if (! isset($params['driverClass']) && ! isset($params['driver'])) { diff --git a/doctrine/dbal/src/Event/ConnectionEventArgs.php b/doctrine/dbal/src/Event/ConnectionEventArgs.php index eeddb91a5..9a69c2541 100644 --- a/doctrine/dbal/src/Event/ConnectionEventArgs.php +++ b/doctrine/dbal/src/Event/ConnectionEventArgs.php @@ -7,20 +7,19 @@ /** * Event Arguments used when a Driver connection is established inside Doctrine\DBAL\Connection. + * + * @deprecated */ class ConnectionEventArgs extends EventArgs { - /** @var Connection */ - private $connection; + private Connection $connection; public function __construct(Connection $connection) { $this->connection = $connection; } - /** - * @return Connection - */ + /** @return Connection */ public function getConnection() { return $this->connection; diff --git a/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php b/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php index d6d1fd9d1..9598f43cc 100644 --- a/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php +++ b/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php @@ -23,6 +23,8 @@ * NLS_DATE_FORMAT="YYYY-MM-DD HH24:MI:SS" * NLS_TIMESTAMP_FORMAT="YYYY-MM-DD HH24:MI:SS" * NLS_TIMESTAMP_TZ_FORMAT="YYYY-MM-DD HH24:MI:SS TZH:TZM" + * + * @deprecated Use {@see \Doctrine\DBAL\Driver\OCI8\Middleware\InitializeSession} instead. */ class OracleSessionInit implements EventSubscriber { @@ -35,9 +37,7 @@ class OracleSessionInit implements EventSubscriber 'NLS_NUMERIC_CHARACTERS' => '.,', ]; - /** - * @param string[] $oracleSessionVars - */ + /** @param string[] $oracleSessionVars */ public function __construct(array $oracleSessionVars = []) { $this->_defaultSessionVars = array_merge($this->_defaultSessionVars, $oracleSessionVars); @@ -68,7 +68,7 @@ public function postConnect(ConnectionEventArgs $args) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSubscribedEvents() { diff --git a/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php b/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php index f7a4e9129..4ce32d628 100644 --- a/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php +++ b/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php @@ -9,15 +9,15 @@ /** * Session init listener for executing a single SQL statement right after a connection is opened. + * + * @deprecated Implement a middleware instead. */ class SQLSessionInit implements EventSubscriber { /** @var string */ protected $sql; - /** - * @param string $sql - */ + /** @param string $sql */ public function __construct($sql) { $this->sql = $sql; @@ -34,7 +34,7 @@ public function postConnect(ConnectionEventArgs $args) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSubscribedEvents() { diff --git a/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php b/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php new file mode 100644 index 000000000..950f05f48 --- /dev/null +++ b/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php @@ -0,0 +1,30 @@ +getConnection()->executeStatement('PRAGMA foreign_keys=ON'); + } + + /** + * {@inheritDoc} + */ + public function getSubscribedEvents() + { + return [Events::postConnect]; + } +} diff --git a/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php index 5a1160a95..9f3ff6ea7 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php @@ -13,20 +13,17 @@ /** * Event Arguments used when SQL queries for adding table columns are generated inside {@see AbstractPlatform}. + * + * @deprecated */ class SchemaAlterTableAddColumnEventArgs extends SchemaEventArgs { - /** @var Column */ - private $column; - - /** @var TableDiff */ - private $tableDiff; - - /** @var AbstractPlatform */ - private $platform; + private Column $column; + private TableDiff $tableDiff; + private AbstractPlatform $platform; /** @var string[] */ - private $sql = []; + private array $sql = []; public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform) { @@ -35,25 +32,19 @@ public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatfo $this->platform = $platform; } - /** - * @return Column - */ + /** @return Column */ public function getColumn() { return $this->column; } - /** - * @return TableDiff - */ + /** @return TableDiff */ public function getTableDiff() { return $this->tableDiff; } - /** - * @return AbstractPlatform - */ + /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; @@ -73,7 +64,7 @@ public function addSql($sql) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3580', 'Passing multiple SQL statements as an array to SchemaAlterTableAddColumnEventaArrgs::addSql() ' . - 'is deprecated. Pass each statement as an individual argument instead.' + 'is deprecated. Pass each statement as an individual argument instead.', ); } @@ -82,9 +73,7 @@ public function addSql($sql) return $this; } - /** - * @return string[] - */ + /** @return string[] */ public function getSql() { return $this->sql; diff --git a/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php index e3fe778dc..9ba37aade 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php @@ -12,20 +12,17 @@ /** * Event Arguments used when SQL queries for changing table columns are generated inside {@see AbstractPlatform}. + * + * @deprecated */ class SchemaAlterTableChangeColumnEventArgs extends SchemaEventArgs { - /** @var ColumnDiff */ - private $columnDiff; - - /** @var TableDiff */ - private $tableDiff; - - /** @var AbstractPlatform */ - private $platform; + private ColumnDiff $columnDiff; + private TableDiff $tableDiff; + private AbstractPlatform $platform; /** @var string[] */ - private $sql = []; + private array $sql = []; public function __construct(ColumnDiff $columnDiff, TableDiff $tableDiff, AbstractPlatform $platform) { @@ -34,25 +31,19 @@ public function __construct(ColumnDiff $columnDiff, TableDiff $tableDiff, Abstra $this->platform = $platform; } - /** - * @return ColumnDiff - */ + /** @return ColumnDiff */ public function getColumnDiff() { return $this->columnDiff; } - /** - * @return TableDiff - */ + /** @return TableDiff */ public function getTableDiff() { return $this->tableDiff; } - /** - * @return AbstractPlatform - */ + /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; @@ -72,9 +63,7 @@ public function addSql($sql) return $this; } - /** - * @return string[] - */ + /** @return string[] */ public function getSql() { return $this->sql; diff --git a/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php index d51b4a204..07c065a9a 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php @@ -11,17 +11,16 @@ /** * Event Arguments used when SQL queries for creating tables are generated inside {@see AbstractPlatform}. + * + * @deprecated */ class SchemaAlterTableEventArgs extends SchemaEventArgs { - /** @var TableDiff */ - private $tableDiff; - - /** @var AbstractPlatform */ - private $platform; + private TableDiff $tableDiff; + private AbstractPlatform $platform; /** @var string[] */ - private $sql = []; + private array $sql = []; public function __construct(TableDiff $tableDiff, AbstractPlatform $platform) { @@ -29,17 +28,13 @@ public function __construct(TableDiff $tableDiff, AbstractPlatform $platform) $this->platform = $platform; } - /** - * @return TableDiff - */ + /** @return TableDiff */ public function getTableDiff() { return $this->tableDiff; } - /** - * @return AbstractPlatform - */ + /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; @@ -59,9 +54,7 @@ public function addSql($sql) return $this; } - /** - * @return string[] - */ + /** @return string[] */ public function getSql() { return $this->sql; diff --git a/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php index 47ed242bc..4122b418c 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php @@ -12,20 +12,17 @@ /** * Event Arguments used when SQL queries for removing table columns are generated inside {@see AbstractPlatform}. + * + * @deprecated */ class SchemaAlterTableRemoveColumnEventArgs extends SchemaEventArgs { - /** @var Column */ - private $column; - - /** @var TableDiff */ - private $tableDiff; - - /** @var AbstractPlatform */ - private $platform; + private Column $column; + private TableDiff $tableDiff; + private AbstractPlatform $platform; /** @var string[] */ - private $sql = []; + private array $sql = []; public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform) { @@ -34,25 +31,19 @@ public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatfo $this->platform = $platform; } - /** - * @return Column - */ + /** @return Column */ public function getColumn() { return $this->column; } - /** - * @return TableDiff - */ + /** @return TableDiff */ public function getTableDiff() { return $this->tableDiff; } - /** - * @return AbstractPlatform - */ + /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; @@ -72,9 +63,7 @@ public function addSql($sql) return $this; } - /** - * @return string[] - */ + /** @return string[] */ public function getSql() { return $this->sql; diff --git a/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php index 2b2cb2201..21d3c1645 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php @@ -12,27 +12,22 @@ /** * Event Arguments used when SQL queries for renaming table columns are generated inside {@see AbstractPlatform}. + * + * @deprecated */ class SchemaAlterTableRenameColumnEventArgs extends SchemaEventArgs { /** @var string */ private $oldColumnName; - /** @var Column */ - private $column; - - /** @var TableDiff */ - private $tableDiff; - - /** @var AbstractPlatform */ - private $platform; + private Column $column; + private TableDiff $tableDiff; + private AbstractPlatform $platform; /** @var string[] */ - private $sql = []; + private array $sql = []; - /** - * @param string $oldColumnName - */ + /** @param string $oldColumnName */ public function __construct($oldColumnName, Column $column, TableDiff $tableDiff, AbstractPlatform $platform) { $this->oldColumnName = $oldColumnName; @@ -41,33 +36,25 @@ public function __construct($oldColumnName, Column $column, TableDiff $tableDiff $this->platform = $platform; } - /** - * @return string - */ + /** @return string */ public function getOldColumnName() { return $this->oldColumnName; } - /** - * @return Column - */ + /** @return Column */ public function getColumn() { return $this->column; } - /** - * @return TableDiff - */ + /** @return TableDiff */ public function getTableDiff() { return $this->tableDiff; } - /** - * @return AbstractPlatform - */ + /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; @@ -87,9 +74,7 @@ public function addSql($sql) return $this; } - /** - * @return string[] - */ + /** @return string[] */ public function getSql() { return $this->sql; diff --git a/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php b/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php index 997a90d1f..04fcbde9c 100644 --- a/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php @@ -7,11 +7,12 @@ /** * Event Arguments used when the portable column definition is generated inside {@see AbstractPlatform}. + * + * @deprecated */ class SchemaColumnDefinitionEventArgs extends SchemaEventArgs { - /** @var Column|null */ - private $column; + private ?Column $column = null; /** * Raw column data as fetched from the database. @@ -26,8 +27,7 @@ class SchemaColumnDefinitionEventArgs extends SchemaEventArgs /** @var string */ private $database; - /** @var Connection */ - private $connection; + private Connection $connection; /** * @param mixed[] $tableColumn @@ -55,41 +55,31 @@ public function setColumn(?Column $column = null) return $this; } - /** - * @return Column|null - */ + /** @return Column|null */ public function getColumn() { return $this->column; } - /** - * @return mixed[] - */ + /** @return mixed[] */ public function getTableColumn() { return $this->tableColumn; } - /** - * @return string - */ + /** @return string */ public function getTable() { return $this->table; } - /** - * @return string - */ + /** @return string */ public function getDatabase() { return $this->database; } - /** - * @return Connection - */ + /** @return Connection */ public function getConnection() { return $this->connection; diff --git a/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php index 6ba1bc708..54f134d14 100644 --- a/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php @@ -12,20 +12,17 @@ /** * Event Arguments used when SQL queries for creating table columns are generated inside {@see AbstractPlatform}. + * + * @deprecated */ class SchemaCreateTableColumnEventArgs extends SchemaEventArgs { - /** @var Column */ - private $column; - - /** @var Table */ - private $table; - - /** @var AbstractPlatform */ - private $platform; + private Column $column; + private Table $table; + private AbstractPlatform $platform; /** @var string[] */ - private $sql = []; + private array $sql = []; public function __construct(Column $column, Table $table, AbstractPlatform $platform) { @@ -34,25 +31,19 @@ public function __construct(Column $column, Table $table, AbstractPlatform $plat $this->platform = $platform; } - /** - * @return Column - */ + /** @return Column */ public function getColumn() { return $this->column; } - /** - * @return Table - */ + /** @return Table */ public function getTable() { return $this->table; } - /** - * @return AbstractPlatform - */ + /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; @@ -72,9 +63,7 @@ public function addSql($sql) return $this; } - /** - * @return string[] - */ + /** @return string[] */ public function getSql() { return $this->sql; diff --git a/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php b/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php index d83f7a264..a7d548deb 100644 --- a/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php @@ -11,23 +11,23 @@ /** * Event Arguments used when SQL queries for creating tables are generated inside {@see AbstractPlatform}. + * + * @deprecated */ class SchemaCreateTableEventArgs extends SchemaEventArgs { - /** @var Table */ - private $table; + private Table $table; /** @var mixed[][] */ - private $columns; + private array $columns; /** @var mixed[] */ - private $options; + private array $options; - /** @var AbstractPlatform */ - private $platform; + private AbstractPlatform $platform; /** @var string[] */ - private $sql = []; + private array $sql = []; /** * @param mixed[][] $columns @@ -41,33 +41,25 @@ public function __construct(Table $table, array $columns, array $options, Abstra $this->platform = $platform; } - /** - * @return Table - */ + /** @return Table */ public function getTable() { return $this->table; } - /** - * @return mixed[][] - */ + /** @return mixed[][] */ public function getColumns() { return $this->columns; } - /** - * @return mixed[] - */ + /** @return mixed[] */ public function getOptions() { return $this->options; } - /** - * @return AbstractPlatform - */ + /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; @@ -87,9 +79,7 @@ public function addSql($sql) return $this; } - /** - * @return string[] - */ + /** @return string[] */ public function getSql() { return $this->sql; diff --git a/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php b/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php index 3d5dd3c01..f45e3a15a 100644 --- a/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php @@ -8,14 +8,15 @@ /** * Event Arguments used when the SQL query for dropping tables are generated inside {@see AbstractPlatform}. + * + * @deprecated */ class SchemaDropTableEventArgs extends SchemaEventArgs { /** @var string|Table */ private $table; - /** @var AbstractPlatform */ - private $platform; + private AbstractPlatform $platform; /** @var string|null */ private $sql; @@ -31,17 +32,13 @@ public function __construct($table, AbstractPlatform $platform) $this->platform = $platform; } - /** - * @return string|Table - */ + /** @return string|Table */ public function getTable() { return $this->table; } - /** - * @return AbstractPlatform - */ + /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; @@ -59,9 +56,7 @@ public function setSql($sql) return $this; } - /** - * @return string|null - */ + /** @return string|null */ public function getSql() { return $this->sql; diff --git a/doctrine/dbal/src/Event/SchemaEventArgs.php b/doctrine/dbal/src/Event/SchemaEventArgs.php index df2bdae09..77d1d3908 100644 --- a/doctrine/dbal/src/Event/SchemaEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaEventArgs.php @@ -6,15 +6,14 @@ /** * Base class for schema related events. + * + * @deprecated */ class SchemaEventArgs extends EventArgs { - /** @var bool */ - private $preventDefault = false; + private bool $preventDefault = false; - /** - * @return SchemaEventArgs - */ + /** @return SchemaEventArgs */ public function preventDefault() { $this->preventDefault = true; @@ -22,9 +21,7 @@ public function preventDefault() return $this; } - /** - * @return bool - */ + /** @return bool */ public function isDefaultPrevented() { return $this->preventDefault; diff --git a/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php b/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php index 7e9f0db6d..dbee55a0e 100644 --- a/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php @@ -7,24 +7,24 @@ /** * Event Arguments used when the portable index definition is generated inside {@see AbstractSchemaManager}. + * + * @deprecated */ class SchemaIndexDefinitionEventArgs extends SchemaEventArgs { - /** @var Index|null */ - private $index; + private ?Index $index = null; /** * Raw index data as fetched from the database. * * @var mixed[] */ - private $tableIndex; + private array $tableIndex; /** @var string */ private $table; - /** @var Connection */ - private $connection; + private Connection $connection; /** * @param mixed[] $tableIndex @@ -49,33 +49,25 @@ public function setIndex(?Index $index = null) return $this; } - /** - * @return Index|null - */ + /** @return Index|null */ public function getIndex() { return $this->index; } - /** - * @return mixed[] - */ + /** @return mixed[] */ public function getTableIndex() { return $this->tableIndex; } - /** - * @return string - */ + /** @return string */ public function getTable() { return $this->table; } - /** - * @return Connection - */ + /** @return Connection */ public function getConnection() { return $this->connection; diff --git a/doctrine/dbal/src/Event/TransactionBeginEventArgs.php b/doctrine/dbal/src/Event/TransactionBeginEventArgs.php index 946e8f2eb..be4ccdf15 100644 --- a/doctrine/dbal/src/Event/TransactionBeginEventArgs.php +++ b/doctrine/dbal/src/Event/TransactionBeginEventArgs.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Event; +/** @deprecated */ class TransactionBeginEventArgs extends TransactionEventArgs { } diff --git a/doctrine/dbal/src/Event/TransactionCommitEventArgs.php b/doctrine/dbal/src/Event/TransactionCommitEventArgs.php index c87d05751..75766b6ff 100644 --- a/doctrine/dbal/src/Event/TransactionCommitEventArgs.php +++ b/doctrine/dbal/src/Event/TransactionCommitEventArgs.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Event; +/** @deprecated */ class TransactionCommitEventArgs extends TransactionEventArgs { } diff --git a/doctrine/dbal/src/Event/TransactionEventArgs.php b/doctrine/dbal/src/Event/TransactionEventArgs.php index e56e214cd..73dc995ec 100644 --- a/doctrine/dbal/src/Event/TransactionEventArgs.php +++ b/doctrine/dbal/src/Event/TransactionEventArgs.php @@ -7,10 +7,10 @@ use Doctrine\Common\EventArgs; use Doctrine\DBAL\Connection; +/** @deprecated */ abstract class TransactionEventArgs extends EventArgs { - /** @var Connection */ - private $connection; + private Connection $connection; public function __construct(Connection $connection) { diff --git a/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php b/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php index 607a5f94a..9e6e650d8 100644 --- a/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php +++ b/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Event; +/** @deprecated */ class TransactionRollBackEventArgs extends TransactionEventArgs { } diff --git a/doctrine/dbal/src/Events.php b/doctrine/dbal/src/Events.php index 1c0b95590..37125df53 100644 --- a/doctrine/dbal/src/Events.php +++ b/doctrine/dbal/src/Events.php @@ -6,6 +6,8 @@ * Container for all DBAL events. * * This class cannot be instantiated. + * + * @deprecated */ final class Events { @@ -18,19 +20,45 @@ private function __construct() { } + /** @deprecated */ public const postConnect = 'postConnect'; - public const onSchemaCreateTable = 'onSchemaCreateTable'; - public const onSchemaCreateTableColumn = 'onSchemaCreateTableColumn'; - public const onSchemaDropTable = 'onSchemaDropTable'; - public const onSchemaAlterTable = 'onSchemaAlterTable'; - public const onSchemaAlterTableAddColumn = 'onSchemaAlterTableAddColumn'; + /** @deprecated */ + public const onSchemaCreateTable = 'onSchemaCreateTable'; + + /** @deprecated */ + public const onSchemaCreateTableColumn = 'onSchemaCreateTableColumn'; + + /** @deprecated */ + public const onSchemaDropTable = 'onSchemaDropTable'; + + /** @deprecated */ + public const onSchemaAlterTable = 'onSchemaAlterTable'; + + /** @deprecated */ + public const onSchemaAlterTableAddColumn = 'onSchemaAlterTableAddColumn'; + + /** @deprecated */ public const onSchemaAlterTableRemoveColumn = 'onSchemaAlterTableRemoveColumn'; + + /** @deprecated */ public const onSchemaAlterTableChangeColumn = 'onSchemaAlterTableChangeColumn'; + + /** @deprecated */ public const onSchemaAlterTableRenameColumn = 'onSchemaAlterTableRenameColumn'; - public const onSchemaColumnDefinition = 'onSchemaColumnDefinition'; - public const onSchemaIndexDefinition = 'onSchemaIndexDefinition'; - public const onTransactionBegin = 'onTransactionBegin'; - public const onTransactionCommit = 'onTransactionCommit'; - public const onTransactionRollBack = 'onTransactionRollBack'; + + /** @deprecated */ + public const onSchemaColumnDefinition = 'onSchemaColumnDefinition'; + + /** @deprecated */ + public const onSchemaIndexDefinition = 'onSchemaIndexDefinition'; + + /** @deprecated */ + public const onTransactionBegin = 'onTransactionBegin'; + + /** @deprecated */ + public const onTransactionCommit = 'onTransactionCommit'; + + /** @deprecated */ + public const onTransactionRollBack = 'onTransactionRollBack'; } diff --git a/doctrine/dbal/src/Exception.php b/doctrine/dbal/src/Exception.php index b8d7804d5..3d2814c73 100644 --- a/doctrine/dbal/src/Exception.php +++ b/doctrine/dbal/src/Exception.php @@ -4,6 +4,7 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; +use SensitiveParameter; use function get_class; use function gettype; @@ -12,9 +13,7 @@ use function spl_object_hash; use function sprintf; -/** - * @psalm-immutable - */ +/** @psalm-immutable */ class Exception extends \Exception { public static function notSupported(string $method): self @@ -22,9 +21,7 @@ public static function notSupported(string $method): self return new self(sprintf("Operation '%s' is not supported by platform.", $method)); } - /** - * @param mixed $invalidPlatform - */ + /** @param mixed $invalidPlatform */ public static function invalidPlatformType($invalidPlatform): self { if (is_object($invalidPlatform)) { @@ -32,8 +29,8 @@ public static function invalidPlatformType($invalidPlatform): self sprintf( "Option 'platform' must be a subtype of '%s', instance of '%s' given", AbstractPlatform::class, - get_class($invalidPlatform) - ) + get_class($invalidPlatform), + ), ); } @@ -41,8 +38,8 @@ public static function invalidPlatformType($invalidPlatform): self sprintf( "Option 'platform' must be an object and subtype of '%s'. Got '%s'", AbstractPlatform::class, - gettype($invalidPlatform) - ) + gettype($invalidPlatform), + ), ); } @@ -59,23 +56,23 @@ public static function invalidPlatformVersionSpecified(string $version, string $ 'Invalid platform version "%s" specified. ' . 'The platform version has to be specified in the format: "%s".', $version, - $expectedFormat - ) + $expectedFormat, + ), ); } - /** - * @param string|null $url The URL that was provided in the connection parameters (if any). - */ - public static function driverRequired(?string $url = null): self - { + /** @param string|null $url The URL that was provided in the connection parameters (if any). */ + public static function driverRequired( + #[SensitiveParameter] + ?string $url = null + ): self { if ($url !== null) { return new self( sprintf( "The options 'driver' or 'driverClass' are mandatory if a connection URL without scheme " . 'is given to DriverManager::getConnection(). Given URL: %s', - $url - ) + $url, + ), ); } @@ -83,9 +80,7 @@ public static function driverRequired(?string $url = null): self 'instance is given to DriverManager::getConnection().'); } - /** - * @param string[] $knownDrivers - */ + /** @param string[] $knownDrivers */ public static function unknownDriver(string $unknownDriverName, array $knownDrivers): self { return new self("The given 'driver' " . $unknownDriverName . ' is unknown, ' . @@ -101,7 +96,7 @@ public static function invalidWrapperClass(string $wrapperClass): self public static function invalidDriverClass(string $driverClass): self { return new self( - "The given 'driverClass' " . $driverClass . ' has to implement the ' . Driver::class . ' interface.' + "The given 'driverClass' " . $driverClass . ' has to implement the ' . Driver::class . ' interface.', ); } @@ -134,14 +129,14 @@ public static function typeNotFound(string $name): self public static function typeNotRegistered(Type $type): self { return new self( - sprintf('Type of the class %s@%s is not registered.', get_class($type), spl_object_hash($type)) + sprintf('Type of the class %s@%s is not registered.', get_class($type), spl_object_hash($type)), ); } public static function typeAlreadyRegistered(Type $type): self { return new self( - sprintf('Type of the class %s@%s is already registered.', get_class($type), spl_object_hash($type)) + sprintf('Type of the class %s@%s is already registered.', get_class($type), spl_object_hash($type)), ); } } diff --git a/doctrine/dbal/src/Exception/ConnectionLost.php b/doctrine/dbal/src/Exception/ConnectionLost.php index 46a424757..c45085839 100644 --- a/doctrine/dbal/src/Exception/ConnectionLost.php +++ b/doctrine/dbal/src/Exception/ConnectionLost.php @@ -2,9 +2,7 @@ namespace Doctrine\DBAL\Exception; -/** - * @psalm-immutable - */ +/** @psalm-immutable */ final class ConnectionLost extends ConnectionException { } diff --git a/doctrine/dbal/src/Exception/DatabaseDoesNotExist.php b/doctrine/dbal/src/Exception/DatabaseDoesNotExist.php index 87eb1381a..dc71c82cb 100644 --- a/doctrine/dbal/src/Exception/DatabaseDoesNotExist.php +++ b/doctrine/dbal/src/Exception/DatabaseDoesNotExist.php @@ -2,9 +2,7 @@ namespace Doctrine\DBAL\Exception; -/** - * @psalm-immutable - */ +/** @psalm-immutable */ class DatabaseDoesNotExist extends DatabaseObjectNotFoundException { } diff --git a/doctrine/dbal/src/Exception/DatabaseRequired.php b/doctrine/dbal/src/Exception/DatabaseRequired.php new file mode 100644 index 000000000..49b7326cd --- /dev/null +++ b/doctrine/dbal/src/Exception/DatabaseRequired.php @@ -0,0 +1,18 @@ +|array */ - private $originalParameters; + private array $originalParameters; /** @var array|array */ - private $originalTypes; + private array $originalTypes; - /** @var int */ - private $originalParameterIndex = 0; + private int $originalParameterIndex = 0; /** @var list */ - private $convertedSQL = []; + private array $convertedSQL = []; /** @var list */ - private $convertedParameteres = []; + private array $convertedParameters = []; /** @var array */ - private $convertedTypes = []; + private array $convertedTypes = []; /** * @param array|array $parameters @@ -77,12 +76,10 @@ public function getSQL(): string return implode('', $this->convertedSQL); } - /** - * @return list - */ + /** @return list */ public function getParameters(): array { - return $this->convertedParameteres; + return $this->convertedParameters; } /** @@ -92,8 +89,8 @@ public function getParameters(): array private function acceptParameter($key, $value): void { if (! isset($this->originalTypes[$key])) { - $this->convertedSQL[] = '?'; - $this->convertedParameteres[] = $value; + $this->convertedSQL[] = '?'; + $this->convertedParameters[] = $value; return; } @@ -101,9 +98,10 @@ private function acceptParameter($key, $value): void $type = $this->originalTypes[$key]; if ( - $type !== Connection::PARAM_INT_ARRAY - && $type !== Connection::PARAM_STR_ARRAY - && $type !== Connection::PARAM_ASCII_STR_ARRAY + $type !== ArrayParameterType::INTEGER + && $type !== ArrayParameterType::STRING + && $type !== ArrayParameterType::ASCII + && $type !== ArrayParameterType::BINARY ) { $this->appendTypedParameter([$value], $type); @@ -116,12 +114,10 @@ private function acceptParameter($key, $value): void return; } - $this->appendTypedParameter($value, $type - Connection::ARRAY_PARAM_OFFSET); + $this->appendTypedParameter($value, ArrayParameterType::toElementParameterType($type)); } - /** - * @return array - */ + /** @return array */ public function getTypes(): array { return $this->convertedTypes; @@ -135,10 +131,10 @@ private function appendTypedParameter(array $values, $type): void { $this->convertedSQL[] = implode(', ', array_fill(0, count($values), '?')); - $index = count($this->convertedParameteres); + $index = count($this->convertedParameters); foreach ($values as $value) { - $this->convertedParameteres[] = $value; + $this->convertedParameters[] = $value; $this->convertedTypes[$index] = $type; $index++; diff --git a/doctrine/dbal/src/FetchMode.php b/doctrine/dbal/src/FetchMode.php index 79fa6a3f3..d80719f25 100644 --- a/doctrine/dbal/src/FetchMode.php +++ b/doctrine/dbal/src/FetchMode.php @@ -4,13 +4,17 @@ /** * Legacy Class that keeps BC for using the legacy APIs fetch()/fetchAll(). + * + * @deprecated Use the dedicated fetch*() methods for the desired fetch mode instead. */ class FetchMode { /** @link PDO::FETCH_ASSOC */ public const ASSOCIATIVE = 2; + /** @link PDO::FETCH_NUM */ public const NUMERIC = 3; + /** @link PDO::FETCH_COLUMN */ public const COLUMN = 7; } diff --git a/doctrine/dbal/src/Id/TableGenerator.php b/doctrine/dbal/src/Id/TableGenerator.php index d4f97fce4..7d7f210e2 100644 --- a/doctrine/dbal/src/Id/TableGenerator.php +++ b/doctrine/dbal/src/Id/TableGenerator.php @@ -57,14 +57,13 @@ */ class TableGenerator { - /** @var Connection */ - private $conn; + private Connection $conn; /** @var string */ private $generatorTableName; /** @var mixed[][] */ - private $sequences = []; + private array $sequences = []; /** * @param string $generatorTableName @@ -86,7 +85,7 @@ public function __construct(Connection $conn, $generatorTableName = 'sequences') $this->conn = DriverManager::getConnection( $conn->getParams(), $conn->getConfiguration(), - $conn->getEventManager() + $conn->getEventManager(), ); $this->generatorTableName = $generatorTableName; @@ -148,7 +147,7 @@ public function nextValue($sequence) } else { $this->conn->insert( $this->generatorTableName, - ['sequence_name' => $sequence, 'sequence_value' => 1, 'sequence_increment_by' => 1] + ['sequence_name' => $sequence, 'sequence_value' => 1, 'sequence_increment_by' => 1], ); $value = 1; } @@ -160,7 +159,7 @@ public function nextValue($sequence) throw new Exception( 'Error occurred while generating ID with TableGenerator, aborted generation: ' . $e->getMessage(), 0, - $e + $e, ); } diff --git a/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php b/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php index f47c9a914..75c9fe9cf 100644 --- a/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php +++ b/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php @@ -11,17 +11,13 @@ use Doctrine\DBAL\Schema\Visitor\Visitor; use Doctrine\Deprecations\Deprecation; -/** - * @deprecated - */ +/** @deprecated */ class TableGeneratorSchemaVisitor implements Visitor { /** @var string */ private $generatorTableName; - /** - * @param string $generatorTableName - */ + /** @param string $generatorTableName */ public function __construct($generatorTableName = 'sequences') { Deprecation::trigger( @@ -34,7 +30,7 @@ public function __construct($generatorTableName = 'sequences') } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptSchema(Schema $schema) { @@ -45,35 +41,35 @@ public function acceptSchema(Schema $schema) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptTable(Table $table) { } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptColumn(Table $table, Column $column) { } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptIndex(Table $table, Index $index) { } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptSequence(Sequence $sequence) { diff --git a/doctrine/dbal/src/Logging/Connection.php b/doctrine/dbal/src/Logging/Connection.php index 2175dc467..5ab111ec2 100644 --- a/doctrine/dbal/src/Logging/Connection.php +++ b/doctrine/dbal/src/Logging/Connection.php @@ -12,12 +12,9 @@ final class Connection extends AbstractConnectionMiddleware { - /** @var LoggerInterface */ - private $logger; + private LoggerInterface $logger; - /** - * @internal This connection can be only instantiated by its driver. - */ + /** @internal This connection can be only instantiated by its driver. */ public function __construct(ConnectionInterface $connection, LoggerInterface $logger) { parent::__construct($connection); @@ -35,7 +32,7 @@ public function prepare(string $sql): DriverStatement return new Statement( parent::prepare($sql), $this->logger, - $sql + $sql, ); } diff --git a/doctrine/dbal/src/Logging/DebugStack.php b/doctrine/dbal/src/Logging/DebugStack.php index 24a2d68cf..1a970d060 100644 --- a/doctrine/dbal/src/Logging/DebugStack.php +++ b/doctrine/dbal/src/Logging/DebugStack.php @@ -38,12 +38,12 @@ public function __construct() Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4967', - 'DebugStack is deprecated.' + 'DebugStack is deprecated.', ); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function startQuery($sql, ?array $params = null, ?array $types = null) { @@ -62,7 +62,7 @@ public function startQuery($sql, ?array $params = null, ?array $types = null) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function stopQuery() { diff --git a/doctrine/dbal/src/Logging/Driver.php b/doctrine/dbal/src/Logging/Driver.php index 533dcc05c..32a5cd2c2 100644 --- a/doctrine/dbal/src/Logging/Driver.php +++ b/doctrine/dbal/src/Logging/Driver.php @@ -7,15 +7,13 @@ use Doctrine\DBAL\Driver as DriverInterface; use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; use Psr\Log\LoggerInterface; +use SensitiveParameter; final class Driver extends AbstractDriverMiddleware { - /** @var LoggerInterface */ - private $logger; + private LoggerInterface $logger; - /** - * @internal This driver can be only instantiated by its middleware. - */ + /** @internal This driver can be only instantiated by its middleware. */ public function __construct(DriverInterface $driver, LoggerInterface $logger) { parent::__construct($driver); @@ -26,13 +24,15 @@ public function __construct(DriverInterface $driver, LoggerInterface $logger) /** * {@inheritDoc} */ - public function connect(array $params) - { + public function connect( + #[SensitiveParameter] + array $params + ) { $this->logger->info('Connecting with parameters {params}', ['params' => $this->maskPassword($params)]); return new Connection( parent::connect($params), - $this->logger + $this->logger, ); } @@ -41,8 +41,10 @@ public function connect(array $params) * * @return array */ - private function maskPassword(array $params): array - { + private function maskPassword( + #[SensitiveParameter] + array $params + ): array { if (isset($params['password'])) { $params['password'] = ''; } diff --git a/doctrine/dbal/src/Logging/LoggerChain.php b/doctrine/dbal/src/Logging/LoggerChain.php index c256dd72c..7a4eaa49a 100644 --- a/doctrine/dbal/src/Logging/LoggerChain.php +++ b/doctrine/dbal/src/Logging/LoggerChain.php @@ -12,24 +12,22 @@ class LoggerChain implements SQLLogger { /** @var iterable */ - private $loggers; + private iterable $loggers; - /** - * @param iterable $loggers - */ + /** @param iterable $loggers */ public function __construct(iterable $loggers = []) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4967', - 'LoggerChain is deprecated' + 'LoggerChain is deprecated', ); $this->loggers = $loggers; } /** - * {@inheritdoc} + * {@inheritDoc} */ public function startQuery($sql, ?array $params = null, ?array $types = null) { @@ -39,7 +37,7 @@ public function startQuery($sql, ?array $params = null, ?array $types = null) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function stopQuery() { diff --git a/doctrine/dbal/src/Logging/Middleware.php b/doctrine/dbal/src/Logging/Middleware.php index 4d5c6b061..da0c1b90f 100644 --- a/doctrine/dbal/src/Logging/Middleware.php +++ b/doctrine/dbal/src/Logging/Middleware.php @@ -10,8 +10,7 @@ final class Middleware implements MiddlewareInterface { - /** @var LoggerInterface */ - private $logger; + private LoggerInterface $logger; public function __construct(LoggerInterface $logger) { diff --git a/doctrine/dbal/src/Logging/Statement.php b/doctrine/dbal/src/Logging/Statement.php index e993767aa..039b93b6a 100644 --- a/doctrine/dbal/src/Logging/Statement.php +++ b/doctrine/dbal/src/Logging/Statement.php @@ -8,28 +8,25 @@ use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use Psr\Log\LoggerInterface; use function array_slice; use function func_get_args; +use function func_num_args; final class Statement extends AbstractStatementMiddleware { - /** @var LoggerInterface */ - private $logger; - - /** @var string */ - private $sql; + private LoggerInterface $logger; + private string $sql; /** @var array|array */ - private $params = []; + private array $params = []; /** @var array|array */ - private $types = []; + private array $types = []; - /** - * @internal This statement can be only instantiated by its connection. - */ + /** @internal This statement can be only instantiated by its connection. */ public function __construct(StatementInterface $statement, LoggerInterface $logger, string $sql) { parent::__construct($statement); @@ -39,10 +36,28 @@ public function __construct(StatementInterface $statement, LoggerInterface $logg } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@see bindValue()} instead. */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5563', + '%s is deprecated. Use bindValue() instead.', + __METHOD__, + ); + + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + $this->params[$param] = &$variable; $this->types[$param] = $type; @@ -50,10 +65,19 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le } /** - * {@inheritdoc} + * {@inheritDoc} */ public function bindValue($param, $value, $type = ParameterType::STRING) { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.', + ); + } + $this->params[$param] = $value; $this->types[$param] = $type; @@ -61,7 +85,7 @@ public function bindValue($param, $value, $type = ParameterType::STRING) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function execute($params = null): ResultInterface { diff --git a/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php b/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php index 39594c76d..d1e3e5aff 100644 --- a/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php +++ b/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php @@ -2,15 +2,19 @@ namespace Doctrine\DBAL\Platforms; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\AbstractAsset; use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\MySQLSchemaManager; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\TransactionIsolationLevel; use Doctrine\DBAL\Types\BlobType; use Doctrine\DBAL\Types\TextType; +use Doctrine\DBAL\Types\Types; use Doctrine\Deprecations\Deprecation; use InvalidArgumentException; @@ -19,13 +23,17 @@ use function array_unique; use function array_values; use function count; +use function func_get_arg; use function func_get_args; +use function func_num_args; use function implode; use function in_array; use function is_numeric; use function is_string; use function sprintf; use function str_replace; +use function strcasecmp; +use function strtolower; use function strtoupper; use function trim; @@ -63,9 +71,17 @@ protected function doModifyLimitQuery($query, $limit, $offset) /** * {@inheritDoc} + * + * @deprecated Use {@see quoteIdentifier()} to quote identifiers instead. */ public function getIdentifierQuoteCharacter() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5388', + 'AbstractMySQLPlatform::getIdentifierQuoteCharacter() is deprecated. Use quoteIdentifier() instead.', + ); + return '`'; } @@ -98,7 +114,7 @@ public function getConcatExpression() } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { @@ -130,6 +146,8 @@ public function getLengthExpression($column) /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListDatabasesSQL() { @@ -137,6 +155,8 @@ public function getListDatabasesSQL() } /** + * @deprecated + * * {@inheritDoc} */ public function getListTableConstraintsSQL($table) @@ -145,6 +165,8 @@ public function getListTableConstraintsSQL($table) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} * * Two approaches to listing the table indexes. The information_schema is @@ -165,6 +187,8 @@ public function getListTableIndexesSQL($table, $database = null) /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { @@ -172,6 +196,8 @@ public function getListViewsSQL($database) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * @param string $table * @param string|null $database * @@ -197,17 +223,35 @@ public function getListTableForeignKeysSQL($table, $database = null) /** * {@inheritDoc} */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) - { + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) + { + if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'Relying on the default string column length on MySQL is deprecated' + . ', specify the length explicitly.', + ); + } + return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); } /** - * {@inheritdoc} + * {@inheritDoc} */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) - { + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) + { + if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'Relying on the default binary column length on MySQL is deprecated' + . ', specify the length explicitly.', + ); + } + return $fixed ? 'BINARY(' . ($length > 0 ? $length : 255) . ')' : 'VARBINARY(' . ($length > 0 ? $length : 255) . ')'; @@ -291,8 +335,8 @@ public function prefersIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/1519', - 'AbstractMySQLPlatform::prefersIdentityColumns() is deprecated.' + 'https://github.com/doctrine/dbal/pull/1519', + 'AbstractMySQLPlatform::prefersIdentityColumns() is deprecated.', ); return true; @@ -310,6 +354,8 @@ public function supportsIdentityColumns() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsInlineColumnComments() { @@ -318,6 +364,8 @@ public function supportsInlineColumnComments() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsColumnCollation() { @@ -325,6 +373,8 @@ public function supportsColumnCollation() } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTablesSQL() @@ -333,6 +383,8 @@ public function getListTablesSQL() } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTableColumnsSQL($table, $database = null) @@ -345,6 +397,19 @@ public function getListTableColumnsSQL($table, $database = null) ' ORDER BY ORDINAL_POSITION ASC'; } + /** + * The SQL snippets required to elucidate a column type + * + * Returns an array of the form [column type SELECT snippet, additional JOIN statement snippet] + * + * @return array{string, string} + */ + public function getColumnTypeSQLSnippets(string $tableAlias = 'c'): array + { + return [$tableAlias . '.COLUMN_TYPE', '']; + } + + /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ public function getListTableMetadataSQL(string $table, ?string $database = null): string { return sprintf( @@ -362,10 +427,43 @@ public function getListTableMetadataSQL(string $table, ?string $database = null) SQL , $this->getDatabaseNameSQL($database), - $this->quoteStringLiteral($table) + $this->quoteStringLiteral($table), ); } + /** + * {@inheritDoc} + */ + public function getCreateTablesSQL(array $tables): array + { + $sql = []; + + foreach ($tables as $table) { + $sql = array_merge($sql, $this->getCreateTableWithoutForeignKeysSQL($table)); + } + + foreach ($tables as $table) { + if (! $table->hasOption('engine') || $this->engineSupportsForeignKeys($table->getOption('engine'))) { + foreach ($table->getForeignKeys() as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL( + $foreignKey, + $table->getQuotedName($this), + ); + } + } elseif (count($table->getForeignKeys()) > 0) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5414', + 'Relying on the DBAL not generating DDL for foreign keys on MySQL engines' + . ' other than InnoDB is deprecated.' + . ' Define foreign key constraints only if they are necessary.', + ); + } + } + + return $sql; + } + /** * {@inheritDoc} */ @@ -402,17 +500,22 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] $query .= $this->buildTableOptions($options); $query .= $this->buildPartitionOptions($options); - $sql = [$query]; - $engine = 'INNODB'; - - if (isset($options['engine'])) { - $engine = strtoupper(trim($options['engine'])); - } + $sql = [$query]; // Propagate foreign key constraints only for InnoDB. - if (isset($options['foreignKeys']) && $engine === 'INNODB') { - foreach ((array) $options['foreignKeys'] as $definition) { - $sql[] = $this->getCreateForeignKeySQL($definition, $name); + if (isset($options['foreignKeys'])) { + if (! isset($options['engine']) || $this->engineSupportsForeignKeys($options['engine'])) { + foreach ($options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $name); + } + } elseif (count($options['foreignKeys']) > 0) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5414', + 'Relying on the DBAL not generating DDL for foreign keys on MySQL engines' + . ' other than InnoDB is deprecated.' + . ' Define foreign key constraints only if they are necessary.', + ); } } @@ -420,7 +523,9 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getDefaultValueDeclarationSQL($column) { @@ -453,6 +558,11 @@ private function buildTableOptions(array $options): string $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']); if (isset($options['collate'])) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5214', + 'The "collate" option is deprecated in favor of "collation" and will be removed in 4.0.', + ); $options['collation'] = $options['collate']; } @@ -500,6 +610,11 @@ private function buildPartitionOptions(array $options): string : ''; } + private function engineSupportsForeignKeys(string $engine): bool + { + return strcasecmp(trim($engine), 'InnoDB') === 0; + } + /** * {@inheritDoc} */ @@ -510,22 +625,32 @@ public function getAlterTableSQL(TableDiff $diff) $newName = $diff->getNewName(); if ($newName !== false) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5663', + 'Generation of SQL that renames a table using %s is deprecated. Use getRenameTableSQL() instead.', + __METHOD__, + ); + $queryParts[] = 'RENAME TO ' . $newName->getQuotedName($this); } - foreach ($diff->addedColumns as $column) { + foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } - $columnArray = array_merge($column->toArray(), [ + $columnProperties = array_merge($column->toArray(), [ 'comment' => $this->getColumnComment($column), ]); - $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL( + $column->getQuotedName($this), + $columnProperties, + ); } - foreach ($diff->removedColumns as $column) { + foreach ($diff->getDroppedColumns() as $column) { if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { continue; } @@ -533,70 +658,94 @@ public function getAlterTableSQL(TableDiff $diff) $queryParts[] = 'DROP ' . $column->getQuotedName($this); } - foreach ($diff->changedColumns as $columnDiff) { + foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } - $column = $columnDiff->column; - $columnArray = $column->toArray(); + $newColumn = $columnDiff->getNewColumn(); - // Don't propagate default value changes for unsupported column types. - if ( - $columnDiff->hasChanged('default') && - count($columnDiff->changedProperties) === 1 && - ($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType) - ) { - continue; - } + $newColumnProperties = array_merge($newColumn->toArray(), [ + 'comment' => $this->getColumnComment($newColumn), + ]); - $columnArray['comment'] = $this->getColumnComment($column); - $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' - . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); + + $queryParts[] = 'CHANGE ' . $oldColumn->getQuotedName($this) . ' ' + . $this->getColumnDeclarationSQL($newColumn->getQuotedName($this), $newColumnProperties); } - foreach ($diff->renamedColumns as $oldColumnName => $column) { + foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { continue; } - $oldColumnName = new Identifier($oldColumnName); - $columnArray = $column->toArray(); - $columnArray['comment'] = $this->getColumnComment($column); - $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' - . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + $oldColumnName = new Identifier($oldColumnName); + + $columnProperties = array_merge($column->toArray(), [ + 'comment' => $this->getColumnComment($column), + ]); + + $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnProperties); } - if (isset($diff->addedIndexes['primary'])) { - $keyColumns = array_unique(array_values($diff->addedIndexes['primary']->getColumns())); + $addedIndexes = $this->indexAssetsByLowerCaseName($diff->getAddedIndexes()); + $modifiedIndexes = $this->indexAssetsByLowerCaseName($diff->getModifiedIndexes()); + $diffModified = false; + + if (isset($addedIndexes['primary'])) { + $keyColumns = array_unique(array_values($addedIndexes['primary']->getColumns())); $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; - unset($diff->addedIndexes['primary']); - } elseif (isset($diff->changedIndexes['primary'])) { + unset($addedIndexes['primary']); + $diffModified = true; + } elseif (isset($modifiedIndexes['primary'])) { + $addedColumns = $this->indexAssetsByLowerCaseName($diff->getAddedColumns()); + // Necessary in case the new primary key includes a new auto_increment column - foreach ($diff->changedIndexes['primary']->getColumns() as $columnName) { - if (isset($diff->addedColumns[$columnName]) && $diff->addedColumns[$columnName]->getAutoincrement()) { - $keyColumns = array_unique(array_values($diff->changedIndexes['primary']->getColumns())); + foreach ($modifiedIndexes['primary']->getColumns() as $columnName) { + if (isset($addedColumns[$columnName]) && $addedColumns[$columnName]->getAutoincrement()) { + $keyColumns = array_unique(array_values($modifiedIndexes['primary']->getColumns())); $queryParts[] = 'DROP PRIMARY KEY'; $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; - unset($diff->changedIndexes['primary']); + unset($modifiedIndexes['primary']); + $diffModified = true; break; } } } + if ($diffModified) { + $diff = new TableDiff( + $diff->name, + $diff->getAddedColumns(), + $diff->getModifiedColumns(), + $diff->getDroppedColumns(), + array_values($addedIndexes), + array_values($modifiedIndexes), + $diff->getDroppedIndexes(), + $diff->getOldTable(), + $diff->getAddedForeignKeys(), + $diff->getModifiedForeignKeys(), + $diff->getDroppedForeignKeys(), + $diff->getRenamedColumns(), + $diff->getRenamedIndexes(), + ); + } + $sql = []; $tableSql = []; if (! $this->onSchemaAlterTable($diff, $tableSql)) { if (count($queryParts) > 0) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' + $sql[] = 'ALTER TABLE ' . ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this) . ' ' . implode(', ', $queryParts); } $sql = array_merge( $this->getPreAlterTableIndexForeignKeySQL($diff), $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) + $this->getPostAlterTableIndexForeignKeySQL($diff), ); } @@ -608,36 +757,38 @@ public function getAlterTableSQL(TableDiff $diff) */ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) { - $sql = []; - $table = $diff->getName($this)->getQuotedName($this); + $sql = []; + + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); - foreach ($diff->changedIndexes as $changedIndex) { + foreach ($diff->getModifiedIndexes() as $changedIndex) { $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $changedIndex)); } - foreach ($diff->removedIndexes as $remKey => $remIndex) { - $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $remIndex)); + foreach ($diff->getDroppedIndexes() as $droppedIndex) { + $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $droppedIndex)); - foreach ($diff->addedIndexes as $addKey => $addIndex) { - if ($remIndex->getColumns() !== $addIndex->getColumns()) { + foreach ($diff->getAddedIndexes() as $addedIndex) { + if ($droppedIndex->getColumns() !== $addedIndex->getColumns()) { continue; } - $indexClause = 'INDEX ' . $addIndex->getName(); + $indexClause = 'INDEX ' . $addedIndex->getName(); - if ($addIndex->isPrimary()) { + if ($addedIndex->isPrimary()) { $indexClause = 'PRIMARY KEY'; - } elseif ($addIndex->isUnique()) { - $indexClause = 'UNIQUE INDEX ' . $addIndex->getName(); + } elseif ($addedIndex->isUnique()) { + $indexClause = 'UNIQUE INDEX ' . $addedIndex->getName(); } - $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', '; + $query = 'ALTER TABLE ' . $tableNameSQL . ' DROP INDEX ' . $droppedIndex->getName() . ', '; $query .= 'ADD ' . $indexClause; - $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex) . ')'; + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addedIndex) . ')'; $sql[] = $query; - unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]); + $diff->unsetAddedIndex($addedIndex); + $diff->unsetDroppedIndex($droppedIndex); break; } @@ -645,8 +796,10 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) $engine = 'INNODB'; - if ($diff->fromTable instanceof Table && $diff->fromTable->hasOption('engine')) { - $engine = strtoupper(trim($diff->fromTable->getOption('engine'))); + $table = $diff->getOldTable(); + + if ($table !== null && $table->hasOption('engine')) { + $engine = strtoupper(trim($table->getOption('engine'))); } // Suppress foreign key constraint propagation on non-supporting engines. @@ -660,7 +813,7 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) $sql, $this->getPreAlterTableAlterIndexForeignKeySQL($diff), parent::getPreAlterTableIndexForeignKeySQL($diff), - $this->getPreAlterTableRenameIndexForeignKeySQL($diff) + $this->getPreAlterTableRenameIndexForeignKeySQL($diff), ); return $sql; @@ -673,21 +826,27 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) */ private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index): array { - $sql = []; + if (! $index->isPrimary()) { + return []; + } + + $table = $diff->getOldTable(); - if (! $index->isPrimary() || ! $diff->fromTable instanceof Table) { - return $sql; + if ($table === null) { + return []; } - $tableName = $diff->getName($this)->getQuotedName($this); + $sql = []; + + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); // Dropping primary keys requires to unset autoincrement attribute on the particular column first. foreach ($index->getColumns() as $columnName) { - if (! $diff->fromTable->hasColumn($columnName)) { + if (! $table->hasColumn($columnName)) { continue; } - $column = $diff->fromTable->getColumn($columnName); + $column = $table->getColumn($columnName); if ($column->getAutoincrement() !== true) { continue; @@ -695,7 +854,7 @@ private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $inde $column->setAutoincrement(false); - $sql[] = 'ALTER TABLE ' . $tableName . ' MODIFY ' . + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); // original autoincrement information might be needed later on by other parts of the table alteration @@ -714,18 +873,45 @@ private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $inde */ private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff): array { - $sql = []; - $table = $diff->getName($this)->getQuotedName($this); + $table = $diff->getOldTable(); + + if ($table === null) { + return []; + } + + $primaryKey = $table->getPrimaryKey(); + + if ($primaryKey === null) { + return []; + } + + $primaryKeyColumns = []; + + foreach ($primaryKey->getColumns() as $columnName) { + if (! $table->hasColumn($columnName)) { + continue; + } + + $primaryKeyColumns[] = $table->getColumn($columnName); + } - foreach ($diff->changedIndexes as $changedIndex) { + if (count($primaryKeyColumns) === 0) { + return []; + } + + $sql = []; + + $tableNameSQL = $table->getQuotedName($this); + + foreach ($diff->getModifiedIndexes() as $changedIndex) { // Changed primary key - if (! $changedIndex->isPrimary() || ! ($diff->fromTable instanceof Table)) { + if (! $changedIndex->isPrimary()) { continue; } - foreach ($diff->fromTable->getPrimaryKeyColumns() as $columnName => $column) { + foreach ($primaryKeyColumns as $column) { // Check if an autoincrement column was dropped from the primary key. - if (! $column->getAutoincrement() || in_array($columnName, $changedIndex->getColumns(), true)) { + if (! $column->getAutoincrement() || in_array($column->getName(), $changedIndex->getColumns(), true)) { continue; } @@ -733,7 +919,7 @@ private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff): array // before we can drop and recreate the primary key. $column->setAutoincrement(false); - $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' . + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); // Restore the autoincrement attribute as it might be needed later on @@ -752,15 +938,16 @@ private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff): array */ protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) { - $sql = []; - $tableName = $diff->getName($this)->getQuotedName($this); + $sql = []; + + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { - if (in_array($foreignKey, $diff->changedForeignKeys, true)) { + if (in_array($foreignKey, $diff->getModifiedForeignKeys(), true)) { continue; } - $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + $sql[] = $this->getDropForeignKeySQL($foreignKey->getQuotedName($this), $tableNameSQL); } return $sql; @@ -778,19 +965,25 @@ protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) */ private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff): array { - if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) { + if (count($diff->getRenamedIndexes()) === 0) { + return []; + } + + $table = $diff->getOldTable(); + + if ($table === null) { return []; } $foreignKeys = []; /** @var ForeignKeyConstraint[] $remainingForeignKeys */ $remainingForeignKeys = array_diff_key( - $diff->fromTable->getForeignKeys(), - $diff->removedForeignKeys + $table->getForeignKeys(), + $diff->getDroppedForeignKeys(), ); foreach ($remainingForeignKeys as $foreignKey) { - foreach ($diff->renamedIndexes as $index) { + foreach ($diff->getRenamedIndexes() as $index) { if ($foreignKey->intersectsIndexColumns($index)) { $foreignKeys[] = $foreignKey; @@ -803,13 +996,13 @@ private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableD } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) { return array_merge( parent::getPostAlterTableIndexForeignKeySQL($diff), - $this->getPostAlterTableRenameIndexForeignKeySQL($diff) + $this->getPostAlterTableRenameIndexForeignKeySQL($diff), ); } @@ -824,17 +1017,17 @@ protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) $newName = $diff->getNewName(); if ($newName !== false) { - $tableName = $newName->getQuotedName($this); + $tableNameSQL = $newName->getQuotedName($this); } else { - $tableName = $diff->getName($this)->getQuotedName($this); + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); } foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { - if (in_array($foreignKey, $diff->changedForeignKeys, true)) { + if (in_array($foreignKey, $diff->getModifiedForeignKeys(), true)) { continue; } - $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); } return $sql; @@ -882,7 +1075,7 @@ public function getSmallIntTypeDeclarationSQL(array $column) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getFloatDeclarationSQL(array $column) { @@ -890,7 +1083,7 @@ public function getFloatDeclarationSQL(array $column) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getDecimalTypeDeclarationSQL(array $column) { @@ -922,6 +1115,8 @@ protected function _getCommonIntegerTypeDeclarationSQL(array $column) /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getColumnCharsetDeclarationSQL($charset) { @@ -930,14 +1125,8 @@ public function getColumnCharsetDeclarationSQL($charset) /** * {@inheritDoc} - */ - public function getColumnCollationDeclarationSQL($collation) - { - return 'COLLATE ' . $this->quoteSingleIdentifier($collation); - } - - /** - * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { @@ -957,20 +1146,34 @@ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey public function getDropIndexSQL($index, $table = null) { if ($index instanceof Index) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $indexName = $index->getQuotedName($this); } elseif (is_string($index)) { $indexName = $index; } else { throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.' + __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.', ); } if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $table = $table->getQuotedName($this); } elseif (! is_string($table)) { throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' + __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', ); } @@ -1019,7 +1222,7 @@ public function getName() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4749', - 'AbstractMySQLPlatform::getName() is deprecated. Identify platforms by their class.' + 'AbstractMySQLPlatform::getName() is deprecated. Identify platforms by their class.', ); return 'mysql'; @@ -1039,52 +1242,68 @@ public function getReadLockSQL() protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'bigint' => 'bigint', - 'binary' => 'binary', - 'blob' => 'blob', - 'char' => 'string', - 'date' => 'date', - 'datetime' => 'datetime', - 'decimal' => 'decimal', - 'double' => 'float', - 'float' => 'float', - 'int' => 'integer', - 'integer' => 'integer', - 'longblob' => 'blob', - 'longtext' => 'text', - 'mediumblob' => 'blob', - 'mediumint' => 'integer', - 'mediumtext' => 'text', - 'numeric' => 'decimal', - 'real' => 'float', - 'set' => 'simple_array', - 'smallint' => 'smallint', - 'string' => 'string', - 'text' => 'text', - 'time' => 'time', - 'timestamp' => 'datetime', - 'tinyblob' => 'blob', - 'tinyint' => 'boolean', - 'tinytext' => 'text', - 'varbinary' => 'binary', - 'varchar' => 'string', - 'year' => 'date', + 'bigint' => Types::BIGINT, + 'binary' => Types::BINARY, + 'blob' => Types::BLOB, + 'char' => Types::STRING, + 'date' => Types::DATE_MUTABLE, + 'datetime' => Types::DATETIME_MUTABLE, + 'decimal' => Types::DECIMAL, + 'double' => Types::FLOAT, + 'float' => Types::FLOAT, + 'int' => Types::INTEGER, + 'integer' => Types::INTEGER, + 'longblob' => Types::BLOB, + 'longtext' => Types::TEXT, + 'mediumblob' => Types::BLOB, + 'mediumint' => Types::INTEGER, + 'mediumtext' => Types::TEXT, + 'numeric' => Types::DECIMAL, + 'real' => Types::FLOAT, + 'set' => Types::SIMPLE_ARRAY, + 'smallint' => Types::SMALLINT, + 'string' => Types::STRING, + 'text' => Types::TEXT, + 'time' => Types::TIME_MUTABLE, + 'timestamp' => Types::DATETIME_MUTABLE, + 'tinyblob' => Types::BLOB, + 'tinyint' => Types::BOOLEAN, + 'tinytext' => Types::TEXT, + 'varbinary' => Types::BINARY, + 'varchar' => Types::STRING, + 'year' => Types::DATE_MUTABLE, ]; } /** * {@inheritDoc} + * + * @deprecated */ public function getVarcharMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'AbstractMySQLPlatform::getVarcharMaxLength() is deprecated.', + ); + return 65535; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getBinaryMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'AbstractMySQLPlatform::getBinaryMaxLength() is deprecated.', + ); + return 65535; } @@ -1099,7 +1318,7 @@ protected function getReservedKeywordsClass() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'AbstractMySQLPlatform::getReservedKeywordsClass() is deprecated,' - . ' use AbstractMySQLPlatform::createReservedKeywordsList() instead.' + . ' use AbstractMySQLPlatform::createReservedKeywordsList() instead.', ); return Keywords\MySQLKeywords::class; @@ -1114,10 +1333,17 @@ protected function getReservedKeywordsClass() public function getDropTemporaryTableSQL($table) { if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $table = $table->getQuotedName($this); } elseif (! is_string($table)) { throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' + __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', ); } @@ -1155,7 +1381,7 @@ public function getBlobTypeDeclarationSQL(array $column) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function quoteStringLiteral($str) { @@ -1165,7 +1391,7 @@ public function quoteStringLiteral($str) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getDefaultTransactionIsolationLevel() { @@ -1177,7 +1403,7 @@ public function supportsColumnLengthIndexes(): bool return true; } - private function getDatabaseNameSQL(?string $databaseName): string + protected function getDatabaseNameSQL(?string $databaseName): string { if ($databaseName !== null) { return $this->quoteStringLiteral($databaseName); @@ -1185,4 +1411,27 @@ private function getDatabaseNameSQL(?string $databaseName): string return $this->getCurrentDatabaseExpression(); } + + public function createSchemaManager(Connection $connection): MySQLSchemaManager + { + return new MySQLSchemaManager($connection, $this); + } + + /** + * @param list $assets + * + * @return array + * + * @template T of AbstractAsset + */ + private function indexAssetsByLowerCaseName(array $assets): array + { + $result = []; + + foreach ($assets as $asset) { + $result[strtolower($asset->getName())] = $asset; + } + + return $result; + } } diff --git a/doctrine/dbal/src/Platforms/AbstractPlatform.php b/doctrine/dbal/src/Platforms/AbstractPlatform.php index e143c6dc5..eba3aefc8 100644 --- a/doctrine/dbal/src/Platforms/AbstractPlatform.php +++ b/doctrine/dbal/src/Platforms/AbstractPlatform.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL\Platforms; use Doctrine\Common\EventManager; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs; use Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs; use Doctrine\DBAL\Event\SchemaAlterTableEventArgs; @@ -16,12 +17,14 @@ use Doctrine\DBAL\Exception\InvalidLockMode; use Doctrine\DBAL\LockMode; use Doctrine\DBAL\Platforms\Keywords\KeywordList; +use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\ColumnDiff; use Doctrine\DBAL\Schema\Constraint; use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\SchemaDiff; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; @@ -86,7 +89,11 @@ abstract class AbstractPlatform */ protected $doctrineTypeComments; - /** @var EventManager|null */ + /** + * @deprecated + * + * @var EventManager|null + */ protected $_eventManager; /** @@ -96,23 +103,49 @@ abstract class AbstractPlatform */ protected $_keywords; + private bool $disableTypeComments = false; + + /** @internal */ + final public function setDisableTypeComments(bool $value): void + { + $this->disableTypeComments = $value; + } + /** * Sets the EventManager used by the Platform. * + * @deprecated + * * @return void */ public function setEventManager(EventManager $eventManager) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + '%s is deprecated.', + __METHOD__, + ); + $this->_eventManager = $eventManager; } /** * Gets the EventManager used by the Platform. * + * @deprecated + * * @return EventManager|null */ public function getEventManager() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + '%s is deprecated.', + __METHOD__, + ); + return $this->_eventManager; } @@ -191,20 +224,25 @@ private function initializeAllDoctrineTypeMappings(): void */ public function getAsciiStringTypeDeclarationSQL(array $column): string { - return $this->getVarcharTypeDeclarationSQL($column); + return $this->getStringTypeDeclarationSQL($column); } /** * Returns the SQL snippet used to declare a VARCHAR column type. * + * @deprecated Use {@link getStringTypeDeclarationSQL()} instead. + * * @param mixed[] $column * * @return string */ public function getVarcharTypeDeclarationSQL(array $column) { - if (! isset($column['length'])) { + if (isset($column['length'])) { + $lengthOmitted = false; + } else { $column['length'] = $this->getVarcharDefaultLength(); + $lengthOmitted = true; } $fixed = $column['fixed'] ?? false; @@ -217,7 +255,19 @@ public function getVarcharTypeDeclarationSQL(array $column) return $this->getClobTypeDeclarationSQL($column); } - return $this->getVarcharTypeDeclarationSQLSnippet($column['length'], $fixed); + return $this->getVarcharTypeDeclarationSQLSnippet($column['length'], $fixed, $lengthOmitted); + } + + /** + * Returns the SQL snippet used to declare a string column type. + * + * @param mixed[] $column + * + * @return string + */ + public function getStringTypeDeclarationSQL(array $column) + { + return $this->getVarcharTypeDeclarationSQL($column); } /** @@ -229,8 +279,11 @@ public function getVarcharTypeDeclarationSQL(array $column) */ public function getBinaryTypeDeclarationSQL(array $column) { - if (! isset($column['length'])) { + if (isset($column['length'])) { + $lengthOmitted = false; + } else { $column['length'] = $this->getBinaryDefaultLength(); + $lengthOmitted = true; } $fixed = $column['fixed'] ?? false; @@ -245,14 +298,14 @@ public function getBinaryTypeDeclarationSQL(array $column) 'Binary column length %d is greater than supported by the platform (%d).' . ' Reduce the column length or use a BLOB column instead.', $column['length'], - $maxLength + $maxLength, ); } return $this->getBlobTypeDeclarationSQL($column); } - return $this->getBinaryTypeDeclarationSQLSnippet($column['length'], $fixed); + return $this->getBinaryTypeDeclarationSQLSnippet($column['length'], $fixed, $lengthOmitted); } /** @@ -270,7 +323,7 @@ public function getGuidTypeDeclarationSQL(array $column) $column['length'] = 36; $column['fixed'] = true; - return $this->getVarcharTypeDeclarationSQL($column); + return $this->getStringTypeDeclarationSQL($column); } /** @@ -296,7 +349,7 @@ public function getJsonTypeDeclarationSQL(array $column) * * @throws Exception If not supported on this platform. */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { throw Exception::notSupported('VARCHARs not supported by Platform.'); } @@ -311,7 +364,7 @@ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) * * @throws Exception If not supported on this platform. */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { throw Exception::notSupported('BINARY/VARBINARY column types are not supported by this platform.'); } @@ -394,7 +447,7 @@ public function getDoctrineTypeMapping($dbType) if (! isset($this->doctrineTypeMapping[$dbType])) { throw new Exception( - 'Unknown database type ' . $dbType . ' requested, ' . static::class . ' may not support it.' + 'Unknown database type ' . $dbType . ' requested, ' . static::class . ' may not support it.', ); } @@ -432,7 +485,7 @@ protected function initializeCommentedDoctrineTypes() 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5058', '%s is deprecated and will be removed in Doctrine DBAL 4.0.', - __METHOD__ + __METHOD__, ); $this->doctrineTypeComments = []; @@ -461,7 +514,7 @@ public function isCommentedDoctrineType(Type $doctrineType) 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5058', '%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.', - __METHOD__ + __METHOD__, ); if ($this->doctrineTypeComments === null) { @@ -484,7 +537,7 @@ public function markDoctrineTypeCommented($doctrineType) 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5058', '%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.', - __METHOD__ + __METHOD__, ); if ($this->doctrineTypeComments === null) { @@ -499,23 +552,41 @@ public function markDoctrineTypeCommented($doctrineType) /** * Gets the comment to append to a column comment that helps parsing this type in reverse engineering. * + * @deprecated This method will be removed without replacement. + * * @return string */ public function getDoctrineTypeComment(Type $doctrineType) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5107', + '%s is deprecated and will be removed in Doctrine DBAL 4.0.', + __METHOD__, + ); + return '(DC2Type:' . $doctrineType->getName() . ')'; } /** * Gets the comment of a passed column modified by potential doctrine type comment hints. * + * @deprecated This method will be removed without replacement. + * * @return string|null */ protected function getColumnComment(Column $column) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5107', + '%s is deprecated and will be removed in Doctrine DBAL 4.0.', + __METHOD__, + ); + $comment = $column->getComment(); - if ($column->getType()->requiresSQLCommentHint($this)) { + if (! $this->disableTypeComments && $column->getType()->requiresSQLCommentHint($this)) { $comment .= $this->getDoctrineTypeComment($column->getType()); } @@ -525,10 +596,18 @@ protected function getColumnComment(Column $column) /** * Gets the character used for identifier quoting. * + * @deprecated Use {@see quoteIdentifier()} to quote identifiers instead. + * * @return string */ public function getIdentifierQuoteCharacter() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5388', + 'AbstractPlatform::getIdentifierQuoteCharacter() is deprecated. Use quoteIdentifier() instead.', + ); + return '"'; } @@ -543,8 +622,8 @@ public function getSqlCommentStartString() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getSqlCommentStartString() is deprecated.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getSqlCommentStartString() is deprecated.', ); return '--'; @@ -561,8 +640,8 @@ public function getSqlCommentEndString() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getSqlCommentEndString() is deprecated.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getSqlCommentEndString() is deprecated.', ); return "\n"; @@ -578,7 +657,7 @@ public function getCharMaxLength(): int Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', - 'AbstractPlatform::getCharMaxLength() is deprecated.' + 'AbstractPlatform::getCharMaxLength() is deprecated.', ); return $this->getVarcharMaxLength(); @@ -596,7 +675,7 @@ public function getVarcharMaxLength() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', - 'AbstractPlatform::getVarcharMaxLength() is deprecated.' + 'AbstractPlatform::getVarcharMaxLength() is deprecated.', ); return 4000; @@ -614,7 +693,7 @@ public function getVarcharDefaultLength() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default varchar column length is deprecated, specify the length explicitly.' + 'Relying on the default varchar column length is deprecated, specify the length explicitly.', ); return 255; @@ -632,7 +711,7 @@ public function getBinaryMaxLength() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', - 'AbstractPlatform::getBinaryMaxLength() is deprecated.' + 'AbstractPlatform::getBinaryMaxLength() is deprecated.', ); return 4000; @@ -650,7 +729,7 @@ public function getBinaryDefaultLength() Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default binary column length is deprecated, specify the length explicitly.' + 'Relying on the default binary column length is deprecated, specify the length explicitly.', ); return 255; @@ -667,9 +746,9 @@ public function getWildcards() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', + 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getWildcards() is deprecated.' - . ' Use AbstractPlatform::getLikeWildcardCharacters() instead.' + . ' Use AbstractPlatform::getLikeWildcardCharacters() instead.', ); return ['%', '_']; @@ -700,8 +779,8 @@ public function getAvgExpression($column) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getAvgExpression() is deprecated. Use AVG() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getAvgExpression() is deprecated. Use AVG() in SQL instead.', ); return 'AVG(' . $column . ')'; @@ -722,8 +801,8 @@ public function getCountExpression($column) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getCountExpression() is deprecated. Use COUNT() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getCountExpression() is deprecated. Use COUNT() in SQL instead.', ); return 'COUNT(' . $column . ')'; @@ -742,8 +821,8 @@ public function getMaxExpression($column) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getMaxExpression() is deprecated. Use MAX() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getMaxExpression() is deprecated. Use MAX() in SQL instead.', ); return 'MAX(' . $column . ')'; @@ -762,8 +841,8 @@ public function getMinExpression($column) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getMinExpression() is deprecated. Use MIN() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getMinExpression() is deprecated. Use MIN() in SQL instead.', ); return 'MIN(' . $column . ')'; @@ -782,8 +861,8 @@ public function getSumExpression($column) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getSumExpression() is deprecated. Use SUM() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getSumExpression() is deprecated. Use SUM() in SQL instead.', ); return 'SUM(' . $column . ')'; @@ -806,8 +885,8 @@ public function getMd5Expression($column) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getMd5Expression() is deprecated.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getMd5Expression() is deprecated.', ); return 'MD5(' . $column . ')'; @@ -838,8 +917,8 @@ public function getSqrtExpression($column) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getSqrtExpression() is deprecated. Use SQRT() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getSqrtExpression() is deprecated. Use SQRT() in SQL instead.', ); return 'SQRT(' . $column . ')'; @@ -859,8 +938,8 @@ public function getRoundExpression($column, $decimals = 0) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getRoundExpression() is deprecated. Use ROUND() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getRoundExpression() is deprecated. Use ROUND() in SQL instead.', ); return 'ROUND(' . $column . ', ' . $decimals . ')'; @@ -930,8 +1009,8 @@ public function getRtrimExpression($str) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getRtrimExpression() is deprecated. Use RTRIM() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getRtrimExpression() is deprecated. Use RTRIM() in SQL instead.', ); return 'RTRIM(' . $str . ')'; @@ -950,8 +1029,8 @@ public function getLtrimExpression($str) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getLtrimExpression() is deprecated. Use LTRIM() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getLtrimExpression() is deprecated. Use LTRIM() in SQL instead.', ); return 'LTRIM(' . $str . ')'; @@ -971,8 +1050,8 @@ public function getUpperExpression($str) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getUpperExpression() is deprecated. Use UPPER() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getUpperExpression() is deprecated. Use UPPER() in SQL instead.', ); return 'UPPER(' . $str . ')'; @@ -992,8 +1071,8 @@ public function getLowerExpression($str) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getLowerExpression() is deprecated. Use LOWER() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getLowerExpression() is deprecated. Use LOWER() in SQL instead.', ); return 'LOWER(' . $str . ')'; @@ -1027,7 +1106,7 @@ public function getNowExpression() Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4753', - 'AbstractPlatform::getNowExpression() is deprecated. Generate dates within the application.' + 'AbstractPlatform::getNowExpression() is deprecated. Generate dates within the application.', ); return 'NOW()'; @@ -1088,8 +1167,8 @@ public function getNotExpression($expression) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getNotExpression() is deprecated. Use NOT() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getNotExpression() is deprecated. Use NOT() in SQL instead.', ); return 'NOT(' . $expression . ')'; @@ -1108,8 +1187,8 @@ public function getIsNullExpression($expression) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getIsNullExpression() is deprecated. Use IS NULL in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getIsNullExpression() is deprecated. Use IS NULL in SQL instead.', ); return $expression . ' IS NULL'; @@ -1128,8 +1207,8 @@ public function getIsNotNullExpression($expression) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getIsNotNullExpression() is deprecated. Use IS NOT NULL in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getIsNotNullExpression() is deprecated. Use IS NOT NULL in SQL instead.', ); return $expression . ' IS NOT NULL'; @@ -1156,8 +1235,8 @@ public function getBetweenExpression($expression, $value1, $value2) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getBetweenExpression() is deprecated. Use BETWEEN in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getBetweenExpression() is deprecated. Use BETWEEN in SQL instead.', ); return $expression . ' BETWEEN ' . $value1 . ' AND ' . $value2; @@ -1176,8 +1255,8 @@ public function getAcosExpression($value) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getAcosExpression() is deprecated. Use ACOS() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getAcosExpression() is deprecated. Use ACOS() in SQL instead.', ); return 'ACOS(' . $value . ')'; @@ -1196,8 +1275,8 @@ public function getSinExpression($value) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getSinExpression() is deprecated. Use SIN() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getSinExpression() is deprecated. Use SIN() in SQL instead.', ); return 'SIN(' . $value . ')'; @@ -1214,8 +1293,8 @@ public function getPiExpression() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getPiExpression() is deprecated. Use PI() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getPiExpression() is deprecated. Use PI() in SQL instead.', ); return 'PI()'; @@ -1234,8 +1313,8 @@ public function getCosExpression($value) { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getCosExpression() is deprecated. Use COS() in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getCosExpression() is deprecated. Use COS() in SQL instead.', ); return 'COS(' . $value . ')'; @@ -1261,8 +1340,8 @@ public function getDateDiffExpression($date1, $date2) /** * Returns the SQL to add the number of given seconds to a date. * - * @param string $date - * @param int $seconds + * @param string $date + * @param int|numeric-string $seconds * * @return string * @@ -1270,14 +1349,22 @@ public function getDateDiffExpression($date1, $date2) */ public function getDateAddSecondsExpression($date, $seconds) { + if (is_int($seconds)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $seconds as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND); } /** * Returns the SQL to subtract the number of given seconds from a date. * - * @param string $date - * @param int $seconds + * @param string $date + * @param int|numeric-string $seconds * * @return string * @@ -1285,14 +1372,22 @@ public function getDateAddSecondsExpression($date, $seconds) */ public function getDateSubSecondsExpression($date, $seconds) { + if (is_int($seconds)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $seconds as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND); } /** * Returns the SQL to add the number of given minutes to a date. * - * @param string $date - * @param int $minutes + * @param string $date + * @param int|numeric-string $minutes * * @return string * @@ -1300,14 +1395,22 @@ public function getDateSubSecondsExpression($date, $seconds) */ public function getDateAddMinutesExpression($date, $minutes) { + if (is_int($minutes)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $minutes as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE); } /** * Returns the SQL to subtract the number of given minutes from a date. * - * @param string $date - * @param int $minutes + * @param string $date + * @param int|numeric-string $minutes * * @return string * @@ -1315,14 +1418,22 @@ public function getDateAddMinutesExpression($date, $minutes) */ public function getDateSubMinutesExpression($date, $minutes) { + if (is_int($minutes)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $minutes as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE); } /** * Returns the SQL to add the number of given hours to a date. * - * @param string $date - * @param int $hours + * @param string $date + * @param int|numeric-string $hours * * @return string * @@ -1330,14 +1441,22 @@ public function getDateSubMinutesExpression($date, $minutes) */ public function getDateAddHourExpression($date, $hours) { + if (is_int($hours)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $hours as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR); } /** * Returns the SQL to subtract the number of given hours to a date. * - * @param string $date - * @param int $hours + * @param string $date + * @param int|numeric-string $hours * * @return string * @@ -1345,14 +1464,22 @@ public function getDateAddHourExpression($date, $hours) */ public function getDateSubHourExpression($date, $hours) { + if (is_int($hours)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $hours as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR); } /** * Returns the SQL to add the number of given days to a date. * - * @param string $date - * @param int $days + * @param string $date + * @param int|numeric-string $days * * @return string * @@ -1360,14 +1487,22 @@ public function getDateSubHourExpression($date, $hours) */ public function getDateAddDaysExpression($date, $days) { + if (is_int($days)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $days as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY); } /** * Returns the SQL to subtract the number of given days to a date. * - * @param string $date - * @param int $days + * @param string $date + * @param int|numeric-string $days * * @return string * @@ -1375,14 +1510,22 @@ public function getDateAddDaysExpression($date, $days) */ public function getDateSubDaysExpression($date, $days) { + if (is_int($days)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $days as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY); } /** * Returns the SQL to add the number of given weeks to a date. * - * @param string $date - * @param int $weeks + * @param string $date + * @param int|numeric-string $weeks * * @return string * @@ -1390,14 +1533,22 @@ public function getDateSubDaysExpression($date, $days) */ public function getDateAddWeeksExpression($date, $weeks) { + if (is_int($weeks)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $weeks as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK); } /** * Returns the SQL to subtract the number of given weeks from a date. * - * @param string $date - * @param int $weeks + * @param string $date + * @param int|numeric-string $weeks * * @return string * @@ -1405,14 +1556,22 @@ public function getDateAddWeeksExpression($date, $weeks) */ public function getDateSubWeeksExpression($date, $weeks) { + if (is_int($weeks)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $weeks as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK); } /** * Returns the SQL to add the number of given months to a date. * - * @param string $date - * @param int $months + * @param string $date + * @param int|numeric-string $months * * @return string * @@ -1420,14 +1579,22 @@ public function getDateSubWeeksExpression($date, $weeks) */ public function getDateAddMonthExpression($date, $months) { + if (is_int($months)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $months as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH); } /** * Returns the SQL to subtract the number of given months to a date. * - * @param string $date - * @param int $months + * @param string $date + * @param int|numeric-string $months * * @return string * @@ -1435,14 +1602,22 @@ public function getDateAddMonthExpression($date, $months) */ public function getDateSubMonthExpression($date, $months) { + if (is_int($months)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $months as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH); } /** * Returns the SQL to add the number of given quarters to a date. * - * @param string $date - * @param int $quarters + * @param string $date + * @param int|numeric-string $quarters * * @return string * @@ -1450,14 +1625,22 @@ public function getDateSubMonthExpression($date, $months) */ public function getDateAddQuartersExpression($date, $quarters) { + if (is_int($quarters)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $quarters as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER); } /** * Returns the SQL to subtract the number of given quarters from a date. * - * @param string $date - * @param int $quarters + * @param string $date + * @param int|numeric-string $quarters * * @return string * @@ -1465,14 +1648,22 @@ public function getDateAddQuartersExpression($date, $quarters) */ public function getDateSubQuartersExpression($date, $quarters) { + if (is_int($quarters)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $quarters as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER); } /** * Returns the SQL to add the number of given years to a date. * - * @param string $date - * @param int $years + * @param string $date + * @param int|numeric-string $years * * @return string * @@ -1480,14 +1671,22 @@ public function getDateSubQuartersExpression($date, $quarters) */ public function getDateAddYearsExpression($date, $years) { + if (is_int($years)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $years as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR); } /** * Returns the SQL to subtract the number of given years from a date. * - * @param string $date - * @param int $years + * @param string $date + * @param int|numeric-string $years * * @return string * @@ -1495,17 +1694,26 @@ public function getDateAddYearsExpression($date, $years) */ public function getDateSubYearsExpression($date, $years) { + if (is_int($years)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3498', + 'Passing $years as an integer is deprecated. Pass it as a numeric string instead.', + ); + } + return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR); } /** * Returns the SQL for a date arithmetic expression. * - * @param string $date The column or literal representing a date to perform the arithmetic operation on. - * @param string $operator The arithmetic operator (+ or -). - * @param int $interval The interval that shall be calculated into the date. - * @param string $unit The unit of the interval that shall be calculated into the date. - * One of the DATE_INTERVAL_UNIT_* constants. + * @param string $date The column or literal representing a date + * to perform the arithmetic operation on. + * @param string $operator The arithmetic operator (+ or -). + * @param int|numeric-string $interval The interval that shall be calculated into the date. + * @param string $unit The unit of the interval that shall be calculated into the date. + * One of the {@see DateIntervalUnit} constants. * * @return string * @@ -1618,16 +1826,30 @@ public function getDropTableSQL($table) $tableArg = $table; if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $table = $table->getQuotedName($this); } if (! is_string($table)) { throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' + __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', ); } if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated.', + Events::onSchemaDropTable, + ); + $eventArgs = new SchemaDropTableEventArgs($tableArg, $this); $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs); @@ -1654,6 +1876,17 @@ public function getDropTableSQL($table) */ public function getDropTemporaryTableSQL($table) { + if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + + $table = $table->getQuotedName($this); + } + return $this->getDropTableSQL($table); } @@ -1670,10 +1903,17 @@ public function getDropTemporaryTableSQL($table) public function getDropIndexSQL($index, $table = null) { if ($index instanceof Index) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $index = $index->getQuotedName($this); } elseif (! is_string($index)) { throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.' + __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.', ); } @@ -1692,11 +1932,25 @@ public function getDropIndexSQL($index, $table = null) */ public function getDropConstraintSQL($constraint, $table) { - if (! $constraint instanceof Constraint) { + if ($constraint instanceof Constraint) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $constraint as a Constraint object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + } else { $constraint = new Identifier($constraint); } - if (! $table instanceof Table) { + if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + } else { $table = new Identifier($table); } @@ -1716,11 +1970,26 @@ public function getDropConstraintSQL($constraint, $table) */ public function getDropForeignKeySQL($foreignKey, $table) { - if (! $foreignKey instanceof ForeignKeyConstraint) { + if ($foreignKey instanceof ForeignKeyConstraint) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $foreignKey as a ForeignKeyConstraint object to %s is deprecated.' + . ' Pass it as a quoted name instead.', + __METHOD__, + ); + } else { $foreignKey = new Identifier($foreignKey); } - if (! $table instanceof Table) { + if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + } else { $table = new Identifier($table); } @@ -1743,8 +2012,9 @@ public function getDropUniqueConstraintSQL(string $name, string $tableName): str * on this platform. * * @param int $createFlags + * @psalm-param int-mask-of $createFlags * - * @return string[] The sequence of SQL statements. + * @return list The list of SQL statements. * * @throws Exception * @throws InvalidArgumentException @@ -1753,10 +2023,54 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE { if (! is_int($createFlags)) { throw new InvalidArgumentException( - 'Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.' + 'Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.', + ); + } + + if (($createFlags & self::CREATE_INDEXES) === 0) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5416', + 'Unsetting the CREATE_INDEXES flag in AbstractPlatform::getCreateTableSQL() is deprecated.', + ); + } + + if (($createFlags & self::CREATE_FOREIGNKEYS) === 0) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5416', + 'Not setting the CREATE_FOREIGNKEYS flag in AbstractPlatform::getCreateTableSQL()' + . ' is deprecated. In order to build the statements that create multiple tables' + . ' referencing each other via foreign keys, use AbstractPlatform::getCreateTablesSQL().', ); } + return $this->buildCreateTableSQL( + $table, + ($createFlags & self::CREATE_INDEXES) > 0, + ($createFlags & self::CREATE_FOREIGNKEYS) > 0, + ); + } + + /** + * @internal + * + * @return list + * + * @throws Exception + */ + final protected function getCreateTableWithoutForeignKeysSQL(Table $table): array + { + return $this->buildCreateTableSQL($table, true, false); + } + + /** + * @return list + * + * @throws Exception + */ + private function buildCreateTableSQL(Table $table, bool $createIndexes, bool $createForeignKeys): array + { if (count($table->getColumns()) === 0) { throw Exception::noColumnsSpecifiedForTable($table->getName()); } @@ -1767,7 +2081,7 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE $options['indexes'] = []; $options['primary'] = []; - if (($createFlags & self::CREATE_INDEXES) > 0) { + if ($createIndexes) { foreach ($table->getIndexes() as $index) { if (! $index->isPrimary()) { $options['indexes'][$index->getQuotedName($this)] = $index; @@ -1784,7 +2098,7 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE } } - if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) { + if ($createForeignKeys) { $options['foreignKeys'] = []; foreach ($table->getForeignKeys() as $fkConstraint) { @@ -1800,6 +2114,13 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE $this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn) ) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated.', + Events::onSchemaCreateTableColumn, + ); + $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this); $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs); @@ -1821,6 +2142,13 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE } if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated.', + Events::onSchemaCreateTable, + ); + $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this); $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs); @@ -1851,6 +2179,58 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE return array_merge($sql, $columnSql); } + /** + * @param list $tables + * + * @return list + * + * @throws Exception + */ + public function getCreateTablesSQL(array $tables): array + { + $sql = []; + + foreach ($tables as $table) { + $sql = array_merge($sql, $this->getCreateTableWithoutForeignKeysSQL($table)); + } + + foreach ($tables as $table) { + foreach ($table->getForeignKeys() as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL( + $foreignKey, + $table->getQuotedName($this), + ); + } + } + + return $sql; + } + + /** + * @param list
$tables + * + * @return list + */ + public function getDropTablesSQL(array $tables): array + { + $sql = []; + + foreach ($tables as $table) { + foreach ($table->getForeignKeys() as $foreignKey) { + $sql[] = $this->getDropForeignKeySQL( + $foreignKey->getQuotedName($this), + $table->getQuotedName($this), + ); + } + } + + foreach ($tables as $table) { + $sql[] = $this->getDropTableSQL($table->getQuotedName($this)); + } + + return $sql; + } + protected function getCommentOnTableSQL(string $tableName, ?string $comment): string { $tableName = new Identifier($tableName); @@ -1858,7 +2238,7 @@ protected function getCommentOnTableSQL(string $tableName, ?string $comment): st return sprintf( 'COMMENT ON TABLE %s IS %s', $tableName->getQuotedName($this), - $this->quoteStringLiteral((string) $comment) + $this->quoteStringLiteral((string) $comment), ); } @@ -1878,7 +2258,7 @@ public function getCommentOnColumnSQL($tableName, $columnName, $comment) 'COMMENT ON COLUMN %s.%s IS %s', $tableName->getQuotedName($this), $columnName->getQuotedName($this), - $this->quoteStringLiteral((string) $comment) + $this->quoteStringLiteral((string) $comment), ); } @@ -1941,7 +2321,7 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] $sql = [$query]; if (isset($options['foreignKeys'])) { - foreach ((array) $options['foreignKeys'] as $definition) { + foreach ($options['foreignKeys'] as $definition) { $sql[] = $this->getCreateForeignKeySQL($definition, $name); } } @@ -1949,14 +2329,22 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] return $sql; } - /** - * @return string - */ + /** @return string */ public function getCreateTemporaryTableSnippetSQL() { return 'CREATE TEMPORARY TABLE'; } + /** + * Generates SQL statements that can be used to apply the diff. + * + * @return list + */ + public function getAlterSchemaSQL(SchemaDiff $diff): array + { + return $diff->toSql($this); + } + /** * Returns the SQL to create a sequence on this platform. * @@ -1997,6 +2385,13 @@ public function getDropSequenceSQL($sequence) } if ($sequence instanceof Sequence) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $sequence as a Sequence object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $sequence = $sequence->getQuotedName($this); } @@ -2018,6 +2413,13 @@ public function getDropSequenceSQL($sequence) public function getCreateConstraintSQL(Constraint $constraint, $table) { if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $table = $table->getQuotedName($this); } @@ -2033,7 +2435,7 @@ public function getCreateConstraintSQL(Constraint $constraint, $table) $query .= ' UNIQUE'; } else { throw new InvalidArgumentException( - 'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().' + 'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().', ); } } elseif ($constraint instanceof UniqueConstraint) { @@ -2062,6 +2464,13 @@ public function getCreateConstraintSQL(Constraint $constraint, $table) public function getCreateIndexSQL(Index $index, $table) { if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $table = $table->getQuotedName($this); } @@ -2069,7 +2478,11 @@ public function getCreateIndexSQL(Index $index, $table) $columns = $index->getColumns(); if (count($columns) === 0) { - throw new InvalidArgumentException("Incomplete definition. 'columns' required."); + throw new InvalidArgumentException(sprintf( + 'Incomplete or invalid index definition %s on table %s', + $name, + $table, + )); } if ($index->isPrimary()) { @@ -2116,6 +2529,13 @@ protected function getCreateIndexSQLFlags(Index $index) public function getCreatePrimaryKeySQL(Index $index, $table) { if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $table = $table->getQuotedName($this); } @@ -2211,6 +2631,13 @@ public function quoteSingleIdentifier($str) public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) { if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $table = $table->getQuotedName($this); } @@ -2222,7 +2649,7 @@ public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) * * This method returns an array of SQL statements, since some platforms need several statements. * - * @return string[] + * @return list * * @throws Exception If not supported on this platform. */ @@ -2231,6 +2658,14 @@ public function getAlterTableSQL(TableDiff $diff) throw Exception::notSupported(__METHOD__); } + /** @return list */ + public function getRenameTableSQL(string $oldName, string $newName): array + { + return [ + sprintf('ALTER TABLE %s RENAME TO %s', $oldName, $newName), + ]; + } + /** * @param mixed[] $columnSql * @@ -2246,6 +2681,13 @@ protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, return false; } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated.', + Events::onSchemaAlterTableAddColumn, + ); + $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this); $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs); @@ -2269,6 +2711,13 @@ protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $dif return false; } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated.', + Events::onSchemaAlterTableRemoveColumn, + ); + $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this); $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs); @@ -2292,6 +2741,13 @@ protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableD return false; } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated.', + Events::onSchemaAlterTableChangeColumn, + ); + $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this); $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs); @@ -2316,6 +2772,13 @@ protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column return false; } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated.', + Events::onSchemaAlterTableRenameColumn, + ); + $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this); $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs); @@ -2339,6 +2802,13 @@ protected function onSchemaAlterTable(TableDiff $diff, &$sql) return false; } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated.', + Events::onSchemaAlterTable, + ); + $eventArgs = new SchemaAlterTableEventArgs($diff, $this); $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs); @@ -2347,72 +2817,72 @@ protected function onSchemaAlterTable(TableDiff $diff, &$sql) return $eventArgs->isDefaultPrevented(); } - /** - * @return string[] - */ + /** @return string[] */ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) { - $tableName = $diff->getName($this)->getQuotedName($this); + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); $sql = []; if ($this->supportsForeignKeyConstraints()) { - foreach ($diff->removedForeignKeys as $foreignKey) { - $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + foreach ($diff->getDroppedForeignKeys() as $foreignKey) { + if ($foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableNameSQL); } - foreach ($diff->changedForeignKeys as $foreignKey) { - $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + foreach ($diff->getModifiedForeignKeys() as $foreignKey) { + $sql[] = $this->getDropForeignKeySQL($foreignKey->getQuotedName($this), $tableNameSQL); } } - foreach ($diff->removedIndexes as $index) { - $sql[] = $this->getDropIndexSQL($index, $tableName); + foreach ($diff->getDroppedIndexes() as $index) { + $sql[] = $this->getDropIndexSQL($index->getQuotedName($this), $tableNameSQL); } - foreach ($diff->changedIndexes as $index) { - $sql[] = $this->getDropIndexSQL($index, $tableName); + foreach ($diff->getModifiedIndexes() as $index) { + $sql[] = $this->getDropIndexSQL($index->getQuotedName($this), $tableNameSQL); } return $sql; } - /** - * @return string[] - */ + /** @return string[] */ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) { $sql = []; $newName = $diff->getNewName(); if ($newName !== false) { - $tableName = $newName->getQuotedName($this); + $tableNameSQL = $newName->getQuotedName($this); } else { - $tableName = $diff->getName($this)->getQuotedName($this); + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); } if ($this->supportsForeignKeyConstraints()) { - foreach ($diff->addedForeignKeys as $foreignKey) { - $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + foreach ($diff->getAddedForeignKeys() as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); } - foreach ($diff->changedForeignKeys as $foreignKey) { - $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + foreach ($diff->getModifiedForeignKeys() as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); } } - foreach ($diff->addedIndexes as $index) { - $sql[] = $this->getCreateIndexSQL($index, $tableName); + foreach ($diff->getAddedIndexes() as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableNameSQL); } - foreach ($diff->changedIndexes as $index) { - $sql[] = $this->getCreateIndexSQL($index, $tableName); + foreach ($diff->getModifiedIndexes() as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableNameSQL); } - foreach ($diff->renamedIndexes as $oldIndexName => $index) { + foreach ($diff->getRenamedIndexes() as $oldIndexName => $index) { $oldIndexName = new Identifier($oldIndexName); $sql = array_merge( $sql, - $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName) + $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableNameSQL), ); } @@ -2526,10 +2996,29 @@ public function getColumnDeclarationSQL($name, array $column) $notnull = ! empty($column['notnull']) ? ' NOT NULL' : ''; - $unique = ! empty($column['unique']) ? - ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + if (! empty($column['unique'])) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5656', + 'The usage of the "unique" column property is deprecated. Use unique constraints instead.', + ); + + $unique = ' ' . $this->getUniqueFieldDeclarationSQL(); + } else { + $unique = ''; + } + + if (! empty($column['check'])) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5656', + 'The usage of the "check" column property is deprecated.', + ); - $check = ! empty($column['check']) ? ' ' . $column['check'] : ''; + $check = ' ' . $column['check']; + } else { + $check = ''; + } $typeDecl = $column['type']->getSQLDeclaration($column, $this); $declaration = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation; @@ -2551,12 +3040,37 @@ public function getColumnDeclarationSQL($name, array $column) */ public function getDecimalTypeDeclarationSQL(array $column) { - $column['precision'] = ! isset($column['precision']) || empty($column['precision']) - ? 10 : $column['precision']; - $column['scale'] = ! isset($column['scale']) || empty($column['scale']) - ? 0 : $column['scale']; + if (empty($column['precision'])) { + if (! isset($column['precision'])) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5637', + 'Relying on the default decimal column precision is deprecated' + . ', specify the precision explicitly.', + ); + } + + $precision = 10; + } else { + $precision = $column['precision']; + } - return 'NUMERIC(' . $column['precision'] . ', ' . $column['scale'] . ')'; + if (empty($column['scale'])) { + if (! isset($column['scale'])) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5637', + 'Relying on the default decimal column scale is deprecated' + . ', specify the scale explicitly.', + ); + } + + $scale = 0; + } else { + $scale = $column['scale']; + } + + return 'NUMERIC(' . $precision . ', ' . $scale . ')'; } /** @@ -2688,21 +3202,39 @@ public function getIndexDeclarationSQL($name, Index $index) * e.g. when a column has the "columnDefinition" keyword. * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate. * + * @deprecated + * * @param mixed[] $column * * @return string */ public function getCustomTypeDeclarationSQL(array $column) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5527', + '%s is deprecated.', + __METHOD__, + ); + return $column['columnDefinition']; } /** * Obtains DBMS specific SQL code portion needed to set an index * declaration to be used in statements like CREATE TABLE. + * + * @deprecated */ public function getIndexFieldDeclarationListSQL(Index $index): string { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5527', + '%s is deprecated.', + __METHOD__, + ); + return implode(', ', $index->getQuotedColumns($this)); } @@ -2710,10 +3242,19 @@ public function getIndexFieldDeclarationListSQL(Index $index): string * Obtains DBMS specific SQL code portion needed to set an index * declaration to be used in statements like CREATE TABLE. * + * @deprecated + * * @param mixed[] $columns */ public function getColumnsFieldDeclarationListSQL(array $columns): string { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5527', + '%s is deprecated.', + __METHOD__, + ); + $ret = []; foreach ($columns as $column => $definition) { @@ -2747,8 +3288,8 @@ public function getTemporaryTableSQL() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getTemporaryTableSQL() is deprecated.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getTemporaryTableSQL() is deprecated.', ); return 'TEMPORARY'; @@ -2876,8 +3417,8 @@ public function getUniqueFieldDeclarationSQL() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getUniqueFieldDeclarationSQL() is deprecated. Use UNIQUE in SQL instead.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getUniqueFieldDeclarationSQL() is deprecated. Use UNIQUE in SQL instead.', ); return 'UNIQUE'; @@ -2908,7 +3449,7 @@ public function getColumnCharsetDeclarationSQL($charset) */ public function getColumnCollationDeclarationSQL($collation) { - return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : ''; + return $this->supportsColumnCollation() ? 'COLLATE ' . $this->quoteSingleIdentifier($collation) : ''; } /** @@ -2923,8 +3464,8 @@ public function prefersIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/1519', - 'AbstractPlatform::prefersIdentityColumns() is deprecated.' + 'https://github.com/doctrine/dbal/pull/1519', + 'AbstractPlatform::prefersIdentityColumns() is deprecated.', ); return false; @@ -2966,9 +3507,11 @@ public function convertBooleans($item) * * The default conversion tries to convert value into bool "(bool)$item" * - * @param mixed $item + * @param T $item * - * @return bool|null + * @return (T is null ? null : bool) + * + * @template T */ public function convertFromBoolean($item) { @@ -3050,6 +3593,8 @@ protected function _getTransactionIsolationLevelSQL($level) } /** + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. + * * @return string * * @throws Exception If not supported on this platform. @@ -3074,13 +3619,15 @@ public function getListNamespacesSQL() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'AbstractPlatform::getListNamespacesSQL() is deprecated,' - . ' use AbstractSchemaManager::listSchemaNames() instead.' + . ' use AbstractSchemaManager::listSchemaNames() instead.', ); throw Exception::notSupported(__METHOD__); } /** + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. + * * @param string $database * * @return string @@ -3093,6 +3640,8 @@ public function getListSequencesSQL($database) } /** + * @deprecated + * * @param string $table * * @return string @@ -3105,6 +3654,8 @@ public function getListTableConstraintsSQL($table) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * @param string $table * @param string $database * @@ -3118,6 +3669,8 @@ public function getListTableColumnsSQL($table, $database = null) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * @return string * * @throws Exception If not supported on this platform. @@ -3138,8 +3691,8 @@ public function getListUsersSQL() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::getListUsersSQL() is deprecated.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getListUsersSQL() is deprecated.', ); throw Exception::notSupported(__METHOD__); @@ -3148,6 +3701,8 @@ public function getListUsersSQL() /** * Returns the SQL to list all views of a database or user. * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. + * * @param string $database * * @return string @@ -3160,6 +3715,8 @@ public function getListViewsSQL($database) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * Returns the list of indexes for the current database. * * The current database parameter is optional but will always be passed @@ -3182,6 +3739,8 @@ public function getListTableIndexesSQL($table, $database = null) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * @param string $table * * @return string @@ -3346,7 +3905,7 @@ public function getFloatDeclarationSQL(array $column) * * @see TransactionIsolationLevel * - * @return int The default isolation level. + * @return TransactionIsolationLevel::* The default isolation level. */ public function getDefaultTransactionIsolationLevel() { @@ -3385,16 +3944,27 @@ public function supportsIdentityColumns() * but support sequences can emulate identity columns by using * sequences. * + * @deprecated + * * @return bool */ public function usesSequenceEmulatedIdentityColumns() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + return false; } /** * Returns the name of the sequence for a particular identity column in a particular table. * + * @deprecated + * * @see usesSequenceEmulatedIdentityColumns * * @param string $tableName The name of the table to return the sequence name for. @@ -3420,8 +3990,8 @@ public function supportsIndexes() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::supportsIndexes() is deprecated.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsIndexes() is deprecated.', ); return true; @@ -3456,8 +4026,8 @@ public function supportsAlterTable() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::supportsAlterTable() is deprecated. All platforms must implement altering tables.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsAlterTable() is deprecated. All platforms must implement altering tables.', ); return true; @@ -3474,8 +4044,8 @@ public function supportsTransactions() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::supportsTransactions() is deprecated.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsTransactions() is deprecated.', ); return true; @@ -3512,8 +4082,8 @@ public function supportsPrimaryConstraints() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::supportsPrimaryConstraints() is deprecated.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsPrimaryConstraints() is deprecated.', ); return true; @@ -3522,10 +4092,18 @@ public function supportsPrimaryConstraints() /** * Whether the platform supports foreign key constraints. * + * @deprecated All platforms should support foreign key constraints. + * * @return bool */ public function supportsForeignKeyConstraints() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5409', + 'AbstractPlatform::supportsForeignKeyConstraints() is deprecated.', + ); + return true; } @@ -3545,7 +4123,7 @@ public function supportsSchemas() * @deprecated * * Platforms that either support or emulate schemas don't automatically - * filter a schema for the namespaced elements in {@see AbstractManager::createSchema()}. + * filter a schema for the namespaced elements in {@see AbstractManager::introspectSchema()}. * * @return bool */ @@ -3554,7 +4132,7 @@ public function canEmulateSchemas() Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4805', - 'AbstractPlatform::canEmulateSchemas() is deprecated.' + 'AbstractPlatform::canEmulateSchemas() is deprecated.', ); return false; @@ -3563,6 +4141,8 @@ public function canEmulateSchemas() /** * Returns the default schema name. * + * @deprecated + * * @return string * * @throws Exception If not supported on this platform. @@ -3577,10 +4157,19 @@ public function getDefaultSchemaName() * * Some databases don't allow to create and drop databases at all or only with certain tools. * + * @deprecated + * * @return bool */ public function supportsCreateDropDatabase() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + return true; } @@ -3595,8 +4184,8 @@ public function supportsGettingAffectedRows() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::supportsGettingAffectedRows() is deprecated.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsGettingAffectedRows() is deprecated.', ); return true; @@ -3625,20 +4214,38 @@ public function supportsCommentOnStatement() /** * Does this platform have native guid type. * + * @deprecated + * * @return bool */ public function hasNativeGuidType() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return false; } /** * Does this platform have native JSON type. * + * @deprecated + * * @return bool */ public function hasNativeJsonType() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return false; } @@ -3653,8 +4260,8 @@ public function supportsViews() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', - 'AbstractPlatform::supportsViews() is deprecated. All platforms must implement support for views.' + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsViews() is deprecated. All platforms must implement support for views.', ); return true; @@ -3728,14 +4335,14 @@ final public function modifyLimitQuery($query, $limit, $offset = 0): string if ($offset < 0) { throw new Exception(sprintf( 'Offset must be a positive integer or zero, %d given', - $offset + $offset, )); } if ($offset > 0 && ! $this->supportsLimitOffset()) { throw new Exception(sprintf( 'Platform %s does not support offset values in limit queries.', - $this->getName() + $this->getName(), )); } @@ -3779,9 +4386,9 @@ public function supportsLimitOffset() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/4724', + 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::supportsViews() is deprecated.' - . ' All platforms must implement support for offsets in modify limit clauses.' + . ' All platforms must implement support for offsets in modify limit clauses.', ); return true; @@ -3883,13 +4490,8 @@ public function rollbackSavePoint($savepoint) */ final public function getReservedKeywordsList(): KeywordList { - // Check for an existing instantiation of the keywords class. - if ($this->_keywords === null) { - // Store the instance so it doesn't need to be generated on every request. - $this->_keywords = $this->createReservedKeywordsList(); - } - - return $this->_keywords; + // Store the instance so it doesn't need to be generated on every request. + return $this->_keywords ??= $this->createReservedKeywordsList(); } /** @@ -3926,7 +4528,7 @@ protected function getReservedKeywordsClass() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'AbstractPlatform::getReservedKeywordsClass() is deprecated,' - . ' use AbstractPlatform::createReservedKeywordsList() instead.' + . ' use AbstractPlatform::createReservedKeywordsList() instead.', ); throw Exception::notSupported(__METHOD__); @@ -3952,10 +4554,19 @@ public function quoteStringLiteral($str) /** * Gets the character used for string literal quoting. * + * @deprecated Use {@see quoteStringLiteral()} to quote string literals instead. + * * @return string */ public function getStringLiteralQuoteCharacter() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5388', + 'AbstractPlatform::getStringLiteralQuoteCharacter() is deprecated.' + . ' Use quoteStringLiteral() instead.', + ); + return "'"; } @@ -3972,7 +4583,7 @@ final public function escapeStringForLike(string $inputString, string $escapeCha return preg_replace( '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u', addcslashes($escapeChar, '\\') . '$1', - $inputString + $inputString, ); } @@ -3984,22 +4595,14 @@ private function columnToArray(Column $column): array { $name = $column->getQuotedName($this); - $columnData = array_merge($column->toArray(), [ + return array_merge($column->toArray(), [ 'name' => $name, 'version' => $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false, 'comment' => $this->getColumnComment($column), ]); - - if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) { - $columnData['length'] = $this->getVarcharDefaultLength(); - } - - return $columnData; } - /** - * @internal - */ + /** @internal */ public function createSQLParser(): Parser { return new Parser(false); @@ -4031,6 +4634,10 @@ public function columnsEqual(Column $column1, Column $column2): bool return false; } + if (! $this->columnDeclarationsMatch($column1, $column2)) { + return false; + } + // If the platform supports inline comments, all comparison is already done above if ($this->supportsInlineColumnComments()) { return true; @@ -4042,4 +4649,28 @@ public function columnsEqual(Column $column1, Column $column2): bool return $column1->getType() === $column2->getType(); } + + /** + * Whether the database data type matches that expected for the doctrine type for the given colunms. + */ + private function columnDeclarationsMatch(Column $column1, Column $column2): bool + { + return ! ( + $column1->hasPlatformOption('declarationMismatch') || + $column2->hasPlatformOption('declarationMismatch') + ); + } + + /** + * Creates the schema manager that can be used to inspect and change the underlying + * database schema according to the dialect of the platform. + * + * @throws Exception + * + * @abstract + */ + public function createSchemaManager(Connection $connection): AbstractSchemaManager + { + throw Exception::notSupported(__METHOD__); + } } diff --git a/doctrine/dbal/src/Platforms/DB2111Platform.php b/doctrine/dbal/src/Platforms/DB2111Platform.php new file mode 100644 index 000000000..40ab42f6e --- /dev/null +++ b/doctrine/dbal/src/Platforms/DB2111Platform.php @@ -0,0 +1,40 @@ + 0) { + $query .= sprintf(' OFFSET %u ROWS', $offset); + } + + if ($limit !== null) { + if ($limit < 0) { + throw new Exception(sprintf('Limit must be a positive integer or zero, %d given', $limit)); + } + + $query .= sprintf(' FETCH %s %u ROWS ONLY', $offset === 0 ? 'FIRST' : 'NEXT', $limit); + } + + return $query; + } +} diff --git a/doctrine/dbal/src/Platforms/DB2Platform.php b/doctrine/dbal/src/Platforms/DB2Platform.php index 979bb2c8f..b203ce8a0 100644 --- a/doctrine/dbal/src/Platforms/DB2Platform.php +++ b/doctrine/dbal/src/Platforms/DB2Platform.php @@ -2,8 +2,10 @@ namespace Doctrine\DBAL\Platforms; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\DB2SchemaManager; use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\TableDiff; @@ -21,26 +23,77 @@ use function sprintf; use function strpos; +/** + * Provides the behavior, features and SQL dialect of the IBM DB2 database platform of the oldest supported version. + */ class DB2Platform extends AbstractPlatform { + /** @see https://www.ibm.com/docs/en/db2/11.5?topic=views-syscatcolumns */ + private const SYSCAT_COLUMNS_GENERATED_DEFAULT = 'D'; + + /** @see https://www.ibm.com/docs/en/db2/11.5?topic=views-syscatindexes */ + private const SYSCAT_INDEXES_UNIQUERULE_PERMITS_DUPLICATES = 'D'; + private const SYSCAT_INDEXES_UNIQUERULE_IMPLEMENTS_PRIMARY_KEY = 'P'; + + /** @see https://www.ibm.com/docs/en/db2/11.5?topic=views-syscattabconst */ + private const SYSCAT_TABCONST_TYPE_PRIMARY_KEY = 'P'; + + /** @see https://www.ibm.com/docs/en/db2/11.5?topic=views-syscatreferences */ + private const SYSCAT_REFERENCES_UPDATERULE_RESTRICT = 'R'; + private const SYSCAT_REFERENCES_DELETERULE_CASCADE = 'C'; + private const SYSCAT_REFERENCES_DELETERULE_SET_NULL = 'N'; + private const SYSCAT_REFERENCES_DELETERULE_RESTRICT = 'R'; + + /** @see https://www.ibm.com/docs/en/db2-for-zos/11?topic=tables-systables */ + private const SYSIBM_SYSTABLES_TYPE_TABLE = 'T'; + + /** + * {@inheritDoc} + * + * @deprecated + */ public function getCharMaxLength(): int { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + '%s() is deprecated.', + __METHOD__, + ); + return 254; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getBinaryMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + '%s() is deprecated.', + __METHOD__, + ); + return 32704; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getBinaryDefaultLength() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'Relying on the default binary column length is deprecated, specify the length explicitly.', + ); + return 1; } @@ -72,34 +125,34 @@ public function getBlobTypeDeclarationSQL(array $column) protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'bigint' => 'bigint', - 'binary' => 'binary', - 'blob' => 'blob', - 'character' => 'string', - 'clob' => 'text', - 'date' => 'date', - 'decimal' => 'decimal', - 'double' => 'float', - 'integer' => 'integer', - 'real' => 'float', - 'smallint' => 'smallint', - 'time' => 'time', - 'timestamp' => 'datetime', - 'varbinary' => 'binary', - 'varchar' => 'string', + 'bigint' => Types::BIGINT, + 'binary' => Types::BINARY, + 'blob' => Types::BLOB, + 'character' => Types::STRING, + 'clob' => Types::TEXT, + 'date' => Types::DATE_MUTABLE, + 'decimal' => Types::DECIMAL, + 'double' => Types::FLOAT, + 'integer' => Types::INTEGER, + 'real' => Types::FLOAT, + 'smallint' => Types::SMALLINT, + 'time' => Types::TIME_MUTABLE, + 'timestamp' => Types::DATETIME_MUTABLE, + 'varbinary' => Types::BINARY, + 'varchar' => Types::STRING, ]; } /** - * {@inheritdoc} + * {@inheritDoc} */ public function isCommentedDoctrineType(Type $doctrineType) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5058', - '%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.', - __METHOD__ + '%s() is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.', + __METHOD__, ); if ($doctrineType->getName() === Types::BOOLEAN) { @@ -114,17 +167,35 @@ public function isCommentedDoctrineType(Type $doctrineType) /** * {@inheritDoc} */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { + if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'Relying on the default string column length on IBM DB2 is deprecated' + . ', specify the length explicitly.', + ); + } + return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(254)') : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); } /** - * {@inheritdoc} + * {@inheritDoc} */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { + if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'Relying on the default binary column length on IBM DB2 is deprecated' + . ', specify the length explicitly.', + ); + } + return $this->getVarcharTypeDeclarationSQLSnippet($length, $fixed) . ' FOR BIT DATA'; } @@ -145,7 +216,8 @@ public function getName() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4749', - 'DB2Platform::getName() is deprecated. Identify platforms by their class.' + '%s() is deprecated. Identify platforms by their class.', + __METHOD__, ); return 'db2'; @@ -197,7 +269,7 @@ protected function _getCommonIntegerTypeDeclarationSQL(array $column) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getBitAndComparisonExpression($value1, $value2) { @@ -205,7 +277,7 @@ public function getBitAndComparisonExpression($value1, $value2) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getBitOrComparisonExpression($value1, $value2) { @@ -213,7 +285,7 @@ public function getBitOrComparisonExpression($value1, $value2) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { @@ -233,7 +305,7 @@ protected function getDateArithmeticIntervalExpression($date, $operator, $interv } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getDateDiffExpression($date1, $date2) { @@ -269,7 +341,7 @@ public function getTimeTypeDeclarationSQL(array $column) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getTruncateTableSQL($tableName, $cascade = false) { @@ -279,6 +351,8 @@ public function getTruncateTableSQL($tableName, $cascade = false) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited. * * @param string $table @@ -313,14 +387,14 @@ public function getListTableColumnsSQL($table, $database = null) c.remarks AS comment, k.colseq, CASE - WHEN c.generated = 'D' THEN 1 + WHEN c.generated = '" . self::SYSCAT_COLUMNS_GENERATED_DEFAULT . "' THEN 1 ELSE 0 END AS autoincrement FROM syscat.columns c LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc ON (k.tabschema = tc.tabschema AND k.tabname = tc.tabname - AND tc.type = 'P')) + AND tc.type = '" . self::SYSCAT_TABCONST_TYPE_PRIMARY_KEY . "')) ON (c.tabschema = k.tabschema AND c.tabname = k.tabname AND c.colname = k.colname) @@ -336,15 +410,20 @@ public function getListTableColumnsSQL($table, $database = null) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTablesSQL() { - return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'"; + return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = '" . self::SYSIBM_SYSTABLES_TYPE_TABLE . "'" + . ' AND CREATOR = CURRENT_USER'; } /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { @@ -352,6 +431,8 @@ public function getListViewsSQL($database) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTableIndexesSQL($table, $database = null) @@ -361,11 +442,13 @@ public function getListTableIndexesSQL($table, $database = null) return "SELECT idx.INDNAME AS key_name, idxcol.COLNAME AS column_name, CASE - WHEN idx.UNIQUERULE = 'P' THEN 1 + WHEN idx.UNIQUERULE = '" . self::SYSCAT_INDEXES_UNIQUERULE_IMPLEMENTS_PRIMARY_KEY . "' + THEN 1 ELSE 0 END AS primary, CASE - WHEN idx.UNIQUERULE = 'D' THEN 1 + WHEN idx.UNIQUERULE = '" . self::SYSCAT_INDEXES_UNIQUERULE_PERMITS_DUPLICATES . "' + THEN 1 ELSE 0 END AS non_unique FROM SYSCAT.INDEXES AS idx @@ -376,6 +459,8 @@ public function getListTableIndexesSQL($table, $database = null) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTableForeignKeysSQL($table) @@ -387,13 +472,13 @@ public function getListTableForeignKeysSQL($table) pkcol.COLNAME AS foreign_column, fk.CONSTNAME AS index_name, CASE - WHEN fk.UPDATERULE = 'R' THEN 'RESTRICT' + WHEN fk.UPDATERULE = '" . self::SYSCAT_REFERENCES_UPDATERULE_RESTRICT . "' THEN 'RESTRICT' ELSE NULL END AS on_update, CASE - WHEN fk.DELETERULE = 'C' THEN 'CASCADE' - WHEN fk.DELETERULE = 'N' THEN 'SET NULL' - WHEN fk.DELETERULE = 'R' THEN 'RESTRICT' + WHEN fk.DELETERULE = '" . self::SYSCAT_REFERENCES_DELETERULE_CASCADE . "' THEN 'CASCADE' + WHEN fk.DELETERULE = '" . self::SYSCAT_REFERENCES_DELETERULE_SET_NULL . "' THEN 'SET NULL' + WHEN fk.DELETERULE = '" . self::SYSCAT_REFERENCES_DELETERULE_RESTRICT . "' THEN 'RESTRICT' ELSE NULL END AS on_delete FROM SYSCAT.REFERENCES AS fk @@ -411,14 +496,25 @@ public function getListTableForeignKeysSQL($table) /** * {@inheritDoc} + * + * @deprecated */ public function supportsCreateDropDatabase() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s() is deprecated.', + __METHOD__, + ); + return false; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsCommentOnStatement() { @@ -451,6 +547,8 @@ public function getCurrentTimestampSQL() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getIndexDeclarationSQL($name, Index $index) { @@ -488,8 +586,10 @@ public function getAlterTableSQL(TableDiff $diff) $columnSql = []; $commentsSQL = []; + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); + $queryParts = []; - foreach ($diff->addedColumns as $column) { + foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } @@ -515,13 +615,13 @@ public function getAlterTableSQL(TableDiff $diff) } $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), + $tableNameSQL, $column->getQuotedName($this), - $comment + $comment, ); } - foreach ($diff->removedColumns as $column) { + foreach ($diff->getDroppedColumns() as $column) { if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { continue; } @@ -529,27 +629,28 @@ public function getAlterTableSQL(TableDiff $diff) $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); } - foreach ($diff->changedColumns as $columnDiff) { + foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } - if ($columnDiff->hasChanged('comment')) { + if ($columnDiff->hasCommentChanged()) { $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), - $columnDiff->column->getQuotedName($this), - $this->getColumnComment($columnDiff->column) + $tableNameSQL, + $columnDiff->getNewColumn()->getQuotedName($this), + $this->getColumnComment($columnDiff->getNewColumn()), ); - - if (count($columnDiff->changedProperties) === 1) { - continue; - } } - $this->gatherAlterColumnSQL($diff->getName($this), $columnDiff, $sql, $queryParts); + $this->gatherAlterColumnSQL( + $tableNameSQL, + $columnDiff, + $sql, + $queryParts, + ); } - foreach ($diff->renamedColumns as $oldColumnName => $column) { + foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { continue; } @@ -564,12 +665,12 @@ public function getAlterTableSQL(TableDiff $diff) if (! $this->onSchemaAlterTable($diff, $tableSql)) { if (count($queryParts) > 0) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(' ', $queryParts); + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . implode(' ', $queryParts); } // Some table alteration operations require a table reorganization. - if (! empty($diff->removedColumns) || ! empty($diff->changedColumns)) { - $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')"; + if (count($diff->getDroppedColumns()) > 0 || count($diff->getModifiedColumns()) > 0) { + $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $tableNameSQL . "')"; } $sql = array_merge($sql, $commentsSQL); @@ -577,33 +678,50 @@ public function getAlterTableSQL(TableDiff $diff) $newName = $diff->getNewName(); if ($newName !== false) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5663', + 'Generation of "rename table" SQL using %s() is deprecated. Use getRenameTableSQL() instead.', + __METHOD__, + ); + $sql[] = sprintf( 'RENAME TABLE %s TO %s', - $diff->getName($this)->getQuotedName($this), - $newName->getQuotedName($this) + $tableNameSQL, + $newName->getQuotedName($this), ); } $sql = array_merge( $this->getPreAlterTableIndexForeignKeySQL($diff), $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) + $this->getPostAlterTableIndexForeignKeySQL($diff), ); } return array_merge($sql, $tableSql, $columnSql); } + /** + * {@inheritDoc} + */ + public function getRenameTableSQL(string $oldName, string $newName): array + { + return [ + sprintf('RENAME TABLE %s TO %s', $oldName, $newName), + ]; + } + /** * Gathers the table alteration SQL for a given column diff. * - * @param Identifier $table The table to gather the SQL for. + * @param string $table The table to gather the SQL for. * @param ColumnDiff $columnDiff The column diff to evaluate. * @param string[] $sql The sequence of table alteration statements to fill. * @param mixed[] $queryParts The sequence of column alteration clauses to fill. */ private function gatherAlterColumnSQL( - Identifier $table, + string $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts @@ -625,7 +743,7 @@ private function gatherAlterColumnSQL( // so we need to trigger a complete ALTER TABLE statement // for each ALTER COLUMN clause. foreach ($alterColumnClauses as $alterColumnClause) { - $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause; + $sql[] = 'ALTER TABLE ' . $table . ' ' . $alterColumnClause; } } @@ -636,33 +754,33 @@ private function gatherAlterColumnSQL( */ private function getAlterColumnClausesSQL(ColumnDiff $columnDiff): array { - $column = $columnDiff->column->toArray(); + $newColumn = $columnDiff->getNewColumn()->toArray(); - $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this); + $alterClause = 'ALTER COLUMN ' . $columnDiff->getNewColumn()->getQuotedName($this); - if ($column['columnDefinition'] !== null) { - return [$alterClause . ' ' . $column['columnDefinition']]; + if ($newColumn['columnDefinition'] !== null) { + return [$alterClause . ' ' . $newColumn['columnDefinition']]; } $clauses = []; if ( - $columnDiff->hasChanged('type') || - $columnDiff->hasChanged('length') || - $columnDiff->hasChanged('precision') || - $columnDiff->hasChanged('scale') || - $columnDiff->hasChanged('fixed') + $columnDiff->hasTypeChanged() || + $columnDiff->hasLengthChanged() || + $columnDiff->hasPrecisionChanged() || + $columnDiff->hasScaleChanged() || + $columnDiff->hasFixedChanged() ) { - $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this); + $clauses[] = $alterClause . ' SET DATA TYPE ' . $newColumn['type']->getSQLDeclaration($newColumn, $this); } - if ($columnDiff->hasChanged('notnull')) { - $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL'; + if ($columnDiff->hasNotNullChanged()) { + $clauses[] = $newColumn['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL'; } - if ($columnDiff->hasChanged('default')) { - if (isset($column['default'])) { - $defaultClause = $this->getDefaultValueDeclarationSQL($column); + if ($columnDiff->hasDefaultChanged()) { + if (isset($newColumn['default'])) { + $defaultClause = $this->getDefaultValueDeclarationSQL($newColumn); if ($defaultClause !== '') { $clauses[] = $alterClause . ' SET' . $defaultClause; @@ -680,38 +798,38 @@ private function getAlterColumnClausesSQL(ColumnDiff $columnDiff): array */ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) { - $sql = []; - $table = $diff->getName($this)->getQuotedName($this); + $sql = []; + + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); - foreach ($diff->removedIndexes as $remKey => $remIndex) { - foreach ($diff->addedIndexes as $addKey => $addIndex) { - if ($remIndex->getColumns() !== $addIndex->getColumns()) { + foreach ($diff->getDroppedIndexes() as $droppedIndex) { + foreach ($diff->getAddedIndexes() as $addedIndex) { + if ($droppedIndex->getColumns() !== $addedIndex->getColumns()) { continue; } - if ($remIndex->isPrimary()) { - $sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; - } elseif ($remIndex->isUnique()) { - $sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this); + if ($droppedIndex->isPrimary()) { + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP PRIMARY KEY'; + } elseif ($droppedIndex->isUnique()) { + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP UNIQUE ' . $droppedIndex->getQuotedName($this); } else { - $sql[] = $this->getDropIndexSQL($remIndex, $table); + $sql[] = $this->getDropIndexSQL($droppedIndex, $tableNameSQL); } - $sql[] = $this->getCreateIndexSQL($addIndex, $table); + $sql[] = $this->getCreateIndexSQL($addedIndex, $tableNameSQL); - unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]); + $diff->unsetAddedIndex($addedIndex); + $diff->unsetDroppedIndex($droppedIndex); break; } } - $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff)); - - return $sql; + return array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff)); } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) { @@ -725,6 +843,8 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getDefaultValueDeclarationSQL($column) { @@ -788,7 +908,7 @@ protected function doModifyLimitQuery($query, $limit, $offset) return sprintf( 'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s', $query, - implode(' AND ', $where) + implode(' AND ', $where), ); } @@ -846,8 +966,9 @@ public function prefersIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/1519', - 'DB2Platform::prefersIdentityColumns() is deprecated.' + 'https://github.com/doctrine/dbal/pull/1519', + '%s() is deprecated.', + __METHOD__, ); return true; @@ -893,13 +1014,16 @@ protected function getReservedKeywordsClass() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', - 'DB2Platform::getReservedKeywordsClass() is deprecated,' - . ' use DB2Platform::createReservedKeywordsList() instead.' + '%s() is deprecated,' + . ' use %s::createReservedKeywordsList() instead.', + __METHOD__, + static::class, ); return Keywords\DB2Keywords::class; } + /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ public function getListTableCommentsSQL(string $table): string { return sprintf( @@ -909,7 +1033,12 @@ public function getListTableCommentsSQL(string $table): string WHERE NAME = UPPER( %s ) SQL , - $this->quoteStringLiteral($table) + $this->quoteStringLiteral($table), ); } + + public function createSchemaManager(Connection $connection): DB2SchemaManager + { + return new DB2SchemaManager($connection, $this); + } } diff --git a/doctrine/dbal/src/Platforms/DateIntervalUnit.php b/doctrine/dbal/src/Platforms/DateIntervalUnit.php index c1f3ca592..a95c4e28b 100644 --- a/doctrine/dbal/src/Platforms/DateIntervalUnit.php +++ b/doctrine/dbal/src/Platforms/DateIntervalUnit.php @@ -22,9 +22,7 @@ final class DateIntervalUnit public const YEAR = 'YEAR'; - /** - * @codeCoverageIgnore - */ + /** @codeCoverageIgnore */ private function __construct() { } diff --git a/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php b/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php index db577d302..d5dbbd734 100644 --- a/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php @@ -2,21 +2,31 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + /** * DB2 Keywords. */ class DB2Keywords extends KeywordList { /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'DB2Keywords::getName() is deprecated.', + ); + return 'DB2'; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getKeywords() { diff --git a/doctrine/dbal/src/Platforms/Keywords/KeywordList.php b/doctrine/dbal/src/Platforms/Keywords/KeywordList.php index 34b703f3e..584277395 100644 --- a/doctrine/dbal/src/Platforms/Keywords/KeywordList.php +++ b/doctrine/dbal/src/Platforms/Keywords/KeywordList.php @@ -14,7 +14,7 @@ abstract class KeywordList { /** @var string[]|null */ - private $keywords; + private ?array $keywords = null; /** * Checks if the given word is a keyword of this dialect/vendor platform. @@ -32,9 +32,7 @@ public function isKeyword($word) return isset($this->keywords[strtoupper($word)]); } - /** - * @return void - */ + /** @return void */ protected function initializeKeywords() { $this->keywords = array_flip(array_map('strtoupper', $this->getKeywords())); @@ -50,6 +48,8 @@ abstract protected function getKeywords(); /** * Returns the name of this keyword list. * + * @deprecated + * * @return string */ abstract public function getName(); diff --git a/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php b/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php index 93c595f28..5417c6caa 100644 --- a/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php @@ -2,15 +2,24 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + class MariaDBKeywords extends MySQLKeywords { + /** @deprecated */ public function getName(): string { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'MariaDBKeywords::getName() is deprecated.', + ); + return 'MariaDB'; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getKeywords(): array { diff --git a/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php b/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php index aaa746b85..f9b50de54 100644 --- a/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + /** * MariaDb reserved keywords list. * @@ -11,8 +13,15 @@ */ final class MariaDb102Keywords extends MariaDBKeywords { + /** @deprecated */ public function getName(): string { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'MariaDb102Keywords::getName() is deprecated.', + ); + return 'MariaDb102'; } } diff --git a/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php b/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php index 19b49fb93..4588aab84 100644 --- a/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + /** * MySQL 5.7 reserved keywords list. * @@ -10,15 +12,23 @@ class MySQL57Keywords extends MySQLKeywords { /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'MySQL57Keywords::getName() is deprecated.', + ); + return 'MySQL57'; } /** - * {@inheritdoc} + * {@inheritDoc} * * @link http://dev.mysql.com/doc/mysqld-version-reference/en/mysqld-version-reference-reservedwords-5-7.html */ diff --git a/doctrine/dbal/src/Platforms/Keywords/MySQL80Keywords.php b/doctrine/dbal/src/Platforms/Keywords/MySQL80Keywords.php index ec1562d24..c62ae9e5b 100644 --- a/doctrine/dbal/src/Platforms/Keywords/MySQL80Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/MySQL80Keywords.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + use function array_merge; /** @@ -10,15 +12,23 @@ class MySQL80Keywords extends MySQL57Keywords { /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'MySQL80Keywords::getName() is deprecated.', + ); + return 'MySQL80'; } /** - * {@inheritdoc} + * {@inheritDoc} * * @link https://dev.mysql.com/doc/refman/8.0/en/keywords.html */ diff --git a/doctrine/dbal/src/Platforms/Keywords/MySQLKeywords.php b/doctrine/dbal/src/Platforms/Keywords/MySQLKeywords.php index 84ca1446a..f9505e0e1 100644 --- a/doctrine/dbal/src/Platforms/Keywords/MySQLKeywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/MySQLKeywords.php @@ -2,21 +2,31 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + /** * MySQL Keywordlist. */ class MySQLKeywords extends KeywordList { /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'MySQLKeywords::getName() is deprecated.', + ); + return 'MySQL'; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getKeywords() { diff --git a/doctrine/dbal/src/Platforms/Keywords/OracleKeywords.php b/doctrine/dbal/src/Platforms/Keywords/OracleKeywords.php index a73a93d60..d2d0094c7 100644 --- a/doctrine/dbal/src/Platforms/Keywords/OracleKeywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/OracleKeywords.php @@ -2,21 +2,31 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + /** * Oracle Keywordlist. */ class OracleKeywords extends KeywordList { /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'OracleKeywords::getName() is deprecated.', + ); + return 'Oracle'; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getKeywords() { diff --git a/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php b/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php index fab018645..5153d2c8a 100644 --- a/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php @@ -4,6 +4,8 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + /** * PostgreSQL 10.0 reserved keywords list. * @@ -11,8 +13,15 @@ */ class PostgreSQL100Keywords extends PostgreSQL94Keywords { + /** @deprecated */ public function getName(): string { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'PostgreSQL100Keywords::getName() is deprecated.', + ); + return 'PostgreSQL100'; } } diff --git a/doctrine/dbal/src/Platforms/Keywords/PostgreSQLKeywords.php b/doctrine/dbal/src/Platforms/Keywords/PostgreSQLKeywords.php index 47ccfdfb4..1ff4142b2 100644 --- a/doctrine/dbal/src/Platforms/Keywords/PostgreSQLKeywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/PostgreSQLKeywords.php @@ -2,21 +2,31 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + /** * Reserved keywords list corresponding to the PostgreSQL database platform of the oldest supported version. */ class PostgreSQLKeywords extends KeywordList { /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'PostgreSQLKeywords::getName() is deprecated.', + ); + return 'PostgreSQL'; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getKeywords() { diff --git a/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php b/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php index bd4faf50f..b33f3e4b3 100644 --- a/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php +++ b/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php @@ -9,30 +9,34 @@ use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\Deprecations\Deprecation; use function count; use function implode; use function str_replace; +/** @deprecated Use database documentation instead. */ class ReservedKeywordsValidator implements Visitor { /** @var KeywordList[] */ - private $keywordLists; + private array $keywordLists; /** @var string[] */ - private $violations = []; + private array $violations = []; - /** - * @param KeywordList[] $keywordLists - */ + /** @param KeywordList[] $keywordLists */ public function __construct(array $keywordLists) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5431', + 'ReservedKeywordsValidator is deprecated. Use database documentation instead.', + ); + $this->keywordLists = $keywordLists; } - /** - * @return string[] - */ + /** @return string[] */ public function getViolations() { return $this->violations; @@ -75,52 +79,52 @@ private function addViolation($asset, $violatedPlatforms): void } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptColumn(Table $table, Column $column) { $this->addViolation( 'Table ' . $table->getName() . ' column ' . $column->getName(), - $this->isReservedWord($column->getName()) + $this->isReservedWord($column->getName()), ); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptIndex(Table $table, Index $index) { } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptSchema(Schema $schema) { } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptSequence(Sequence $sequence) { } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptTable(Table $table) { $this->addViolation( 'Table ' . $table->getName(), - $this->isReservedWord($table->getName()) + $this->isReservedWord($table->getName()), ); } } diff --git a/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php b/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php index 0ea413ce7..76614cf95 100644 --- a/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + /** * Microsoft SQL Server 2012 reserved keyword dictionary. * Reserved keywords list corresponding to the Microsoft SQL Server database platform of the oldest supported version. @@ -9,15 +11,23 @@ class SQLServerKeywords extends KeywordList { /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'SQLServerKeywords::getName() is deprecated.', + ); + return 'SQLServer'; } /** - * {@inheritdoc} + * {@inheritDoc} * * @link http://msdn.microsoft.com/en-us/library/aa238507%28v=sql.80%29.aspx */ diff --git a/doctrine/dbal/src/Platforms/Keywords/SQLiteKeywords.php b/doctrine/dbal/src/Platforms/Keywords/SQLiteKeywords.php index a0c9c797e..9a3edeb57 100644 --- a/doctrine/dbal/src/Platforms/Keywords/SQLiteKeywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/SQLiteKeywords.php @@ -2,21 +2,31 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + /** * SQLite Keywordlist. */ class SQLiteKeywords extends KeywordList { /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'SQLiteKeywords::getName() is deprecated.', + ); + return 'SQLite'; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getKeywords() { diff --git a/doctrine/dbal/src/Platforms/MariaDBPlatform.php b/doctrine/dbal/src/Platforms/MariaDBPlatform.php index e877b70fe..1dddeccaf 100644 --- a/doctrine/dbal/src/Platforms/MariaDBPlatform.php +++ b/doctrine/dbal/src/Platforms/MariaDBPlatform.php @@ -11,7 +11,20 @@ class MariaDBPlatform extends MySQLPlatform { /** - * {@inheritdoc} + * {@inheritDoc} + * + * Hop over the {@see AbstractMySQLPlatform} implementation until 4.0.x + * where {@see MariaDBPlatform} no longer extends {@see MySQLPlatform}. + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function getDefaultValueDeclarationSQL($column) + { + return AbstractPlatform::getDefaultValueDeclarationSQL($column); + } + + /** + * {@inheritDoc} * * @link https://mariadb.com/kb/en/library/json-data-type/ */ @@ -20,16 +33,14 @@ public function getJsonTypeDeclarationSQL(array $column): string return 'LONGTEXT'; } - /** - * @deprecated Implement {@see createReservedKeywordsList()} instead. - */ + /** @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass(): string { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'MariaDb1027Platform::getReservedKeywordsClass() is deprecated,' - . ' use MariaDb1027Platform::createReservedKeywordsList() instead.' + . ' use MariaDb1027Platform::createReservedKeywordsList() instead.', ); return Keywords\MariaDb102Keywords::class; diff --git a/doctrine/dbal/src/Platforms/MariaDb1027Platform.php b/doctrine/dbal/src/Platforms/MariaDb1027Platform.php index 93c7d3488..68f381332 100644 --- a/doctrine/dbal/src/Platforms/MariaDb1027Platform.php +++ b/doctrine/dbal/src/Platforms/MariaDb1027Platform.php @@ -8,7 +8,7 @@ * Note: Should not be used with versions prior to 10.2.7. * * @deprecated This class will be merged with {@see MariaDBPlatform} in 4.0 because support for MariaDB - * releases prior to 10.2.7 will be dropped. + * releases prior to 10.4.3 will be dropped. */ class MariaDb1027Platform extends MariaDBPlatform { diff --git a/doctrine/dbal/src/Platforms/MariaDb1043Platform.php b/doctrine/dbal/src/Platforms/MariaDb1043Platform.php new file mode 100644 index 000000000..926c6b952 --- /dev/null +++ b/doctrine/dbal/src/Platforms/MariaDb1043Platform.php @@ -0,0 +1,117 @@ +getColumnTypeSQLSnippets(); + + return sprintf( + <<getDatabaseNameSQL($database), + $this->quoteStringLiteral($table), + ); + } + + /** + * Generate SQL snippets to reverse the aliasing of JSON to LONGTEXT. + * + * MariaDb aliases columns specified as JSON to LONGTEXT and sets a CHECK constraint to ensure the column + * is valid json. This function generates the SQL snippets which reverse this aliasing i.e. report a column + * as JSON where it was originally specified as such instead of LONGTEXT. + * + * The CHECK constraints are stored in information_schema.CHECK_CONSTRAINTS so JOIN that table. + * + * @return array{string, string} + */ + public function getColumnTypeSQLSnippets(string $tableAlias = 'c'): array + { + if ($this->getJsonTypeDeclarationSQL([]) !== 'JSON') { + return parent::getColumnTypeSQLSnippets($tableAlias); + } + + $columnTypeSQL = <<getJsonTypeDeclarationSQL([]) === 'JSON' && ($column['type'] ?? null) instanceof JsonType) { + unset($column['collation']); + unset($column['charset']); + } + + return parent::getColumnDeclarationSQL($name, $column); + } +} diff --git a/doctrine/dbal/src/Platforms/MariaDb1052Platform.php b/doctrine/dbal/src/Platforms/MariaDb1052Platform.php new file mode 100644 index 000000000..4241187fd --- /dev/null +++ b/doctrine/dbal/src/Platforms/MariaDb1052Platform.php @@ -0,0 +1,38 @@ +getQuotedName($this)]; + } +} diff --git a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider.php b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider.php index 4aa0faf6c..d52ca74a2 100644 --- a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider.php +++ b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider.php @@ -4,9 +4,7 @@ namespace Doctrine\DBAL\Platforms\MySQL; -/** - * @internal - */ +/** @internal */ interface CollationMetadataProvider { public function getCollationCharset(string $collation): ?string; diff --git a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php index 243a10f2a..513c08b6e 100644 --- a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php +++ b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php @@ -8,9 +8,7 @@ use function array_key_exists; -/** - * @internal - */ +/** @internal */ final class CachingCollationMetadataProvider implements CollationMetadataProvider { /** @var CollationMetadataProvider */ diff --git a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php index 401c940e0..8dc2421a0 100644 --- a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php +++ b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php @@ -8,9 +8,7 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\MySQL\CollationMetadataProvider; -/** - * @internal - */ +/** @internal */ final class ConnectionCollationMetadataProvider implements CollationMetadataProvider { /** @var Connection */ @@ -21,9 +19,7 @@ public function __construct(Connection $connection) $this->connection = $connection; } - /** - * @throws Exception - */ + /** @throws Exception */ public function getCollationCharset(string $collation): ?string { $charset = $this->connection->fetchOne( @@ -33,7 +29,7 @@ public function getCollationCharset(string $collation): ?string WHERE COLLATION_NAME = ?; SQL , - [$collation] + [$collation], ); if ($charset !== false) { diff --git a/doctrine/dbal/src/Platforms/MySQL/Comparator.php b/doctrine/dbal/src/Platforms/MySQL/Comparator.php index 74e7fe3ba..c27fef744 100644 --- a/doctrine/dbal/src/Platforms/MySQL/Comparator.php +++ b/doctrine/dbal/src/Platforms/MySQL/Comparator.php @@ -5,6 +5,7 @@ use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Schema\Comparator as BaseComparator; use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; use function array_diff_assoc; use function array_intersect_key; @@ -21,9 +22,7 @@ class Comparator extends BaseComparator /** @var CollationMetadataProvider */ private $collationMetadataProvider; - /** - * @internal The comparator can be only instantiated by a schema manager. - */ + /** @internal The comparator can be only instantiated by a schema manager. */ public function __construct(AbstractMySQLPlatform $platform, CollationMetadataProvider $collationMetadataProvider) { parent::__construct($platform); @@ -31,6 +30,14 @@ public function __construct(AbstractMySQLPlatform $platform, CollationMetadataPr $this->collationMetadataProvider = $collationMetadataProvider; } + public function compareTables(Table $fromTable, Table $toTable): TableDiff + { + return parent::compareTables( + $this->normalizeColumns($fromTable), + $this->normalizeColumns($toTable), + ); + } + /** * {@inheritDoc} */ @@ -38,7 +45,7 @@ public function diffTable(Table $fromTable, Table $toTable) { return parent::diffTable( $this->normalizeColumns($fromTable), - $this->normalizeColumns($toTable) + $this->normalizeColumns($toTable), ); } diff --git a/doctrine/dbal/src/Platforms/MySQL57Platform.php b/doctrine/dbal/src/Platforms/MySQL57Platform.php index 3975bf2ca..f3899959d 100644 --- a/doctrine/dbal/src/Platforms/MySQL57Platform.php +++ b/doctrine/dbal/src/Platforms/MySQL57Platform.php @@ -17,15 +17,24 @@ class MySQL57Platform extends MySQLPlatform { /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function hasNativeJsonType() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getJsonTypeDeclarationSQL(array $column) { @@ -38,7 +47,7 @@ public function createSQLParser(): Parser } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) { @@ -46,7 +55,7 @@ protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) { @@ -54,7 +63,7 @@ protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) { @@ -62,7 +71,7 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) } /** - * {@inheritdoc} + * {@inheritDoc} * * @deprecated Implement {@see createReservedKeywordsList()} instead. */ @@ -72,14 +81,14 @@ protected function getReservedKeywordsClass() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'MySQL57Platform::getReservedKeywordsClass() is deprecated,' - . ' use MySQL57Platform::createReservedKeywordsList() instead.' + . ' use MySQL57Platform::createReservedKeywordsList() instead.', ); return Keywords\MySQL57Keywords::class; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function initializeDoctrineTypeMappings() { diff --git a/doctrine/dbal/src/Platforms/MySQL80Platform.php b/doctrine/dbal/src/Platforms/MySQL80Platform.php index 536a79f4b..9ea6ee881 100644 --- a/doctrine/dbal/src/Platforms/MySQL80Platform.php +++ b/doctrine/dbal/src/Platforms/MySQL80Platform.php @@ -10,7 +10,7 @@ class MySQL80Platform extends MySQL57Platform { /** - * {@inheritdoc} + * {@inheritDoc} * * @deprecated Implement {@see createReservedKeywordsList()} instead. */ @@ -20,7 +20,7 @@ protected function getReservedKeywordsClass() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'MySQL80Platform::getReservedKeywordsClass() is deprecated,' - . ' use MySQL80Platform::createReservedKeywordsList() instead.' + . ' use MySQL80Platform::createReservedKeywordsList() instead.', ); return Keywords\MySQL80Keywords::class; diff --git a/doctrine/dbal/src/Platforms/OraclePlatform.php b/doctrine/dbal/src/Platforms/OraclePlatform.php index 7eabb7fa3..1d4578979 100644 --- a/doctrine/dbal/src/Platforms/OraclePlatform.php +++ b/doctrine/dbal/src/Platforms/OraclePlatform.php @@ -2,15 +2,18 @@ namespace Doctrine\DBAL\Platforms; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\OracleSchemaManager; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\TransactionIsolationLevel; use Doctrine\DBAL\Types\BinaryType; +use Doctrine\DBAL\Types\Types; use Doctrine\Deprecations\Deprecation; use InvalidArgumentException; @@ -76,7 +79,7 @@ public function getNowExpression($type = 'timestamp') Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4753', - 'OraclePlatform::getNowExpression() is deprecated. Generate dates within the application.' + 'OraclePlatform::getNowExpression() is deprecated. Generate dates within the application.', ); switch ($type) { @@ -101,7 +104,7 @@ public function getLocateExpression($str, $substr, $startPos = false) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { @@ -183,6 +186,13 @@ public function getBitOrComparisonExpression($value1, $value2) public function getCreatePrimaryKeySQL(Index $index, $table): string { if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $table = $table->getQuotedName($this); } @@ -348,25 +358,51 @@ protected function _getCommonIntegerTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { + if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'Relying on the default string column length on Oracle is deprecated' + . ', specify the length explicitly.', + ); + } + return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(2000)') : ($length > 0 ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)'); } /** - * {@inheritdoc} + * {@inheritDoc} */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { + if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'Relying on the default binary column length on Oracle is deprecated' + . ', specify the length explicitly.', + ); + } + return 'RAW(' . ($length > 0 ? $length : $this->getBinaryMaxLength()) . ')'; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getBinaryMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'OraclePlatform::getBinaryMaxLength() is deprecated.', + ); + return 2000; } @@ -380,6 +416,8 @@ public function getClobTypeDeclarationSQL(array $column) /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListDatabasesSQL() { @@ -388,6 +426,8 @@ public function getListDatabasesSQL() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListSequencesSQL($database) { @@ -430,6 +470,8 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html @@ -470,6 +512,8 @@ public function getListTableIndexesSQL($table, $database = null) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTablesSQL() @@ -479,6 +523,8 @@ public function getListTablesSQL() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { @@ -524,7 +570,7 @@ public function getCreateAutoincrementSql($name, $table, $start = 1) $sequenceName = $this->getIdentitySequenceName( $tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName, - $nameIdentifier->isQuoted() ? $quotedName : $unquotedName + $nameIdentifier->isQuoted() ? $quotedName : $unquotedName, ); $sequence = new Sequence($sequenceName, $start); $sql[] = $this->getCreateSequenceSQL($sequence); @@ -569,7 +615,7 @@ public function getDropAutoincrementSql($table) $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table); $identitySequenceName = $this->getIdentitySequenceName( $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(), - '' + '', ); return [ @@ -626,6 +672,8 @@ private function getAutoincrementIdentifierName(Identifier $table): string } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTableForeignKeysSQL($table) @@ -658,6 +706,8 @@ public function getListTableForeignKeysSQL($table) } /** + * @deprecated + * * {@inheritDoc} */ public function getListTableConstraintsSQL($table) @@ -669,6 +719,8 @@ public function getListTableConstraintsSQL($table) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTableColumnsSQL($table, $database = null) @@ -708,7 +760,7 @@ public function getListTableColumnsSQL($table, $database = null) $colCommentsOwnerCondition, $tabColumnsTableName, $table, - $tabColumnsOwnerCondition + $tabColumnsOwnerCondition, ); } @@ -717,11 +769,26 @@ public function getListTableColumnsSQL($table, $database = null) */ public function getDropForeignKeySQL($foreignKey, $table) { - if (! $foreignKey instanceof ForeignKeyConstraint) { + if ($foreignKey instanceof ForeignKeyConstraint) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $foreignKey as a ForeignKeyConstraint object to %s is deprecated.' + . ' Pass it as a quoted name instead.', + __METHOD__, + ); + } else { $foreignKey = new Identifier($foreignKey); } - if (! $table instanceof Table) { + if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + } else { $table = new Identifier($table); } @@ -732,7 +799,9 @@ public function getDropForeignKeySQL($foreignKey, $table) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { @@ -750,7 +819,9 @@ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getForeignKeyReferentialActionSQL($action) { @@ -800,7 +871,9 @@ public function getAlterTableSQL(TableDiff $diff) $fields = []; - foreach ($diff->addedColumns as $column) { + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); + + foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } @@ -813,49 +886,48 @@ public function getAlterTableSQL(TableDiff $diff) } $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), + $tableNameSQL, $column->getQuotedName($this), - $comment + $comment, ); } if (count($fields) > 0) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) - . ' ADD (' . implode(', ', $fields) . ')'; + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ADD (' . implode(', ', $fields) . ')'; } $fields = []; - foreach ($diff->changedColumns as $columnDiff) { + foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } - $column = $columnDiff->column; + $newColumn = $columnDiff->getNewColumn(); // Do not generate column alteration clause if type is binary and only fixed property has changed. // Oracle only supports binary type columns with variable length. // Avoids unnecessary table alteration statements. if ( - $column->getType() instanceof BinaryType && - $columnDiff->hasChanged('fixed') && + $newColumn->getType() instanceof BinaryType && + $columnDiff->hasFixedChanged() && count($columnDiff->changedProperties) === 1 ) { continue; } - $columnHasChangedComment = $columnDiff->hasChanged('comment'); + $columnHasChangedComment = $columnDiff->hasCommentChanged(); /** * Do not add query part if only comment has changed */ if (! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) { - $columnInfo = $column->toArray(); + $newColumnProperties = $newColumn->toArray(); - if (! $columnDiff->hasChanged('notnull')) { - unset($columnInfo['notnull']); + if (! $columnDiff->hasNotNullChanged()) { + unset($newColumnProperties['notnull']); } - $fields[] = $column->getQuotedName($this) . $this->getColumnDeclarationSQL('', $columnInfo); + $fields[] = $newColumn->getQuotedName($this) . $this->getColumnDeclarationSQL('', $newColumnProperties); } if (! $columnHasChangedComment) { @@ -863,30 +935,29 @@ public function getAlterTableSQL(TableDiff $diff) } $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), - $column->getQuotedName($this), - $this->getColumnComment($column) + $tableNameSQL, + $newColumn->getQuotedName($this), + $this->getColumnComment($newColumn), ); } if (count($fields) > 0) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) - . ' MODIFY (' . implode(', ', $fields) . ')'; + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY (' . implode(', ', $fields) . ')'; } - foreach ($diff->renamedColumns as $oldColumnName => $column) { + foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { continue; } $oldColumnName = new Identifier($oldColumnName); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . - ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) + . ' TO ' . $column->getQuotedName($this); } $fields = []; - foreach ($diff->removedColumns as $column) { + foreach ($diff->getDroppedColumns() as $column) { if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { continue; } @@ -895,8 +966,7 @@ public function getAlterTableSQL(TableDiff $diff) } if (count($fields) > 0) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) - . ' DROP (' . implode(', ', $fields) . ')'; + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP (' . implode(', ', $fields) . ')'; } $tableSql = []; @@ -907,17 +977,24 @@ public function getAlterTableSQL(TableDiff $diff) $newName = $diff->getNewName(); if ($newName !== false) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5663', + 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', + __METHOD__, + ); + $sql[] = sprintf( 'ALTER TABLE %s RENAME TO %s', - $diff->getName($this)->getQuotedName($this), - $newName->getQuotedName($this) + $tableNameSQL, + $newName->getQuotedName($this), ); } $sql = array_merge( $this->getPreAlterTableIndexForeignKeySQL($diff), $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) + $this->getPostAlterTableIndexForeignKeySQL($diff), ); } @@ -925,7 +1002,9 @@ public function getAlterTableSQL(TableDiff $diff) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getColumnDeclarationSQL($name, array $column) { @@ -940,11 +1019,29 @@ public function getColumnDeclarationSQL($name, array $column) $notnull = $column['notnull'] ? ' NOT NULL' : ' NULL'; } - $unique = ! empty($column['unique']) ? - ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + if (! empty($column['unique'])) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5656', + 'The usage of the "unique" column property is deprecated. Use unique constraints instead.', + ); + + $unique = ' ' . $this->getUniqueFieldDeclarationSQL(); + } else { + $unique = ''; + } + + if (! empty($column['check'])) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5656', + 'The usage of the "check" column property is deprecated.', + ); - $check = ! empty($column['check']) ? - ' ' . $column['check'] : ''; + $check = ' ' . $column['check']; + } else { + $check = ''; + } $typeDecl = $column['type']->getSQLDeclaration($column, $this); $columnDef = $typeDecl . $default . $notnull . $unique . $check; @@ -954,7 +1051,7 @@ public function getColumnDeclarationSQL($name, array $column) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) { @@ -967,15 +1064,26 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function usesSequenceEmulatedIdentityColumns() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + return true; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the OraclePlatform class hierarchy. */ public function getIdentitySequenceName($tableName, $columnName) { @@ -995,6 +1103,8 @@ public function getIdentitySequenceName($tableName, $columnName) /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsCommentOnStatement() { @@ -1009,7 +1119,7 @@ public function getName() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4749', - 'OraclePlatform::getName() is deprecated. Identify platforms by their class.' + 'OraclePlatform::getName() is deprecated. Identify platforms by their class.', ); return 'oracle'; @@ -1131,29 +1241,29 @@ public function getDummySelectSQL() protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'binary_double' => 'float', - 'binary_float' => 'float', - 'binary_integer' => 'boolean', - 'blob' => 'blob', - 'char' => 'string', - 'clob' => 'text', - 'date' => 'date', - 'float' => 'float', - 'integer' => 'integer', - 'long' => 'string', - 'long raw' => 'blob', - 'nchar' => 'string', - 'nclob' => 'text', - 'number' => 'integer', - 'nvarchar2' => 'string', - 'pls_integer' => 'boolean', - 'raw' => 'binary', - 'rowid' => 'string', - 'timestamp' => 'datetime', - 'timestamptz' => 'datetimetz', - 'urowid' => 'string', - 'varchar' => 'string', - 'varchar2' => 'string', + 'binary_double' => Types::FLOAT, + 'binary_float' => Types::FLOAT, + 'binary_integer' => Types::BOOLEAN, + 'blob' => Types::BLOB, + 'char' => Types::STRING, + 'clob' => Types::TEXT, + 'date' => Types::DATE_MUTABLE, + 'float' => Types::FLOAT, + 'integer' => Types::INTEGER, + 'long' => Types::STRING, + 'long raw' => Types::BLOB, + 'nchar' => Types::STRING, + 'nclob' => Types::TEXT, + 'number' => Types::INTEGER, + 'nvarchar2' => Types::STRING, + 'pls_integer' => Types::BOOLEAN, + 'raw' => Types::BINARY, + 'rowid' => Types::STRING, + 'timestamp' => Types::DATETIME_MUTABLE, + 'timestamptz' => Types::DATETIMETZ_MUTABLE, + 'urowid' => Types::STRING, + 'varchar' => Types::STRING, + 'varchar2' => Types::STRING, ]; } @@ -1176,7 +1286,7 @@ protected function getReservedKeywordsClass() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'OraclePlatform::getReservedKeywordsClass() is deprecated,' - . ' use OraclePlatform::createReservedKeywordsList() instead.' + . ' use OraclePlatform::createReservedKeywordsList() instead.', ); return Keywords\OracleKeywords::class; @@ -1190,6 +1300,7 @@ public function getBlobTypeDeclarationSQL(array $column) return 'BLOB'; } + /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ public function getListTableCommentsSQL(string $table, ?string $database = null): string { $tableCommentsName = 'user_tab_comments'; @@ -1198,7 +1309,7 @@ public function getListTableCommentsSQL(string $table, ?string $database = null) if ($database !== null && $database !== '/') { $tableCommentsName = 'all_tab_comments'; $ownerCondition = ' AND owner = ' . $this->quoteStringLiteral( - $this->normalizeIdentifier($database)->getName() + $this->normalizeIdentifier($database)->getName(), ); } @@ -1209,7 +1320,12 @@ public function getListTableCommentsSQL(string $table, ?string $database = null) , $tableCommentsName, $this->quoteStringLiteral($this->normalizeIdentifier($table)->getName()), - $ownerCondition + $ownerCondition, ); } + + public function createSchemaManager(Connection $connection): OracleSchemaManager + { + return new OracleSchemaManager($connection, $this); + } } diff --git a/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php b/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php index a9419817a..aa023ba79 100644 --- a/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php +++ b/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php @@ -15,33 +15,16 @@ */ class PostgreSQL100Platform extends PostgreSQL94Platform { - /** - * @deprecated Implement {@see createReservedKeywordsList()} instead. - */ + /** @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass(): string { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'PostgreSQL100Platform::getReservedKeywordsClass() is deprecated,' - . ' use PostgreSQL100Platform::createReservedKeywordsList() instead.' + . ' use PostgreSQL100Platform::createReservedKeywordsList() instead.', ); return PostgreSQL100Keywords::class; } - - /** - * {@inheritDoc} - */ - public function getListSequencesSQL($database): string - { - return 'SELECT sequence_name AS relname, - sequence_schema AS schemaname, - minimum_value AS min_value, - increment AS increment_by - FROM information_schema.sequences - WHERE sequence_catalog = ' . $this->quoteStringLiteral($database) . " - AND sequence_schema NOT LIKE 'pg\_%' - AND sequence_schema != 'information_schema'"; - } } diff --git a/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php b/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php index 477e08af9..9e6648cbd 100644 --- a/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php +++ b/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php @@ -2,17 +2,19 @@ namespace Doctrine\DBAL\Platforms; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\ColumnDiff; use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\PostgreSQLSchemaManager; use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Types\BinaryType; use Doctrine\DBAL\Types\BlobType; -use Doctrine\DBAL\Types\PhpIntegerMappingType; -use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Types\Types; use Doctrine\Deprecations\Deprecation; use UnexpectedValueException; @@ -38,11 +40,10 @@ */ class PostgreSQLPlatform extends AbstractPlatform { - /** @var bool */ - private $useBooleanTrueFalseStrings = true; + private bool $useBooleanTrueFalseStrings = true; /** @var string[][] PostgreSQL booleans literals */ - private $booleanLiterals = [ + private array $booleanLiterals = [ 'true' => [ 't', 'true', @@ -98,7 +99,7 @@ public function getNowExpression() Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4753', - 'PostgreSQLPlatform::getNowExpression() is deprecated. Generate dates within the application.' + 'PostgreSQLPlatform::getNowExpression() is deprecated. Generate dates within the application.', ); return 'LOCALTIMESTAMP(0)'; @@ -128,7 +129,7 @@ public function getLocateExpression($str, $substr, $startPos = false) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { @@ -170,10 +171,19 @@ public function supportsSchemas() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getDefaultSchemaName() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + return 'public'; } @@ -186,7 +196,9 @@ public function supportsIdentityColumns() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsPartialIndexes() { @@ -194,23 +206,43 @@ public function supportsPartialIndexes() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function usesSequenceEmulatedIdentityColumns() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + return true; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getIdentitySequenceName($tableName, $columnName) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + return $tableName . '_' . $columnName . '_seq'; } /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsCommentOnStatement() { @@ -219,14 +251,25 @@ public function supportsCommentOnStatement() /** * {@inheritDoc} + * + * @deprecated */ public function hasNativeGuidType() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListDatabasesSQL() { @@ -244,7 +287,7 @@ public function getListNamespacesSQL() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'PostgreSQLPlatform::getListNamespacesSQL() is deprecated,' - . ' use PostgreSQLSchemaManager::listSchemaNames() instead.' + . ' use PostgreSQLSchemaManager::listSchemaNames() instead.', ); return "SELECT schema_name AS nspname @@ -255,17 +298,24 @@ public function getListNamespacesSQL() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListSequencesSQL($database) { - return "SELECT sequence_name AS relname, - sequence_schema AS schemaname + return 'SELECT sequence_name AS relname, + sequence_schema AS schemaname, + minimum_value AS min_value, + increment AS increment_by FROM information_schema.sequences - WHERE sequence_schema NOT LIKE 'pg\_%' + WHERE sequence_catalog = ' . $this->quoteStringLiteral($database) . " + AND sequence_schema NOT LIKE 'pg\_%' AND sequence_schema != 'information_schema'"; } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTablesSQL() @@ -282,6 +332,8 @@ public function getListTablesSQL() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { @@ -293,6 +345,8 @@ public function getListViewsSQL($database) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * @param string $table * @param string|null $database * @@ -312,6 +366,8 @@ public function getListTableForeignKeysSQL($table, $database = null) } /** + * @deprecated + * * {@inheritDoc} */ public function getListTableConstraintsSQL($table) @@ -334,11 +390,13 @@ public function getListTableConstraintsSQL($table) ) SQL , - $table + $table, ); } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html @@ -380,11 +438,13 @@ private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias $classAlias, $table, $namespaceAlias, - $schema + $schema, ); } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTableColumnsSQL($table, $database = null) @@ -424,6 +484,8 @@ public function getListTableColumnsSQL($table, $database = null) /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { @@ -462,37 +524,45 @@ public function getAlterTableSQL(TableDiff $diff) $commentsSQL = []; $columnSql = []; - foreach ($diff->addedColumns as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + $table = $diff->getOldTable() ?? $diff->getName($this); + + $tableNameSQL = $table->getQuotedName($this); + + foreach ($diff->getAddedColumns() as $addedColumn) { + if ($this->onSchemaAlterTableAddColumn($addedColumn, $diff, $columnSql)) { continue; } - $query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + $query = 'ADD ' . $this->getColumnDeclarationSQL( + $addedColumn->getQuotedName($this), + $addedColumn->toArray(), + ); + + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; - $comment = $this->getColumnComment($column); + $comment = $this->getColumnComment($addedColumn); if ($comment === null || $comment === '') { continue; } $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), - $column->getQuotedName($this), - $comment + $tableNameSQL, + $addedColumn->getQuotedName($this), + $comment, ); } - foreach ($diff->removedColumns as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + foreach ($diff->getDroppedColumns() as $droppedColumn) { + if ($this->onSchemaAlterTableRemoveColumn($droppedColumn, $diff, $columnSql)) { continue; } - $query = 'DROP ' . $column->getQuotedName($this); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + $query = 'DROP ' . $droppedColumn->getQuotedName($this); + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } - foreach ($diff->changedColumns as $columnDiff) { + foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } @@ -501,88 +571,94 @@ public function getAlterTableSQL(TableDiff $diff) continue; } - $oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this); - $column = $columnDiff->column; + $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); + $newColumn = $columnDiff->getNewColumn(); + + $oldColumnName = $oldColumn->getQuotedName($this); if ( - $columnDiff->hasChanged('type') - || $columnDiff->hasChanged('precision') - || $columnDiff->hasChanged('scale') - || $columnDiff->hasChanged('fixed') + $columnDiff->hasTypeChanged() + || $columnDiff->hasPrecisionChanged() + || $columnDiff->hasScaleChanged() + || $columnDiff->hasFixedChanged() ) { - $type = $column->getType(); + $type = $newColumn->getType(); // SERIAL/BIGSERIAL are not "real" types and we can't alter a column to that type - $columnDefinition = $column->toArray(); + $columnDefinition = $newColumn->toArray(); $columnDefinition['autoincrement'] = false; // here was a server version check before, but DBAL API does not support this anymore. $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSQLDeclaration($columnDefinition, $this); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } - if ($columnDiff->hasChanged('default') || $this->typeChangeBreaksDefaultValue($columnDiff)) { - $defaultClause = $column->getDefault() === null + if ($columnDiff->hasDefaultChanged()) { + $defaultClause = $newColumn->getDefault() === null ? ' DROP DEFAULT' - : ' SET' . $this->getDefaultValueDeclarationSQL($column->toArray()); - $query = 'ALTER ' . $oldColumnName . $defaultClause; - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + : ' SET' . $this->getDefaultValueDeclarationSQL($newColumn->toArray()); + + $query = 'ALTER ' . $oldColumnName . $defaultClause; + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } - if ($columnDiff->hasChanged('notnull')) { - $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotnull() ? 'SET' : 'DROP') . ' NOT NULL'; - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + if ($columnDiff->hasNotNullChanged()) { + $query = 'ALTER ' . $oldColumnName . ' ' . ($newColumn->getNotnull() ? 'SET' : 'DROP') . ' NOT NULL'; + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } - if ($columnDiff->hasChanged('autoincrement')) { - if ($column->getAutoincrement()) { + if ($columnDiff->hasAutoIncrementChanged()) { + if ($newColumn->getAutoincrement()) { // add autoincrement - $seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName); + $seqName = $this->getIdentitySequenceName( + $table->getName(), + $oldColumnName, + ); $sql[] = 'CREATE SEQUENCE ' . $seqName; $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ') FROM ' - . $diff->getName($this)->getQuotedName($this) . '))'; + . $tableNameSQL . '))'; $query = 'ALTER ' . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; } else { // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have $query = 'ALTER ' . $oldColumnName . ' DROP DEFAULT'; - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; } + + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } - $newComment = $this->getColumnComment($column); $oldComment = $this->getOldColumnComment($columnDiff); + $newComment = $this->getColumnComment($newColumn); if ( - $columnDiff->hasChanged('comment') - || ($columnDiff->fromColumn !== null && $oldComment !== $newComment) + $columnDiff->hasCommentChanged() + || ($columnDiff->getOldColumn() !== null && $oldComment !== $newComment) ) { $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), - $column->getQuotedName($this), - $newComment + $tableNameSQL, + $newColumn->getQuotedName($this), + $newComment, ); } - if (! $columnDiff->hasChanged('length')) { + if (! $columnDiff->hasLengthChanged()) { continue; } $query = 'ALTER ' . $oldColumnName . ' TYPE ' - . $column->getType()->getSQLDeclaration($column->toArray(), $this); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + . $newColumn->getType()->getSQLDeclaration($newColumn->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } - foreach ($diff->renamedColumns as $oldColumnName => $column) { + foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { continue; } $oldColumnName = new Identifier($oldColumnName); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . - ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) + . ' TO ' . $column->getQuotedName($this); } $tableSql = []; @@ -593,17 +669,24 @@ public function getAlterTableSQL(TableDiff $diff) $newName = $diff->getNewName(); if ($newName !== false) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5663', + 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', + __METHOD__, + ); + $sql[] = sprintf( 'ALTER TABLE %s RENAME TO %s', - $diff->getName($this)->getQuotedName($this), - $newName->getQuotedName($this) + $tableNameSQL, + $newName->getQuotedName($this), ); } $sql = array_merge( $this->getPreAlterTableIndexForeignKeySQL($diff), $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) + $this->getPostAlterTableIndexForeignKeySQL($diff), ); } @@ -621,25 +704,25 @@ public function getAlterTableSQL(TableDiff $diff) */ private function isUnchangedBinaryColumn(ColumnDiff $columnDiff): bool { - $columnType = $columnDiff->column->getType(); + $newColumnType = $columnDiff->getNewColumn()->getType(); - if (! $columnType instanceof BinaryType && ! $columnType instanceof BlobType) { + if (! $newColumnType instanceof BinaryType && ! $newColumnType instanceof BlobType) { return false; } - $fromColumn = $columnDiff->fromColumn instanceof Column ? $columnDiff->fromColumn : null; + $oldColumn = $columnDiff->getOldColumn() instanceof Column ? $columnDiff->getOldColumn() : null; - if ($fromColumn !== null) { - $fromColumnType = $fromColumn->getType(); + if ($oldColumn !== null) { + $oldColumnType = $oldColumn->getType(); - if (! $fromColumnType instanceof BinaryType && ! $fromColumnType instanceof BlobType) { + if (! $oldColumnType instanceof BinaryType && ! $oldColumnType instanceof BlobType) { return false; } return count(array_diff($columnDiff->changedProperties, ['type', 'length', 'fixed'])) === 0; } - if ($columnDiff->hasChanged('type')) { + if ($columnDiff->hasTypeChanged()) { return false; } @@ -647,7 +730,7 @@ private function isUnchangedBinaryColumn(ColumnDiff $columnDiff): bool } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) { @@ -660,7 +743,9 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getCommentOnColumnSQL($tableName, $columnName, $comment) { @@ -672,7 +757,7 @@ public function getCommentOnColumnSQL($tableName, $columnName, $comment) 'COMMENT ON COLUMN %s.%s IS %s', $tableName->getQuotedName($this), $columnName->getQuotedName($this), - $comment + $comment, ); } @@ -726,6 +811,36 @@ public function getDropForeignKeySQL($foreignKey, $table) return $this->getDropConstraintSQL($foreignKey, $table); } + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index && $index->isPrimary() && $table !== null) { + $constraintName = $index->getName() === 'primary' ? $this->tableName($table) . '_pkey' : $index->getName(); + + return $this->getDropConstraintSQL($constraintName, $table); + } + + if ($index === '"primary"' && $table !== null) { + $constraintName = $this->tableName($table) . '_pkey'; + + return $this->getDropConstraintSQL($constraintName, $table); + } + + return parent::getDropIndexSQL($index, $table); + } + + /** + * @param Table|string|null $table + * + * @return string + */ + private function tableName($table) + { + return $table instanceof Table ? $table->getName() : (string) $table; + } + /** * {@inheritDoc} */ @@ -738,7 +853,9 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; } - $query = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; + $unlogged = isset($options['unlogged']) && $options['unlogged'] === true ? ' UNLOGGED' : ''; + + $query = 'CREATE' . $unlogged . ' TABLE ' . $name . ' (' . $queryFields . ')'; $sql = [$query]; @@ -755,7 +872,7 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] } if (isset($options['foreignKeys'])) { - foreach ((array) $options['foreignKeys'] as $definition) { + foreach ($options['foreignKeys'] as $definition) { $sql[] = $this->getCreateForeignKeySQL($definition, $name); } } @@ -843,16 +960,14 @@ public function convertBooleans($item) return $this->doConvertBooleans( $item, - /** - * @param mixed $value - */ + /** @param mixed $value */ static function ($value) { if ($value === null) { return 'NULL'; } return $value === true ? 'true' : 'false'; - } + }, ); } @@ -867,17 +982,21 @@ public function convertBooleansToDatabaseValue($item) return $this->doConvertBooleans( $item, - /** - * @param mixed $value - */ + /** @param mixed $value */ static function ($value): ?int { return $value === null ? null : (int) $value; - } + }, ); } /** * {@inheritDoc} + * + * @param T $item + * + * @return (T is null ? null : bool) + * + * @template T */ public function convertFromBoolean($item) { @@ -1007,7 +1126,7 @@ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) { @@ -1030,7 +1149,7 @@ public function getName() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4749', - 'PostgreSQLPlatform::getName() is deprecated. Identify platforms by their class.' + 'PostgreSQLPlatform::getName() is deprecated. Identify platforms by their class.', ); return 'postgresql'; @@ -1081,79 +1200,110 @@ public function getReadLockSQL() protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'bigint' => 'bigint', - 'bigserial' => 'bigint', - 'bool' => 'boolean', - 'boolean' => 'boolean', - 'bpchar' => 'string', - 'bytea' => 'blob', - 'char' => 'string', - 'date' => 'date', - 'datetime' => 'datetime', - 'decimal' => 'decimal', - 'double' => 'float', - 'double precision' => 'float', - 'float' => 'float', - 'float4' => 'float', - 'float8' => 'float', - 'inet' => 'string', - 'int' => 'integer', - 'int2' => 'smallint', - 'int4' => 'integer', - 'int8' => 'bigint', - 'integer' => 'integer', - 'interval' => 'string', - 'json' => 'json', - 'jsonb' => 'json', - 'money' => 'decimal', - 'numeric' => 'decimal', - 'serial' => 'integer', - 'serial4' => 'integer', - 'serial8' => 'bigint', - 'real' => 'float', - 'smallint' => 'smallint', - 'text' => 'text', - 'time' => 'time', - 'timestamp' => 'datetime', - 'timestamptz' => 'datetimetz', - 'timetz' => 'time', - 'tsvector' => 'text', - 'uuid' => 'guid', - 'varchar' => 'string', - 'year' => 'date', - '_varchar' => 'string', + 'bigint' => Types::BIGINT, + 'bigserial' => Types::BIGINT, + 'bool' => Types::BOOLEAN, + 'boolean' => Types::BOOLEAN, + 'bpchar' => Types::STRING, + 'bytea' => Types::BLOB, + 'char' => Types::STRING, + 'date' => Types::DATE_MUTABLE, + 'datetime' => Types::DATETIME_MUTABLE, + 'decimal' => Types::DECIMAL, + 'double' => Types::FLOAT, + 'double precision' => Types::FLOAT, + 'float' => Types::FLOAT, + 'float4' => Types::FLOAT, + 'float8' => Types::FLOAT, + 'inet' => Types::STRING, + 'int' => Types::INTEGER, + 'int2' => Types::SMALLINT, + 'int4' => Types::INTEGER, + 'int8' => Types::BIGINT, + 'integer' => Types::INTEGER, + 'interval' => Types::STRING, + 'json' => Types::JSON, + 'jsonb' => Types::JSON, + 'money' => Types::DECIMAL, + 'numeric' => Types::DECIMAL, + 'serial' => Types::INTEGER, + 'serial4' => Types::INTEGER, + 'serial8' => Types::BIGINT, + 'real' => Types::FLOAT, + 'smallint' => Types::SMALLINT, + 'text' => Types::TEXT, + 'time' => Types::TIME_MUTABLE, + 'timestamp' => Types::DATETIME_MUTABLE, + 'timestamptz' => Types::DATETIMETZ_MUTABLE, + 'timetz' => Types::TIME_MUTABLE, + 'tsvector' => Types::TEXT, + 'uuid' => Types::GUID, + 'varchar' => Types::STRING, + 'year' => Types::DATE_MUTABLE, + '_varchar' => Types::STRING, ]; } /** * {@inheritDoc} + * + * @deprecated */ public function getVarcharMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'PostgreSQLPlatform::getVarcharMaxLength() is deprecated.', + ); + return 65535; } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getBinaryMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'PostgreSQLPlatform::getBinaryMaxLength() is deprecated.', + ); + return 0; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getBinaryDefaultLength() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'Relying on the default binary column length is deprecated, specify the length explicitly.', + ); + return 0; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function hasNativeJsonType() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } @@ -1168,7 +1318,7 @@ protected function getReservedKeywordsClass() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'PostgreSQLPlatform::getReservedKeywordsClass() is deprecated,' - . ' use PostgreSQLPlatform::createReservedKeywordsList() instead.' + . ' use PostgreSQLPlatform::createReservedKeywordsList() instead.', ); return Keywords\PostgreSQL94Keywords::class; @@ -1183,11 +1333,13 @@ public function getBlobTypeDeclarationSQL(array $column) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getDefaultValueDeclarationSQL($column) { - if ($this->isSerialColumn($column)) { + if (isset($column['autoincrement']) && $column['autoincrement'] === true) { return ''; } @@ -1195,7 +1347,9 @@ public function getDefaultValueDeclarationSQL($column) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsColumnCollation() { @@ -1203,15 +1357,7 @@ public function supportsColumnCollation() } /** - * {@inheritdoc} - */ - public function getColumnCollationDeclarationSQL($collation) - { - return 'COLLATE ' . $this->quoteSingleIdentifier($collation); - } - - /** - * {@inheritdoc} + * {@inheritDoc} */ public function getJsonTypeDeclarationSQL(array $column) { @@ -1222,43 +1368,18 @@ public function getJsonTypeDeclarationSQL(array $column) return 'JSON'; } - /** - * @param mixed[] $column - */ - private function isSerialColumn(array $column): bool + private function getOldColumnComment(ColumnDiff $columnDiff): ?string { - return isset($column['type'], $column['autoincrement']) - && $column['autoincrement'] === true - && $this->isIntegerType($column['type']); - } + $oldColumn = $columnDiff->getOldColumn(); - /** - * Check whether the type of a column is changed in a way that invalidates the default value for the column - */ - private function typeChangeBreaksDefaultValue(ColumnDiff $columnDiff): bool - { - if ($columnDiff->fromColumn === null) { - return $columnDiff->hasChanged('type'); + if ($oldColumn !== null) { + return $this->getColumnComment($oldColumn); } - $oldTypeIsInteger = $this->isIntegerType($columnDiff->fromColumn->getType()); - $newTypeIsInteger = $this->isIntegerType($columnDiff->column->getType()); - - // default should not be changed when switching between integer types and the default comes from a sequence - return $columnDiff->hasChanged('type') - && ! ($oldTypeIsInteger && $newTypeIsInteger && $columnDiff->column->getAutoincrement()); - } - - private function isIntegerType(Type $type): bool - { - return $type instanceof PhpIntegerMappingType; - } - - private function getOldColumnComment(ColumnDiff $columnDiff): ?string - { - return $columnDiff->fromColumn !== null ? $this->getColumnComment($columnDiff->fromColumn) : null; + return null; } + /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ public function getListTableMetadataSQL(string $table, ?string $schema = null): string { if ($schema !== null) { @@ -1270,7 +1391,12 @@ public function getListTableMetadataSQL(string $table, ?string $schema = null): SELECT obj_description(%s::regclass) AS table_comment; SQL , - $this->quoteStringLiteral($table) + $this->quoteStringLiteral($table), ); } + + public function createSchemaManager(Connection $connection): PostgreSQLSchemaManager + { + return new PostgreSQLSchemaManager($connection, $this); + } } diff --git a/doctrine/dbal/src/Platforms/SQLServer/Comparator.php b/doctrine/dbal/src/Platforms/SQLServer/Comparator.php index 0db1a6a57..41ef422bf 100644 --- a/doctrine/dbal/src/Platforms/SQLServer/Comparator.php +++ b/doctrine/dbal/src/Platforms/SQLServer/Comparator.php @@ -5,6 +5,7 @@ use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\DBAL\Schema\Comparator as BaseComparator; use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; /** * Compares schemas in the context of SQL Server platform. @@ -13,12 +14,9 @@ */ class Comparator extends BaseComparator { - /** @var string */ - private $databaseCollation; + private string $databaseCollation; - /** - * @internal The comparator can be only instantiated by a schema manager. - */ + /** @internal The comparator can be only instantiated by a schema manager. */ public function __construct(SQLServerPlatform $platform, string $databaseCollation) { parent::__construct($platform); @@ -26,22 +24,29 @@ public function __construct(SQLServerPlatform $platform, string $databaseCollati $this->databaseCollation = $databaseCollation; } + public function compareTables(Table $fromTable, Table $toTable): TableDiff + { + return parent::compareTables( + $this->normalizeColumns($fromTable), + $this->normalizeColumns($toTable), + ); + } + /** * {@inheritDoc} */ public function diffTable(Table $fromTable, Table $toTable) { - $fromTable = clone $fromTable; - $toTable = clone $toTable; - - $this->normalizeColumns($fromTable); - $this->normalizeColumns($toTable); - - return parent::diffTable($fromTable, $toTable); + return parent::diffTable( + $this->normalizeColumns($fromTable), + $this->normalizeColumns($toTable), + ); } - private function normalizeColumns(Table $table): void + private function normalizeColumns(Table $table): Table { + $table = clone $table; + foreach ($table->getColumns() as $column) { $options = $column->getPlatformOptions(); @@ -52,5 +57,7 @@ private function normalizeColumns(Table $table): void unset($options['collation']); $column->setPlatformOptions($options); } + + return $table; } } diff --git a/doctrine/dbal/src/Platforms/SQLServerPlatform.php b/doctrine/dbal/src/Platforms/SQLServerPlatform.php index 732cb6363..113055ba8 100644 --- a/doctrine/dbal/src/Platforms/SQLServerPlatform.php +++ b/doctrine/dbal/src/Platforms/SQLServerPlatform.php @@ -2,6 +2,7 @@ namespace Doctrine\DBAL\Platforms; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception\InvalidLockMode; use Doctrine\DBAL\LockMode; use Doctrine\DBAL\Schema\Column; @@ -10,8 +11,10 @@ use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\SQLServerSchemaManager; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\Types; use Doctrine\Deprecations\Deprecation; use InvalidArgumentException; @@ -22,7 +25,9 @@ use function crc32; use function dechex; use function explode; +use function func_get_arg; use function func_get_args; +use function func_num_args; use function implode; use function is_array; use function is_bool; @@ -45,7 +50,7 @@ class SQLServerPlatform extends AbstractPlatform { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getCurrentDateSQL() { @@ -53,7 +58,7 @@ public function getCurrentDateSQL() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getCurrentTimeSQL() { @@ -72,7 +77,7 @@ private function getConvertExpression($dataType, $expression): string } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { @@ -105,8 +110,8 @@ public function prefersIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/1519', - 'SQLServerPlatform::prefersIdentityColumns() is deprecated.' + 'https://github.com/doctrine/dbal/pull/1519', + 'SQLServerPlatform::prefersIdentityColumns() is deprecated.', ); return true; @@ -131,7 +136,7 @@ public function supportsReleaseSavepoints() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function supportsSchemas() { @@ -139,15 +144,26 @@ public function supportsSchemas() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getDefaultSchemaName() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + return 'dbo'; } /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsColumnCollation() { @@ -174,7 +190,9 @@ public function getCreateSequenceSQL(Sequence $sequence): string } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListSequencesSQL($database) { @@ -189,7 +207,7 @@ public function getListSequencesSQL($database) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSequenceNextValSQL($sequence) { @@ -198,9 +216,18 @@ public function getSequenceNextValSQL($sequence) /** * {@inheritDoc} + * + * @deprecated */ public function hasNativeGuidType() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } @@ -229,18 +256,32 @@ public function getDropForeignKeySQL($foreignKey, $table) public function getDropIndexSQL($index, $table = null) { if ($index instanceof Index) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $index = $index->getQuotedName($this); } elseif (! is_string($index)) { throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.' + __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.', ); } if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as an Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $table = $table->getQuotedName($this); } elseif (! is_string($table)) { throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' + __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', ); } @@ -316,7 +357,7 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] } if (isset($options['foreignKeys'])) { - foreach ((array) $options['foreignKeys'] as $definition) { + foreach ($options['foreignKeys'] as $definition) { $sql[] = $this->getCreateForeignKeySQL($definition, $name); } } @@ -330,6 +371,13 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] public function getCreatePrimaryKeySQL(Index $index, $table) { if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $identifier = $table->getQuotedName($this); } else { $identifier = $table; @@ -380,7 +428,7 @@ protected function getCreateColumnCommentSQL($tableName, $columnName, $comment) 'TABLE', $tableSQL, 'COLUMN', - $columnName + $columnName, ); } @@ -469,17 +517,24 @@ public function getAlterTableSQL(TableDiff $diff) $columnSql = []; $commentsSql = []; - foreach ($diff->addedColumns as $column) { + $table = $diff->getOldTable() ?? $diff->getName($this); + + $tableName = $table->getName(); + + foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } - $columnDef = $column->toArray(); - $addColumnSql = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); - if (isset($columnDef['default'])) { - $addColumnSql .= ' CONSTRAINT ' . - $this->generateDefaultConstraintName($diff->name, $column->getQuotedName($this)) . - $this->getDefaultValueDeclarationSQL($columnDef); + $columnProperties = $column->toArray(); + + $addColumnSql = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnProperties); + + if (isset($columnProperties['default'])) { + $addColumnSql .= ' CONSTRAINT ' . $this->generateDefaultConstraintName( + $tableName, + $column->getQuotedName($this), + ) . $this->getDefaultValueDeclarationSQL($columnProperties); } $queryParts[] = $addColumnSql; @@ -491,13 +546,13 @@ public function getAlterTableSQL(TableDiff $diff) } $commentsSql[] = $this->getCreateColumnCommentSQL( - $diff->name, + $tableName, $column->getQuotedName($this), - $comment + $comment, ); } - foreach ($diff->removedColumns as $column) { + foreach ($diff->getDroppedColumns() as $column) { if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { continue; } @@ -505,86 +560,97 @@ public function getAlterTableSQL(TableDiff $diff) $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); } - foreach ($diff->changedColumns as $columnDiff) { + foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } - $column = $columnDiff->column; - $comment = $this->getColumnComment($column); - $hasComment = ! empty($comment) || is_numeric($comment); + $newColumn = $columnDiff->getNewColumn(); + $newComment = $this->getColumnComment($newColumn); + $hasNewComment = ! empty($newComment) || is_numeric($newComment); + + $oldColumn = $columnDiff->getOldColumn(); - if ($columnDiff->fromColumn instanceof Column) { - $fromComment = $this->getColumnComment($columnDiff->fromColumn); - $hasFromComment = ! empty($fromComment) || is_numeric($fromComment); + if ($oldColumn instanceof Column) { + $oldComment = $this->getColumnComment($oldColumn); + $hasOldComment = ! empty($oldComment) || is_numeric($oldComment); - if ($hasFromComment && $hasComment && $fromComment !== $comment) { + if ($hasOldComment && $hasNewComment && $oldComment !== $newComment) { $commentsSql[] = $this->getAlterColumnCommentSQL( - $diff->name, - $column->getQuotedName($this), - $comment + $tableName, + $newColumn->getQuotedName($this), + $newComment, ); - } elseif ($hasFromComment && ! $hasComment) { - $commentsSql[] = $this->getDropColumnCommentSQL($diff->name, $column->getQuotedName($this)); - } elseif (! $hasFromComment && $hasComment) { + } elseif ($hasOldComment && ! $hasNewComment) { + $commentsSql[] = $this->getDropColumnCommentSQL( + $tableName, + $newColumn->getQuotedName($this), + ); + } elseif (! $hasOldComment && $hasNewComment) { $commentsSql[] = $this->getCreateColumnCommentSQL( - $diff->name, - $column->getQuotedName($this), - $comment + $tableName, + $newColumn->getQuotedName($this), + $newComment, ); } } // Do not add query part if only comment has changed. - if ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1) { + if ($columnDiff->hasCommentChanged() && count($columnDiff->changedProperties) === 1) { continue; } $requireDropDefaultConstraint = $this->alterColumnRequiresDropDefaultConstraint($columnDiff); if ($requireDropDefaultConstraint) { - $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( - $diff->name, - $columnDiff->oldColumnName - ); + $oldColumn = $columnDiff->getOldColumn(); + + if ($oldColumn !== null) { + $oldColumnName = $oldColumn->getName(); + } else { + $oldColumnName = $columnDiff->oldColumnName; + } + + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause($tableName, $oldColumnName); } - $columnDef = $column->toArray(); + $columnProperties = $newColumn->toArray(); $queryParts[] = 'ALTER COLUMN ' . - $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + $this->getColumnDeclarationSQL($newColumn->getQuotedName($this), $columnProperties); if ( - ! isset($columnDef['default']) - || (! $requireDropDefaultConstraint && ! $columnDiff->hasChanged('default')) + ! isset($columnProperties['default']) + || (! $requireDropDefaultConstraint && ! $columnDiff->hasDefaultChanged()) ) { continue; } - $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($tableName, $newColumn); } - foreach ($diff->renamedColumns as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + $tableNameSQL = $table->getQuotedName($this); + + foreach ($diff->getRenamedColumns() as $oldColumnName => $newColumn) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $newColumn, $diff, $columnSql)) { continue; } $oldColumnName = new Identifier($oldColumnName); - $sql[] = "sp_rename '" . - $diff->getName($this)->getQuotedName($this) . '.' . $oldColumnName->getQuotedName($this) . - "', '" . $column->getQuotedName($this) . "', 'COLUMN'"; + $sql[] = "sp_rename '" . $tableNameSQL . '.' . $oldColumnName->getQuotedName($this) . + "', '" . $newColumn->getQuotedName($this) . "', 'COLUMN'"; // Recreate default constraint with new column name if necessary (for future reference). - if ($column->getDefault() === null) { + if ($newColumn->getDefault() === null) { continue; } $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( - $diff->name, - $oldColumnName->getQuotedName($this) + $tableName, + $oldColumnName->getQuotedName($this), ); - $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($tableName, $newColumn); } $tableSql = []; @@ -594,7 +660,7 @@ public function getAlterTableSQL(TableDiff $diff) } foreach ($queryParts as $query) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } $sql = array_merge($sql, $commentsSql); @@ -602,35 +668,57 @@ public function getAlterTableSQL(TableDiff $diff) $newName = $diff->getNewName(); if ($newName !== false) { - $sql[] = "sp_rename '" . $diff->getName($this)->getQuotedName($this) . "', '" . $newName->getName() . "'"; + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5663', + 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', + __METHOD__, + ); - /** - * Rename table's default constraints names - * to match the new table name. - * This is necessary to ensure that the default - * constraints can be referenced in future table - * alterations as the table name is encoded in - * default constraints' names. - */ - $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($newName->getName()) . "') + ''', ''OBJECT'';' " . - 'FROM sys.default_constraints dc ' . - 'JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id ' . - "WHERE tbl.name = '" . $newName->getName() . "';" . - 'EXEC sp_executesql @sql'; + $sql = array_merge($sql, $this->getRenameTableSQL($tableName, $newName->getName())); } $sql = array_merge( $this->getPreAlterTableIndexForeignKeySQL($diff), $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) + $this->getPostAlterTableIndexForeignKeySQL($diff), ); return array_merge($sql, $tableSql, $columnSql); } + /** + * {@inheritDoc} + */ + public function getRenameTableSQL(string $oldName, string $newName): array + { + return [ + sprintf('sp_rename %s, %s', $this->quoteStringLiteral($oldName), $this->quoteStringLiteral($newName)), + + /* Rename table's default constraints names + * to match the new table name. + * This is necessary to ensure that the default + * constraints can be referenced in future table + * alterations as the table name is encoded in + * default constraints' names. */ + sprintf( + <<<'SQL' + DECLARE @sql NVARCHAR(MAX) = N''; + SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' + + REPLACE(dc.name, '%s', '%s') + ''', ''OBJECT'';' + FROM sys.default_constraints dc + JOIN sys.tables tbl + ON dc.parent_object_id = tbl.object_id + WHERE tbl.name = %s; + EXEC sp_executesql @sql + SQL, + $this->generateIdentifierName($oldName), + $this->generateIdentifierName($newName), + $this->quoteStringLiteral($newName), + ), + ]; + } + /** * Returns the SQL clause for adding a default constraint in an ALTER TABLE statement. * @@ -666,27 +754,29 @@ private function getAlterTableDropDefaultConstraintClause($tableName, $columnNam */ private function alterColumnRequiresDropDefaultConstraint(ColumnDiff $columnDiff): bool { + $oldColumn = $columnDiff->getOldColumn(); + // We can only decide whether to drop an existing default constraint // if we know the original default value. - if (! $columnDiff->fromColumn instanceof Column) { + if (! $oldColumn instanceof Column) { return false; } // We only need to drop an existing default constraint if we know the // column was defined with a default value before. - if ($columnDiff->fromColumn->getDefault() === null) { + if ($oldColumn->getDefault() === null) { return false; } // We need to drop an existing default constraint if the column was // defined with a default value before and it has changed. - if ($columnDiff->hasChanged('default')) { + if ($columnDiff->hasDefaultChanged()) { return true; } // We need to drop an existing default constraint if the column was // defined with a default value before and the native column type has changed. - return $columnDiff->hasChanged('type') || $columnDiff->hasChanged('fixed'); + return $columnDiff->hasTypeChanged() || $columnDiff->hasFixedChanged(); } /** @@ -725,7 +815,7 @@ protected function getAlterColumnCommentSQL($tableName, $columnName, $comment) 'TABLE', $tableSQL, 'COLUMN', - $columnName + $columnName, ); } @@ -763,12 +853,12 @@ protected function getDropColumnCommentSQL($tableName, $columnName) 'TABLE', $tableSQL, 'COLUMN', - $columnName + $columnName, ); } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) { @@ -776,7 +866,7 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) "EXEC sp_rename N'%s.%s', N'%s', N'INDEX'", $tableName, $oldIndexName, - $index->getQuotedName($this) + $index->getQuotedName($this), ), ]; } @@ -893,6 +983,8 @@ public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierCol } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTablesSQL() @@ -904,6 +996,8 @@ public function getListTablesSQL() } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTableColumnsSQL($table, $database = null) @@ -937,6 +1031,8 @@ public function getListTableColumnsSQL($table, $database = null) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * @param string $table * @param string|null $database * @@ -963,6 +1059,8 @@ public function getListTableForeignKeysSQL($table, $database = null) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTableIndexesSQL($table, $database = null) @@ -987,6 +1085,8 @@ public function getListTableIndexesSQL($table, $database = null) /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { @@ -1084,6 +1184,8 @@ public function getConcatExpression() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListDatabasesSQL() { @@ -1101,7 +1203,7 @@ public function getListNamespacesSQL() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'SQLServerPlatform::getListNamespacesSQL() is deprecated,' - . ' use SQLServerSchemaManager::listSchemaNames() instead.' + . ' use SQLServerSchemaManager::listSchemaNames() instead.', ); return "SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')"; @@ -1197,28 +1299,54 @@ public function getAsciiStringTypeDeclarationSQL(array $column): string /** * {@inheritDoc} */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { + if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'Relying on the default string column length on SQL Server is deprecated' + . ', specify the length explicitly.', + ); + } + return $fixed - ? ($length > 0 ? 'NCHAR(' . $length . ')' : 'CHAR(255)') - : ($length > 0 ? 'NVARCHAR(' . $length . ')' : 'NVARCHAR(255)'); + ? 'NCHAR(' . ($length > 0 ? $length : 255) . ')' + : 'NVARCHAR(' . ($length > 0 ? $length : 255) . ')'; } /** - * {@inheritdoc} + * {@inheritDoc} */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { + if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'Relying on the default binary column length on SQL Server is deprecated' + . ', specify the length explicitly.', + ); + } + return $fixed ? 'BINARY(' . ($length > 0 ? $length : 255) . ')' : 'VARBINARY(' . ($length > 0 ? $length : 255) . ')'; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getBinaryMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'SQLServerPlatform::getBinaryMaxLength() is deprecated.', + ); + return 8000; } @@ -1390,36 +1518,36 @@ public function getName() protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'bigint' => 'bigint', - 'binary' => 'binary', - 'bit' => 'boolean', - 'blob' => 'blob', - 'char' => 'string', - 'date' => 'date', - 'datetime' => 'datetime', - 'datetime2' => 'datetime', - 'datetimeoffset' => 'datetimetz', - 'decimal' => 'decimal', - 'double' => 'float', - 'double precision' => 'float', - 'float' => 'float', - 'image' => 'blob', - 'int' => 'integer', - 'money' => 'integer', - 'nchar' => 'string', - 'ntext' => 'text', - 'numeric' => 'decimal', - 'nvarchar' => 'string', - 'real' => 'float', - 'smalldatetime' => 'datetime', - 'smallint' => 'smallint', - 'smallmoney' => 'integer', - 'text' => 'text', - 'time' => 'time', - 'tinyint' => 'smallint', - 'uniqueidentifier' => 'guid', - 'varbinary' => 'binary', - 'varchar' => 'string', + 'bigint' => Types::BIGINT, + 'binary' => Types::BINARY, + 'bit' => Types::BOOLEAN, + 'blob' => Types::BLOB, + 'char' => Types::STRING, + 'date' => Types::DATE_MUTABLE, + 'datetime' => Types::DATETIME_MUTABLE, + 'datetime2' => Types::DATETIME_MUTABLE, + 'datetimeoffset' => Types::DATETIMETZ_MUTABLE, + 'decimal' => Types::DECIMAL, + 'double' => Types::FLOAT, + 'double precision' => Types::FLOAT, + 'float' => Types::FLOAT, + 'image' => Types::BLOB, + 'int' => Types::INTEGER, + 'money' => Types::INTEGER, + 'nchar' => Types::STRING, + 'ntext' => Types::TEXT, + 'numeric' => Types::DECIMAL, + 'nvarchar' => Types::STRING, + 'real' => Types::FLOAT, + 'smalldatetime' => Types::DATETIME_MUTABLE, + 'smallint' => Types::SMALLINT, + 'smallmoney' => Types::INTEGER, + 'text' => Types::TEXT, + 'time' => Types::TIME_MUTABLE, + 'tinyint' => Types::SMALLINT, + 'uniqueidentifier' => Types::GUID, + 'varbinary' => Types::BINARY, + 'varchar' => Types::STRING, ]; } @@ -1448,7 +1576,9 @@ public function rollbackSavePoint($savepoint) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getForeignKeyReferentialActionSQL($action) { @@ -1497,7 +1627,7 @@ protected function getReservedKeywordsClass() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'SQLServerPlatform::getReservedKeywordsClass() is deprecated,' - . ' use SQLServerPlatform::createReservedKeywordsList() instead.' + . ' use SQLServerPlatform::createReservedKeywordsList() instead.', ); return Keywords\SQLServer2012Keywords::class; @@ -1530,9 +1660,9 @@ public function getBlobTypeDeclarationSQL(array $column) } /** - * {@inheritdoc} + * {@inheritDoc} * - * Modifies column declaration order as it differs in Microsoft SQL Server. + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getColumnDeclarationSQL($name, array $column) { @@ -1544,11 +1674,29 @@ public function getColumnDeclarationSQL($name, array $column) $notnull = ! empty($column['notnull']) ? ' NOT NULL' : ''; - $unique = ! empty($column['unique']) ? - ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + if (! empty($column['unique'])) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5656', + 'The usage of the "unique" column property is deprecated. Use unique constraints instead.', + ); - $check = ! empty($column['check']) ? - ' ' . $column['check'] : ''; + $unique = ' ' . $this->getUniqueFieldDeclarationSQL(); + } else { + $unique = ''; + } + + if (! empty($column['check'])) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5656', + 'The usage of the "check" column property is deprecated.', + ); + + $check = ' ' . $column['check']; + } else { + $check = ''; + } $typeDecl = $column['type']->getSQLDeclaration($column, $this); $columnDef = $typeDecl . $collation . $notnull . $unique . $check; @@ -1557,6 +1705,16 @@ public function getColumnDeclarationSQL($name, array $column) return $name . ' ' . $columnDef; } + /** + * {@inheritDoc} + * + * SQL Server does not support quoting collation identifiers. + */ + public function getColumnCollationDeclarationSQL($collation) + { + return 'COLLATE ' . $collation; + } + public function columnsEqual(Column $column1, Column $column2): bool { if (! parent::columnsEqual($column1, $column2)) { @@ -1606,10 +1764,11 @@ protected function getCommentOnTableSQL(string $tableName, ?string $comment): st SQL , $this->quoteStringLiteral((string) $comment), - $this->quoteStringLiteral($tableName) + $this->quoteStringLiteral($tableName), ); } + /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ public function getListTableMetadataSQL(string $table): string { return sprintf( @@ -1623,13 +1782,11 @@ public function getListTableMetadataSQL(string $table): string (tbl.name=N%s and SCHEMA_NAME(tbl.schema_id)=N'dbo' and p.name=N'MS_Description') SQL , - $this->quoteStringLiteral($table) + $this->quoteStringLiteral($table), ); } - /** - * @param string $query - */ + /** @param string $query */ private function shouldAddOrderBy($query): bool { // Find the position of the last instance of ORDER BY and ensure it is not within a parenthetical statement @@ -1657,4 +1814,9 @@ private function shouldAddOrderBy($query): bool return true; } + + public function createSchemaManager(Connection $connection): SQLServerSchemaManager + { + return new SQLServerSchemaManager($connection, $this); + } } diff --git a/doctrine/dbal/src/Platforms/SQLite/Comparator.php b/doctrine/dbal/src/Platforms/SQLite/Comparator.php index d27ee86d7..5218871c6 100644 --- a/doctrine/dbal/src/Platforms/SQLite/Comparator.php +++ b/doctrine/dbal/src/Platforms/SQLite/Comparator.php @@ -5,6 +5,7 @@ use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\Comparator as BaseComparator; use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; use function strcasecmp; @@ -15,30 +16,35 @@ */ class Comparator extends BaseComparator { - /** - * @internal The comparator can be only instantiated by a schema manager. - */ + /** @internal The comparator can be only instantiated by a schema manager. */ public function __construct(SqlitePlatform $platform) { parent::__construct($platform); } + public function compareTables(Table $fromTable, Table $toTable): TableDiff + { + return parent::compareTables( + $this->normalizeColumns($fromTable), + $this->normalizeColumns($toTable), + ); + } + /** * {@inheritDoc} */ public function diffTable(Table $fromTable, Table $toTable) { - $fromTable = clone $fromTable; - $toTable = clone $toTable; - - $this->normalizeColumns($fromTable); - $this->normalizeColumns($toTable); - - return parent::diffTable($fromTable, $toTable); + return parent::diffTable( + $this->normalizeColumns($fromTable), + $this->normalizeColumns($toTable), + ); } - private function normalizeColumns(Table $table): void + private function normalizeColumns(Table $table): Table { + $table = clone $table; + foreach ($table->getColumns() as $column) { $options = $column->getPlatformOptions(); @@ -49,5 +55,7 @@ private function normalizeColumns(Table $table): void unset($options['collation']); $column->setPlatformOptions($options); } + + return $table; } } diff --git a/doctrine/dbal/src/Platforms/SqlitePlatform.php b/doctrine/dbal/src/Platforms/SqlitePlatform.php index 6d5adda49..5acefc5c8 100644 --- a/doctrine/dbal/src/Platforms/SqlitePlatform.php +++ b/doctrine/dbal/src/Platforms/SqlitePlatform.php @@ -2,6 +2,7 @@ namespace Doctrine\DBAL\Platforms; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver\API\SQLite\UserDefinedFunctions; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Column; @@ -10,10 +11,12 @@ use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\SchemaException; +use Doctrine\DBAL\Schema\SqliteSchemaManager; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\TransactionIsolationLevel; use Doctrine\DBAL\Types; +use Doctrine\DBAL\Types\IntegerType; use Doctrine\Deprecations\Deprecation; use function array_combine; @@ -22,6 +25,7 @@ use function array_search; use function array_unique; use function array_values; +use function count; use function implode; use function is_numeric; use function sprintf; @@ -39,6 +43,8 @@ */ class SqlitePlatform extends AbstractPlatform { + private bool $schemaEmulationEnabled = true; + /** * {@inheritDoc} */ @@ -59,7 +65,7 @@ public function getNowExpression($type = 'timestamp') Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4753', - 'SqlitePlatform::getNowExpression() is deprecated. Generate dates within the application.' + 'SqlitePlatform::getNowExpression() is deprecated. Generate dates within the application.', ); switch ($type) { @@ -75,6 +81,14 @@ public function getNowExpression($type = 'timestamp') } } + /** + * {@inheritDoc} + */ + public function getModExpression($expression1, $expression2) + { + return $expression1 . ' % ' . $expression2; + } + /** * {@inheritDoc} */ @@ -117,15 +131,17 @@ public function getSubstringExpression($string, $start, $length = null) */ public function getLocateExpression($str, $substr, $startPos = false) { - if ($startPos === false) { - return 'LOCATE(' . $str . ', ' . $substr . ')'; + if ($startPos === false || $startPos === 1 || $startPos === '1') { + return 'INSTR(' . $str . ', ' . $substr . ')'; } - return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')'; + return 'CASE WHEN INSTR(SUBSTR(' . $str . ', ' . $startPos . '), ' . $substr + . ') > 0 THEN INSTR(SUBSTR(' . $str . ', ' . $startPos . '), ' . $substr . ') + ' . $startPos + . ' - 1 ELSE 0 END'; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { @@ -166,14 +182,15 @@ public function getDateDiffExpression($date1, $date2) /** * {@inheritDoc} * - * The SQLite platform doesn't support the concept of a database, therefore, it always returns an empty string + * The DBAL doesn't support databases on the SQLite platform. The expression here always returns a fixed string * as an indicator of an implicitly selected database. * - * @see \Doctrine\DBAL\Connection::getDatabase() + * @link https://www.sqlite.org/lang_select.html + * @see Connection::getDatabase() */ public function getCurrentDatabaseExpression(): string { - return "''"; + return "'main'"; } /** @@ -212,8 +229,8 @@ public function prefersIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pulls/1519', - 'SqlitePlatform::prefersIdentityColumns() is deprecated.' + 'https://github.com/doctrine/dbal/pull/1519', + 'SqlitePlatform::prefersIdentityColumns() is deprecated.', ); return true; @@ -249,12 +266,21 @@ public function getBigIntTypeDeclarationSQL(array $column) } /** + * @deprecated Use {@see getSmallIntTypeDeclarationSQL()} instead. + * * @param array $column * * @return string */ public function getTinyIntTypeDeclarationSQL(array $column) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5511', + '%s is deprecated. Use getSmallIntTypeDeclarationSQL() instead.', + __METHOD__, + ); + // SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT columns if (! empty($column['autoincrement'])) { return $this->getIntegerTypeDeclarationSQL($column); @@ -277,12 +303,21 @@ public function getSmallIntTypeDeclarationSQL(array $column) } /** + * @deprecated Use {@see getIntegerTypeDeclarationSQL()} instead. + * * @param array $column * * @return string */ public function getMediumIntTypeDeclarationSQL(array $column) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5511', + '%s is deprecated. Use getIntegerTypeDeclarationSQL() instead.', + __METHOD__, + ); + // SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT columns if (! empty($column['autoincrement'])) { return $this->getIntegerTypeDeclarationSQL($column); @@ -328,17 +363,39 @@ protected function _getCommonIntegerTypeDeclarationSQL(array $column) return ! empty($column['unsigned']) ? ' UNSIGNED' : ''; } + /** + * Disables schema emulation. + * + * Schema emulation is enabled by default to maintain backwards compatibility. + * Disable it to opt-in to the behavior of DBAL 4. + * + * @deprecated Will be removed in DBAL 4.0. + */ + public function disableSchemaEmulation(): void + { + $this->schemaEmulationEnabled = false; + } + + private function emulateSchemaNamespacing(string $tableName): string + { + return $this->schemaEmulationEnabled + ? str_replace('.', '__', $tableName) + : $tableName; + } + /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) { return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint( $foreignKey->getQuotedLocalColumns($this), - str_replace('.', '__', $foreignKey->getQuotedForeignTableName($this)), + $this->emulateSchemaNamespacing($foreignKey->getQuotedForeignTableName($this)), $foreignKey->getQuotedForeignColumns($this), $foreignKey->getName(), - $foreignKey->getOptions() + $foreignKey->getOptions(), )); } @@ -347,7 +404,7 @@ public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) */ protected function _getCreateTableSQL($name, array $columns, array $options = []) { - $name = str_replace('.', '__', $name); + $name = $this->emulateSchemaNamespacing($name); $queryFields = $this->getColumnDeclarationListSQL($columns); if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { @@ -425,7 +482,7 @@ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) { @@ -433,18 +490,34 @@ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getBinaryMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'SqlitePlatform::getBinaryMaxLength() is deprecated.', + ); + return 0; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function getBinaryDefaultLength() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'Relying on the default binary column length is deprecated, specify the length explicitly.', + ); + return 0; } @@ -457,39 +530,47 @@ public function getClobTypeDeclarationSQL(array $column) } /** + * @deprecated + * * {@inheritDoc} */ public function getListTableConstraintsSQL($table) { - $table = str_replace('.', '__', $table); + $table = $this->emulateSchemaNamespacing($table); return sprintf( "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = %s AND sql NOT NULL ORDER BY name", - $this->quoteStringLiteral($table) + $this->quoteStringLiteral($table), ); } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTableColumnsSQL($table, $database = null) { - $table = str_replace('.', '__', $table); + $table = $this->emulateSchemaNamespacing($table); return sprintf('PRAGMA table_info(%s)', $this->quoteStringLiteral($table)); } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTableIndexesSQL($table, $database = null) { - $table = str_replace('.', '__', $table); + $table = $this->emulateSchemaNamespacing($table); return sprintf('PRAGMA index_list(%s)', $this->quoteStringLiteral($table)); } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTablesSQL() @@ -505,6 +586,8 @@ public function getListTablesSQL() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { @@ -513,6 +596,8 @@ public function getListViewsSQL($database) /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { @@ -536,9 +621,18 @@ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey /** * {@inheritDoc} + * + * @deprecated */ public function supportsCreateDropDatabase() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + return false; } @@ -552,6 +646,8 @@ public function supportsIdentityColumns() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsColumnCollation() { @@ -560,6 +656,8 @@ public function supportsColumnCollation() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsInlineColumnComments() { @@ -574,7 +672,7 @@ public function getName() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4749', - 'SqlitePlatform::getName() is deprecated. Identify platforms by their class.' + 'SqlitePlatform::getName() is deprecated. Identify platforms by their class.', ); return 'sqlite'; @@ -586,7 +684,7 @@ public function getName() public function getTruncateTableSQL($tableName, $cascade = false) { $tableIdentifier = new Identifier($tableName); - $tableName = str_replace('.', '__', $tableIdentifier->getQuotedName($this)); + $tableName = $this->emulateSchemaNamespacing($tableIdentifier->getQuotedName($this)); return 'DELETE FROM ' . $tableName; } @@ -644,6 +742,8 @@ public function getForUpdateSQL() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getInlineColumnCommentSQL($comment) { @@ -661,38 +761,38 @@ private function getInlineTableCommentSQL(string $comment): string protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'bigint' => 'bigint', - 'bigserial' => 'bigint', - 'blob' => 'blob', - 'boolean' => 'boolean', - 'char' => 'string', - 'clob' => 'text', - 'date' => 'date', - 'datetime' => 'datetime', - 'decimal' => 'decimal', - 'double' => 'float', - 'double precision' => 'float', - 'float' => 'float', - 'image' => 'string', - 'int' => 'integer', - 'integer' => 'integer', - 'longtext' => 'text', - 'longvarchar' => 'string', - 'mediumint' => 'integer', - 'mediumtext' => 'text', - 'ntext' => 'string', - 'numeric' => 'decimal', - 'nvarchar' => 'string', - 'real' => 'float', - 'serial' => 'integer', - 'smallint' => 'smallint', - 'text' => 'text', - 'time' => 'time', - 'timestamp' => 'datetime', - 'tinyint' => 'boolean', - 'tinytext' => 'text', - 'varchar' => 'string', - 'varchar2' => 'string', + 'bigint' => Types\Types::BIGINT, + 'bigserial' => Types\Types::BIGINT, + 'blob' => Types\Types::BLOB, + 'boolean' => Types\Types::BOOLEAN, + 'char' => Types\Types::STRING, + 'clob' => Types\Types::TEXT, + 'date' => Types\Types::DATE_MUTABLE, + 'datetime' => Types\Types::DATETIME_MUTABLE, + 'decimal' => Types\Types::DECIMAL, + 'double' => Types\Types::FLOAT, + 'double precision' => Types\Types::FLOAT, + 'float' => Types\Types::FLOAT, + 'image' => Types\Types::STRING, + 'int' => Types\Types::INTEGER, + 'integer' => Types\Types::INTEGER, + 'longtext' => Types\Types::TEXT, + 'longvarchar' => Types\Types::STRING, + 'mediumint' => Types\Types::INTEGER, + 'mediumtext' => Types\Types::TEXT, + 'ntext' => Types\Types::STRING, + 'numeric' => Types\Types::DECIMAL, + 'nvarchar' => Types\Types::STRING, + 'real' => Types\Types::FLOAT, + 'serial' => Types\Types::INTEGER, + 'smallint' => Types\Types::SMALLINT, + 'text' => Types\Types::TEXT, + 'time' => Types\Types::TIME_MUTABLE, + 'timestamp' => Types\Types::DATETIME_MUTABLE, + 'tinyint' => Types\Types::BOOLEAN, + 'tinytext' => Types\Types::TEXT, + 'varchar' => Types\Types::STRING, + 'varchar2' => Types\Types::STRING, ]; } @@ -707,7 +807,7 @@ protected function getReservedKeywordsClass() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'SqlitePlatform::getReservedKeywordsClass() is deprecated,' - . ' use SqlitePlatform::createReservedKeywordsList() instead.' + . ' use SqlitePlatform::createReservedKeywordsList() instead.', ); return Keywords\SQLiteKeywords::class; @@ -718,22 +818,7 @@ protected function getReservedKeywordsClass() */ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) { - if (! $diff->fromTable instanceof Table) { - throw new Exception( - 'Sqlite platform requires for alter table the table diff with reference to original table schema' - ); - } - - $sql = []; - foreach ($diff->fromTable->getIndexes() as $index) { - if ($index->isPrimary()) { - continue; - } - - $sql[] = $this->getDropIndexSQL($index, $diff->name); - } - - return $sql; + return []; } /** @@ -741,11 +826,11 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) */ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) { - $fromTable = $diff->fromTable; + $table = $diff->getOldTable(); - if (! $fromTable instanceof Table) { + if (! $table instanceof Table) { throw new Exception( - 'Sqlite platform requires for alter table the table diff with reference to original table schema' + 'Sqlite platform requires for alter table the table diff with reference to original table schema', ); } @@ -756,7 +841,7 @@ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) $tableName = $diff->getName($this); } - foreach ($this->getIndexesInAlteredTable($diff, $fromTable) as $index) { + foreach ($this->getIndexesInAlteredTable($diff, $table) as $index) { if ($index->isPrimary()) { continue; } @@ -792,7 +877,7 @@ public function getBlobTypeDeclarationSQL(array $column) */ public function getTemporaryTableName($tableName) { - $tableName = str_replace('.', '__', $tableName); + $tableName = $this->emulateSchemaNamespacing($tableName); return $tableName; } @@ -813,18 +898,38 @@ public function canEmulateSchemas() Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4805', - 'SqlitePlatform::canEmulateSchemas() is deprecated.' + 'SqlitePlatform::canEmulateSchemas() is deprecated.', ); - return true; + return $this->schemaEmulationEnabled; } /** * {@inheritDoc} */ - public function supportsForeignKeyConstraints() + public function getCreateTablesSQL(array $tables): array { - return false; + $sql = []; + + foreach ($tables as $table) { + $sql = array_merge($sql, $this->getCreateTableSQL($table)); + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropTablesSQL(array $tables): array + { + $sql = []; + + foreach ($tables as $table) { + $sql[] = $this->getDropTableSQL($table->getQuotedName($this)); + } + + return $sql; } /** @@ -836,7 +941,7 @@ public function getCreatePrimaryKeySQL(Index $index, $table) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) { @@ -844,7 +949,7 @@ public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getDropForeignKeySQL($foreignKey, $table) { @@ -865,6 +970,7 @@ public function getCreateConstraintSQL(Constraint $constraint, $table) * {@inheritDoc} * * @param int|null $createFlags + * @psalm-param int-mask-of|null $createFlags */ public function getCreateTableSQL(Table $table, $createFlags = null) { @@ -874,6 +980,8 @@ public function getCreateTableSQL(Table $table, $createFlags = null) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * @param string $table * @param string|null $database * @@ -881,7 +989,7 @@ public function getCreateTableSQL(Table $table, $createFlags = null) */ public function getListTableForeignKeysSQL($table, $database = null) { - $table = str_replace('.', '__', $table); + $table = $this->emulateSchemaNamespacing($table); return sprintf('PRAGMA foreign_key_list(%s)', $this->quoteStringLiteral($table)); } @@ -896,15 +1004,14 @@ public function getAlterTableSQL(TableDiff $diff) return $sql; } - $fromTable = $diff->fromTable; - if (! $fromTable instanceof Table) { + $table = $diff->getOldTable(); + + if (! $table instanceof Table) { throw new Exception( - 'Sqlite platform requires for alter table the table diff with reference to original table schema' + 'Sqlite platform requires for alter table the table diff with reference to original table schema', ); } - $table = clone $fromTable; - $columns = []; $oldColumnNames = []; $newColumnNames = []; @@ -916,12 +1023,12 @@ public function getAlterTableSQL(TableDiff $diff) $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this); } - foreach ($diff->removedColumns as $columnName => $column) { + foreach ($diff->getDroppedColumns() as $column) { if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { continue; } - $columnName = strtolower($columnName); + $columnName = strtolower($column->getName()); if (! isset($columns[$columnName])) { continue; } @@ -929,17 +1036,23 @@ public function getAlterTableSQL(TableDiff $diff) unset( $columns[$columnName], $oldColumnNames[$columnName], - $newColumnNames[$columnName] + $newColumnNames[$columnName], ); } - foreach ($diff->renamedColumns as $oldColumnName => $column) { + foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { continue; } $oldColumnName = strtolower($oldColumnName); - $columns = $this->replaceColumn($diff->name, $columns, $oldColumnName, $column); + + $columns = $this->replaceColumn( + $table->getName(), + $columns, + $oldColumnName, + $column, + ); if (! isset($newColumnNames[$oldColumnName])) { continue; @@ -948,27 +1061,35 @@ public function getAlterTableSQL(TableDiff $diff) $newColumnNames[$oldColumnName] = $column->getQuotedName($this); } - foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } - $oldColumnName = strtolower($oldColumnName); - $columns = $this->replaceColumn($diff->name, $columns, $oldColumnName, $columnDiff->column); + $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); + + $oldColumnName = strtolower($oldColumn->getName()); + + $columns = $this->replaceColumn( + $table->getName(), + $columns, + $oldColumnName, + $columnDiff->getNewColumn(), + ); if (! isset($newColumnNames[$oldColumnName])) { continue; } - $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this); + $newColumnNames[$oldColumnName] = $columnDiff->getNewColumn()->getQuotedName($this); } - foreach ($diff->addedColumns as $columnName => $column) { + foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } - $columns[strtolower($columnName)] = $column; + $columns[strtolower($column->getName())] = $column; } $sql = []; @@ -979,10 +1100,10 @@ public function getAlterTableSQL(TableDiff $diff) $newTable = new Table( $table->getQuotedName($this), $columns, - $this->getPrimaryIndexInAlteredTable($diff, $fromTable), + $this->getPrimaryIndexInAlteredTable($diff, $table), [], - $this->getForeignKeysInAlteredTable($diff, $fromTable), - $table->getOptions() + $this->getForeignKeysInAlteredTable($diff, $table), + $table->getOptions(), ); $newTable->addOption('alter', true); @@ -992,9 +1113,9 @@ public function getAlterTableSQL(TableDiff $diff) 'CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', $dataTable->getQuotedName($this), implode(', ', $oldColumnNames), - $table->getQuotedName($this) + $table->getQuotedName($this), ); - $sql[] = $this->getDropTableSQL($fromTable); + $sql[] = $this->getDropTableSQL($table); $sql = array_merge($sql, $this->getCreateTableSQL($newTable)); $sql[] = sprintf( @@ -1002,17 +1123,24 @@ public function getAlterTableSQL(TableDiff $diff) $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), - $dataTable->getQuotedName($this) + $dataTable->getQuotedName($this), ); - $sql[] = $this->getDropTableSQL($dataTable); + $sql[] = $this->getDropTableSQL($dataTable->getQuotedName($this)); $newName = $diff->getNewName(); if ($newName !== false) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5663', + 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', + __METHOD__, + ); + $sql[] = sprintf( 'ALTER TABLE %s RENAME TO %s', $newTable->getQuotedName($this), - $newName->getQuotedName($this) + $newName->getQuotedName($this), ); } @@ -1058,22 +1186,28 @@ private function replaceColumn($tableName, array $columns, $columnName, Column $ private function getSimpleAlterTableSQL(TableDiff $diff) { // Suppress changes on integer type autoincrement columns. - foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { - if ( - $columnDiff->fromColumn === null || - ! $columnDiff->column->getAutoincrement() || - ! $columnDiff->column->getType() instanceof Types\IntegerType - ) { + foreach ($diff->getModifiedColumns() as $columnDiff) { + $oldColumn = $columnDiff->getOldColumn(); + + if ($oldColumn === null) { + continue; + } + + $newColumn = $columnDiff->getNewColumn(); + + if (! $newColumn->getAutoincrement() || ! $newColumn->getType() instanceof IntegerType) { continue; } - if (! $columnDiff->hasChanged('type') && $columnDiff->hasChanged('unsigned')) { + $oldColumnName = $oldColumn->getName(); + + if (! $columnDiff->hasTypeChanged() && $columnDiff->hasUnsignedChanged()) { unset($diff->changedColumns[$oldColumnName]); continue; } - $fromColumnType = $columnDiff->fromColumn->getType(); + $fromColumnType = $oldColumn->getType(); if (! ($fromColumnType instanceof Types\SmallIntType) && ! ($fromColumnType instanceof Types\BigIntType)) { continue; @@ -1083,27 +1217,27 @@ private function getSimpleAlterTableSQL(TableDiff $diff) } if ( - ! empty($diff->renamedColumns) - || ! empty($diff->addedForeignKeys) - || ! empty($diff->addedIndexes) - || ! empty($diff->changedColumns) - || ! empty($diff->changedForeignKeys) - || ! empty($diff->changedIndexes) - || ! empty($diff->removedColumns) - || ! empty($diff->removedForeignKeys) - || ! empty($diff->removedIndexes) - || ! empty($diff->renamedIndexes) + count($diff->getModifiedColumns()) > 0 + || count($diff->getDroppedColumns()) > 0 + || count($diff->getRenamedColumns()) > 0 + || count($diff->getAddedIndexes()) > 0 + || count($diff->getModifiedIndexes()) > 0 + || count($diff->getDroppedIndexes()) > 0 + || count($diff->getRenamedIndexes()) > 0 + || count($diff->getAddedForeignKeys()) > 0 + || count($diff->getModifiedForeignKeys()) > 0 + || count($diff->getDroppedForeignKeys()) > 0 ) { return false; } - $table = new Table($diff->name); + $table = $diff->getOldTable() ?? $diff->getName($this); $sql = []; $tableSql = []; $columnSql = []; - foreach ($diff->addedColumns as $column) { + foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } @@ -1125,8 +1259,8 @@ private function getSimpleAlterTableSQL(TableDiff $diff) } $definition['name'] = $column->getQuotedName($this); - if ($type instanceof Types\StringType && $definition['length'] === null) { - $definition['length'] = 255; + if ($type instanceof Types\StringType) { + $definition['length'] ??= 255; } $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ADD COLUMN ' @@ -1135,6 +1269,14 @@ private function getSimpleAlterTableSQL(TableDiff $diff) if (! $this->onSchemaAlterTable($diff, $tableSql)) { if ($diff->newName !== false) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5663', + 'Generation of SQL that renames a table using %s is deprecated.' + . ' Use getRenameTableSQL() instead.', + __METHOD__, + ); + $newTable = new Identifier($diff->newName); $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' RENAME TO ' @@ -1145,9 +1287,7 @@ private function getSimpleAlterTableSQL(TableDiff $diff) return array_merge($sql, $tableSql, $columnSql); } - /** - * @return string[] - */ + /** @return string[] */ private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable): array { $columns = []; @@ -1156,8 +1296,8 @@ private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable) $columns[strtolower($columnName)] = $column->getName(); } - foreach ($diff->removedColumns as $columnName => $column) { - $columnName = strtolower($columnName); + foreach ($diff->getDroppedColumns() as $column) { + $columnName = strtolower($column->getName()); if (! isset($columns[$columnName])) { continue; } @@ -1165,19 +1305,22 @@ private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable) unset($columns[$columnName]); } - foreach ($diff->renamedColumns as $oldColumnName => $column) { + foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { $columnName = $column->getName(); $columns[strtolower($oldColumnName)] = $columnName; $columns[strtolower($columnName)] = $columnName; } - foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { - $columnName = $columnDiff->column->getName(); - $columns[strtolower($oldColumnName)] = $columnName; - $columns[strtolower($columnName)] = $columnName; + foreach ($diff->getModifiedColumns() as $columnDiff) { + $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); + + $oldColumnName = $oldColumn->getName(); + $newColumnName = $columnDiff->getNewColumn()->getName(); + $columns[strtolower($oldColumnName)] = $newColumnName; + $columns[strtolower($newColumnName)] = $newColumnName; } - foreach ($diff->addedColumns as $column) { + foreach ($diff->getAddedColumns() as $column) { $columnName = $column->getName(); $columns[strtolower($columnName)] = $columnName; } @@ -1185,16 +1328,14 @@ private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable) return $columns; } - /** - * @return Index[] - */ + /** @return Index[] */ private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable): array { $indexes = $fromTable->getIndexes(); $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable); foreach ($indexes as $key => $index) { - foreach ($diff->renamedIndexes as $oldIndexName => $renamedIndex) { + foreach ($diff->getRenamedIndexes() as $oldIndexName => $renamedIndex) { if (strtolower($key) !== strtolower($oldIndexName)) { continue; } @@ -1228,11 +1369,11 @@ private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable): ar $indexColumns, $index->isUnique(), $index->isPrimary(), - $index->getFlags() + $index->getFlags(), ); } - foreach ($diff->removedIndexes as $index) { + foreach ($diff->getDroppedIndexes() as $index) { $indexName = strtolower($index->getName()); if (strlen($indexName) === 0 || ! isset($indexes[$indexName])) { continue; @@ -1241,7 +1382,13 @@ private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable): ar unset($indexes[$indexName]); } - foreach (array_merge($diff->changedIndexes, $diff->addedIndexes, $diff->renamedIndexes) as $index) { + foreach ( + array_merge( + $diff->getModifiedIndexes(), + $diff->getAddedIndexes(), + $diff->getRenamedIndexes(), + ) as $index + ) { $indexName = strtolower($index->getName()); if (strlen($indexName) > 0) { $indexes[$indexName] = $index; @@ -1253,9 +1400,7 @@ private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable): ar return $indexes; } - /** - * @return ForeignKeyConstraint[] - */ + /** @return ForeignKeyConstraint[] */ private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable): array { $foreignKeys = $fromTable->getForeignKeys(); @@ -1288,11 +1433,11 @@ private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable) $constraint->getForeignTableName(), $constraint->getForeignColumns(), $constraint->getName(), - $constraint->getOptions() + $constraint->getOptions(), ); } - foreach ($diff->removedForeignKeys as $constraint) { + foreach ($diff->getDroppedForeignKeys() as $constraint) { if (! $constraint instanceof ForeignKeyConstraint) { $constraint = new Identifier($constraint); } @@ -1305,7 +1450,7 @@ private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable) unset($foreignKeys[$constraintName]); } - foreach (array_merge($diff->changedForeignKeys, $diff->addedForeignKeys) as $constraint) { + foreach (array_merge($diff->getModifiedForeignKeys(), $diff->getAddedForeignKeys()) as $constraint) { $constraintName = strtolower($constraint->getName()); if (strlen($constraintName) > 0) { $foreignKeys[$constraintName] = $constraint; @@ -1317,9 +1462,7 @@ private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable) return $foreignKeys; } - /** - * @return Index[] - */ + /** @return Index[] */ private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $fromTable): array { $primaryIndex = []; @@ -1334,4 +1477,9 @@ private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $fromTable return $primaryIndex; } + + public function createSchemaManager(Connection $connection): SqliteSchemaManager + { + return new SqliteSchemaManager($connection, $this); + } } diff --git a/doctrine/dbal/src/Platforms/TrimMode.php b/doctrine/dbal/src/Platforms/TrimMode.php index eb499ee46..01356c0d3 100644 --- a/doctrine/dbal/src/Platforms/TrimMode.php +++ b/doctrine/dbal/src/Platforms/TrimMode.php @@ -14,9 +14,7 @@ final class TrimMode public const BOTH = 3; - /** - * @codeCoverageIgnore - */ + /** @codeCoverageIgnore */ private function __construct() { } diff --git a/doctrine/dbal/src/Portability/Connection.php b/doctrine/dbal/src/Portability/Connection.php index a22ab92ff..fc1bdd9ad 100644 --- a/doctrine/dbal/src/Portability/Connection.php +++ b/doctrine/dbal/src/Portability/Connection.php @@ -18,8 +18,7 @@ final class Connection extends AbstractConnectionMiddleware public const PORTABILITY_EMPTY_TO_NULL = 4; public const PORTABILITY_FIX_CASE = 8; - /** @var Converter */ - private $converter; + private Converter $converter; public function __construct(ConnectionInterface $connection, Converter $converter) { @@ -32,7 +31,7 @@ public function prepare(string $sql): DriverStatement { return new Statement( parent::prepare($sql), - $this->converter + $this->converter, ); } @@ -40,7 +39,7 @@ public function query(string $sql): DriverResult { return new Result( parent::query($sql), - $this->converter + $this->converter, ); } } diff --git a/doctrine/dbal/src/Portability/Converter.php b/doctrine/dbal/src/Portability/Converter.php index eb76eb782..d0503977b 100644 --- a/doctrine/dbal/src/Portability/Converter.php +++ b/doctrine/dbal/src/Portability/Converter.php @@ -10,8 +10,14 @@ use function is_string; use function rtrim; +use const CASE_LOWER; +use const CASE_UPPER; + final class Converter { + public const CASE_LOWER = CASE_LOWER; + public const CASE_UPPER = CASE_UPPER; + /** @var callable */ private $convertNumeric; @@ -31,10 +37,12 @@ final class Converter private $convertFirstColumn; /** - * @param bool $convertEmptyStringToNull Whether each empty string should be converted to NULL - * @param bool $rightTrimString Whether each string should right-trimmed - * @param int|null $case Convert the case of the column names - * (one of {@see CASE_LOWER} and {@see CASE_UPPER}) + * @param bool $convertEmptyStringToNull Whether each empty string should + * be converted to NULL + * @param bool $rightTrimString Whether each string should right-trimmed + * @param self::CASE_LOWER|self::CASE_UPPER|null $case Convert the case of the column names + * (one of {@see self::CASE_LOWER} and + * {@see self::CASE_UPPER}) */ public function __construct(bool $convertEmptyStringToNull, bool $rightTrimString, ?int $case) { @@ -182,8 +190,8 @@ private function createConvertValue(bool $convertEmptyStringToNull, bool $rightT /** * Creates a function that will convert each array-row retrieved from the database * - * @param callable|null $function The function that will convert each value - * @param int|null $case Column name case + * @param callable|null $function The function that will convert each value + * @param self::CASE_LOWER|self::CASE_UPPER|null $case Column name case * * @return callable|null The resulting function or NULL if no conversion is needed */ diff --git a/doctrine/dbal/src/Portability/Driver.php b/doctrine/dbal/src/Portability/Driver.php index 6d4484f9f..a5ac9679a 100644 --- a/doctrine/dbal/src/Portability/Driver.php +++ b/doctrine/dbal/src/Portability/Driver.php @@ -7,20 +7,23 @@ use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; use LogicException; use PDO; +use SensitiveParameter; use function method_exists; -use const CASE_LOWER; -use const CASE_UPPER; - final class Driver extends AbstractDriverMiddleware { - /** @var int */ - private $mode; + private int $mode; - /** @var int */ - private $case; + /** @var 0|ColumnCase::LOWER|ColumnCase::UPPER */ + private int $case; + /** + * @param 0|ColumnCase::LOWER|ColumnCase::UPPER $case Determines how the column case will be treated. + * 0: The case will be left as is in the database. + * {@see ColumnCase::LOWER}: The case will be lowercased. + * {@see ColumnCase::UPPER}: The case will be uppercased. + */ public function __construct(DriverInterface $driver, int $mode, int $case) { parent::__construct($driver); @@ -32,13 +35,15 @@ public function __construct(DriverInterface $driver, int $mode, int $case) /** * {@inheritDoc} */ - public function connect(array $params) - { + public function connect( + #[SensitiveParameter] + array $params + ) { $connection = parent::connect($params); $portability = (new OptimizeFlags())( $this->getDatabasePlatform(), - $this->mode + $this->mode, ); $case = null; @@ -54,9 +59,12 @@ public function connect(array $params) if ($nativeConnection instanceof PDO) { $portability &= ~Connection::PORTABILITY_FIX_CASE; - $nativeConnection->setAttribute(PDO::ATTR_CASE, $this->case); + $nativeConnection->setAttribute( + PDO::ATTR_CASE, + $this->case === ColumnCase::LOWER ? PDO::CASE_LOWER : PDO::CASE_UPPER, + ); } else { - $case = $this->case === ColumnCase::LOWER ? CASE_LOWER : CASE_UPPER; + $case = $this->case === ColumnCase::LOWER ? Converter::CASE_LOWER : Converter::CASE_UPPER; } } @@ -69,7 +77,7 @@ public function connect(array $params) return new Connection( $connection, - new Converter($convertEmptyStringToNull, $rightTrimString, $case) + new Converter($convertEmptyStringToNull, $rightTrimString, $case), ); } } diff --git a/doctrine/dbal/src/Portability/Middleware.php b/doctrine/dbal/src/Portability/Middleware.php index b00147062..fae2c5567 100644 --- a/doctrine/dbal/src/Portability/Middleware.php +++ b/doctrine/dbal/src/Portability/Middleware.php @@ -4,17 +4,23 @@ namespace Doctrine\DBAL\Portability; +use Doctrine\DBAL\ColumnCase; use Doctrine\DBAL\Driver as DriverInterface; use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface; final class Middleware implements MiddlewareInterface { - /** @var int */ - private $mode; + private int $mode; - /** @var int */ - private $case; + /** @var 0|ColumnCase::LOWER|ColumnCase::UPPER */ + private int $case; + /** + * @param 0|ColumnCase::LOWER|ColumnCase::UPPER $case Determines how the column case will be treated. + * 0: The case will be left as is in the database. + * {@see ColumnCase::LOWER}: The case will be lowercased. + * {@see ColumnCase::UPPER}: The case will be uppercased. + */ public function __construct(int $mode, int $case) { $this->mode = $mode; diff --git a/doctrine/dbal/src/Portability/OptimizeFlags.php b/doctrine/dbal/src/Portability/OptimizeFlags.php index 13367c92a..884a936c7 100644 --- a/doctrine/dbal/src/Portability/OptimizeFlags.php +++ b/doctrine/dbal/src/Portability/OptimizeFlags.php @@ -17,9 +17,9 @@ final class OptimizeFlags * Platform-specific portability flags that need to be excluded from the user-provided mode * since the platform already operates in this mode to avoid unnecessary conversion overhead. * - * @var array + * @var array */ - private static $platforms = [ + private static array $platforms = [ DB2Platform::class => 0, OraclePlatform::class => Connection::PORTABILITY_EMPTY_TO_NULL, PostgreSQLPlatform::class => 0, diff --git a/doctrine/dbal/src/Portability/Result.php b/doctrine/dbal/src/Portability/Result.php index d8440b663..da1eca985 100644 --- a/doctrine/dbal/src/Portability/Result.php +++ b/doctrine/dbal/src/Portability/Result.php @@ -9,12 +9,9 @@ final class Result extends AbstractResultMiddleware { - /** @var Converter */ - private $converter; + private Converter $converter; - /** - * @internal The result can be only instantiated by the portability connection or statement. - */ + /** @internal The result can be only instantiated by the portability connection or statement. */ public function __construct(ResultInterface $result, Converter $converter) { parent::__construct($result); @@ -28,7 +25,7 @@ public function __construct(ResultInterface $result, Converter $converter) public function fetchNumeric() { return $this->converter->convertNumeric( - parent::fetchNumeric() + parent::fetchNumeric(), ); } @@ -38,7 +35,7 @@ public function fetchNumeric() public function fetchAssociative() { return $this->converter->convertAssociative( - parent::fetchAssociative() + parent::fetchAssociative(), ); } @@ -48,7 +45,7 @@ public function fetchAssociative() public function fetchOne() { return $this->converter->convertOne( - parent::fetchOne() + parent::fetchOne(), ); } @@ -58,7 +55,7 @@ public function fetchOne() public function fetchAllNumeric(): array { return $this->converter->convertAllNumeric( - parent::fetchAllNumeric() + parent::fetchAllNumeric(), ); } @@ -68,7 +65,7 @@ public function fetchAllNumeric(): array public function fetchAllAssociative(): array { return $this->converter->convertAllAssociative( - parent::fetchAllAssociative() + parent::fetchAllAssociative(), ); } @@ -78,7 +75,7 @@ public function fetchAllAssociative(): array public function fetchFirstColumn(): array { return $this->converter->convertFirstColumn( - parent::fetchFirstColumn() + parent::fetchFirstColumn(), ); } } diff --git a/doctrine/dbal/src/Portability/Statement.php b/doctrine/dbal/src/Portability/Statement.php index b104cf78a..8fcd79d4a 100644 --- a/doctrine/dbal/src/Portability/Statement.php +++ b/doctrine/dbal/src/Portability/Statement.php @@ -11,8 +11,7 @@ */ final class Statement extends AbstractStatementMiddleware { - /** @var Converter */ - private $converter; + private Converter $converter; /** * Wraps Statement and applies portability measures. @@ -25,13 +24,13 @@ public function __construct(DriverStatement $stmt, Converter $converter) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function execute($params = null): ResultInterface { return new Result( parent::execute($params), - $this->converter + $this->converter, ); } } diff --git a/doctrine/dbal/src/Query.php b/doctrine/dbal/src/Query.php index ea6024cd8..bfc9b14e2 100644 --- a/doctrine/dbal/src/Query.php +++ b/doctrine/dbal/src/Query.php @@ -15,24 +15,22 @@ final class Query { /** * The SQL query. - * - * @var string */ - private $sql; + private string $sql; /** * The parameters bound to the query. * * @var array */ - private $params; + private array $params; /** * The types of the parameters bound to the query. * * @var array */ - private $types; + private array $types; /** * @param array $params @@ -52,17 +50,13 @@ public function getSQL(): string return $this->sql; } - /** - * @return array - */ + /** @return array */ public function getParams(): array { return $this->params; } - /** - * @return array - */ + /** @return array */ public function getTypes(): array { return $this->types; diff --git a/doctrine/dbal/src/Query/Expression/CompositeExpression.php b/doctrine/dbal/src/Query/Expression/CompositeExpression.php index 85de9ae93..de1d929a0 100644 --- a/doctrine/dbal/src/Query/Expression/CompositeExpression.php +++ b/doctrine/dbal/src/Query/Expression/CompositeExpression.php @@ -37,7 +37,7 @@ class CompositeExpression implements Countable * * @var self[]|string[] */ - private $parts = []; + private array $parts = []; /** * @internal Use the and() / or() factory methods. @@ -54,7 +54,7 @@ public function __construct($type, array $parts = []) Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3864', - 'Do not use CompositeExpression constructor directly, use static and() and or() factory methods.' + 'Do not use CompositeExpression constructor directly, use static and() and or() factory methods.', ); } @@ -90,7 +90,7 @@ public function addMultiple(array $parts = []) Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3844', - 'CompositeExpression::addMultiple() is deprecated, use CompositeExpression::with() instead.' + 'CompositeExpression::addMultiple() is deprecated, use CompositeExpression::with() instead.', ); foreach ($parts as $part) { @@ -114,7 +114,7 @@ public function add($part) Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3844', - 'CompositeExpression::add() is deprecated, use CompositeExpression::with() instead.' + 'CompositeExpression::add() is deprecated, use CompositeExpression::with() instead.', ); if ($part === null) { diff --git a/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php b/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php index 1ce11b7b9..d532b2894 100644 --- a/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php +++ b/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php @@ -25,10 +25,8 @@ class ExpressionBuilder /** * The DBAL Connection. - * - * @var Connection */ - private $connection; + private Connection $connection; /** * Initializes a new ExpressionBuilder. @@ -75,7 +73,7 @@ public function andX($x = null) Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3851', - 'ExpressionBuilder::andX() is deprecated, use ExpressionBuilder::and() instead.' + 'ExpressionBuilder::andX() is deprecated, use ExpressionBuilder::and() instead.', ); return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); @@ -94,7 +92,7 @@ public function orX($x = null) Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3851', - 'ExpressionBuilder::orX() is deprecated, use ExpressionBuilder::or() instead.' + 'ExpressionBuilder::orX() is deprecated, use ExpressionBuilder::or() instead.', ); return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); diff --git a/doctrine/dbal/src/Query/QueryBuilder.php b/doctrine/dbal/src/Query/QueryBuilder.php index d256b74a6..ba76fdf93 100644 --- a/doctrine/dbal/src/Query/QueryBuilder.php +++ b/doctrine/dbal/src/Query/QueryBuilder.php @@ -2,6 +2,7 @@ namespace Doctrine\DBAL\Query; +use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; use Doctrine\DBAL\ParameterType; @@ -37,26 +38,28 @@ */ class QueryBuilder { - /* - * The query types. - */ + /** @deprecated */ public const SELECT = 0; + + /** @deprecated */ public const DELETE = 1; + + /** @deprecated */ public const UPDATE = 2; + + /** @deprecated */ public const INSERT = 3; - /* - * The builder states. - */ + /** @deprecated */ public const STATE_DIRTY = 0; + + /** @deprecated */ public const STATE_CLEAN = 1; /** * The DBAL Connection. - * - * @var Connection */ - private $connection; + private Connection $connection; /* * The default values of SQL parts collection @@ -79,14 +82,12 @@ class QueryBuilder * * @var mixed[] */ - private $sqlParts = self::SQL_PARTS_DEFAULTS; + private array $sqlParts = self::SQL_PARTS_DEFAULTS; /** * The complete SQL string for this query. - * - * @var string|null */ - private $sql; + private ?string $sql = null; /** * The query parameters. @@ -100,42 +101,41 @@ class QueryBuilder * * @var array|array */ - private $paramTypes = []; + private array $paramTypes = []; /** * The type of query this is. Can be select, update or delete. * - * @var int + * @psalm-var self::SELECT|self::DELETE|self::UPDATE|self::INSERT */ - private $type = self::SELECT; + private int $type = self::SELECT; /** * The state of the query object. Can be dirty or clean. * - * @var int + * @psalm-var self::STATE_* */ - private $state = self::STATE_CLEAN; + private int $state = self::STATE_CLEAN; /** * The index of the first result to retrieve. - * - * @var int */ - private $firstResult = 0; + private int $firstResult = 0; /** * The maximum number of results to retrieve or NULL to retrieve all results. - * - * @var int|null */ - private $maxResults; + private ?int $maxResults = null; /** * The counter of bound parameters used with {@see bindValue). - * - * @var int */ - private $boundCounter = 0; + private int $boundCounter = 0; + + /** + * The query cache profile used for caching results. + */ + private ?QueryCacheProfile $resultCacheProfile = null; /** * Initializes a new QueryBuilder. @@ -171,30 +171,57 @@ public function expr() /** * Gets the type of the currently built query. * + * @deprecated If necessary, track the type of the query being built outside of the builder. + * * @return int */ public function getType() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5551', + 'Relying on the type of the query being built is deprecated.' + . ' If necessary, track the type of the query being built outside of the builder.', + ); + return $this->type; } /** * Gets the associated DBAL Connection for this query builder. * + * @deprecated Use the connection used to instantiate the builder instead. + * * @return Connection */ public function getConnection() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5780', + '%s is deprecated. Use the connection used to instantiate the builder instead.', + __METHOD__, + ); + return $this->connection; } /** * Gets the state of this query builder instance. * + * @deprecated The builder state is an internal concern. + * * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + * @psalm-return self::STATE_* */ public function getState() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5551', + 'Relying on the query builder state is deprecated as it is an internal concern.', + ); + return $this->state; } @@ -208,7 +235,7 @@ public function getState() */ public function fetchAssociative() { - return $this->connection->fetchAssociative($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAssociative(); } /** @@ -221,7 +248,7 @@ public function fetchAssociative() */ public function fetchNumeric() { - return $this->connection->fetchNumeric($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchNumeric(); } /** @@ -234,7 +261,7 @@ public function fetchNumeric() */ public function fetchOne() { - return $this->connection->fetchOne($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchOne(); } /** @@ -246,7 +273,7 @@ public function fetchOne() */ public function fetchAllNumeric(): array { - return $this->connection->fetchAllNumeric($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAllNumeric(); } /** @@ -258,7 +285,7 @@ public function fetchAllNumeric(): array */ public function fetchAllAssociative(): array { - return $this->connection->fetchAllAssociative($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAllAssociative(); } /** @@ -271,7 +298,7 @@ public function fetchAllAssociative(): array */ public function fetchAllKeyValue(): array { - return $this->connection->fetchAllKeyValue($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAllKeyValue(); } /** @@ -285,7 +312,7 @@ public function fetchAllKeyValue(): array */ public function fetchAllAssociativeIndexed(): array { - return $this->connection->fetchAllAssociativeIndexed($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAllAssociativeIndexed(); } /** @@ -297,7 +324,7 @@ public function fetchAllAssociativeIndexed(): array */ public function fetchFirstColumn(): array { - return $this->connection->fetchFirstColumn($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchFirstColumn(); } /** @@ -307,7 +334,12 @@ public function fetchFirstColumn(): array */ public function executeQuery(): Result { - return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); + return $this->connection->executeQuery( + $this->getSQL(), + $this->params, + $this->paramTypes, + $this->resultCacheProfile, + ); } /** @@ -339,16 +371,16 @@ public function execute() Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4578', - 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeQuery() for SQL queries instead.' + 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeQuery() for SQL queries instead.', ); - return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery(); } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4578', - 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeStatement() for SQL statements instead.' + 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeStatement() for SQL statements instead.', ); return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); @@ -386,7 +418,6 @@ public function getSQL() break; case self::SELECT: - default: $sql = $this->getSQLForSelect(); break; } @@ -414,10 +445,17 @@ public function getSQL() * * @return $this This QueryBuilder instance. */ - public function setParameter($key, $value, $type = null) + public function setParameter($key, $value, $type = ParameterType::STRING) { if ($type !== null) { $this->paramTypes[$key] = $type; + } else { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5550', + 'Using NULL as prepared statement parameter type is deprecated.' + . 'Omit or use Parameter::STRING instead', + ); } $this->params[$key] = $value; @@ -490,11 +528,11 @@ public function getParameterTypes() * * @param int|string $key The key of the bound parameter type * - * @return int|string|Type|null The value of the bound parameter type + * @return int|string|Type The value of the bound parameter type */ public function getParameterType($key) { - return $this->paramTypes[$key] ?? null; + return $this->paramTypes[$key] ?? ParameterType::STRING; } /** @@ -629,7 +667,7 @@ public function select($select = null/*, string ...$selects*/) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::select() is deprecated, ' . - 'pass each value as an individual variadic argument instead.' + 'pass each value as an individual variadic argument instead.', ); } @@ -688,7 +726,7 @@ public function addSelect($select = null/*, string ...$selects*/) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::addSelect() is deprecated, ' . - 'pass each value as an individual variadic argument instead.' + 'pass each value as an individual variadic argument instead.', ); } @@ -949,7 +987,7 @@ public function set($key, $value) * ->from('counters', 'c') * ->where('c.id = ?'); * - * // You can optionally programatically build and/or expressions + * // You can optionally programmatically build and/or expressions * $qb = $conn->createQueryBuilder(); * * $or = $qb->expr()->orx(); @@ -1069,7 +1107,7 @@ public function groupBy($groupBy/*, string ...$groupBys*/) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::groupBy() is deprecated, ' . - 'pass each value as an individual variadic argument instead.' + 'pass each value as an individual variadic argument instead.', ); } @@ -1107,7 +1145,7 @@ public function addGroupBy($groupBy/*, string ...$groupBys*/) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::addGroupBy() is deprecated, ' . - 'pass each value as an individual variadic argument instead.' + 'pass each value as an individual variadic argument instead.', ); } @@ -1287,9 +1325,7 @@ public function getQueryParts() */ public function resetQueryParts($queryPartNames = null) { - if ($queryPartNames === null) { - $queryPartNames = array_keys($this->sqlParts); - } + $queryPartNames ??= array_keys($this->sqlParts); foreach ($queryPartNames as $queryPartName) { $this->resetQueryPart($queryPartName); @@ -1314,9 +1350,7 @@ public function resetQueryPart($queryPartName) return $this; } - /** - * @throws QueryException - */ + /** @throws QueryException */ private function getSQLForSelect(): string { $query = 'SELECT ' . ($this->sqlParts['distinct'] ? 'DISTINCT ' : '') . @@ -1332,7 +1366,7 @@ private function getSQLForSelect(): string return $this->connection->getDatabasePlatform()->modifyLimitQuery( $query, $this->maxResults, - $this->firstResult + $this->firstResult, ); } @@ -1515,7 +1549,7 @@ private function getSQLForJoins($fromAlias, array &$knownAliases): string if (isset($this->sqlParts['join'][$fromAlias])) { foreach ($this->sqlParts['join'][$fromAlias] as $join) { if (array_key_exists($join['joinAlias'], $knownAliases)) { - throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases)); + throw QueryException::nonUniqueAlias((string) $join['joinAlias'], array_keys($knownAliases)); } $sql .= ' ' . strtoupper($join['joinType']) @@ -1564,4 +1598,29 @@ public function __clone() $this->params[$name] = clone $param; } } + + /** + * Enables caching of the results of this query, for given amount of seconds + * and optionally specified witch key to use for the cache entry. + * + * @return $this + */ + public function enableResultCache(QueryCacheProfile $cacheProfile): self + { + $this->resultCacheProfile = $cacheProfile; + + return $this; + } + + /** + * Disables caching of the results of this query. + * + * @return $this + */ + public function disableResultCache(): self + { + $this->resultCacheProfile = null; + + return $this; + } } diff --git a/doctrine/dbal/src/Query/QueryException.php b/doctrine/dbal/src/Query/QueryException.php index 58e941e98..90d1f47d9 100644 --- a/doctrine/dbal/src/Query/QueryException.php +++ b/doctrine/dbal/src/Query/QueryException.php @@ -6,9 +6,7 @@ use function implode; -/** - * @psalm-immutable - */ +/** @psalm-immutable */ class QueryException extends Exception { /** diff --git a/doctrine/dbal/src/Result.php b/doctrine/dbal/src/Result.php index dce821c6e..fdc7daa94 100644 --- a/doctrine/dbal/src/Result.php +++ b/doctrine/dbal/src/Result.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\Driver\Exception as DriverException; use Doctrine\DBAL\Driver\Result as DriverResult; use Doctrine\DBAL\Exception\NoKeyValue; +use Doctrine\Deprecations\Deprecation; use LogicException; use Traversable; @@ -15,15 +16,10 @@ class Result { - /** @var DriverResult */ - private $result; + private DriverResult $result; + private Connection $connection; - /** @var Connection */ - private $connection; - - /** - * @internal The result can be only instantiated by {@see Connection} or {@see Statement}. - */ + /** @internal The result can be only instantiated by {@see Connection} or {@see Statement}. */ public function __construct(DriverResult $result, Connection $connection) { $this->result = $result; @@ -228,9 +224,7 @@ public function iterateColumn(): Traversable } } - /** - * @throws Exception - */ + /** @throws Exception */ public function rowCount(): int { try { @@ -240,9 +234,7 @@ public function rowCount(): int } } - /** - * @throws Exception - */ + /** @throws Exception */ public function columnCount(): int { try { @@ -257,9 +249,7 @@ public function free(): void $this->result->free(); } - /** - * @throws Exception - */ + /** @throws Exception */ private function ensureHasKeyValue(): void { $columnCount = $this->columnCount(); @@ -272,7 +262,9 @@ private function ensureHasKeyValue(): void /** * BC layer for a wide-spread use-case of old DBAL APIs * - * @deprecated This API is deprecated and will be removed after 2022 + * @deprecated Use {@see fetchNumeric()}, {@see fetchAssociative()} or {@see fetchOne()} instead. + * + * @psalm-param FetchMode::* $mode * * @return mixed * @@ -280,6 +272,13 @@ private function ensureHasKeyValue(): void */ public function fetch(int $mode = FetchMode::ASSOCIATIVE) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4007', + '%s is deprecated, please use fetchNumeric(), fetchAssociative() or fetchOne() instead.', + __METHOD__, + ); + if (func_num_args() > 1) { throw new LogicException('Only invocations with one argument are still supported by this legacy API.'); } @@ -302,7 +301,9 @@ public function fetch(int $mode = FetchMode::ASSOCIATIVE) /** * BC layer for a wide-spread use-case of old DBAL APIs * - * @deprecated This API is deprecated and will be removed after 2022 + * @deprecated Use {@see fetchAllNumeric()}, {@see fetchAllAssociative()} or {@see fetchFirstColumn()} instead. + * + * @psalm-param FetchMode::* $mode * * @return list * @@ -310,6 +311,13 @@ public function fetch(int $mode = FetchMode::ASSOCIATIVE) */ public function fetchAll(int $mode = FetchMode::ASSOCIATIVE): array { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4007', + '%s is deprecated, please use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.', + __METHOD__, + ); + if (func_num_args() > 1) { throw new LogicException('Only invocations with one argument are still supported by this legacy API.'); } diff --git a/doctrine/dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php b/doctrine/dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php new file mode 100644 index 000000000..2e392e661 --- /dev/null +++ b/doctrine/dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php @@ -0,0 +1,85 @@ +platform = $platform; + } + + /** + * @return list + * + * @throws Exception + */ + public function buildSQL(Schema $schema): array + { + return array_merge( + $this->buildNamespaceStatements($schema->getNamespaces()), + $this->buildSequenceStatements($schema->getSequences()), + $this->buildTableStatements($schema->getTables()), + ); + } + + /** + * @param list $namespaces + * + * @return list + * + * @throws Exception + */ + private function buildNamespaceStatements(array $namespaces): array + { + $statements = []; + + if ($this->platform->supportsSchemas()) { + foreach ($namespaces as $namespace) { + $statements[] = $this->platform->getCreateSchemaSQL($namespace); + } + } + + return $statements; + } + + /** + * @param list
$tables + * + * @return list + * + * @throws Exception + */ + private function buildTableStatements(array $tables): array + { + return $this->platform->getCreateTablesSQL($tables); + } + + /** + * @param list $sequences + * + * @return list + * + * @throws Exception + */ + private function buildSequenceStatements(array $sequences): array + { + $statements = []; + + foreach ($sequences as $sequence) { + $statements[] = $this->platform->getCreateSequenceSQL($sequence); + } + + return $statements; + } +} diff --git a/doctrine/dbal/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php b/doctrine/dbal/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php new file mode 100644 index 000000000..8de742a31 --- /dev/null +++ b/doctrine/dbal/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php @@ -0,0 +1,62 @@ +platform = $platform; + } + + /** + * @return list + * + * @throws Exception + */ + public function buildSQL(Schema $schema): array + { + return array_merge( + $this->buildSequenceStatements($schema->getSequences()), + $this->buildTableStatements($schema->getTables()), + ); + } + + /** + * @param list
$tables + * + * @return list + */ + private function buildTableStatements(array $tables): array + { + return $this->platform->getDropTablesSQL($tables); + } + + /** + * @param list $sequences + * + * @return list + * + * @throws Exception + */ + private function buildSequenceStatements(array $sequences): array + { + $statements = []; + + foreach ($sequences as $sequence) { + $statements[] = $this->platform->getDropSequenceSQL($sequence); + } + + return $statements; + } +} diff --git a/doctrine/dbal/src/SQL/Parser.php b/doctrine/dbal/src/SQL/Parser.php index b3747e0c0..ae0d4428f 100644 --- a/doctrine/dbal/src/SQL/Parser.php +++ b/doctrine/dbal/src/SQL/Parser.php @@ -44,8 +44,7 @@ final class Parser private const SPECIAL = '[' . self::SPECIAL_CHARS . ']'; private const OTHER = '[^' . self::SPECIAL_CHARS . ']+'; - /** @var string */ - private $sqlPattern; + private string $sqlPattern; public function __construct(bool $mySQLStringEscaping) { diff --git a/doctrine/dbal/src/Schema/AbstractAsset.php b/doctrine/dbal/src/Schema/AbstractAsset.php index 69e337602..ab8fdec03 100644 --- a/doctrine/dbal/src/Schema/AbstractAsset.php +++ b/doctrine/dbal/src/Schema/AbstractAsset.php @@ -123,7 +123,7 @@ public function getFullQualifiedName($defaultNamespaceName) 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4814', 'AbstractAsset::getFullQualifiedName() is deprecated.' - . ' Use AbstractAsset::getNamespaceName() and ::getName() instead.' + . ' Use AbstractAsset::getNamespaceName() and ::getName() instead.', ); $name = $this->getName(); diff --git a/doctrine/dbal/src/Schema/AbstractSchemaManager.php b/doctrine/dbal/src/Schema/AbstractSchemaManager.php index e44580b54..7e1fd93ff 100644 --- a/doctrine/dbal/src/Schema/AbstractSchemaManager.php +++ b/doctrine/dbal/src/Schema/AbstractSchemaManager.php @@ -7,7 +7,9 @@ use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; use Doctrine\DBAL\Events; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Exception\DatabaseRequired; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Result; use Doctrine\Deprecations\Deprecation; use Throwable; @@ -47,9 +49,7 @@ abstract class AbstractSchemaManager */ protected $_platform; - /** - * @param T $platform - */ + /** @param T $platform */ public function __construct(Connection $connection, AbstractPlatform $platform) { $this->_conn = $connection; @@ -59,10 +59,19 @@ public function __construct(Connection $connection, AbstractPlatform $platform) /** * Returns the associated platform. * + * @deprecated Use {@link Connection::getDatabasePlatform()} instead. + * * @return T */ public function getDatabasePlatform() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5387', + 'AbstractSchemaManager::getDatabasePlatform() is deprecated.' + . ' Use Connection::getDatabasePlatform() instead.', + ); + return $this->_platform; } @@ -85,7 +94,7 @@ public function tryMethod() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', - 'AbstractSchemaManager::tryMethod() is deprecated.' + 'AbstractSchemaManager::tryMethod() is deprecated.', ); $args = func_get_args(); @@ -134,7 +143,7 @@ public function listNamespaceNames() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'AbstractSchemaManager::listNamespaceNames() is deprecated,' - . ' use AbstractSchemaManager::listSchemaNames() instead.' + . ' use AbstractSchemaManager::listSchemaNames() instead.', ); $sql = $this->_platform->getListNamespacesSQL(); @@ -168,7 +177,13 @@ public function listSchemaNames(): array public function listSequences($database = null) { if ($database === null) { - $database = $this->_conn->getDatabase(); + $database = $this->getDatabase(__METHOD__); + } else { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5284', + 'Passing $database to AbstractSchemaManager::listSequences() is deprecated.', + ); } $sql = $this->_platform->getListSequencesSQL($database); @@ -198,7 +213,13 @@ public function listSequences($database = null) public function listTableColumns($table, $database = null) { if ($database === null) { - $database = $this->_conn->getDatabase(); + $database = $this->getDatabase(__METHOD__); + } else { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5284', + 'Passing $database to AbstractSchemaManager::listTableColumns() is deprecated.', + ); } $sql = $this->_platform->getListTableColumnsSQL($table, $database); @@ -208,6 +229,34 @@ public function listTableColumns($table, $database = null) return $this->_getPortableTableColumnList($table, $database, $tableColumns); } + /** + * @param string $table + * @param string|null $database + * + * @return Column[] + * + * @throws Exception + */ + protected function doListTableColumns($table, $database = null): array + { + if ($database === null) { + $database = $this->getDatabase(__METHOD__); + } else { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5284', + 'Passing $database to AbstractSchemaManager::doListTableColumns() is deprecated.', + ); + } + + return $this->_getPortableTableColumnList( + $table, + $database, + $this->selectTableColumns($database, $this->normalizeName($table)) + ->fetchAllAssociative(), + ); + } + /** * Lists the indexes for a given table returning an array of Index instances. * @@ -228,6 +277,27 @@ public function listTableIndexes($table) return $this->_getPortableTableIndexesList($tableIndexes, $table); } + /** + * @param string $table + * + * @return Index[] + * + * @throws Exception + */ + protected function doListTableIndexes($table): array + { + $database = $this->getDatabase(__METHOD__); + $table = $this->normalizeName($table); + + return $this->_getPortableTableIndexesList( + $this->selectIndexColumns( + $database, + $table, + )->fetchAllAssociative(), + $table, + ); + } + /** * Returns true if all the given tables exist. * @@ -246,7 +316,7 @@ public function tablesExist($names) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3580', 'The usage of a string $tableNames in AbstractSchemaManager::tablesExist() is deprecated. ' . - 'Pass a one-element array instead.' + 'Pass a one-element array instead.', ); } @@ -272,6 +342,23 @@ public function listTableNames() return $this->filterAssetNames($tableNames); } + /** + * @return list + * + * @throws Exception + */ + protected function doListTableNames(): array + { + $database = $this->getDatabase(__METHOD__); + + return $this->filterAssetNames( + $this->_getPortableTablesList( + $this->selectTableNames($database) + ->fetchAllAssociative(), + ), + ); + } + /** * Filters asset names if they are configured to return only a subset of all * the found elements. @@ -293,7 +380,7 @@ protected function filterAssetNames($assetNames) /** * Lists the tables for this connection. * - * @return Table[] + * @return list
* * @throws Exception */ @@ -303,13 +390,50 @@ public function listTables() $tables = []; foreach ($tableNames as $tableName) { - $tables[] = $this->listTableDetails($tableName); + $tables[] = $this->introspectTable($tableName); + } + + return $tables; + } + + /** + * @return list
+ * + * @throws Exception + */ + protected function doListTables(): array + { + $database = $this->getDatabase(__METHOD__); + + $tableColumnsByTable = $this->fetchTableColumnsByTable($database); + $indexColumnsByTable = $this->fetchIndexColumnsByTable($database); + $foreignKeyColumnsByTable = $this->fetchForeignKeyColumnsByTable($database); + $tableOptionsByTable = $this->fetchTableOptionsByTable($database); + + $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter(); + $tables = []; + + foreach ($tableColumnsByTable as $tableName => $tableColumns) { + if ($filter !== null && ! $filter($tableName)) { + continue; + } + + $tables[] = new Table( + $tableName, + $this->_getPortableTableColumnList($tableName, $database, $tableColumns), + $this->_getPortableTableIndexesList($indexColumnsByTable[$tableName] ?? [], $tableName), + [], + $this->_getPortableTableForeignKeysList($foreignKeyColumnsByTable[$tableName] ?? []), + $tableOptionsByTable[$tableName] ?? [], + ); } return $tables; } /** + * @deprecated Use {@see introspectTable()} instead. + * * @param string $name * * @return Table @@ -318,6 +442,13 @@ public function listTables() */ public function listTableDetails($name) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5595', + '%s is deprecated. Use introspectTable() instead.', + __METHOD__, + ); + $columns = $this->listTableColumns($name); $foreignKeys = []; @@ -330,6 +461,165 @@ public function listTableDetails($name) return new Table($name, $columns, $indexes, [], $foreignKeys); } + /** + * @param string $name + * + * @throws Exception + */ + protected function doListTableDetails($name): Table + { + $database = $this->getDatabase(__METHOD__); + + $normalizedName = $this->normalizeName($name); + + $tableOptionsByTable = $this->fetchTableOptionsByTable($database, $normalizedName); + + if ($this->_platform->supportsForeignKeyConstraints()) { + $foreignKeys = $this->listTableForeignKeys($name); + } else { + $foreignKeys = []; + } + + return new Table( + $name, + $this->listTableColumns($name, $database), + $this->listTableIndexes($name), + [], + $foreignKeys, + $tableOptionsByTable[$normalizedName] ?? [], + ); + } + + /** + * An extension point for those platforms where case sensitivity of the object name depends on whether it's quoted. + * + * Such platforms should convert a possibly quoted name into a value of the corresponding case. + */ + protected function normalizeName(string $name): string + { + $identifier = new Identifier($name); + + return $identifier->getName(); + } + + /** + * Selects names of tables in the specified database. + * + * @throws Exception + * + * @abstract + */ + protected function selectTableNames(string $databaseName): Result + { + throw Exception::notSupported(__METHOD__); + } + + /** + * Selects definitions of table columns in the specified database. If the table name is specified, narrows down + * the selection to this table. + * + * @throws Exception + * + * @abstract + */ + protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result + { + throw Exception::notSupported(__METHOD__); + } + + /** + * Selects definitions of index columns in the specified database. If the table name is specified, narrows down + * the selection to this table. + * + * @throws Exception + */ + protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result + { + throw Exception::notSupported(__METHOD__); + } + + /** + * Selects definitions of foreign key columns in the specified database. If the table name is specified, + * narrows down the selection to this table. + * + * @throws Exception + */ + protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result + { + throw Exception::notSupported(__METHOD__); + } + + /** + * Fetches definitions of table columns in the specified database and returns them grouped by table name. + * + * @return array>> + * + * @throws Exception + */ + protected function fetchTableColumnsByTable(string $databaseName): array + { + return $this->fetchAllAssociativeGrouped($this->selectTableColumns($databaseName)); + } + + /** + * Fetches definitions of index columns in the specified database and returns them grouped by table name. + * + * @return array>> + * + * @throws Exception + */ + protected function fetchIndexColumnsByTable(string $databaseName): array + { + return $this->fetchAllAssociativeGrouped($this->selectIndexColumns($databaseName)); + } + + /** + * Fetches definitions of foreign key columns in the specified database and returns them grouped by table name. + * + * @return array>> + * + * @throws Exception + */ + protected function fetchForeignKeyColumnsByTable(string $databaseName): array + { + if (! $this->_platform->supportsForeignKeyConstraints()) { + return []; + } + + return $this->fetchAllAssociativeGrouped( + $this->selectForeignKeyColumns($databaseName), + ); + } + + /** + * Fetches table options for the tables in the specified database and returns them grouped by table name. + * If the table name is specified, narrows down the selection to this table. + * + * @return array> + * + * @throws Exception + */ + protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array + { + throw Exception::notSupported(__METHOD__); + } + + /** + * Introspects the table with the given name. + * + * @throws Exception + */ + public function introspectTable(string $name): Table + { + $table = $this->listTableDetails($name); + + if ($table->getColumns() === []) { + throw SchemaException::tableDoesNotExist($name); + } + + return $table; + } + /** * Lists the views this connection has. * @@ -359,7 +649,13 @@ public function listViews() public function listTableForeignKeys($table, $database = null) { if ($database === null) { - $database = $this->_conn->getDatabase(); + $database = $this->getDatabase(__METHOD__); + } else { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5284', + 'Passing $database to AbstractSchemaManager::listTableForeignKeys() is deprecated.', + ); } $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); @@ -368,6 +664,34 @@ public function listTableForeignKeys($table, $database = null) return $this->_getPortableTableForeignKeysList($tableForeignKeys); } + /** + * @param string $table + * @param string|null $database + * + * @return ForeignKeyConstraint[] + * + * @throws Exception + */ + protected function doListTableForeignKeys($table, $database = null): array + { + if ($database === null) { + $database = $this->getDatabase(__METHOD__); + } else { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5284', + 'Passing $database to AbstractSchemaManager::listTableForeignKeys() is deprecated.', + ); + } + + return $this->_getPortableTableForeignKeysList( + $this->selectForeignKeyColumns( + $database, + $this->normalizeName($table), + )->fetchAllAssociative(), + ); + } + /* drop*() Methods */ /** @@ -383,7 +707,9 @@ public function listTableForeignKeys($table, $database = null) */ public function dropDatabase($database) { - $this->_execSql($this->_platform->getDropDatabaseSQL($database)); + $this->_conn->executeStatement( + $this->_platform->getDropDatabaseSQL($database), + ); } /** @@ -393,7 +719,9 @@ public function dropDatabase($database) */ public function dropSchema(string $schemaName): void { - $this->_execSql($this->_platform->getDropSchemaSQL($schemaName)); + $this->_conn->executeStatement( + $this->_platform->getDropSchemaSQL($schemaName), + ); } /** @@ -407,7 +735,9 @@ public function dropSchema(string $schemaName): void */ public function dropTable($name) { - $this->_execSql($this->_platform->getDropTableSQL($name)); + $this->_conn->executeStatement( + $this->_platform->getDropTableSQL($name), + ); } /** @@ -423,10 +753,30 @@ public function dropTable($name) public function dropIndex($index, $table) { if ($index instanceof Index) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + $index = $index->getQuotedName($this->_platform); } - $this->_execSql($this->_platform->getDropIndexSQL($index, $table)); + if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as an Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + + $table = $table->getQuotedName($this->_platform); + } + + $this->_conn->executeStatement( + $this->_platform->getDropIndexSQL($index, $table), + ); } /** @@ -442,7 +792,21 @@ public function dropIndex($index, $table) */ public function dropConstraint(Constraint $constraint, $table) { - $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table)); + if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + + $table = $table->getQuotedName($this->_platform); + } + + $this->_conn->executeStatement($this->_platform->getDropConstraintSQL( + $constraint->getQuotedName($this->_platform), + $table, + )); } /** @@ -457,7 +821,32 @@ public function dropConstraint(Constraint $constraint, $table) */ public function dropForeignKey($foreignKey, $table) { - $this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table)); + if ($foreignKey instanceof ForeignKeyConstraint) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $foreignKey as a ForeignKeyConstraint object to %s is deprecated.' + . ' Pass it as a quoted name instead.', + __METHOD__, + ); + + $foreignKey = $foreignKey->getQuotedName($this->_platform); + } + + if ($table instanceof Table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4798', + 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', + __METHOD__, + ); + + $table = $table->getQuotedName($this->_platform); + } + + $this->_conn->executeStatement( + $this->_platform->getDropForeignKeySQL($foreignKey, $table), + ); } /** @@ -471,7 +860,9 @@ public function dropForeignKey($foreignKey, $table) */ public function dropSequence($name) { - $this->_execSql($this->_platform->getDropSequenceSQL($name)); + $this->_conn->executeStatement( + $this->_platform->getDropSequenceSQL($name), + ); } /** @@ -481,7 +872,9 @@ public function dropSequence($name) */ public function dropUniqueConstraint(string $name, string $tableName): void { - $this->_execSql($this->_platform->getDropUniqueConstraintSQL($name, $tableName)); + $this->_conn->executeStatement( + $this->_platform->getDropUniqueConstraintSQL($name, $tableName), + ); } /** @@ -495,11 +888,19 @@ public function dropUniqueConstraint(string $name, string $tableName): void */ public function dropView($name) { - $this->_execSql($this->_platform->getDropViewSQL($name)); + $this->_conn->executeStatement( + $this->_platform->getDropViewSQL($name), + ); } /* create*() Methods */ + /** @throws Exception */ + public function createSchemaObjects(Schema $schema): void + { + $this->_execSql($schema->toSql($this->_platform)); + } + /** * Creates a new database. * @@ -511,7 +912,9 @@ public function dropView($name) */ public function createDatabase($database) { - $this->_execSql($this->_platform->getCreateDatabaseSQL($database)); + $this->_conn->executeStatement( + $this->_platform->getCreateDatabaseSQL($database), + ); } /** @@ -538,7 +941,9 @@ public function createTable(Table $table) */ public function createSequence($sequence) { - $this->_execSql($this->_platform->getCreateSequenceSQL($sequence)); + $this->_conn->executeStatement( + $this->_platform->getCreateSequenceSQL($sequence), + ); } /** @@ -554,7 +959,9 @@ public function createSequence($sequence) */ public function createConstraint(Constraint $constraint, $table) { - $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table)); + $this->_conn->executeStatement( + $this->_platform->getCreateConstraintSQL($constraint, $table), + ); } /** @@ -568,7 +975,9 @@ public function createConstraint(Constraint $constraint, $table) */ public function createIndex(Index $index, $table) { - $this->_execSql($this->_platform->getCreateIndexSQL($index, $table)); + $this->_conn->executeStatement( + $this->_platform->getCreateIndexSQL($index, $table), + ); } /** @@ -583,7 +992,9 @@ public function createIndex(Index $index, $table) */ public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) { - $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table)); + $this->_conn->executeStatement( + $this->_platform->getCreateForeignKeySQL($foreignKey, $table), + ); } /** @@ -593,7 +1004,9 @@ public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) */ public function createUniqueConstraint(UniqueConstraint $uniqueConstraint, string $tableName): void { - $this->_execSql($this->_platform->getCreateUniqueConstraintSQL($uniqueConstraint, $tableName)); + $this->_conn->executeStatement( + $this->_platform->getCreateUniqueConstraintSQL($uniqueConstraint, $tableName), + ); } /** @@ -605,11 +1018,22 @@ public function createUniqueConstraint(UniqueConstraint $uniqueConstraint, strin */ public function createView(View $view) { - $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql())); + $this->_conn->executeStatement( + $this->_platform->getCreateViewSQL( + $view->getQuotedName($this->_platform), + $view->getSql(), + ), + ); } /* dropAndCreate*() Methods */ + /** @throws Exception */ + public function dropSchemaObjects(Schema $schema): void + { + $this->_execSql($schema->toDropSql($this->_platform)); + } + /** * Drops and creates a constraint. * @@ -635,7 +1059,7 @@ public function dropAndCreateConstraint(Constraint $constraint, $table) . ' Use AbstractSchemaManager::dropIndex() and AbstractSchemaManager::createIndex(),' . ' AbstractSchemaManager::dropForeignKey() and AbstractSchemaManager::createForeignKey()' . ' or AbstractSchemaManager::dropUniqueConstraint()' - . ' and AbstractSchemaManager::createUniqueConstraint() instead.' + . ' and AbstractSchemaManager::createUniqueConstraint() instead.', ); $this->tryMethod('dropConstraint', $constraint, $table); @@ -659,7 +1083,7 @@ public function dropAndCreateIndex(Index $index, $table) 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateIndex() is deprecated.' - . ' Use AbstractSchemaManager::dropIndex() and AbstractSchemaManager::createIndex() instead.' + . ' Use AbstractSchemaManager::dropIndex() and AbstractSchemaManager::createIndex() instead.', ); $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table); @@ -685,7 +1109,7 @@ public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateForeignKey() is deprecated.' - . ' Use AbstractSchemaManager::dropForeignKey() and AbstractSchemaManager::createForeignKey() instead.' + . ' Use AbstractSchemaManager::dropForeignKey() and AbstractSchemaManager::createForeignKey() instead.', ); $this->tryMethod('dropForeignKey', $foreignKey, $table); @@ -707,7 +1131,7 @@ public function dropAndCreateSequence(Sequence $sequence) 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateSequence() is deprecated.' - . ' Use AbstractSchemaManager::dropSequence() and AbstractSchemaManager::createSequence() instead.' + . ' Use AbstractSchemaManager::dropSequence() and AbstractSchemaManager::createSequence() instead.', ); $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform)); @@ -729,7 +1153,7 @@ public function dropAndCreateTable(Table $table) 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateTable() is deprecated.' - . ' Use AbstractSchemaManager::dropTable() and AbstractSchemaManager::createTable() instead.' + . ' Use AbstractSchemaManager::dropTable() and AbstractSchemaManager::createTable() instead.', ); $this->tryMethod('dropTable', $table->getQuotedName($this->_platform)); @@ -753,7 +1177,7 @@ public function dropAndCreateDatabase($database) 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateDatabase() is deprecated.' - . ' Use AbstractSchemaManager::dropDatabase() and AbstractSchemaManager::createDatabase() instead.' + . ' Use AbstractSchemaManager::dropDatabase() and AbstractSchemaManager::createDatabase() instead.', ); $this->tryMethod('dropDatabase', $database); @@ -775,7 +1199,7 @@ public function dropAndCreateView(View $view) 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateView() is deprecated.' - . ' Use AbstractSchemaManager::dropView() and AbstractSchemaManager::createView() instead.' + . ' Use AbstractSchemaManager::dropView() and AbstractSchemaManager::createView() instead.', ); $this->tryMethod('dropView', $view->getQuotedName($this->_platform)); @@ -789,7 +1213,7 @@ public function dropAndCreateView(View $view) */ public function alterSchema(SchemaDiff $schemaDiff): void { - $this->_execSql($schemaDiff->toSql($this->_platform)); + $this->_execSql($this->_platform->getAlterSchemaSQL($schemaDiff)); } /** @@ -800,7 +1224,7 @@ public function alterSchema(SchemaDiff $schemaDiff): void public function migrateSchema(Schema $toSchema): void { $schemaDiff = $this->createComparator() - ->compareSchemas($this->createSchema(), $toSchema); + ->compareSchemas($this->introspectSchema(), $toSchema); $this->alterSchema($schemaDiff); } @@ -816,9 +1240,7 @@ public function migrateSchema(Schema $toSchema): void */ public function alterTable(TableDiff $tableDiff) { - foreach ($this->_platform->getAlterTableSQL($tableDiff) as $ddlQuery) { - $this->_execSql($ddlQuery); - } + $this->_execSql($this->_platform->getAlterTableSQL($tableDiff)); } /** @@ -833,9 +1255,7 @@ public function alterTable(TableDiff $tableDiff) */ public function renameTable($name, $newName) { - $tableDiff = new TableDiff($name); - $tableDiff->newName = $newName; - $this->alterTable($tableDiff); + $this->_execSql($this->_platform->getRenameTableSQL($name, $newName)); } /** @@ -874,7 +1294,7 @@ protected function getPortableNamespacesList(array $namespaces) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'AbstractSchemaManager::getPortableNamespacesList() is deprecated,' - . ' use AbstractSchemaManager::listSchemaNames() instead.' + . ' use AbstractSchemaManager::listSchemaNames() instead.', ); $namespacesList = []; @@ -911,7 +1331,7 @@ protected function getPortableNamespaceDefinition(array $namespace) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'AbstractSchemaManager::getPortableNamespaceDefinition() is deprecated,' - . ' use AbstractSchemaManager::listSchemaNames() instead.' + . ' use AbstractSchemaManager::listSchemaNames() instead.', ); return $namespace; @@ -970,6 +1390,13 @@ protected function _getPortableTableColumnList($table, $database, $tableColumns) $defaultPrevented = false; if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated. Use a custom schema manager instead.', + Events::onSchemaColumnDefinition, + ); + $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn); $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs); @@ -1055,6 +1482,13 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null $defaultPrevented = false; if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5784', + 'Subscribing to %s events is deprecated. Use a custom schema manager instead.', + Events::onSchemaColumnDefinition, + ); + $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn); $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs); @@ -1069,7 +1503,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null $data['unique'], $data['primary'], $data['flags'], - $data['options'] + $data['options'], ); } @@ -1108,31 +1542,6 @@ protected function _getPortableTableDefinition($table) return $table; } - /** - * @param mixed[][] $users - * - * @return string[][] - */ - protected function _getPortableUsersList($users) - { - $list = []; - foreach ($users as $value) { - $list[] = $this->_getPortableUserDefinition($value); - } - - return $list; - } - - /** - * @param string[] $user - * - * @return string[] - */ - protected function _getPortableUserDefinition($user) - { - return $user; - } - /** * @param mixed[][] $views * @@ -1185,6 +1594,8 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) * @param mixed $tableForeignKey * * @return ForeignKeyConstraint + * + * @abstract */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) { @@ -1192,6 +1603,8 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) } /** + * @internal + * * @param string[]|string $sql * * @return void @@ -1208,12 +1621,21 @@ protected function _execSql($sql) /** * Creates a schema instance for the current database. * + * @deprecated Use {@link introspectSchema()} instead. + * * @return Schema * * @throws Exception */ public function createSchema() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5613', + '%s is deprecated. Use introspectSchema() instead.', + __METHOD__, + ); + $schemaNames = []; if ($this->_platform->supportsSchemas()) { @@ -1231,6 +1653,16 @@ public function createSchema() return new Schema($tables, $sequences, $this->createSchemaConfig(), $schemaNames); } + /** + * Returns a {@see Schema} instance representing the current database schema. + * + * @throws Exception + */ + public function introspectSchema(): Schema + { + return $this->createSchema(); + } + /** * Creates the configuration for this schema. * @@ -1283,7 +1715,7 @@ public function getSchemaSearchPaths() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4821', - 'AbstractSchemaManager::getSchemaSearchPaths() is deprecated.' + 'AbstractSchemaManager::getSchemaSearchPaths() is deprecated.', ); $database = $this->_conn->getDatabase(); @@ -1332,8 +1764,37 @@ public function removeDoctrineTypeFromComment($comment, $type) return str_replace('(DC2Type:' . $type . ')', '', $comment); } + /** @throws Exception */ + private function getDatabase(string $methodName): string + { + $database = $this->_conn->getDatabase(); + + if ($database === null) { + throw DatabaseRequired::new($methodName); + } + + return $database; + } + public function createComparator(): Comparator { - return new Comparator($this->getDatabasePlatform()); + return new Comparator($this->_platform); + } + + /** + * @return array>> + * + * @throws Exception + */ + private function fetchAllAssociativeGrouped(Result $result): array + { + $data = []; + + foreach ($result->fetchAllAssociative() as $row) { + $tableName = $this->_getPortableTableDefinition($row); + $data[$tableName][] = $row; + } + + return $data; } } diff --git a/doctrine/dbal/src/Schema/Column.php b/doctrine/dbal/src/Schema/Column.php index 714bbc428..03fd686f4 100644 --- a/doctrine/dbal/src/Schema/Column.php +++ b/doctrine/dbal/src/Schema/Column.php @@ -4,6 +4,7 @@ use Doctrine\DBAL\Schema\Exception\UnknownColumnOption; use Doctrine\DBAL\Types\Type; +use Doctrine\Deprecations\Deprecation; use function array_merge; use function is_numeric; @@ -50,7 +51,11 @@ class Column extends AbstractAsset /** @var string|null */ protected $_comment; - /** @var mixed[] */ + /** + * @deprecated Use {@link $_platformOptions} instead + * + * @var mixed[] + */ protected $_customSchemaOptions = []; /** @@ -90,9 +95,7 @@ public function setOptions(array $options) return $this; } - /** - * @return Column - */ + /** @return Column */ public function setType(Type $type) { $this->_type = $type; @@ -222,7 +225,7 @@ public function setPlatformOption($name, $value) } /** - * @param string $value + * @param string|null $value * * @return Column */ @@ -233,73 +236,55 @@ public function setColumnDefinition($value) return $this; } - /** - * @return Type - */ + /** @return Type */ public function getType() { return $this->_type; } - /** - * @return int|null - */ + /** @return int|null */ public function getLength() { return $this->_length; } - /** - * @return int - */ + /** @return int */ public function getPrecision() { return $this->_precision; } - /** - * @return int - */ + /** @return int */ public function getScale() { return $this->_scale; } - /** - * @return bool - */ + /** @return bool */ public function getUnsigned() { return $this->_unsigned; } - /** - * @return bool - */ + /** @return bool */ public function getFixed() { return $this->_fixed; } - /** - * @return bool - */ + /** @return bool */ public function getNotnull() { return $this->_notnull; } - /** - * @return string|null - */ + /** @return string|null */ public function getDefault() { return $this->_default; } - /** - * @return mixed[] - */ + /** @return mixed[] */ public function getPlatformOptions() { return $this->_platformOptions; @@ -325,17 +310,13 @@ public function getPlatformOption($name) return $this->_platformOptions[$name]; } - /** - * @return string|null - */ + /** @return string|null */ public function getColumnDefinition() { return $this->_columnDefinition; } - /** - * @return bool - */ + /** @return bool */ public function getAutoincrement() { return $this->_autoincrement; @@ -365,15 +346,15 @@ public function setComment($comment) return $this; } - /** - * @return string|null - */ + /** @return string|null */ public function getComment() { return $this->_comment; } /** + * @deprecated Use {@link setPlatformOption()} instead + * * @param string $name * @param mixed $value * @@ -381,54 +362,90 @@ public function getComment() */ public function setCustomSchemaOption($name, $value) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5476', + 'Column::setCustomSchemaOption() is deprecated. Use setPlatformOption() instead.', + ); + $this->_customSchemaOptions[$name] = $value; return $this; } /** + * @deprecated Use {@link hasPlatformOption()} instead + * * @param string $name * * @return bool */ public function hasCustomSchemaOption($name) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5476', + 'Column::hasCustomSchemaOption() is deprecated. Use hasPlatformOption() instead.', + ); + return isset($this->_customSchemaOptions[$name]); } /** + * @deprecated Use {@link getPlatformOption()} instead + * * @param string $name * * @return mixed */ public function getCustomSchemaOption($name) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5476', + 'Column::getCustomSchemaOption() is deprecated. Use getPlatformOption() instead.', + ); + return $this->_customSchemaOptions[$name]; } /** + * @deprecated Use {@link setPlatformOptions()} instead + * * @param mixed[] $customSchemaOptions * * @return Column */ public function setCustomSchemaOptions(array $customSchemaOptions) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5476', + 'Column::setCustomSchemaOptions() is deprecated. Use setPlatformOptions() instead.', + ); + $this->_customSchemaOptions = $customSchemaOptions; return $this; } /** + * @deprecated Use {@link getPlatformOptions()} instead + * * @return mixed[] */ public function getCustomSchemaOptions() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5476', + 'Column::getCustomSchemaOptions() is deprecated. Use getPlatformOptions() instead.', + ); + return $this->_customSchemaOptions; } - /** - * @return mixed[] - */ + /** @return mixed[] */ public function toArray() { return array_merge([ diff --git a/doctrine/dbal/src/Schema/ColumnDiff.php b/doctrine/dbal/src/Schema/ColumnDiff.php index 83402fd0f..bd1b0eee0 100644 --- a/doctrine/dbal/src/Schema/ColumnDiff.php +++ b/doctrine/dbal/src/Schema/ColumnDiff.php @@ -11,19 +11,39 @@ */ class ColumnDiff { - /** @var string */ + /** + * @deprecated Use {@see $fromColumn} and {@see Column::getName()} instead. + * + * @var string + */ public $oldColumnName; - /** @var Column */ + /** + * @internal Use {@see getNewColumn()} instead. + * + * @var Column + */ public $column; - /** @var string[] */ + /** + * @deprecated Use {@see hasTypeChanged()}, {@see hasLengthChanged()}, {@see hasPrecisionChanged()}, + * {@see hasScaleChanged()}, {@see hasUnsignedChanged()}, {@see hasFixedChanged()}, {@see hasNotNullChanged()}, + * {@see hasDefaultChanged()}, {@see hasAutoIncrementChanged()} or {@see hasCommentChanged()} instead. + * + * @var string[] + */ public $changedProperties = []; - /** @var Column|null */ + /** + * @internal Use {@see getOldColumn()} instead. + * + * @var Column|null + */ public $fromColumn; /** + * @internal The diff can be only instantiated by a {@see Comparator}. + * * @param string $oldColumnName * @param string[] $changedProperties */ @@ -38,7 +58,7 @@ public function __construct( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4785', 'Not passing the $fromColumn to %s is deprecated.', - __METHOD__ + __METHOD__, ); } @@ -48,7 +68,71 @@ public function __construct( $this->fromColumn = $fromColumn; } + public function getOldColumn(): ?Column + { + return $this->fromColumn; + } + + public function getNewColumn(): Column + { + return $this->column; + } + + public function hasTypeChanged(): bool + { + return $this->hasChanged('type'); + } + + public function hasLengthChanged(): bool + { + return $this->hasChanged('length'); + } + + public function hasPrecisionChanged(): bool + { + return $this->hasChanged('precision'); + } + + public function hasScaleChanged(): bool + { + return $this->hasChanged('scale'); + } + + public function hasUnsignedChanged(): bool + { + return $this->hasChanged('unsigned'); + } + + public function hasFixedChanged(): bool + { + return $this->hasChanged('fixed'); + } + + public function hasNotNullChanged(): bool + { + return $this->hasChanged('notnull'); + } + + public function hasDefaultChanged(): bool + { + return $this->hasChanged('default'); + } + + public function hasAutoIncrementChanged(): bool + { + return $this->hasChanged('autoincrement'); + } + + public function hasCommentChanged(): bool + { + return $this->hasChanged('comment'); + } + /** + * @deprecated Use {@see hasTypeChanged()}, {@see hasLengthChanged()}, {@see hasPrecisionChanged()}, + * {@see hasScaleChanged()}, {@see hasUnsignedChanged()}, {@see hasFixedChanged()}, {@see hasNotNullChanged()}, + * {@see hasDefaultChanged()}, {@see hasAutoIncrementChanged()} or {@see hasCommentChanged()} instead. + * * @param string $propertyName * * @return bool @@ -59,12 +143,27 @@ public function hasChanged($propertyName) } /** + * @deprecated Use {@see $fromColumn} instead. + * * @return Identifier */ public function getOldColumnName() { - $quote = $this->fromColumn !== null && $this->fromColumn->isQuoted(); + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5622', + '%s is deprecated. Use $fromColumn instead.', + __METHOD__, + ); + + if ($this->fromColumn !== null) { + $name = $this->fromColumn->getName(); + $quote = $this->fromColumn->isQuoted(); + } else { + $name = $this->oldColumnName; + $quote = false; + } - return new Identifier($this->oldColumnName, $quote); + return new Identifier($name, $quote); } } diff --git a/doctrine/dbal/src/Schema/Comparator.php b/doctrine/dbal/src/Schema/Comparator.php index 9c18fe67a..28e7f2f73 100644 --- a/doctrine/dbal/src/Schema/Comparator.php +++ b/doctrine/dbal/src/Schema/Comparator.php @@ -27,12 +27,9 @@ */ class Comparator { - /** @var AbstractPlatform|null */ - private $platform; + private ?AbstractPlatform $platform; - /** - * @internal The comparator can be only instantiated by a schema manager. - */ + /** @internal The comparator can be only instantiated by a schema manager. */ public function __construct(?AbstractPlatform $platform = null) { if ($platform === null) { @@ -41,16 +38,14 @@ public function __construct(?AbstractPlatform $platform = null) 'https://github.com/doctrine/dbal/pull/4746', 'Not passing a $platform to %s is deprecated.' . ' Use AbstractSchemaManager::createComparator() to instantiate the comparator.', - __METHOD__ + __METHOD__, ); } $this->platform = $platform; } - /** - * @param list $args - */ + /** @param list $args */ public function __call(string $method, array $args): SchemaDiff { if ($method !== 'compareSchemas') { @@ -60,15 +55,21 @@ public function __call(string $method, array $args): SchemaDiff return $this->doCompareSchemas(...$args); } - /** - * @param list $args - */ + /** @param list $args */ public static function __callStatic(string $method, array $args): SchemaDiff { if ($method !== 'compareSchemas') { throw new BadMethodCallException(sprintf('Unknown method "%s"', $method)); } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4707', + 'Calling %s::%s() statically is deprecated.', + self::class, + $method, + ); + $comparator = new self(); return $comparator->doCompareSchemas(...$args); @@ -87,8 +88,16 @@ private function doCompareSchemas( Schema $fromSchema, Schema $toSchema ) { - $diff = new SchemaDiff(); - $diff->fromSchema = $fromSchema; + $createdSchemas = []; + $droppedSchemas = []; + $createdTables = []; + $alteredTables = []; + $droppedTables = []; + $createdSequences = []; + $alteredSequences = []; + $droppedSequences = []; + + $orphanedForeignKeys = []; $foreignKeysToTable = []; @@ -97,7 +106,7 @@ private function doCompareSchemas( continue; } - $diff->newNamespaces[$namespace] = $namespace; + $createdSchemas[$namespace] = $namespace; } foreach ($fromSchema->getNamespaces() as $namespace) { @@ -105,21 +114,21 @@ private function doCompareSchemas( continue; } - $diff->removedNamespaces[$namespace] = $namespace; + $droppedSchemas[$namespace] = $namespace; } foreach ($toSchema->getTables() as $table) { $tableName = $table->getShortestName($toSchema->getName()); if (! $fromSchema->hasTable($tableName)) { - $diff->newTables[$tableName] = $toSchema->getTable($tableName); + $createdTables[$tableName] = $toSchema->getTable($tableName); } else { $tableDifferences = $this->diffTable( $fromSchema->getTable($tableName), - $toSchema->getTable($tableName) + $toSchema->getTable($tableName), ); if ($tableDifferences !== false) { - $diff->changedTables[$tableName] = $tableDifferences; + $alteredTables[$tableName] = $tableDifferences; } } } @@ -130,7 +139,7 @@ private function doCompareSchemas( $table = $fromSchema->getTable($tableName); if (! $toSchema->hasTable($tableName)) { - $diff->removedTables[$tableName] = $table; + $droppedTables[$tableName] = $table; } // also remember all foreign keys that point to a specific table @@ -144,31 +153,37 @@ private function doCompareSchemas( } } - foreach ($diff->removedTables as $tableName => $table) { + foreach ($droppedTables as $tableName => $table) { if (! isset($foreignKeysToTable[$tableName])) { continue; } - $diff->orphanedForeignKeys = array_merge($diff->orphanedForeignKeys, $foreignKeysToTable[$tableName]); + foreach ($foreignKeysToTable[$tableName] as $foreignKey) { + if (isset($droppedTables[strtolower($foreignKey->getLocalTableName())])) { + continue; + } + + $orphanedForeignKeys[] = $foreignKey; + } // deleting duplicated foreign keys present on both on the orphanedForeignKey // and the removedForeignKeys from changedTables foreach ($foreignKeysToTable[$tableName] as $foreignKey) { // strtolower the table name to make if compatible with getShortestName $localTableName = strtolower($foreignKey->getLocalTableName()); - if (! isset($diff->changedTables[$localTableName])) { + if (! isset($alteredTables[$localTableName])) { continue; } - foreach ($diff->changedTables[$localTableName]->removedForeignKeys as $key => $removedForeignKey) { - assert($removedForeignKey instanceof ForeignKeyConstraint); + foreach ($alteredTables[$localTableName]->getDroppedForeignKeys() as $droppedForeignKey) { + assert($droppedForeignKey instanceof ForeignKeyConstraint); // We check if the key is from the removed table if not we skip. - if ($tableName !== strtolower($removedForeignKey->getForeignTableName())) { + if ($tableName !== strtolower($droppedForeignKey->getForeignTableName())) { continue; } - unset($diff->changedTables[$localTableName]->removedForeignKeys[$key]); + $alteredTables[$localTableName]->unsetDroppedForeignKey($droppedForeignKey); } } } @@ -177,11 +192,11 @@ private function doCompareSchemas( $sequenceName = $sequence->getShortestName($toSchema->getName()); if (! $fromSchema->hasSequence($sequenceName)) { if (! $this->isAutoIncrementSequenceInSchema($fromSchema, $sequence)) { - $diff->newSequences[] = $sequence; + $createdSequences[] = $sequence; } } else { if ($this->diffSequence($sequence, $fromSchema->getSequence($sequenceName))) { - $diff->changedSequences[] = $toSchema->getSequence($sequenceName); + $alteredSequences[] = $toSchema->getSequence($sequenceName); } } } @@ -197,9 +212,23 @@ private function doCompareSchemas( continue; } - $diff->removedSequences[] = $sequence; + $droppedSequences[] = $sequence; } + $diff = new SchemaDiff( + $createdTables, + $alteredTables, + $droppedTables, + $fromSchema, + $createdSchemas, + $droppedSchemas, + $createdSequences, + $alteredSequences, + $droppedSequences, + ); + + $diff->orphanedForeignKeys = $orphanedForeignKeys; + return $diff; } @@ -215,7 +244,7 @@ public function compare(Schema $fromSchema, Schema $toSchema) Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4707', - 'Method compare() is deprecated. Use a non-static call to compareSchemas() instead.' + 'Method compare() is deprecated. Use a non-static call to compareSchemas() instead.', ); return $this->compareSchemas($fromSchema, $toSchema); @@ -236,9 +265,7 @@ private function isAutoIncrementSequenceInSchema($schema, $sequence): bool return false; } - /** - * @return bool - */ + /** @return bool */ public function diffSequence(Sequence $sequence1, Sequence $sequence2) { if ($sequence1->getAllocationSize() !== $sequence2->getAllocationSize()) { @@ -253,15 +280,46 @@ public function diffSequence(Sequence $sequence1, Sequence $sequence2) * * If there are no differences this method returns the boolean false. * + * @deprecated Use {@see compareTables()} and, optionally, {@see TableDiff::isEmpty()} instead. + * * @return TableDiff|false * * @throws Exception */ public function diffTable(Table $fromTable, Table $toTable) { - $changes = 0; - $tableDifferences = new TableDiff($fromTable->getName()); - $tableDifferences->fromTable = $fromTable; + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5770', + '%s is deprecated. Use compareTables() instead.', + __METHOD__, + ); + + $diff = $this->compareTables($fromTable, $toTable); + + if ($diff->isEmpty()) { + return false; + } + + return $diff; + } + + /** + * Compares the tables and returns the difference between them. + * + * @throws Exception + */ + public function compareTables(Table $fromTable, Table $toTable): TableDiff + { + $addedColumns = []; + $modifiedColumns = []; + $droppedColumns = []; + $addedIndexes = []; + $modifiedIndexes = []; + $droppedIndexes = []; + $addedForeignKeys = []; + $modifiedForeignKeys = []; + $droppedForeignKeys = []; $fromTableColumns = $fromTable->getColumns(); $toTableColumns = $toTable->getColumns(); @@ -272,16 +330,15 @@ public function diffTable(Table $fromTable, Table $toTable) continue; } - $tableDifferences->addedColumns[$columnName] = $column; - $changes++; + $addedColumns[$columnName] = $column; } /* See if there are any removed columns in "to" table */ foreach ($fromTableColumns as $columnName => $column) { // See if column is removed in "to" table. if (! $toTable->hasColumn($columnName)) { - $tableDifferences->removedColumns[$columnName] = $column; - $changes++; + $droppedColumns[$columnName] = $column; + continue; } @@ -298,40 +355,37 @@ public function diffTable(Table $fromTable, Table $toTable) continue; } - $tableDifferences->changedColumns[$column->getName()] = new ColumnDiff( + $modifiedColumns[$column->getName()] = new ColumnDiff( $column->getName(), $toColumn, $changedProperties, - $column + $column, ); - - $changes++; } - $this->detectColumnRenamings($tableDifferences); + $renamedColumns = $this->detectRenamedColumns($addedColumns, $droppedColumns); $fromTableIndexes = $fromTable->getIndexes(); $toTableIndexes = $toTable->getIndexes(); /* See if all the indexes in "from" table exist in "to" table */ foreach ($toTableIndexes as $indexName => $index) { - if (($index->isPrimary() && $fromTable->hasPrimaryKey()) || $fromTable->hasIndex($indexName)) { + if (($index->isPrimary() && $fromTable->getPrimaryKey() !== null) || $fromTable->hasIndex($indexName)) { continue; } - $tableDifferences->addedIndexes[$indexName] = $index; - $changes++; + $addedIndexes[$indexName] = $index; } /* See if there are any removed indexes in "to" table */ foreach ($fromTableIndexes as $indexName => $index) { // See if index is removed in "to" table. if ( - ($index->isPrimary() && ! $toTable->hasPrimaryKey()) || + ($index->isPrimary() && $toTable->getPrimaryKey() === null) || ! $index->isPrimary() && ! $toTable->hasIndex($indexName) ) { - $tableDifferences->removedIndexes[$indexName] = $index; - $changes++; + $droppedIndexes[$indexName] = $index; + continue; } @@ -343,11 +397,10 @@ public function diffTable(Table $fromTable, Table $toTable) continue; } - $tableDifferences->changedIndexes[$indexName] = $toTableIndex; - $changes++; + $modifiedIndexes[$indexName] = $toTableIndex; } - $this->detectIndexRenamings($tableDifferences); + $renamedIndexes = $this->detectRenamedIndexes($addedIndexes, $droppedIndexes); $fromForeignKeys = $fromTable->getForeignKeys(); $toForeignKeys = $toTable->getForeignKeys(); @@ -358,8 +411,8 @@ public function diffTable(Table $fromTable, Table $toTable) unset($fromForeignKeys[$fromKey], $toForeignKeys[$toKey]); } else { if (strtolower($fromConstraint->getName()) === strtolower($toConstraint->getName())) { - $tableDifferences->changedForeignKeys[] = $toConstraint; - $changes++; + $modifiedForeignKeys[] = $toConstraint; + unset($fromForeignKeys[$fromKey], $toForeignKeys[$toKey]); } } @@ -367,102 +420,137 @@ public function diffTable(Table $fromTable, Table $toTable) } foreach ($fromForeignKeys as $fromConstraint) { - $tableDifferences->removedForeignKeys[] = $fromConstraint; - $changes++; + $droppedForeignKeys[] = $fromConstraint; } foreach ($toForeignKeys as $toConstraint) { - $tableDifferences->addedForeignKeys[] = $toConstraint; - $changes++; - } - - return $changes > 0 ? $tableDifferences : false; + $addedForeignKeys[] = $toConstraint; + } + + return new TableDiff( + $toTable->getName(), + $addedColumns, + $modifiedColumns, + $droppedColumns, + $addedIndexes, + $modifiedIndexes, + $droppedIndexes, + $fromTable, + $addedForeignKeys, + $modifiedForeignKeys, + $droppedForeignKeys, + $renamedColumns, + $renamedIndexes, + ); } /** * Try to find columns that only changed their name, rename operations maybe cheaper than add/drop * however ambiguities between different possibilities should not lead to renaming at all. + * + * @param array $addedColumns + * @param array $removedColumns + * + * @return array + * + * @throws Exception */ - private function detectColumnRenamings(TableDiff $tableDifferences): void + private function detectRenamedColumns(array &$addedColumns, array &$removedColumns): array { - $renameCandidates = []; - foreach ($tableDifferences->addedColumns as $addedColumnName => $addedColumn) { - foreach ($tableDifferences->removedColumns as $removedColumn) { + $candidatesByName = []; + + foreach ($addedColumns as $addedColumnName => $addedColumn) { + foreach ($removedColumns as $removedColumn) { if (! $this->columnsEqual($addedColumn, $removedColumn)) { continue; } - $renameCandidates[$addedColumn->getName()][] = [$removedColumn, $addedColumn, $addedColumnName]; + $candidatesByName[$addedColumn->getName()][] = [$removedColumn, $addedColumn, $addedColumnName]; } } - foreach ($renameCandidates as $candidateColumns) { - if (count($candidateColumns) !== 1) { + $renamedColumns = []; + + foreach ($candidatesByName as $candidates) { + if (count($candidates) !== 1) { continue; } - [$removedColumn, $addedColumn] = $candidateColumns[0]; + [$removedColumn, $addedColumn] = $candidates[0]; $removedColumnName = $removedColumn->getName(); $addedColumnName = strtolower($addedColumn->getName()); - if (isset($tableDifferences->renamedColumns[$removedColumnName])) { + if (isset($renamedColumns[$removedColumnName])) { continue; } - $tableDifferences->renamedColumns[$removedColumnName] = $addedColumn; + $renamedColumns[$removedColumnName] = $addedColumn; unset( - $tableDifferences->addedColumns[$addedColumnName], - $tableDifferences->removedColumns[strtolower($removedColumnName)] + $addedColumns[$addedColumnName], + $removedColumns[strtolower($removedColumnName)], ); } + + return $renamedColumns; } /** * Try to find indexes that only changed their name, rename operations maybe cheaper than add/drop * however ambiguities between different possibilities should not lead to renaming at all. + * + * @param array $addedIndexes + * @param array $removedIndexes + * + * @return array */ - private function detectIndexRenamings(TableDiff $tableDifferences): void + private function detectRenamedIndexes(array &$addedIndexes, array &$removedIndexes): array { - $renameCandidates = []; + $candidatesByName = []; // Gather possible rename candidates by comparing each added and removed index based on semantics. - foreach ($tableDifferences->addedIndexes as $addedIndexName => $addedIndex) { - foreach ($tableDifferences->removedIndexes as $removedIndex) { + foreach ($addedIndexes as $addedIndexName => $addedIndex) { + foreach ($removedIndexes as $removedIndex) { if ($this->diffIndex($addedIndex, $removedIndex)) { continue; } - $renameCandidates[$addedIndex->getName()][] = [$removedIndex, $addedIndex, $addedIndexName]; + $candidatesByName[$addedIndex->getName()][] = [$removedIndex, $addedIndex, $addedIndexName]; } } - foreach ($renameCandidates as $candidateIndexes) { + $renamedIndexes = []; + + foreach ($candidatesByName as $candidates) { // If the current rename candidate contains exactly one semantically equal index, // we can safely rename it. - // Otherwise it is unclear if a rename action is really intended, + // Otherwise, it is unclear if a rename action is really intended, // therefore we let those ambiguous indexes be added/dropped. - if (count($candidateIndexes) !== 1) { + if (count($candidates) !== 1) { continue; } - [$removedIndex, $addedIndex] = $candidateIndexes[0]; + [$removedIndex, $addedIndex] = $candidates[0]; $removedIndexName = strtolower($removedIndex->getName()); $addedIndexName = strtolower($addedIndex->getName()); - if (isset($tableDifferences->renamedIndexes[$removedIndexName])) { + if (isset($renamedIndexes[$removedIndexName])) { continue; } - $tableDifferences->renamedIndexes[$removedIndexName] = $addedIndex; + $renamedIndexes[$removedIndexName] = $addedIndex; unset( - $tableDifferences->addedIndexes[$addedIndexName], - $tableDifferences->removedIndexes[$removedIndexName] + $addedIndexes[$addedIndexName], + $removedIndexes[$removedIndexName], ); } + + return $renamedIndexes; } /** + * @internal The method should be only used from within the {@see Comparator} class hierarchy. + * * @return bool */ public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2) @@ -495,6 +583,8 @@ public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint /** * Compares the definitions of the given columns * + * @internal The method should be only used from within the {@see Comparator} class hierarchy. + * * @throws Exception */ public function columnsEqual(Column $column1, Column $column2): bool @@ -512,10 +602,19 @@ public function columnsEqual(Column $column1, Column $column2): bool * If there are differences this method returns the changed properties as a * string array, otherwise an empty array gets returned. * + * @deprecated Use {@see columnsEqual()} instead. + * * @return string[] */ public function diffColumn(Column $column1, Column $column2) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5650', + '%s is deprecated. Use diffTable() instead.', + __METHOD__, + ); + $properties1 = $column1->toArray(); $properties2 = $column2->toArray(); @@ -603,13 +702,15 @@ public function diffColumn(Column $column1, Column $column2) /** * Finds the difference between the indexes $index1 and $index2. * - * Compares $index1 with $index2 and returns $index2 if there are any + * Compares $index1 with $index2 and returns true if there are any * differences or false in case there are no differences. * + * @internal The method should be only used from within the {@see Comparator} class hierarchy. + * * @return bool */ public function diffIndex(Index $index1, Index $index2) { - return ! ($index1->isFullfilledBy($index2) && $index2->isFullfilledBy($index1)); + return ! ($index1->isFulfilledBy($index2) && $index2->isFulfilledBy($index1)); } } diff --git a/doctrine/dbal/src/Schema/Constraint.php b/doctrine/dbal/src/Schema/Constraint.php index 85338c783..f47ee1fd1 100644 --- a/doctrine/dbal/src/Schema/Constraint.php +++ b/doctrine/dbal/src/Schema/Constraint.php @@ -11,14 +11,10 @@ */ interface Constraint { - /** - * @return string - */ + /** @return string */ public function getName(); - /** - * @return string - */ + /** @return string */ public function getQuotedName(AbstractPlatform $platform); /** diff --git a/doctrine/dbal/src/Schema/DB2SchemaManager.php b/doctrine/dbal/src/Schema/DB2SchemaManager.php index e06baf36e..a07136fec 100644 --- a/doctrine/dbal/src/Schema/DB2SchemaManager.php +++ b/doctrine/dbal/src/Schema/DB2SchemaManager.php @@ -4,14 +4,18 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\DB2Platform; +use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; +use Doctrine\Deprecations\Deprecation; use function array_change_key_case; +use function implode; use function preg_match; use function str_replace; use function strpos; use function strtolower; +use function strtoupper; use function substr; use const CASE_LOWER; @@ -24,22 +28,64 @@ class DB2SchemaManager extends AbstractSchemaManager { /** - * {@inheritdoc} - * - * Apparently creator is the schema not the user who created it: - * {@link http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.sqlref/db2z_sysibmsystablestable.htm} + * {@inheritDoc} */ public function listTableNames() { - $sql = $this->_platform->getListTablesSQL() . ' AND CREATOR = CURRENT_USER'; + return $this->doListTableNames(); + } + + /** + * {@inheritDoc} + */ + public function listTables() + { + return $this->doListTables(); + } + + /** + * {@inheritDoc} + * + * @deprecated Use {@see introspectTable()} instead. + */ + public function listTableDetails($name) + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5595', + '%s is deprecated. Use introspectTable() instead.', + __METHOD__, + ); + + return $this->doListTableDetails($name); + } + + /** + * {@inheritDoc} + */ + public function listTableColumns($table, $database = null) + { + return $this->doListTableColumns($table, $database); + } - $tables = $this->_conn->fetchAllAssociative($sql); + /** + * {@inheritDoc} + */ + public function listTableIndexes($table) + { + return $this->doListTableIndexes($table); + } - return $this->filterAssetNames($this->_getPortableTablesList($tables)); + /** + * {@inheritDoc} + */ + public function listTableForeignKeys($table, $database = null) + { + return $this->doListTableForeignKeys($table, $database); } /** - * {@inheritdoc} + * {@inheritDoc} * * @throws Exception */ @@ -102,17 +148,13 @@ protected function _getPortableTableColumnDefinition($tableColumn) $options = [ 'length' => $length, - 'unsigned' => false, 'fixed' => (bool) $fixed, 'default' => $default, 'autoincrement' => (bool) $tableColumn['autoincrement'], 'notnull' => $tableColumn['nulls'] === 'N', - 'scale' => null, - 'precision' => null, 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null, - 'platformOptions' => [], ]; if ($scale !== null && $precision !== null) { @@ -124,21 +166,17 @@ protected function _getPortableTableColumnDefinition($tableColumn) } /** - * {@inheritdoc} + * {@inheritDoc} */ - protected function _getPortableTablesList($tables) + protected function _getPortableTableDefinition($table) { - $tableNames = []; - foreach ($tables as $tableRow) { - $tableRow = array_change_key_case($tableRow, CASE_LOWER); - $tableNames[] = $tableRow['name']; - } + $table = array_change_key_case($table, CASE_LOWER); - return $tableNames; + return $table['name']; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { @@ -151,7 +189,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) { @@ -160,12 +198,12 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) $tableForeignKey['foreign_table'], $tableForeignKey['foreign_columns'], $tableForeignKey['name'], - $tableForeignKey['options'] + $tableForeignKey['options'], ); } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableForeignKeysList($tableForeignKeys) { @@ -213,7 +251,7 @@ protected function _getPortableForeignKeyRuleDef($def) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableViewDefinition($view) { @@ -229,21 +267,185 @@ protected function _getPortableViewDefinition($view) return new View($view['name'], $sql); } + protected function normalizeName(string $name): string + { + $identifier = new Identifier($name); + + return $identifier->isQuoted() ? $identifier->getName() : strtoupper($name); + } + + protected function selectTableNames(string $databaseName): Result + { + $sql = <<<'SQL' +SELECT NAME +FROM SYSIBM.SYSTABLES +WHERE TYPE = 'T' + AND CREATOR = ? +SQL; + + return $this->_conn->executeQuery($sql, [$databaseName]); + } + + protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' C.TABNAME AS NAME,'; + } + + $sql .= <<<'SQL' + C.COLNAME, + C.TYPENAME, + C.CODEPAGE, + C.NULLS, + C.LENGTH, + C.SCALE, + C.REMARKS AS COMMENT, + CASE + WHEN C.GENERATED = 'D' THEN 1 + ELSE 0 + END AS AUTOINCREMENT, + C.DEFAULT +FROM SYSCAT.COLUMNS C + JOIN SYSCAT.TABLES AS T + ON T.TABSCHEMA = C.TABSCHEMA + AND T.TABNAME = C.TABNAME +SQL; + + $conditions = ['C.TABSCHEMA = ?', "T.TYPE = 'T'"]; + $params = [$databaseName]; + + if ($tableName !== null) { + $conditions[] = 'C.TABNAME = ?'; + $params[] = $tableName; + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY C.TABNAME, C.COLNO'; + + return $this->_conn->executeQuery($sql, $params); + } + + protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' IDX.TABNAME AS NAME,'; + } + + $sql .= <<<'SQL' + IDX.INDNAME AS KEY_NAME, + IDXCOL.COLNAME AS COLUMN_NAME, + CASE + WHEN IDX.UNIQUERULE = 'P' THEN 1 + ELSE 0 + END AS PRIMARY, + CASE + WHEN IDX.UNIQUERULE = 'D' THEN 1 + ELSE 0 + END AS NON_UNIQUE + FROM SYSCAT.INDEXES AS IDX + JOIN SYSCAT.TABLES AS T + ON IDX.TABSCHEMA = T.TABSCHEMA AND IDX.TABNAME = T.TABNAME + JOIN SYSCAT.INDEXCOLUSE AS IDXCOL + ON IDX.INDSCHEMA = IDXCOL.INDSCHEMA AND IDX.INDNAME = IDXCOL.INDNAME +SQL; + + $conditions = ['IDX.TABSCHEMA = ?', "T.TYPE = 'T'"]; + $params = [$databaseName]; + + if ($tableName !== null) { + $conditions[] = 'IDX.TABNAME = ?'; + $params[] = $tableName; + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY IDX.INDNAME, IDXCOL.COLSEQ'; + + return $this->_conn->executeQuery($sql, $params); + } + + protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' R.TABNAME AS NAME,'; + } + + $sql .= <<<'SQL' + FKCOL.COLNAME AS LOCAL_COLUMN, + R.REFTABNAME AS FOREIGN_TABLE, + PKCOL.COLNAME AS FOREIGN_COLUMN, + R.CONSTNAME AS INDEX_NAME, + CASE + WHEN R.UPDATERULE = 'R' THEN 'RESTRICT' + END AS ON_UPDATE, + CASE + WHEN R.DELETERULE = 'C' THEN 'CASCADE' + WHEN R.DELETERULE = 'N' THEN 'SET NULL' + WHEN R.DELETERULE = 'R' THEN 'RESTRICT' + END AS ON_DELETE + FROM SYSCAT.REFERENCES AS R + JOIN SYSCAT.TABLES AS T + ON T.TABSCHEMA = R.TABSCHEMA + AND T.TABNAME = R.TABNAME + JOIN SYSCAT.KEYCOLUSE AS FKCOL + ON FKCOL.CONSTNAME = R.CONSTNAME + AND FKCOL.TABSCHEMA = R.TABSCHEMA + AND FKCOL.TABNAME = R.TABNAME + JOIN SYSCAT.KEYCOLUSE AS PKCOL + ON PKCOL.CONSTNAME = R.REFKEYNAME + AND PKCOL.TABSCHEMA = R.REFTABSCHEMA + AND PKCOL.TABNAME = R.REFTABNAME + AND PKCOL.COLSEQ = FKCOL.COLSEQ +SQL; + + $conditions = ['R.TABSCHEMA = ?', "T.TYPE = 'T'"]; + $params = [$databaseName]; + + if ($tableName !== null) { + $conditions[] = 'R.TABNAME = ?'; + $params[] = $tableName; + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY R.CONSTNAME, FKCOL.COLSEQ'; + + return $this->_conn->executeQuery($sql, $params); + } + /** - * {@inheritdoc} + * {@inheritDoc} */ - public function listTableDetails($name): Table + protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { - $table = parent::listTableDetails($name); + $sql = 'SELECT NAME, REMARKS'; + + $conditions = []; + $params = []; + + if ($tableName !== null) { + $conditions[] = 'NAME = ?'; + $params[] = $tableName; + } + + $sql .= ' FROM SYSIBM.SYSTABLES'; + + if ($conditions !== []) { + $sql .= ' WHERE ' . implode(' AND ', $conditions); + } - $sql = $this->_platform->getListTableCommentsSQL($name); + /** @var array> $metadata */ + $metadata = $this->_conn->executeQuery($sql, $params) + ->fetchAllAssociativeIndexed(); - $tableOptions = $this->_conn->fetchAssociative($sql); + $tableOptions = []; + foreach ($metadata as $table => $data) { + $data = array_change_key_case($data, CASE_LOWER); - if ($tableOptions !== false) { - $table->addOption('comment', $tableOptions['REMARKS']); + $tableOptions[$table] = ['comment' => $data['remarks']]; } - return $table; + return $tableOptions; } } diff --git a/doctrine/dbal/src/Schema/DefaultSchemaManagerFactory.php b/doctrine/dbal/src/Schema/DefaultSchemaManagerFactory.php new file mode 100644 index 000000000..efba87f29 --- /dev/null +++ b/doctrine/dbal/src/Schema/DefaultSchemaManagerFactory.php @@ -0,0 +1,20 @@ +getDatabasePlatform()->createSchemaManager($connection); + } +} diff --git a/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php b/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php new file mode 100644 index 000000000..cc7acea03 --- /dev/null +++ b/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php @@ -0,0 +1,21 @@ +getName(), + implode(', ', $foreignKey->getColumns()), + $foreignKey->getForeignTableName(), + implode(', ', $foreignKey->getForeignColumns()), + ), + ); + } +} diff --git a/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php b/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php new file mode 100644 index 000000000..008bd5f0e --- /dev/null +++ b/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php @@ -0,0 +1,21 @@ +_columns[$column] = new Identifier($column); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getColumns() { @@ -94,7 +92,7 @@ public function getColumns() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getQuotedColumns(AbstractPlatform $platform) { @@ -118,9 +116,7 @@ public function getQuotedColumns(AbstractPlatform $platform) return $columns; } - /** - * @return string[] - */ + /** @return string[] */ public function getUnquotedColumns() { return array_map([$this, 'trimQuotes'], $this->getColumns()); @@ -136,17 +132,13 @@ public function isSimpleIndex() return ! $this->_isPrimary && ! $this->_isUnique; } - /** - * @return bool - */ + /** @return bool */ public function isUnique() { return $this->_isUnique; } - /** - * @return bool - */ + /** @return bool */ public function isPrimary() { return $this->_isPrimary; @@ -194,11 +186,21 @@ public function spansColumns(array $columnNames) } /** - * Checks if the other index already fulfills all the indexing and constraint needs of the current one. + * Keeping misspelled function name for backwards compatibility + * + * @deprecated Use {@see isFulfilledBy()} instead. * * @return bool */ public function isFullfilledBy(Index $other) + { + return $this->isFulfilledBy($other); + } + + /** + * Checks if the other index already fulfills all the indexing and constraint needs of the current one. + */ + public function isFulfilledBy(Index $other): bool { // allow the other index to be equally large only. It being larger is an option // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo) @@ -326,9 +328,7 @@ public function getOption($name) return $this->options[strtolower($name)]; } - /** - * @return mixed[] - */ + /** @return mixed[] */ public function getOptions() { return $this->options; diff --git a/doctrine/dbal/src/Schema/LegacySchemaManagerFactory.php b/doctrine/dbal/src/Schema/LegacySchemaManagerFactory.php new file mode 100644 index 000000000..01c856b6e --- /dev/null +++ b/doctrine/dbal/src/Schema/LegacySchemaManagerFactory.php @@ -0,0 +1,19 @@ +getDriver()->getSchemaManager( + $connection, + $connection->getDatabasePlatform(), + ); + } +} diff --git a/doctrine/dbal/src/Schema/MySQLSchemaManager.php b/doctrine/dbal/src/Schema/MySQLSchemaManager.php index 81ba4cab6..5faae24fa 100644 --- a/doctrine/dbal/src/Schema/MySQLSchemaManager.php +++ b/doctrine/dbal/src/Schema/MySQLSchemaManager.php @@ -7,12 +7,15 @@ use Doctrine\DBAL\Platforms\MySQL; use Doctrine\DBAL\Platforms\MySQL\CollationMetadataProvider\CachingCollationMetadataProvider; use Doctrine\DBAL\Platforms\MySQL\CollationMetadataProvider\ConnectionCollationMetadataProvider; +use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Type; +use Doctrine\Deprecations\Deprecation; use function array_change_key_case; use function array_shift; use function assert; use function explode; +use function implode; use function is_string; use function preg_match; use function strpos; @@ -29,9 +32,7 @@ */ class MySQLSchemaManager extends AbstractSchemaManager { - /** - * @see https://mariadb.com/kb/en/library/string-literals/#escape-sequences - */ + /** @see https://mariadb.com/kb/en/library/string-literals/#escape-sequences */ private const MARIADB_ESCAPE_SEQUENCES = [ '\\0' => "\0", "\\'" => "'", @@ -50,34 +51,80 @@ class MySQLSchemaManager extends AbstractSchemaManager ]; /** - * {@inheritdoc} + * {@inheritDoc} */ - protected function _getPortableViewDefinition($view) + public function listTableNames() { - return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); + return $this->doListTableNames(); } /** - * {@inheritdoc} + * {@inheritDoc} */ - protected function _getPortableTableDefinition($table) + public function listTables() { - return array_shift($table); + return $this->doListTables(); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@see introspectTable()} instead. */ - protected function _getPortableUserDefinition($user) + public function listTableDetails($name) { - return [ - 'user' => $user['User'], - 'password' => $user['Password'], - ]; + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5595', + '%s is deprecated. Use introspectTable() instead.', + __METHOD__, + ); + + return $this->doListTableDetails($name); } /** - * {@inheritdoc} + * {@inheritDoc} + */ + public function listTableColumns($table, $database = null) + { + return $this->doListTableColumns($table, $database); + } + + /** + * {@inheritDoc} + */ + public function listTableIndexes($table) + { + return $this->doListTableIndexes($table); + } + + /** + * {@inheritDoc} + */ + public function listTableForeignKeys($table, $database = null) + { + return $this->doListTableForeignKeys($table, $database); + } + + /** + * {@inheritDoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); + } + + /** + * {@inheritDoc} + */ + protected function _getPortableTableDefinition($table) + { + return array_shift($table); + } + + /** + * {@inheritDoc} */ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { @@ -107,7 +154,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableDatabaseDefinition($database) { @@ -115,7 +162,7 @@ protected function _getPortableDatabaseDefinition($database) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableColumnDefinition($tableColumn) { @@ -136,7 +183,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) $scale = null; $precision = null; - $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $origType = $this->_platform->getDoctrineTypeMapping($dbType); // In cases where not connected to a database DESCRIBE $table does not return 'Comment' if (isset($tableColumn['comment'])) { @@ -144,6 +191,12 @@ protected function _getPortableTableColumnDefinition($tableColumn) $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); } + // Check underlying database type where doctrine type is inferred from DC2Type comment + // and set a flag if it is not as expected. + if ($origType !== $type && $this->expectedDbType($type, $tableColumn) !== $dbType) { + $tableColumn['declarationMismatch'] = true; + } + switch ($dbType) { case 'char': case 'binary': @@ -159,7 +212,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) preg_match( '([A-Za-z]+\(([0-9]+),([0-9]+)\))', $tableColumn['type'], - $match + $match, ) === 1 ) { $precision = $match[1]; @@ -239,9 +292,35 @@ protected function _getPortableTableColumnDefinition($tableColumn) $column->setPlatformOption('collation', $tableColumn['collation']); } + if (isset($tableColumn['declarationMismatch'])) { + $column->setPlatformOption('declarationMismatch', $tableColumn['declarationMismatch']); + } + return $column; } + /** + * Returns the database data type for a given doctrine type and column + * + * Note that for data types that depend on length where length is not part of the column definition + * and therefore the $tableColumn['length'] will not be set, for example TEXT (which could be LONGTEXT, + * MEDIUMTEXT) or BLOB (LONGBLOB or TINYBLOB), the expectedDbType cannot be inferred exactly, merely + * the default type. + * + * This method is intended to be used to determine underlying database type where doctrine type is + * inferred from a DC2Type comment. + * + * @param mixed[] $tableColumn + */ + private function expectedDbType(string $type, array $tableColumn): string + { + $_type = Type::getType($type); + $expectedDbType = strtolower($_type->getSQLDeclaration($tableColumn, $this->_platform)); + $expectedDbType = strtok($expectedDbType, '(), '); + + return $expectedDbType === false ? '' : $expectedDbType; + } + /** * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers. * @@ -283,7 +362,7 @@ private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?str } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableForeignKeysList($tableForeignKeys) { @@ -313,69 +392,217 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name']; } - $result = []; - foreach ($list as $constraint) { - $result[] = new ForeignKeyConstraint( - $constraint['local'], - $constraint['foreignTable'], - $constraint['foreign'], - $constraint['name'], - [ - 'onDelete' => $constraint['onDelete'], - 'onUpdate' => $constraint['onUpdate'], - ] - ); - } - - return $result; + return parent::_getPortableTableForeignKeysList($list); } /** - * {@inheritdoc} + * {@inheritDoc} */ - public function listTableDetails($name) + protected function _getPortableTableForeignKeyDefinition($tableForeignKey): ForeignKeyConstraint { - $table = parent::listTableDetails($name); + return new ForeignKeyConstraint( + $tableForeignKey['local'], + $tableForeignKey['foreignTable'], + $tableForeignKey['foreign'], + $tableForeignKey['name'], + [ + 'onDelete' => $tableForeignKey['onDelete'], + 'onUpdate' => $tableForeignKey['onUpdate'], + ], + ); + } - $sql = $this->_platform->getListTableMetadataSQL($name); + public function createComparator(): Comparator + { + return new MySQL\Comparator( + $this->_platform, + new CachingCollationMetadataProvider( + new ConnectionCollationMetadataProvider($this->_conn), + ), + ); + } - $tableOptions = $this->_conn->fetchAssociative($sql); + protected function selectTableNames(string $databaseName): Result + { + $sql = <<<'SQL' +SELECT TABLE_NAME +FROM information_schema.TABLES +WHERE TABLE_SCHEMA = ? + AND TABLE_TYPE = 'BASE TABLE' +ORDER BY TABLE_NAME +SQL; + + return $this->_conn->executeQuery($sql, [$databaseName]); + } - if ($tableOptions === false) { - return $table; - } + protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result + { + [$columnTypeSQL, $joinCheckConstraintSQL] = $this->_platform->getColumnTypeSQLSnippets(); - $table->addOption('engine', $tableOptions['ENGINE']); + $sql = 'SELECT'; - if ($tableOptions['TABLE_COLLATION'] !== null) { - $table->addOption('collation', $tableOptions['TABLE_COLLATION']); + if ($tableName === null) { + $sql .= ' c.TABLE_NAME,'; } - $table->addOption('charset', $tableOptions['CHARACTER_SET_NAME']); + $sql .= <<_conn->executeQuery($sql, $params); + } + + protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; - if ($tableOptions['AUTO_INCREMENT'] !== null) { - $table->addOption('autoincrement', $tableOptions['AUTO_INCREMENT']); + if ($tableName === null) { + $sql .= ' TABLE_NAME,'; } - $table->addOption('comment', $tableOptions['TABLE_COMMENT']); - $table->addOption('create_options', $this->parseCreateOptions($tableOptions['CREATE_OPTIONS'])); + $sql .= <<<'SQL' + NON_UNIQUE AS Non_Unique, + INDEX_NAME AS Key_name, + COLUMN_NAME AS Column_Name, + SUB_PART AS Sub_Part, + INDEX_TYPE AS Index_Type +FROM information_schema.STATISTICS +SQL; + + $conditions = ['TABLE_SCHEMA = ?']; + $params = [$databaseName]; + + if ($tableName !== null) { + $conditions[] = 'TABLE_NAME = ?'; + $params[] = $tableName; + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY SEQ_IN_INDEX'; - return $table; + return $this->_conn->executeQuery($sql, $params); } - public function createComparator(): Comparator + protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result { - return new MySQL\Comparator( - $this->getDatabasePlatform(), - new CachingCollationMetadataProvider( - new ConnectionCollationMetadataProvider($this->_conn) - ) - ); + $sql = 'SELECT DISTINCT'; + + if ($tableName === null) { + $sql .= ' k.TABLE_NAME,'; + } + + $sql .= <<<'SQL' + k.CONSTRAINT_NAME, + k.COLUMN_NAME, + k.REFERENCED_TABLE_NAME, + k.REFERENCED_COLUMN_NAME, + k.ORDINAL_POSITION /*!50116, + c.UPDATE_RULE, + c.DELETE_RULE */ +FROM information_schema.key_column_usage k /*!50116 +INNER JOIN information_schema.referential_constraints c +ON c.CONSTRAINT_NAME = k.CONSTRAINT_NAME +AND c.TABLE_NAME = k.TABLE_NAME */ +SQL; + + $conditions = ['k.TABLE_SCHEMA = ?']; + $params = [$databaseName]; + + if ($tableName !== null) { + $conditions[] = 'k.TABLE_NAME = ?'; + $params[] = $tableName; + } + + $conditions[] = 'k.REFERENCED_COLUMN_NAME IS NOT NULL'; + + $sql .= ' WHERE ' . implode(' AND ', $conditions) + // The schema name is passed multiple times in the WHERE clause instead of using a JOIN condition + // in order to avoid performance issues on MySQL older than 8.0 and the corresponding MariaDB versions + // caused by https://bugs.mysql.com/bug.php?id=81347. + // Use a string literal for the database name since the internal PDO SQL parser + // cannot recognize parameter placeholders inside conditional comments + . ' /*!50116 AND c.CONSTRAINT_SCHEMA = ' . $this->_conn->quote($databaseName) . ' */' + . ' ORDER BY k.ORDINAL_POSITION'; + + return $this->_conn->executeQuery($sql, $params); } /** - * @return string[]|true[] + * {@inheritDoc} */ + protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array + { + $sql = <<<'SQL' + SELECT t.TABLE_NAME, + t.ENGINE, + t.AUTO_INCREMENT, + t.TABLE_COMMENT, + t.CREATE_OPTIONS, + t.TABLE_COLLATION, + ccsa.CHARACTER_SET_NAME + FROM information_schema.TABLES t + INNER JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY ccsa + ON ccsa.COLLATION_NAME = t.TABLE_COLLATION +SQL; + + $conditions = ['t.TABLE_SCHEMA = ?']; + $params = [$databaseName]; + + if ($tableName !== null) { + $conditions[] = 't.TABLE_NAME = ?'; + $params[] = $tableName; + } + + $conditions[] = "t.TABLE_TYPE = 'BASE TABLE'"; + + $sql .= ' WHERE ' . implode(' AND ', $conditions); + + /** @var array> $metadata */ + $metadata = $this->_conn->executeQuery($sql, $params) + ->fetchAllAssociativeIndexed(); + + $tableOptions = []; + foreach ($metadata as $table => $data) { + $data = array_change_key_case($data, CASE_LOWER); + + $tableOptions[$table] = [ + 'engine' => $data['engine'], + 'collation' => $data['table_collation'], + 'charset' => $data['character_set_name'], + 'autoincrement' => $data['auto_increment'], + 'comment' => $data['table_comment'], + 'create_options' => $this->parseCreateOptions($data['create_options']), + ]; + } + + return $tableOptions; + } + + /** @return string[]|true[] */ private function parseCreateOptions(?string $string): array { $options = []; diff --git a/doctrine/dbal/src/Schema/OracleSchemaManager.php b/doctrine/dbal/src/Schema/OracleSchemaManager.php index 762492e72..073752214 100644 --- a/doctrine/dbal/src/Schema/OracleSchemaManager.php +++ b/doctrine/dbal/src/Schema/OracleSchemaManager.php @@ -4,15 +4,19 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\OraclePlatform; +use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Type; +use Doctrine\Deprecations\Deprecation; use function array_change_key_case; use function array_values; +use function implode; use function is_string; use function preg_match; use function str_replace; use function strpos; use function strtolower; +use function strtoupper; use function trim; use const CASE_LOWER; @@ -25,29 +29,74 @@ class OracleSchemaManager extends AbstractSchemaManager { /** - * {@inheritdoc} + * {@inheritDoc} */ - protected function _getPortableViewDefinition($view) + public function listTableNames() { - $view = array_change_key_case($view, CASE_LOWER); + return $this->doListTableNames(); + } - return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']); + /** + * {@inheritDoc} + */ + public function listTables() + { + return $this->doListTables(); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated Use {@see introspectTable()} instead. */ - protected function _getPortableUserDefinition($user) + public function listTableDetails($name) { - $user = array_change_key_case($user, CASE_LOWER); + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5595', + '%s is deprecated. Use introspectTable() instead.', + __METHOD__, + ); - return [ - 'user' => $user['username'], - ]; + return $this->doListTableDetails($name); + } + + /** + * {@inheritDoc} + */ + public function listTableColumns($table, $database = null) + { + return $this->doListTableColumns($table, $database); + } + + /** + * {@inheritDoc} + */ + public function listTableIndexes($table) + { + return $this->doListTableIndexes($table); } /** - * {@inheritdoc} + * {@inheritDoc} + */ + public function listTableForeignKeys($table, $database = null) + { + return $this->doListTableForeignKeys($table, $database); + } + + /** + * {@inheritDoc} + */ + protected function _getPortableViewDefinition($view) + { + $view = array_change_key_case($view, CASE_LOWER); + + return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']); + } + + /** + * {@inheritDoc} */ protected function _getPortableTableDefinition($table) { @@ -57,7 +106,7 @@ protected function _getPortableTableDefinition($table) } /** - * {@inheritdoc} + * {@inheritDoc} * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html */ @@ -88,7 +137,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableColumnDefinition($tableColumn) { @@ -187,7 +236,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableForeignKeysList($tableForeignKeys) { @@ -215,22 +264,25 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) $list[$value['constraint_name']]['foreign'][$value['position']] = $foreignColumn; } - $result = []; - foreach ($list as $constraint) { - $result[] = new ForeignKeyConstraint( - array_values($constraint['local']), - $this->getQuotedIdentifierName($constraint['foreignTable']), - array_values($constraint['foreign']), - $this->getQuotedIdentifierName($constraint['name']), - ['onDelete' => $constraint['onDelete']] - ); - } + return parent::_getPortableTableForeignKeysList($list); + } - return $result; + /** + * {@inheritDoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey): ForeignKeyConstraint + { + return new ForeignKeyConstraint( + array_values($tableForeignKey['local']), + $this->getQuotedIdentifierName($tableForeignKey['foreignTable']), + array_values($tableForeignKey['foreign']), + $this->getQuotedIdentifierName($tableForeignKey['name']), + ['onDelete' => $tableForeignKey['onDelete']], + ); } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableSequenceDefinition($sequence) { @@ -239,12 +291,12 @@ protected function _getPortableSequenceDefinition($sequence) return new Sequence( $this->getQuotedIdentifierName($sequence['sequence_name']), (int) $sequence['increment_by'], - (int) $sequence['min_value'] + (int) $sequence['min_value'], ); } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableDatabaseDefinition($database) { @@ -254,7 +306,7 @@ protected function _getPortableDatabaseDefinition($database) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function createDatabase($database) { @@ -292,7 +344,7 @@ public function dropAutoincrement($table) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function dropTable($name) { @@ -318,22 +370,170 @@ private function getQuotedIdentifierName($identifier): string return $identifier; } + protected function selectTableNames(string $databaseName): Result + { + $sql = <<<'SQL' +SELECT TABLE_NAME +FROM ALL_TABLES +WHERE OWNER = :OWNER +ORDER BY TABLE_NAME +SQL; + + return $this->_conn->executeQuery($sql, ['OWNER' => $databaseName]); + } + + protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' C.TABLE_NAME,'; + } + + $sql .= <<<'SQL' + C.COLUMN_NAME, + C.DATA_TYPE, + C.DATA_DEFAULT, + C.DATA_PRECISION, + C.DATA_SCALE, + C.CHAR_LENGTH, + C.DATA_LENGTH, + C.NULLABLE, + D.COMMENTS + FROM ALL_TAB_COLUMNS C + INNER JOIN ALL_TABLES T + ON T.OWNER = C.OWNER + AND T.TABLE_NAME = C.TABLE_NAME + LEFT JOIN ALL_COL_COMMENTS D + ON D.OWNER = C.OWNER + AND D.TABLE_NAME = C.TABLE_NAME + AND D.COLUMN_NAME = C.COLUMN_NAME +SQL; + + $conditions = ['C.OWNER = :OWNER']; + $params = ['OWNER' => $databaseName]; + + if ($tableName !== null) { + $conditions[] = 'C.TABLE_NAME = :TABLE_NAME'; + $params['TABLE_NAME'] = $tableName; + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY C.COLUMN_ID'; + + return $this->_conn->executeQuery($sql, $params); + } + + protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' IND_COL.TABLE_NAME,'; + } + + $sql .= <<<'SQL' + IND_COL.INDEX_NAME AS NAME, + IND.INDEX_TYPE AS TYPE, + DECODE(IND.UNIQUENESS, 'NONUNIQUE', 0, 'UNIQUE', 1) AS IS_UNIQUE, + IND_COL.COLUMN_NAME, + IND_COL.COLUMN_POSITION AS COLUMN_POS, + CON.CONSTRAINT_TYPE AS IS_PRIMARY + FROM ALL_IND_COLUMNS IND_COL + LEFT JOIN ALL_INDEXES IND + ON IND.OWNER = IND_COL.INDEX_OWNER + AND IND.INDEX_NAME = IND_COL.INDEX_NAME + LEFT JOIN ALL_CONSTRAINTS CON + ON CON.OWNER = IND_COL.INDEX_OWNER + AND CON.INDEX_NAME = IND_COL.INDEX_NAME +SQL; + + $conditions = ['IND_COL.INDEX_OWNER = :OWNER']; + $params = ['OWNER' => $databaseName]; + + if ($tableName !== null) { + $conditions[] = 'IND_COL.TABLE_NAME = :TABLE_NAME'; + $params['TABLE_NAME'] = $tableName; + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY IND_COL.TABLE_NAME, IND_COL.INDEX_NAME' + . ', IND_COL.COLUMN_POSITION'; + + return $this->_conn->executeQuery($sql, $params); + } + + protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' COLS.TABLE_NAME,'; + } + + $sql .= <<<'SQL' + ALC.CONSTRAINT_NAME, + ALC.DELETE_RULE, + COLS.COLUMN_NAME LOCAL_COLUMN, + COLS.POSITION, + R_COLS.TABLE_NAME REFERENCES_TABLE, + R_COLS.COLUMN_NAME FOREIGN_COLUMN + FROM ALL_CONS_COLUMNS COLS + LEFT JOIN ALL_CONSTRAINTS ALC ON ALC.OWNER = COLS.OWNER AND ALC.CONSTRAINT_NAME = COLS.CONSTRAINT_NAME + LEFT JOIN ALL_CONS_COLUMNS R_COLS ON R_COLS.OWNER = ALC.R_OWNER AND + R_COLS.CONSTRAINT_NAME = ALC.R_CONSTRAINT_NAME AND + R_COLS.POSITION = COLS.POSITION +SQL; + + $conditions = ["ALC.CONSTRAINT_TYPE = 'R'", 'COLS.OWNER = :OWNER']; + $params = ['OWNER' => $databaseName]; + + if ($tableName !== null) { + $conditions[] = 'COLS.TABLE_NAME = :TABLE_NAME'; + $params['TABLE_NAME'] = $tableName; + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY COLS.TABLE_NAME, COLS.CONSTRAINT_NAME' + . ', COLS.POSITION'; + + return $this->_conn->executeQuery($sql, $params); + } + /** - * {@inheritdoc} + * {@inheritDoc} */ - public function listTableDetails($name): Table + protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { - $table = parent::listTableDetails($name); + $sql = 'SELECT TABLE_NAME, COMMENTS'; + + $conditions = ['OWNER = :OWNER']; + $params = ['OWNER' => $databaseName]; + + if ($tableName !== null) { + $conditions[] = 'TABLE_NAME = :TABLE_NAME'; + $params['TABLE_NAME'] = $tableName; + } + + $sql .= ' FROM ALL_TAB_COMMENTS WHERE ' . implode(' AND ', $conditions); - $sql = $this->_platform->getListTableCommentsSQL($name); + /** @var array> $metadata */ + $metadata = $this->_conn->executeQuery($sql, $params) + ->fetchAllAssociativeIndexed(); - $tableOptions = $this->_conn->fetchAssociative($sql); + $tableOptions = []; + foreach ($metadata as $table => $data) { + $data = array_change_key_case($data, CASE_LOWER); - if ($tableOptions !== false) { - $tableOptions = array_change_key_case($tableOptions, CASE_LOWER); - $table->addOption('comment', $tableOptions['comments']); + $tableOptions[$table] = [ + 'comment' => $data['comments'], + ]; } - return $table; + return $tableOptions; + } + + protected function normalizeName(string $name): string + { + $identifier = new Identifier($name); + + return $identifier->isQuoted() ? $identifier->getName() : strtoupper($name); } } diff --git a/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php b/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php index 03239bde5..13fde63b2 100644 --- a/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php +++ b/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php @@ -4,17 +4,20 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Result; +use Doctrine\DBAL\Types\JsonType; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; use Doctrine\Deprecations\Deprecation; use function array_change_key_case; use function array_filter; -use function array_keys; use function array_map; +use function array_merge; use function array_shift; use function assert; use function explode; +use function get_class; use function implode; use function in_array; use function preg_match; @@ -35,7 +38,64 @@ class PostgreSQLSchemaManager extends AbstractSchemaManager { /** @var string[]|null */ - private $existingSchemaPaths; + private ?array $existingSchemaPaths = null; + + /** + * {@inheritDoc} + */ + public function listTableNames() + { + return $this->doListTableNames(); + } + + /** + * {@inheritDoc} + */ + public function listTables() + { + return $this->doListTables(); + } + + /** + * {@inheritDoc} + * + * @deprecated Use {@see introspectTable()} instead. + */ + public function listTableDetails($name) + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5595', + '%s is deprecated. Use introspectTable() instead.', + __METHOD__, + ); + + return $this->doListTableDetails($name); + } + + /** + * {@inheritDoc} + */ + public function listTableColumns($table, $database = null) + { + return $this->doListTableColumns($table, $database); + } + + /** + * {@inheritDoc} + */ + public function listTableIndexes($table) + { + return $this->doListTableIndexes($table); + } + + /** + * {@inheritDoc} + */ + public function listTableForeignKeys($table, $database = null) + { + return $this->doListTableForeignKeys($table, $database); + } /** * Gets all the existing schema names. @@ -52,7 +112,7 @@ public function getSchemaNames() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'PostgreSQLSchemaManager::getSchemaNames() is deprecated,' - . ' use PostgreSQLSchemaManager::listSchemaNames() instead.' + . ' use PostgreSQLSchemaManager::listSchemaNames() instead.', ); return $this->listNamespaceNames(); @@ -69,7 +129,7 @@ public function listSchemaNames(): array FROM information_schema.schemata WHERE schema_name NOT LIKE 'pg\_%' AND schema_name != 'information_schema' -SQL +SQL, ); } @@ -83,7 +143,7 @@ public function getSchemaSearchPaths() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4821', - 'PostgreSQLSchemaManager::getSchemaSearchPaths() is deprecated.' + 'PostgreSQLSchemaManager::getSchemaSearchPaths() is deprecated.', ); $params = $this->_conn->getParams(); @@ -158,7 +218,7 @@ public function determineExistingSchemaSearchPaths() } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) { @@ -169,7 +229,7 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) preg_match( '(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], - $match + $match, ) === 1 ) { $onUpdate = $match[1]; @@ -179,7 +239,7 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) preg_match( '(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], - $match + $match, ) === 1 ) { $onDelete = $match[1]; @@ -199,12 +259,12 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) $foreignTable, $foreignColumns, $tableForeignKey['conname'], - ['onUpdate' => $onUpdate, 'onDelete' => $onDelete] + ['onUpdate' => $onUpdate, 'onDelete' => $onDelete], ); } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableViewDefinition($view) { @@ -212,18 +272,7 @@ protected function _getPortableViewDefinition($view) } /** - * {@inheritdoc} - */ - protected function _getPortableUserDefinition($user) - { - return [ - 'user' => $user['usename'], - 'password' => $user['passwd'], - ]; - } - - /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableDefinition($table) { @@ -237,7 +286,7 @@ protected function _getPortableTableDefinition($table) } /** - * {@inheritdoc} + * {@inheritDoc} * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html */ @@ -249,7 +298,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null $columnNameSql = sprintf( 'SELECT attnum, attname FROM pg_attribute WHERE attrelid=%d AND attnum IN (%s) ORDER BY attnum ASC', $row['indrelid'], - implode(' ,', $colNumbers) + implode(' ,', $colNumbers), ); $indexColumns = $this->_conn->fetchAllAssociative($columnNameSql); @@ -276,7 +325,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableDatabaseDefinition($database) { @@ -284,33 +333,7 @@ protected function _getPortableDatabaseDefinition($database) } /** - * {@inheritdoc} - */ - protected function _getPortableSequencesList($sequences) - { - $sequenceDefinitions = []; - - foreach ($sequences as $sequence) { - if ($sequence['schemaname'] !== 'public') { - $sequenceName = $sequence['schemaname'] . '.' . $sequence['relname']; - } else { - $sequenceName = $sequence['relname']; - } - - $sequenceDefinitions[$sequenceName] = $sequence; - } - - $list = []; - - foreach ($this->filterAssetNames(array_keys($sequenceDefinitions)) as $sequenceName) { - $list[] = $this->_getPortableSequenceDefinition($sequenceDefinitions[$sequenceName]); - } - - return $list; - } - - /** - * {@inheritdoc} + * {@inheritDoc} * * @deprecated Use {@see listSchemaNames()} instead. */ @@ -320,14 +343,14 @@ protected function getPortableNamespaceDefinition(array $namespace) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'PostgreSQLSchemaManager::getPortableNamespaceDefinition() is deprecated,' - . ' use PostgreSQLSchemaManager::listSchemaNames() instead.' + . ' use PostgreSQLSchemaManager::listSchemaNames() instead.', ); return $namespace['nspname']; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableSequenceDefinition($sequence) { @@ -337,20 +360,11 @@ protected function _getPortableSequenceDefinition($sequence) $sequenceName = $sequence['relname']; } - if (! isset($sequence['increment_by'], $sequence['min_value'])) { - /** @var string[] $data */ - $data = $this->_conn->fetchAssociative( - 'SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName) - ); - - $sequence += $data; - } - return new Sequence($sequenceName, (int) $sequence['increment_by'], (int) $sequence['min_value']); } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableColumnDefinition($tableColumn) { @@ -479,7 +493,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) preg_match( '([A-Za-z]+\(([0-9]+),([0-9]+)\))', $tableColumn['complete_type'], - $match + $match, ) === 1 ) { $precision = $match[1]; @@ -503,7 +517,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) $tableColumn['default'] !== null && preg_match( "('([^']+)'::)", $tableColumn['default'], - $match + $match, ) === 1 ) { $tableColumn['default'] = $match[1]; @@ -516,7 +530,6 @@ protected function _getPortableTableColumnDefinition($tableColumn) 'precision' => $precision, 'scale' => $scale, 'fixed' => $fixed, - 'unsigned' => false, 'autoincrement' => $autoincrement, 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' ? $tableColumn['comment'] @@ -525,11 +538,25 @@ protected function _getPortableTableColumnDefinition($tableColumn) $column = new Column($tableColumn['field'], Type::getType($type), $options); - if (isset($tableColumn['collation']) && ! empty($tableColumn['collation'])) { + if (! empty($tableColumn['collation'])) { $column->setPlatformOption('collation', $tableColumn['collation']); } if ($column->getType()->getName() === Types::JSON) { + if (! $column->getType() instanceof JsonType) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5049', + <<<'DEPRECATION' + %s not extending %s while being named %s is deprecated, + and will lead to jsonb never to being used in 4.0., + DEPRECATION, + get_class($column->getType()), + JsonType::class, + Types::JSON, + ); + } + $column->setPlatformOption('jsonb', $jsonb); } @@ -564,21 +591,184 @@ private function parseDefaultExpression(?string $default): ?string return str_replace("''", "'", $default); } + protected function selectTableNames(string $databaseName): Result + { + $sql = <<<'SQL' +SELECT quote_ident(table_name) AS table_name, + table_schema AS schema_name +FROM information_schema.tables +WHERE table_catalog = ? + AND table_schema NOT LIKE 'pg\_%' + AND table_schema != 'information_schema' + AND table_name != 'geometry_columns' + AND table_name != 'spatial_ref_sys' + AND table_type = 'BASE TABLE' +SQL; + + return $this->_conn->executeQuery($sql, [$databaseName]); + } + + protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' c.relname AS table_name, n.nspname AS schema_name,'; + } + + $sql .= <<<'SQL' + a.attnum, + quote_ident(a.attname) AS field, + t.typname AS type, + format_type(a.atttypid, a.atttypmod) AS complete_type, + (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation, + (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, + (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM + pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, + a.attnotnull AS isnotnull, + (SELECT 't' + FROM pg_index + WHERE c.oid = pg_index.indrelid + AND pg_index.indkey[0] = a.attnum + AND pg_index.indisprimary = 't' + ) AS pri, + (SELECT pg_get_expr(adbin, adrelid) + FROM pg_attrdef + WHERE c.oid = pg_attrdef.adrelid + AND pg_attrdef.adnum=a.attnum + ) AS default, + (SELECT pg_description.description + FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid + ) AS comment + FROM pg_attribute a + INNER JOIN pg_class c + ON c.oid = a.attrelid + INNER JOIN pg_type t + ON t.oid = a.atttypid + INNER JOIN pg_namespace n + ON n.oid = c.relnamespace + LEFT JOIN pg_depend d + ON d.objid = c.oid + AND d.deptype = 'e' + AND d.classid = (SELECT oid FROM pg_class WHERE relname = 'pg_class') +SQL; + + $conditions = array_merge([ + 'a.attnum > 0', + "c.relkind = 'r'", + 'd.refobjid IS NULL', + ], $this->buildQueryConditions($tableName)); + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY a.attnum'; + + return $this->_conn->executeQuery($sql); + } + + protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' tc.relname AS table_name, tn.nspname AS schema_name,'; + } + + $sql .= <<<'SQL' + quote_ident(ic.relname) AS relname, + i.indisunique, + i.indisprimary, + i.indkey, + i.indrelid, + pg_get_expr(indpred, indrelid) AS "where" + FROM pg_index i + JOIN pg_class AS tc ON tc.oid = i.indrelid + JOIN pg_namespace tn ON tn.oid = tc.relnamespace + JOIN pg_class AS ic ON ic.oid = i.indexrelid + WHERE ic.oid IN ( + SELECT indexrelid + FROM pg_index i, pg_class c, pg_namespace n +SQL; + + $conditions = array_merge([ + 'c.oid = i.indrelid', + 'c.relnamespace = n.oid', + ], $this->buildQueryConditions($tableName)); + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ')'; + + return $this->_conn->executeQuery($sql); + } + + protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' tc.relname AS table_name, tn.nspname AS schema_name,'; + } + + $sql .= <<<'SQL' + quote_ident(r.conname) as conname, + pg_get_constraintdef(r.oid, true) as condef + FROM pg_constraint r + JOIN pg_class AS tc ON tc.oid = r.conrelid + JOIN pg_namespace tn ON tn.oid = tc.relnamespace + WHERE r.conrelid IN + ( + SELECT c.oid + FROM pg_class c, pg_namespace n +SQL; + + $conditions = array_merge(['n.oid = c.relnamespace'], $this->buildQueryConditions($tableName)); + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ") AND r.contype = 'f'"; + + return $this->_conn->executeQuery($sql); + } + /** - * {@inheritdoc} + * {@inheritDoc} */ - public function listTableDetails($name): Table + protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { - $table = parent::listTableDetails($name); + $sql = <<<'SQL' +SELECT c.relname, + CASE c.relpersistence WHEN 'u' THEN true ELSE false END as unlogged, + obj_description(c.oid, 'pg_class') AS comment +FROM pg_class c + INNER JOIN pg_namespace n + ON n.oid = c.relnamespace +SQL; - $sql = $this->_platform->getListTableMetadataSQL($name); + $conditions = array_merge(["c.relkind = 'r'"], $this->buildQueryConditions($tableName)); - $tableOptions = $this->_conn->fetchAssociative($sql); + $sql .= ' WHERE ' . implode(' AND ', $conditions); + + return $this->_conn->fetchAllAssociativeIndexed($sql); + } - if ($tableOptions !== false) { - $table->addOption('comment', $tableOptions['table_comment']); + /** + * @param string|null $tableName + * + * @return list + */ + private function buildQueryConditions($tableName): array + { + $conditions = []; + + if ($tableName !== null) { + if (strpos($tableName, '.') !== false) { + [$schemaName, $tableName] = explode('.', $tableName); + $conditions[] = 'n.nspname = ' . $this->_platform->quoteStringLiteral($schemaName); + } else { + $conditions[] = 'n.nspname = ANY(current_schemas(false))'; + } + + $identifier = new Identifier($tableName); + $conditions[] = 'c.relname = ' . $this->_platform->quoteStringLiteral($identifier->getName()); } - return $table; + $conditions[] = "n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')"; + + return $conditions; } } diff --git a/doctrine/dbal/src/Schema/SQLServerSchemaManager.php b/doctrine/dbal/src/Schema/SQLServerSchemaManager.php index 8a8713f09..64538e689 100644 --- a/doctrine/dbal/src/Schema/SQLServerSchemaManager.php +++ b/doctrine/dbal/src/Schema/SQLServerSchemaManager.php @@ -5,11 +5,15 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\SQLServer; use Doctrine\DBAL\Platforms\SQLServerPlatform; +use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Type; use Doctrine\Deprecations\Deprecation; +use function array_change_key_case; use function assert; use function count; +use function explode; +use function implode; use function is_string; use function preg_match; use function sprintf; @@ -17,6 +21,8 @@ use function strpos; use function strtok; +use const CASE_LOWER; + /** * SQL Server Schema Manager. * @@ -24,8 +30,64 @@ */ class SQLServerSchemaManager extends AbstractSchemaManager { - /** @var string|null */ - private $databaseCollation; + private ?string $databaseCollation = null; + + /** + * {@inheritDoc} + */ + public function listTableNames() + { + return $this->doListTableNames(); + } + + /** + * {@inheritDoc} + */ + public function listTables() + { + return $this->doListTables(); + } + + /** + * {@inheritDoc} + * + * @deprecated Use {@see introspectTable()} instead. + */ + public function listTableDetails($name) + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5595', + '%s is deprecated. Use introspectTable() instead.', + __METHOD__, + ); + + return $this->doListTableDetails($name); + } + + /** + * {@inheritDoc} + */ + public function listTableColumns($table, $database = null) + { + return $this->doListTableColumns($table, $database); + } + + /** + * {@inheritDoc} + */ + public function listTableIndexes($table) + { + return $this->doListTableIndexes($table); + } + + /** + * {@inheritDoc} + */ + public function listTableForeignKeys($table, $database = null) + { + return $this->doListTableForeignKeys($table, $database); + } /** * {@inheritDoc} @@ -37,12 +99,12 @@ public function listSchemaNames(): array SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys') -SQL +SQL, ); } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableSequenceDefinition($sequence) { @@ -50,7 +112,7 @@ protected function _getPortableSequenceDefinition($sequence) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableColumnDefinition($tableColumn) { @@ -147,7 +209,7 @@ private function parseDefaultExpression(string $value): ?string } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableForeignKeysList($tableForeignKeys) { @@ -177,7 +239,7 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { @@ -191,7 +253,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) { @@ -200,24 +262,24 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) $tableForeignKey['foreign_table'], $tableForeignKey['foreign_columns'], $tableForeignKey['name'], - $tableForeignKey['options'] + $tableForeignKey['options'], ); } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableDefinition($table) { - if (isset($table['schema_name']) && $table['schema_name'] !== 'dbo') { - return $table['schema_name'] . '.' . $table['name']; + if ($table['schema_name'] !== 'dbo') { + return $table['schema_name'] . '.' . $table['table_name']; } - return $table['name']; + return $table['table_name']; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableDatabaseDefinition($database) { @@ -225,7 +287,7 @@ protected function _getPortableDatabaseDefinition($database) } /** - * {@inheritdoc} + * {@inheritDoc} * * @deprecated Use {@see listSchemaNames()} instead. */ @@ -235,14 +297,14 @@ protected function getPortableNamespaceDefinition(array $namespace) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'SQLServerSchemaManager::getPortableNamespaceDefinition() is deprecated,' - . ' use SQLServerSchemaManager::listSchemaNames() instead.' + . ' use SQLServerSchemaManager::listSchemaNames() instead.', ); return $namespace['name']; } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableViewDefinition($view) { @@ -251,39 +313,23 @@ protected function _getPortableViewDefinition($view) } /** - * {@inheritdoc} + * {@inheritDoc} */ - public function listTableIndexes($table) + public function alterTable(TableDiff $tableDiff) { - $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); + $droppedColumns = $tableDiff->getDroppedColumns(); - try { - $tableIndexes = $this->_conn->fetchAllAssociative($sql); - } catch (Exception $e) { - if (strpos($e->getMessage(), 'SQLSTATE [01000, 15472]') === 0) { - return []; - } - - throw $e; - } + if (count($droppedColumns) > 0) { + $tableName = ($tableDiff->getOldTable() ?? $tableDiff->getName($this->_platform))->getName(); - return $this->_getPortableTableIndexesList($tableIndexes, $table); - } - - /** - * {@inheritdoc} - */ - public function alterTable(TableDiff $tableDiff) - { - if (count($tableDiff->removedColumns) > 0) { - foreach ($tableDiff->removedColumns as $col) { - foreach ($this->getColumnConstraints($tableDiff->name, $col->getName()) as $constraint) { + foreach ($droppedColumns as $col) { + foreach ($this->getColumnConstraints($tableName, $col->getName()) as $constraint) { $this->_conn->executeStatement( sprintf( 'ALTER TABLE %s DROP CONSTRAINT %s', - $tableDiff->name, - $constraint - ) + $tableName, + $constraint, + ), ); } } @@ -317,41 +363,17 @@ private function getColumnConstraints(string $table, string $column): iterable AND c.name = ? SQL , - [$table, $column] + [$table, $column], ); } - /** - * @param string $name - * - * @throws Exception - */ - public function listTableDetails($name): Table - { - $table = parent::listTableDetails($name); - - $sql = $this->_platform->getListTableMetadataSQL($name); - - $tableOptions = $this->_conn->fetchAssociative($sql); - - if ($tableOptions !== false) { - $table->addOption('comment', $tableOptions['table_comment']); - } - - return $table; - } - - /** - * @throws Exception - */ + /** @throws Exception */ public function createComparator(): Comparator { - return new SQLServer\Comparator($this->getDatabasePlatform(), $this->getDatabaseCollation()); + return new SQLServer\Comparator($this->_platform, $this->getDatabaseCollation()); } - /** - * @throws Exception - */ + /** @throws Exception */ private function getDatabaseCollation(): string { if ($this->databaseCollation === null) { @@ -368,4 +390,214 @@ private function getDatabaseCollation(): string return $this->databaseCollation; } + + protected function selectTableNames(string $databaseName): Result + { + // The "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams + $sql = <<<'SQL' +SELECT name AS table_name, + SCHEMA_NAME(schema_id) AS schema_name +FROM sys.objects +WHERE type = 'U' + AND name != 'sysdiagrams' +ORDER BY name +SQL; + + return $this->_conn->executeQuery($sql); + } + + protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' obj.name AS table_name, scm.name AS schema_name,'; + } + + $sql .= <<<'SQL' + col.name, + type.name AS type, + col.max_length AS length, + ~col.is_nullable AS notnull, + def.definition AS [default], + col.scale, + col.precision, + col.is_identity AS autoincrement, + col.collation_name AS collation, + -- CAST avoids driver error for sql_variant type + CAST(prop.value AS NVARCHAR(MAX)) AS comment + FROM sys.columns AS col + JOIN sys.types AS type + ON col.user_type_id = type.user_type_id + JOIN sys.objects AS obj + ON col.object_id = obj.object_id + JOIN sys.schemas AS scm + ON obj.schema_id = scm.schema_id + LEFT JOIN sys.default_constraints def + ON col.default_object_id = def.object_id + AND col.object_id = def.parent_object_id + LEFT JOIN sys.extended_properties AS prop + ON obj.object_id = prop.major_id + AND col.column_id = prop.minor_id + AND prop.name = 'MS_Description' +SQL; + + // The "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams + $conditions = ["obj.type = 'U'", "obj.name != 'sysdiagrams'"]; + $params = []; + + if ($tableName !== null) { + $conditions[] = $this->getTableWhereClause($tableName, 'scm.name', 'obj.name'); + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions); + + return $this->_conn->executeQuery($sql, $params); + } + + protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' tbl.name AS table_name, scm.name AS schema_name,'; + } + + $sql .= <<<'SQL' + idx.name AS key_name, + col.name AS column_name, + ~idx.is_unique AS non_unique, + idx.is_primary_key AS [primary], + CASE idx.type + WHEN '1' THEN 'clustered' + WHEN '2' THEN 'nonclustered' + ELSE NULL + END AS flags + FROM sys.tables AS tbl + JOIN sys.schemas AS scm + ON tbl.schema_id = scm.schema_id + JOIN sys.indexes AS idx + ON tbl.object_id = idx.object_id + JOIN sys.index_columns AS idxcol + ON idx.object_id = idxcol.object_id + AND idx.index_id = idxcol.index_id + JOIN sys.columns AS col + ON idxcol.object_id = col.object_id + AND idxcol.column_id = col.column_id +SQL; + + $conditions = []; + $params = []; + + if ($tableName !== null) { + $conditions[] = $this->getTableWhereClause($tableName, 'scm.name', 'tbl.name'); + $sql .= ' WHERE ' . implode(' AND ', $conditions); + } + + $sql .= ' ORDER BY idx.index_id, idxcol.key_ordinal'; + + return $this->_conn->executeQuery($sql, $params); + } + + protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' OBJECT_NAME (f.parent_object_id) AS table_name, SCHEMA_NAME(f.schema_id) AS schema_name,'; + } + + $sql .= <<<'SQL' + f.name AS ForeignKey, + SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName, + OBJECT_NAME (f.parent_object_id) AS TableName, + COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName, + SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName, + OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, + COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName, + f.delete_referential_action_desc, + f.update_referential_action_desc + FROM sys.foreign_keys AS f + INNER JOIN sys.foreign_key_columns AS fc + INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id + ON f.OBJECT_ID = fc.constraint_object_id +SQL; + + $conditions = []; + $params = []; + + if ($tableName !== null) { + $conditions[] = $this->getTableWhereClause( + $tableName, + 'SCHEMA_NAME(f.schema_id)', + 'OBJECT_NAME(f.parent_object_id)', + ); + + $sql .= ' WHERE ' . implode(' AND ', $conditions); + } + + $sql .= ' ORDER BY fc.constraint_column_id'; + + return $this->_conn->executeQuery($sql, $params); + } + + /** + * {@inheritDoc} + */ + protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array + { + $sql = <<<'SQL' + SELECT + tbl.name, + p.value AS [table_comment] + FROM + sys.tables AS tbl + INNER JOIN sys.extended_properties AS p ON p.major_id=tbl.object_id AND p.minor_id=0 AND p.class=1 +SQL; + + $conditions = ["SCHEMA_NAME(tbl.schema_id) = N'dbo'", "p.name = N'MS_Description'"]; + $params = []; + + if ($tableName !== null) { + $conditions[] = "tbl.name = N'" . $tableName . "'"; + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions); + + /** @var array> $metadata */ + $metadata = $this->_conn->executeQuery($sql, $params) + ->fetchAllAssociativeIndexed(); + + $tableOptions = []; + foreach ($metadata as $table => $data) { + $data = array_change_key_case($data, CASE_LOWER); + + $tableOptions[$table] = [ + 'comment' => $data['table_comment'], + ]; + } + + return $tableOptions; + } + + /** + * Returns the where clause to filter schema and table name in a query. + * + * @param string $table The full qualified name of the table. + * @param string $schemaColumn The name of the column to compare the schema to in the where clause. + * @param string $tableColumn The name of the column to compare the table to in the where clause. + */ + private function getTableWhereClause($table, $schemaColumn, $tableColumn): string + { + if (strpos($table, '.') !== false) { + [$schema, $table] = explode('.', $table); + $schema = $this->_platform->quoteStringLiteral($schema); + $table = $this->_platform->quoteStringLiteral($table); + } else { + $schema = 'SCHEMA_NAME()'; + $table = $this->_platform->quoteStringLiteral($table); + } + + return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); + } } diff --git a/doctrine/dbal/src/Schema/Schema.php b/doctrine/dbal/src/Schema/Schema.php index 9be9e3508..703c83c59 100644 --- a/doctrine/dbal/src/Schema/Schema.php +++ b/doctrine/dbal/src/Schema/Schema.php @@ -2,11 +2,12 @@ namespace Doctrine\DBAL\Schema; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector; -use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; use Doctrine\DBAL\Schema\Visitor\NamespaceVisitor; use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\SQL\Builder\CreateSchemaObjectsSQLBuilder; +use Doctrine\DBAL\SQL\Builder\DropSchemaObjectsSQLBuilder; use Doctrine\Deprecations\Deprecation; use function array_keys; @@ -44,7 +45,7 @@ class Schema extends AbstractAsset * * @var string[] */ - private $namespaces = []; + private array $namespaces = []; /** @var Table[] */ protected $_tables = []; @@ -68,9 +69,7 @@ public function __construct( ?SchemaConfig $schemaConfig = null, array $namespaces = [] ) { - if ($schemaConfig === null) { - $schemaConfig = new SchemaConfig(); - } + $schemaConfig ??= new SchemaConfig(); $this->_schemaConfig = $schemaConfig; $this->_setName($schemaConfig->getName() ?? 'public'); @@ -98,7 +97,7 @@ public function hasExplicitForeignKeyIndexes() Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4822', - 'Schema::hasExplicitForeignKeyIndexes() is deprecated.' + 'Schema::hasExplicitForeignKeyIndexes() is deprecated.', ); return $this->_schemaConfig->hasExplicitForeignKeyIndexes(); @@ -192,9 +191,7 @@ public function getTable($name) return $this->_tables[$name]; } - /** - * @param string $name - */ + /** @param string $name */ private function getFullQualifiedAssetName($name): string { $name = $this->getUnquotedAssetName($name); @@ -267,7 +264,7 @@ public function getTableNames() 'https://github.com/doctrine/dbal/pull/4800', 'Schema::getTableNames() is deprecated.' . ' Use Schema::getTables() and Table::getName() instead.', - __METHOD__ + __METHOD__, ); return array_keys($this->_tables); @@ -302,9 +299,7 @@ public function getSequence($name) return $this->_sequences[$name]; } - /** - * @return Sequence[] - */ + /** @return Sequence[] */ public function getSequences() { return $this->_sequences; @@ -427,27 +422,29 @@ public function dropSequence($name) /** * Returns an array of necessary SQL queries to create the schema on the given platform. * - * @return string[] + * @return list + * + * @throws Exception */ public function toSql(AbstractPlatform $platform) { - $sqlCollector = new CreateSchemaSqlCollector($platform); - $this->visit($sqlCollector); + $builder = new CreateSchemaObjectsSQLBuilder($platform); - return $sqlCollector->getQueries(); + return $builder->buildSQL($this); } /** * Return an array of necessary SQL queries to drop the schema on the given platform. * - * @return string[] + * @return list + * + * @throws Exception */ public function toDropSql(AbstractPlatform $platform) { - $dropSqlCollector = new DropSchemaSqlCollector($platform); - $this->visit($dropSqlCollector); + $builder = new DropSchemaObjectsSQLBuilder($platform); - return $dropSqlCollector->getQueries(); + return $builder->buildSQL($this); } /** @@ -479,10 +476,18 @@ public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform } /** + * @deprecated + * * @return void */ public function visit(Visitor $visitor) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5435', + 'Schema::visit() is deprecated.', + ); + $visitor->acceptSchema($this); if ($visitor instanceof NamespaceVisitor) { diff --git a/doctrine/dbal/src/Schema/SchemaConfig.php b/doctrine/dbal/src/Schema/SchemaConfig.php index 92e0701f0..5e394514b 100644 --- a/doctrine/dbal/src/Schema/SchemaConfig.php +++ b/doctrine/dbal/src/Schema/SchemaConfig.php @@ -35,7 +35,7 @@ public function hasExplicitForeignKeyIndexes() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4822', - 'SchemaConfig::hasExplicitForeignKeyIndexes() is deprecated.' + 'SchemaConfig::hasExplicitForeignKeyIndexes() is deprecated.', ); return $this->hasExplicitForeignKeyIndexes; @@ -53,7 +53,7 @@ public function setExplicitForeignKeyIndexes($flag) Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4822', - 'SchemaConfig::setExplicitForeignKeyIndexes() is deprecated.' + 'SchemaConfig::setExplicitForeignKeyIndexes() is deprecated.', ); $this->hasExplicitForeignKeyIndexes = (bool) $flag; @@ -69,9 +69,7 @@ public function setMaxIdentifierLength($length) $this->maxIdentifierLength = (int) $length; } - /** - * @return int - */ + /** @return int */ public function getMaxIdentifierLength() { return $this->maxIdentifierLength; diff --git a/doctrine/dbal/src/Schema/SchemaDiff.php b/doctrine/dbal/src/Schema/SchemaDiff.php index 50d0d12fc..a6a866579 100644 --- a/doctrine/dbal/src/Schema/SchemaDiff.php +++ b/doctrine/dbal/src/Schema/SchemaDiff.php @@ -3,8 +3,11 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; +use function array_filter; use function array_merge; +use function count; /** * Differences between two schemas. @@ -14,12 +17,18 @@ */ class SchemaDiff { - /** @var Schema|null */ + /** + * @deprecated + * + * @var Schema|null + */ public $fromSchema; /** * All added namespaces. * + * @internal Use {@link getCreatedSchemas()} instead. + * * @var string[] */ public $newNamespaces = []; @@ -27,6 +36,8 @@ class SchemaDiff /** * All removed namespaces. * + * @internal Use {@link getDroppedSchemas()} instead. + * * @var string[] */ public $removedNamespaces = []; @@ -34,6 +45,8 @@ class SchemaDiff /** * All added tables. * + * @internal Use {@link getCreatedTables()} instead. + * * @var Table[] */ public $newTables = []; @@ -41,6 +54,8 @@ class SchemaDiff /** * All changed tables. * + * @internal Use {@link getAlteredTables()} instead. + * * @var TableDiff[] */ public $changedTables = []; @@ -48,35 +63,141 @@ class SchemaDiff /** * All removed tables. * + * @internal Use {@link getDroppedTables()} instead. + * * @var Table[] */ public $removedTables = []; - /** @var Sequence[] */ + /** + * @internal Use {@link getCreatedSequences()} instead. + * + * @var Sequence[] + */ public $newSequences = []; - /** @var Sequence[] */ + /** + * @internal Use {@link getAlteredSequences()} instead. + * + * @var Sequence[] + */ public $changedSequences = []; - /** @var Sequence[] */ + /** + * @internal Use {@link getDroppedSequences()} instead. + * + * @var Sequence[] + */ public $removedSequences = []; - /** @var ForeignKeyConstraint[] */ + /** + * @deprecated + * + * @var ForeignKeyConstraint[] + */ public $orphanedForeignKeys = []; /** * Constructs an SchemaDiff object. * - * @param Table[] $newTables - * @param TableDiff[] $changedTables - * @param Table[] $removedTables + * @internal The diff can be only instantiated by a {@see Comparator}. + * + * @param Table[] $newTables + * @param TableDiff[] $changedTables + * @param Table[] $removedTables + * @param array $createdSchemas + * @param array $droppedSchemas + * @param array $createdSequences + * @param array $alteredSequences + * @param array $droppedSequences + */ + public function __construct( + $newTables = [], + $changedTables = [], + $removedTables = [], + ?Schema $fromSchema = null, + $createdSchemas = [], + $droppedSchemas = [], + $createdSequences = [], + $alteredSequences = [], + $droppedSequences = [] + ) { + $this->newTables = $newTables; + + $this->changedTables = array_filter($changedTables, static function (TableDiff $diff): bool { + return ! $diff->isEmpty(); + }); + + $this->removedTables = $removedTables; + $this->fromSchema = $fromSchema; + $this->newNamespaces = $createdSchemas; + $this->removedNamespaces = $droppedSchemas; + $this->newSequences = $createdSequences; + $this->changedSequences = $alteredSequences; + $this->removedSequences = $droppedSequences; + } + + /** @return array */ + public function getCreatedSchemas(): array + { + return $this->newNamespaces; + } + + /** @return array */ + public function getDroppedSchemas(): array + { + return $this->removedNamespaces; + } + + /** @return array
*/ + public function getCreatedTables(): array + { + return $this->newTables; + } + + /** @return array */ + public function getAlteredTables(): array + { + return $this->changedTables; + } + + /** @return array
*/ + public function getDroppedTables(): array + { + return $this->removedTables; + } + + /** @return array */ + public function getCreatedSequences(): array + { + return $this->newSequences; + } + + /** @return array */ + public function getAlteredSequences(): array + { + return $this->changedSequences; + } + + /** @return array */ + public function getDroppedSequences(): array + { + return $this->removedSequences; + } + + /** + * Returns whether the diff is empty (contains no changes). */ - public function __construct($newTables = [], $changedTables = [], $removedTables = [], ?Schema $fromSchema = null) + public function isEmpty(): bool { - $this->newTables = $newTables; - $this->changedTables = $changedTables; - $this->removedTables = $removedTables; - $this->fromSchema = $fromSchema; + return count($this->newNamespaces) === 0 + && count($this->removedNamespaces) === 0 + && count($this->newTables) === 0 + && count($this->changedTables) === 0 + && count($this->removedTables) === 0 + && count($this->newSequences) === 0 + && count($this->changedSequences) === 0 + && count($this->removedSequences) === 0; } /** @@ -88,33 +209,51 @@ public function __construct($newTables = [], $changedTables = [], $removedTables * * This way it is ensured that assets are deleted which might not be relevant to the metadata schema at all. * - * @return string[] + * @deprecated + * + * @return list */ public function toSaveSql(AbstractPlatform $platform) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5766', + '%s is deprecated.', + __METHOD__, + ); + return $this->_toSql($platform, true); } /** - * @return string[] + * @deprecated Use {@link AbstractPlatform::getAlterSchemaSQL()} instead. + * + * @return list */ public function toSql(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5766', + '%s is deprecated. Use AbstractPlatform::getAlterSchemaSQL() instead.', + __METHOD__, + ); + return $this->_toSql($platform, false); } /** * @param bool $saveMode * - * @return string[] + * @return list */ protected function _toSql(AbstractPlatform $platform, $saveMode = false) { $sql = []; if ($platform->supportsSchemas()) { - foreach ($this->newNamespaces as $newNamespace) { - $sql[] = $platform->getCreateSchemaSQL($newNamespace); + foreach ($this->getCreatedSchemas() as $schema) { + $sql[] = $platform->getCreateSchemaSQL($schema); } } @@ -125,46 +264,28 @@ protected function _toSql(AbstractPlatform $platform, $saveMode = false) } if ($platform->supportsSequences() === true) { - foreach ($this->changedSequences as $sequence) { + foreach ($this->getAlteredSequences() as $sequence) { $sql[] = $platform->getAlterSequenceSQL($sequence); } if ($saveMode === false) { - foreach ($this->removedSequences as $sequence) { + foreach ($this->getDroppedSequences() as $sequence) { $sql[] = $platform->getDropSequenceSQL($sequence); } } - foreach ($this->newSequences as $sequence) { + foreach ($this->getCreatedSequences() as $sequence) { $sql[] = $platform->getCreateSequenceSQL($sequence); } } - $foreignKeySql = []; - foreach ($this->newTables as $table) { - $sql = array_merge( - $sql, - $platform->getCreateTableSQL($table, AbstractPlatform::CREATE_INDEXES) - ); - - if (! $platform->supportsForeignKeyConstraints()) { - continue; - } - - foreach ($table->getForeignKeys() as $foreignKey) { - $foreignKeySql[] = $platform->getCreateForeignKeySQL($foreignKey, $table); - } - } - - $sql = array_merge($sql, $foreignKeySql); + $sql = array_merge($sql, $platform->getCreateTablesSQL($this->getCreatedTables())); if ($saveMode === false) { - foreach ($this->removedTables as $table) { - $sql[] = $platform->getDropTableSQL($table); - } + $sql = array_merge($sql, $platform->getDropTablesSQL($this->getDroppedTables())); } - foreach ($this->changedTables as $tableDiff) { + foreach ($this->getAlteredTables() as $tableDiff) { $sql = array_merge($sql, $platform->getAlterTableSQL($tableDiff)); } diff --git a/doctrine/dbal/src/Schema/SchemaException.php b/doctrine/dbal/src/Schema/SchemaException.php index d4e22380e..4ec091f8d 100644 --- a/doctrine/dbal/src/Schema/SchemaException.php +++ b/doctrine/dbal/src/Schema/SchemaException.php @@ -3,26 +3,59 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\Exception\ColumnAlreadyExists; +use Doctrine\DBAL\Schema\Exception\ColumnDoesNotExist; +use Doctrine\DBAL\Schema\Exception\ForeignKeyDoesNotExist; +use Doctrine\DBAL\Schema\Exception\IndexAlreadyExists; +use Doctrine\DBAL\Schema\Exception\IndexDoesNotExist; +use Doctrine\DBAL\Schema\Exception\IndexNameInvalid; +use Doctrine\DBAL\Schema\Exception\NamedForeignKeyRequired; +use Doctrine\DBAL\Schema\Exception\NamespaceAlreadyExists; +use Doctrine\DBAL\Schema\Exception\SequenceAlreadyExists; +use Doctrine\DBAL\Schema\Exception\SequenceDoesNotExist; +use Doctrine\DBAL\Schema\Exception\TableAlreadyExists; +use Doctrine\DBAL\Schema\Exception\TableDoesNotExist; +use Doctrine\DBAL\Schema\Exception\UniqueConstraintDoesNotExist; -use function implode; use function sprintf; -/** - * @psalm-immutable - */ +/** @psalm-immutable */ class SchemaException extends Exception { - public const TABLE_DOESNT_EXIST = 10; - public const TABLE_ALREADY_EXISTS = 20; - public const COLUMN_DOESNT_EXIST = 30; - public const COLUMN_ALREADY_EXISTS = 40; - public const INDEX_DOESNT_EXIST = 50; - public const INDEX_ALREADY_EXISTS = 60; - public const SEQUENCE_DOENST_EXIST = 70; - public const SEQUENCE_ALREADY_EXISTS = 80; - public const INDEX_INVALID_NAME = 90; - public const FOREIGNKEY_DOESNT_EXIST = 100; - public const CONSTRAINT_DOESNT_EXIST = 110; + /** @deprecated Use {@see TableDoesNotExist} instead. */ + public const TABLE_DOESNT_EXIST = 10; + + /** @deprecated Use {@see TableAlreadyExists} instead. */ + public const TABLE_ALREADY_EXISTS = 20; + + /** @deprecated Use {@see ColumnDoesNotExist} instead. */ + public const COLUMN_DOESNT_EXIST = 30; + + /** @deprecated Use {@see ColumnAlreadyExists} instead. */ + public const COLUMN_ALREADY_EXISTS = 40; + + /** @deprecated Use {@see IndexDoesNotExist} instead. */ + public const INDEX_DOESNT_EXIST = 50; + + /** @deprecated Use {@see IndexAlreadyExists} instead. */ + public const INDEX_ALREADY_EXISTS = 60; + + /** @deprecated Use {@see SequenceDoesNotExist} instead. */ + public const SEQUENCE_DOENST_EXIST = 70; + + /** @deprecated Use {@see SequenceAlreadyExists} instead. */ + public const SEQUENCE_ALREADY_EXISTS = 80; + + /** @deprecated Use {@see IndexNameInvalid} instead. */ + public const INDEX_INVALID_NAME = 90; + + /** @deprecated Use {@see ForeignKeyDoesNotExist} instead. */ + public const FOREIGNKEY_DOESNT_EXIST = 100; + + /** @deprecated Use {@see UniqueConstraintDoesNotExist} instead. */ + public const CONSTRAINT_DOESNT_EXIST = 110; + + /** @deprecated Use {@see NamespaceAlreadyExists} instead. */ public const NAMESPACE_ALREADY_EXISTS = 120; /** @@ -32,7 +65,7 @@ class SchemaException extends Exception */ public static function tableDoesNotExist($tableName) { - return new self("There is no table with name '" . $tableName . "' in the schema.", self::TABLE_DOESNT_EXIST); + return TableDoesNotExist::new($tableName); } /** @@ -42,10 +75,7 @@ public static function tableDoesNotExist($tableName) */ public static function indexNameInvalid($indexName) { - return new self( - sprintf('Invalid index-name %s given, has to be [a-zA-Z0-9_]', $indexName), - self::INDEX_INVALID_NAME - ); + return IndexNameInvalid::new($indexName); } /** @@ -56,10 +86,7 @@ public static function indexNameInvalid($indexName) */ public static function indexDoesNotExist($indexName, $table) { - return new self( - sprintf("Index '%s' does not exist on table '%s'.", $indexName, $table), - self::INDEX_DOESNT_EXIST - ); + return IndexDoesNotExist::new($indexName, $table); } /** @@ -70,10 +97,7 @@ public static function indexDoesNotExist($indexName, $table) */ public static function indexAlreadyExists($indexName, $table) { - return new self( - sprintf("An index with name '%s' was already defined on table '%s'.", $indexName, $table), - self::INDEX_ALREADY_EXISTS - ); + return IndexAlreadyExists::new($indexName, $table); } /** @@ -84,10 +108,7 @@ public static function indexAlreadyExists($indexName, $table) */ public static function columnDoesNotExist($columnName, $table) { - return new self( - sprintf("There is no column with name '%s' on table '%s'.", $columnName, $table), - self::COLUMN_DOESNT_EXIST - ); + return ColumnDoesNotExist::new($columnName, $table); } /** @@ -97,10 +118,7 @@ public static function columnDoesNotExist($columnName, $table) */ public static function namespaceAlreadyExists($namespaceName) { - return new self( - sprintf("The namespace with name '%s' already exists.", $namespaceName), - self::NAMESPACE_ALREADY_EXISTS - ); + return NamespaceAlreadyExists::new($namespaceName); } /** @@ -110,7 +128,7 @@ public static function namespaceAlreadyExists($namespaceName) */ public static function tableAlreadyExists($tableName) { - return new self("The table with name '" . $tableName . "' already exists.", self::TABLE_ALREADY_EXISTS); + return TableAlreadyExists::new($tableName); } /** @@ -121,10 +139,7 @@ public static function tableAlreadyExists($tableName) */ public static function columnAlreadyExists($tableName, $columnName) { - return new self( - "The column '" . $columnName . "' on table '" . $tableName . "' already exists.", - self::COLUMN_ALREADY_EXISTS - ); + return ColumnAlreadyExists::new($tableName, $columnName); } /** @@ -134,7 +149,7 @@ public static function columnAlreadyExists($tableName, $columnName) */ public static function sequenceAlreadyExists($name) { - return new self("The sequence '" . $name . "' already exists.", self::SEQUENCE_ALREADY_EXISTS); + return SequenceAlreadyExists::new($name); } /** @@ -144,7 +159,7 @@ public static function sequenceAlreadyExists($name) */ public static function sequenceDoesNotExist($name) { - return new self("There exists no sequence with the name '" . $name . "'.", self::SEQUENCE_DOENST_EXIST); + return SequenceDoesNotExist::new($name); } /** @@ -155,10 +170,7 @@ public static function sequenceDoesNotExist($name) */ public static function uniqueConstraintDoesNotExist($constraintName, $table) { - return new self( - sprintf('There exists no unique constraint with the name "%s" on table "%s".', $constraintName, $table), - self::CONSTRAINT_DOESNT_EXIST - ); + return UniqueConstraintDoesNotExist::new($constraintName, $table); } /** @@ -169,23 +181,13 @@ public static function uniqueConstraintDoesNotExist($constraintName, $table) */ public static function foreignKeyDoesNotExist($fkName, $table) { - return new self( - sprintf("There exists no foreign key with the name '%s' on table '%s'.", $fkName, $table), - self::FOREIGNKEY_DOESNT_EXIST - ); + return ForeignKeyDoesNotExist::new($fkName, $table); } - /** - * @return SchemaException - */ + /** @return SchemaException */ public static function namedForeignKeyRequired(Table $localTable, ForeignKeyConstraint $foreignKey) { - return new self( - 'The performed schema operation on ' . $localTable->getName() . ' requires a named foreign key, ' . - 'but the given foreign key from (' . implode(', ', $foreignKey->getColumns()) . ') onto foreign table ' . - "'" . $foreignKey->getForeignTableName() . "' (" . implode(', ', $foreignKey->getForeignColumns()) . ')' . - ' is currently unnamed.' - ); + return NamedForeignKeyRequired::new($localTable, $foreignKey); } /** @@ -196,7 +198,7 @@ public static function namedForeignKeyRequired(Table $localTable, ForeignKeyCons public static function alterTableChangeNotSupported($changeName) { return new self( - sprintf("Alter table change not supported, given '%s'", $changeName) + sprintf("Alter table change not supported, given '%s'", $changeName), ); } } diff --git a/doctrine/dbal/src/Schema/SchemaManagerFactory.php b/doctrine/dbal/src/Schema/SchemaManagerFactory.php new file mode 100644 index 000000000..37c32e0c7 --- /dev/null +++ b/doctrine/dbal/src/Schema/SchemaManagerFactory.php @@ -0,0 +1,17 @@ +cache = $cache; } - /** - * @return int - */ + /** @return int */ public function getAllocationSize() { return $this->allocationSize; } - /** - * @return int - */ + /** @return int */ public function getInitialValue() { return $this->initialValue; } - /** - * @return int|null - */ + /** @return int|null */ public function getCache() { return $this->cache; @@ -139,10 +134,18 @@ public function isAutoIncrementsFor(Table $table) } /** + * @deprecated + * * @return void */ public function visit(Visitor $visitor) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5435', + 'Sequence::visit() is deprecated.', + ); + $visitor->acceptSequence($this); } } diff --git a/doctrine/dbal/src/Schema/SqliteSchemaManager.php b/doctrine/dbal/src/Schema/SqliteSchemaManager.php index 3e0e5cfec..13f5c5147 100644 --- a/doctrine/dbal/src/Schema/SqliteSchemaManager.php +++ b/doctrine/dbal/src/Schema/SqliteSchemaManager.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\SQLite; use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\StringType; use Doctrine\DBAL\Types\TextType; use Doctrine\DBAL\Types\Type; @@ -14,15 +15,17 @@ use function array_change_key_case; use function array_map; use function array_merge; -use function array_reverse; +use function count; use function explode; use function file_exists; +use function implode; use function preg_match; use function preg_match_all; use function preg_quote; use function preg_replace; use function rtrim; use function str_replace; +use function strcasecmp; use function strpos; use function strtolower; use function trim; @@ -39,7 +42,72 @@ class SqliteSchemaManager extends AbstractSchemaManager { /** - * {@inheritdoc} + * {@inheritDoc} + */ + public function listTableNames() + { + return $this->doListTableNames(); + } + + /** + * {@inheritDoc} + */ + public function listTables() + { + return $this->doListTables(); + } + + /** + * {@inheritDoc} + * + * @deprecated Use {@see introspectTable()} instead. + */ + public function listTableDetails($name) + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5595', + '%s is deprecated. Use introspectTable() instead.', + __METHOD__, + ); + + return $this->doListTableDetails($name); + } + + /** + * {@inheritDoc} + */ + public function listTableColumns($table, $database = null) + { + return $this->doListTableColumns($table, $database); + } + + /** + * {@inheritDoc} + */ + public function listTableIndexes($table) + { + return $this->doListTableIndexes($table); + } + + /** + * {@inheritDoc} + */ + protected function fetchForeignKeyColumnsByTable(string $databaseName): array + { + $columnsByTable = parent::fetchForeignKeyColumnsByTable($databaseName); + + if (count($columnsByTable) > 0) { + foreach ($columnsByTable as $table => $columns) { + $columnsByTable[$table] = $this->addDetailsToTableForeignKeyColumns($table, $columns); + } + } + + return $columnsByTable; + } + + /** + * {@inheritDoc} * * @deprecated Delete the database file using the filesystem. */ @@ -48,7 +116,7 @@ public function dropDatabase($database) Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4963', - 'SqliteSchemaManager::dropDatabase() is deprecated. Delete the database file using the filesystem.' + 'SqliteSchemaManager::dropDatabase() is deprecated. Delete the database file using the filesystem.', ); if (! file_exists($database)) { @@ -59,7 +127,7 @@ public function dropDatabase($database) } /** - * {@inheritdoc} + * {@inheritDoc} * * @deprecated The engine will create the database file automatically. */ @@ -69,7 +137,7 @@ public function createDatabase($database) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4963', 'SqliteSchemaManager::createDatabase() is deprecated.' - . ' The engine will create the database file automatically.' + . ' The engine will create the database file automatically.', ); $params = $this->_conn->getParams(); @@ -83,29 +151,19 @@ public function createDatabase($database) } /** - * {@inheritdoc} - */ - public function renameTable($name, $newName) - { - $tableDiff = new TableDiff($name); - $tableDiff->fromTable = $this->listTableDetails($name); - $tableDiff->newName = $newName; - $this->alterTable($tableDiff); - } - - /** - * {@inheritdoc} + * {@inheritDoc} */ public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) { - $tableDiff = $this->getTableDiffForAlterForeignKey($table); - $tableDiff->addedForeignKeys[] = $foreignKey; + if (! $table instanceof Table) { + $table = $this->listTableDetails($table); + } - $this->alterTable($tableDiff); + $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [$foreignKey])); } /** - * {@inheritdoc} + * {@inheritDoc} * * @deprecated Use {@see dropForeignKey()} and {@see createForeignKey()} instead. */ @@ -115,87 +173,55 @@ public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'SqliteSchemaManager::dropAndCreateForeignKey() is deprecated.' - . ' Use SqliteSchemaManager::dropForeignKey() and SqliteSchemaManager::createForeignKey() instead.' + . ' Use SqliteSchemaManager::dropForeignKey() and SqliteSchemaManager::createForeignKey() instead.', ); - $tableDiff = $this->getTableDiffForAlterForeignKey($table); - $tableDiff->changedForeignKeys[] = $foreignKey; + if (! $table instanceof Table) { + $table = $this->listTableDetails($table); + } - $this->alterTable($tableDiff); + $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [], [$foreignKey])); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function dropForeignKey($foreignKey, $table) { - $tableDiff = $this->getTableDiffForAlterForeignKey($table); - $tableDiff->removedForeignKeys[] = $foreignKey; + if (! $table instanceof Table) { + $table = $this->listTableDetails($table); + } - $this->alterTable($tableDiff); + $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [], [], [$foreignKey])); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function listTableForeignKeys($table, $database = null) { - if ($database === null) { - $database = $this->_conn->getDatabase(); - } - - $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); - $tableForeignKeys = $this->_conn->fetchAllAssociative($sql); - - if (! empty($tableForeignKeys)) { - $createSql = $this->getCreateTableSQL($table); - - if ( - preg_match_all( - '# - (?:CONSTRAINT\s+([^\s]+)\s+)? - (?:FOREIGN\s+KEY[^\)]+\)\s*)? - REFERENCES\s+[^\s]+\s+(?:\([^)]+\))? - (?: - [^,]*? - (NOT\s+DEFERRABLE|DEFERRABLE) - (?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))? - )?#isx', - $createSql, - $match - ) > 0 - ) { - $names = array_reverse($match[1]); - $deferrable = array_reverse($match[2]); - $deferred = array_reverse($match[3]); - } else { - $names = $deferrable = $deferred = []; - } + $table = $this->normalizeName($table); - foreach ($tableForeignKeys as $key => $value) { - $id = $value['id']; + $columns = $this->selectForeignKeyColumns('', $table) + ->fetchAllAssociative(); - $tableForeignKeys[$key] = array_merge($tableForeignKeys[$key], [ - 'constraint_name' => isset($names[$id]) && $names[$id] !== '' ? $names[$id] : $id, - 'deferrable' => isset($deferrable[$id]) && strtolower($deferrable[$id]) === 'deferrable', - 'deferred' => isset($deferred[$id]) && strtolower($deferred[$id]) === 'deferred', - ]); - } + if (count($columns) > 0) { + $columns = $this->addDetailsToTableForeignKeyColumns($table, $columns); } - return $this->_getPortableTableForeignKeysList($tableForeignKeys); + return $this->_getPortableTableForeignKeysList($columns); } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableDefinition($table) { - return $table['name']; + return $table['table_name']; } /** - * {@inheritdoc} + * {@inheritDoc} * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html */ @@ -218,7 +244,7 @@ static function (array $a, array $b): int { } return $a['pk'] - $b['pk']; - } + }, ); foreach ($indexArray as $indexColumnRow) { @@ -259,7 +285,7 @@ static function (array $a, array $b): int { } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableColumnList($table, $database, $tableColumns) { @@ -301,7 +327,7 @@ protected function _getPortableTableColumnList($table, $database, $tableColumns) if ($type instanceof StringType || $type instanceof TextType) { $column->setPlatformOption( 'collation', - $this->parseColumnCollationFromSQL($columnName, $createSql) ?? 'BINARY' + $this->parseColumnCollationFromSQL($columnName, $createSql) ?? 'BINARY', ); } @@ -326,7 +352,7 @@ protected function _getPortableTableColumnList($table, $database, $tableColumns) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableColumnDefinition($tableColumn) { @@ -391,21 +417,20 @@ protected function _getPortableTableColumnDefinition($tableColumn) } $options = [ - 'length' => $length, - 'unsigned' => $unsigned, - 'fixed' => $fixed, - 'notnull' => $notnull, - 'default' => $default, + 'length' => $length, + 'unsigned' => $unsigned, + 'fixed' => $fixed, + 'notnull' => $notnull, + 'default' => $default, 'precision' => $precision, 'scale' => $scale, - 'autoincrement' => false, ]; return new Column($tableColumn['name'], Type::getType($type), $options); } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableViewDefinition($view) { @@ -413,15 +438,15 @@ protected function _getPortableViewDefinition($view) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function _getPortableTableForeignKeysList($tableForeignKeys) { $list = []; foreach ($tableForeignKeys as $value) { $value = array_change_key_case($value, CASE_LOWER); - $name = $value['constraint_name']; - if (! isset($list[$name])) { + $id = $value['id']; + if (! isset($list[$id])) { if (! isset($value['on_delete']) || $value['on_delete'] === 'RESTRICT') { $value['on_delete'] = null; } @@ -430,8 +455,8 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) $value['on_update'] = null; } - $list[$name] = [ - 'name' => $name, + $list[$id] = [ + 'name' => $value['constraint_name'], 'local' => [], 'foreign' => [], 'foreignTable' => $value['table'], @@ -442,49 +467,45 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) ]; } - $list[$name]['local'][] = $value['from']; + $list[$id]['local'][] = $value['from']; if ($value['to'] === null) { + // Inferring a shorthand form for the foreign key constraint, where the "to" field is empty. + // @see https://www.sqlite.org/foreignkeys.html#fk_indexes. + $foreignTableIndexes = $this->_getPortableTableIndexesList([], $value['table']); + + if (! isset($foreignTableIndexes['primary'])) { + continue; + } + + $list[$id]['foreign'] = [...$list[$id]['foreign'], ...$foreignTableIndexes['primary']->getColumns()]; + continue; } - $list[$name]['foreign'][] = $value['to']; + $list[$id]['foreign'][] = $value['to']; } - $result = []; - foreach ($list as $constraint) { - $result[] = new ForeignKeyConstraint( - $constraint['local'], - $constraint['foreignTable'], - $constraint['foreign'], - $constraint['name'], - [ - 'onDelete' => $constraint['onDelete'], - 'onUpdate' => $constraint['onUpdate'], - 'deferrable' => $constraint['deferrable'], - 'deferred' => $constraint['deferred'], - ] - ); - } - - return $result; + return parent::_getPortableTableForeignKeysList($list); } /** - * @param Table|string $table - * - * @throws Exception + * {@inheritDoc} */ - private function getTableDiffForAlterForeignKey($table): TableDiff + protected function _getPortableTableForeignKeyDefinition($tableForeignKey): ForeignKeyConstraint { - if (! $table instanceof Table) { - $table = $this->listTableDetails($table); - } - - $tableDiff = new TableDiff($table->getName()); - $tableDiff->fromTable = $table; - - return $tableDiff; + return new ForeignKeyConstraint( + $tableForeignKey['local'], + $tableForeignKey['foreignTable'], + $tableForeignKey['foreign'], + $tableForeignKey['name'], + [ + 'onDelete' => $tableForeignKey['onDelete'], + 'onUpdate' => $tableForeignKey['onUpdate'], + 'deferrable' => $tableForeignKey['deferrable'], + 'deferred' => $tableForeignKey['deferred'], + ], + ); } private function parseColumnCollationFromSQL(string $column, string $sql): ?string @@ -533,9 +554,7 @@ private function parseColumnCommentFromSQL(string $column, string $sql): ?string return $comment === '' ? null : $comment; } - /** - * @throws Exception - */ + /** @throws Exception */ private function getCreateTableSQL(string $table): string { $sql = $this->_conn->fetchOne( @@ -552,7 +571,7 @@ private function getCreateTableSQL(string $table): string AND name = ? SQL , - [$table] + [$table], ); if ($sql !== false) { @@ -563,28 +582,73 @@ private function getCreateTableSQL(string $table): string } /** - * {@inheritDoc} + * @param list> $columns + * + * @return list> + * + * @throws Exception + */ + private function addDetailsToTableForeignKeyColumns(string $table, array $columns): array + { + $foreignKeyDetails = $this->getForeignKeyDetails($table); + $foreignKeyCount = count($foreignKeyDetails); + + foreach ($columns as $i => $column) { + // SQLite identifies foreign keys in reverse order of appearance in SQL + $columns[$i] = array_merge($column, $foreignKeyDetails[$foreignKeyCount - $column['id'] - 1]); + } + + return $columns; + } + + /** + * @param string $table + * + * @return list> * - * @param string $name + * @throws Exception */ - public function listTableDetails($name): Table + private function getForeignKeyDetails($table) { - $table = parent::listTableDetails($name); + $createSql = $this->getCreateTableSQL($table); - $tableCreateSql = $this->getCreateTableSQL($name); + if ( + preg_match_all( + '# + (?:CONSTRAINT\s+(\S+)\s+)? + (?:FOREIGN\s+KEY[^)]+\)\s*)? + REFERENCES\s+\S+\s*(?:\([^)]+\))? + (?: + [^,]*? + (NOT\s+DEFERRABLE|DEFERRABLE) + (?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))? + )?#isx', + $createSql, + $match, + ) === 0 + ) { + return []; + } - $comment = $this->parseTableCommentFromSQL($name, $tableCreateSql); + $names = $match[1]; + $deferrable = $match[2]; + $deferred = $match[3]; + $details = []; - if ($comment !== null) { - $table->addOption('comment', $comment); + for ($i = 0, $count = count($match[0]); $i < $count; $i++) { + $details[] = [ + 'constraint_name' => isset($names[$i]) && $names[$i] !== '' ? $names[$i] : null, + 'deferrable' => isset($deferrable[$i]) && strcasecmp($deferrable[$i], 'deferrable') === 0, + 'deferred' => isset($deferred[$i]) && strcasecmp($deferred[$i], 'deferred') === 0, + ]; } - return $table; + return $details; } public function createComparator(): Comparator { - return new SQLite\Comparator($this->getDatabasePlatform()); + return new SQLite\Comparator($this->_platform); } /** @@ -597,10 +661,130 @@ public function getSchemaSearchPaths() Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4821', - 'SqliteSchemaManager::getSchemaSearchPaths() is deprecated.' + 'SqliteSchemaManager::getSchemaSearchPaths() is deprecated.', ); // SQLite does not support schemas or databases return []; } + + protected function selectTableNames(string $databaseName): Result + { + $sql = <<<'SQL' +SELECT name AS table_name +FROM sqlite_master +WHERE type = 'table' + AND name != 'sqlite_sequence' + AND name != 'geometry_columns' + AND name != 'spatial_ref_sys' +UNION ALL +SELECT name +FROM sqlite_temp_master +WHERE type = 'table' +ORDER BY name +SQL; + + return $this->_conn->executeQuery($sql); + } + + protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = <<<'SQL' + SELECT t.name AS table_name, + c.* + FROM sqlite_master t + JOIN pragma_table_info(t.name) c +SQL; + + $conditions = [ + "t.type = 'table'", + "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')", + ]; + $params = []; + + if ($tableName !== null) { + $conditions[] = 't.name = ?'; + $params[] = str_replace('.', '__', $tableName); + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, c.cid'; + + return $this->_conn->executeQuery($sql, $params); + } + + protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = <<<'SQL' + SELECT t.name AS table_name, + i.* + FROM sqlite_master t + JOIN pragma_index_list(t.name) i +SQL; + + $conditions = [ + "t.type = 'table'", + "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')", + ]; + $params = []; + + if ($tableName !== null) { + $conditions[] = 't.name = ?'; + $params[] = str_replace('.', '__', $tableName); + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, i.seq'; + + return $this->_conn->executeQuery($sql, $params); + } + + protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = <<<'SQL' + SELECT t.name AS table_name, + p.* + FROM sqlite_master t + JOIN pragma_foreign_key_list(t.name) p + ON p."seq" != "-1" +SQL; + + $conditions = [ + "t.type = 'table'", + "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')", + ]; + $params = []; + + if ($tableName !== null) { + $conditions[] = 't.name = ?'; + $params[] = str_replace('.', '__', $tableName); + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, p.id DESC, p.seq'; + + return $this->_conn->executeQuery($sql, $params); + } + + /** + * {@inheritDoc} + */ + protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array + { + if ($tableName === null) { + $tables = $this->listTableNames(); + } else { + $tables = [$tableName]; + } + + $tableOptions = []; + foreach ($tables as $table) { + $comment = $this->parseTableCommentFromSQL($table, $this->getCreateTableSQL($table)); + + if ($comment === null) { + continue; + } + + $tableOptions[$table]['comment'] = $comment; + } + + return $tableOptions; + } } diff --git a/doctrine/dbal/src/Schema/Table.php b/doctrine/dbal/src/Schema/Table.php index 4f591eb63..ce4cc3260 100644 --- a/doctrine/dbal/src/Schema/Table.php +++ b/doctrine/dbal/src/Schema/Table.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Schema\Exception\InvalidTableName; use Doctrine\DBAL\Schema\Visitor\Visitor; use Doctrine\DBAL\Types\Type; +use Doctrine\Deprecations\Deprecation; use function array_filter; use function array_keys; @@ -46,7 +47,7 @@ class Table extends AbstractAsset protected $_schemaConfig; /** @var Index[] */ - private $implicitIndexes = []; + private array $implicitIndexes = []; /** * @param Column[] $columns @@ -91,17 +92,13 @@ public function __construct( $this->_options = array_merge($this->_options, $options); } - /** - * @return void - */ + /** @return void */ public function setSchemaConfig(SchemaConfig $schemaConfig) { $this->_schemaConfig = $schemaConfig; } - /** - * @return int - */ + /** @return int */ protected function _getMaxIdentifierLength() { if ($this->_schemaConfig instanceof SchemaConfig) { @@ -148,13 +145,11 @@ public function setPrimaryKey(array $columnNames, $indexName = false) */ public function addIndex(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) { - if ($indexName === null) { - $indexName = $this->_generateIdentifierName( - array_merge([$this->getName()], $columnNames), - 'idx', - $this->_getMaxIdentifierLength() - ); - } + $indexName ??= $this->_generateIdentifierName( + array_merge([$this->getName()], $columnNames), + 'idx', + $this->_getMaxIdentifierLength(), + ); return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options)); } @@ -172,13 +167,11 @@ public function addUniqueConstraint( array $flags = [], array $options = [] ): Table { - if ($indexName === null) { - $indexName = $this->_generateIdentifierName( - array_merge([$this->getName()], $columnNames), - 'uniq', - $this->_getMaxIdentifierLength() - ); - } + $indexName ??= $this->_generateIdentifierName( + array_merge([$this->getName()], $columnNames), + 'uniq', + $this->_getMaxIdentifierLength(), + ); return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options)); } @@ -231,13 +224,11 @@ public function dropIndex($name) */ public function addUniqueIndex(array $columnNames, $indexName = null, array $options = []) { - if ($indexName === null) { - $indexName = $this->_generateIdentifierName( - array_merge([$this->getName()], $columnNames), - 'uniq', - $this->_getMaxIdentifierLength() - ); - } + $indexName ??= $this->_generateIdentifierName( + array_merge([$this->getName()], $columnNames), + 'uniq', + $this->_getMaxIdentifierLength(), + ); return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options)); } @@ -358,6 +349,8 @@ public function addColumn($name, $typeName, array $options = []) /** * Change Column Details. * + * @deprecated Use {@link modifyColumn()} instead. + * * @param string $name * @param mixed[] $options * @@ -366,6 +359,26 @@ public function addColumn($name, $typeName, array $options = []) * @throws SchemaException */ public function changeColumn($name, array $options) + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5747', + '%s is deprecated. Use modifyColumn() instead.', + __METHOD__, + ); + + return $this->modifyColumn($name, $options); + } + + /** + * @param string $name + * @param mixed[] $options + * + * @return self + * + * @throws SchemaException + */ + public function modifyColumn($name, array $options) { $column = $this->getColumn($name); $column->setOptions($options); @@ -411,13 +424,11 @@ public function addForeignKeyConstraint( array $options = [], $name = null ) { - if ($name === null) { - $name = $this->_generateIdentifierName( - array_merge((array) $this->getName(), $localColumnNames), - 'fk', - $this->_getMaxIdentifierLength() - ); - } + $name ??= $this->_generateIdentifierName( + array_merge([$this->getName()], $localColumnNames), + 'fk', + $this->_getMaxIdentifierLength(), + ); if ($foreignTable instanceof Table) { foreach ($foreignColumnNames as $columnName) { @@ -438,7 +449,7 @@ public function addForeignKeyConstraint( $foreignTable, $foreignColumnNames, $name, - $options + $options, ); return $this->_addForeignKeyConstraint($constraint); @@ -488,7 +499,7 @@ protected function _addIndex(Index $indexCandidate) $replacedImplicitIndexes = []; foreach ($this->implicitIndexes as $name => $implicitIndex) { - if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) { + if (! $implicitIndex->isFulfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) { continue; } @@ -515,9 +526,7 @@ protected function _addIndex(Index $indexCandidate) return $this; } - /** - * @return self - */ + /** @return self */ protected function _addUniqueConstraint(UniqueConstraint $constraint): Table { $mergedNames = array_merge([$this->getName()], $constraint->getColumns()); @@ -537,7 +546,7 @@ protected function _addUniqueConstraint(UniqueConstraint $constraint): Table $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, true, false); foreach ($this->_indexes as $existingIndex) { - if ($indexCandidate->isFullfilledBy($existingIndex)) { + if ($indexCandidate->isFulfilledBy($existingIndex)) { return $this; } } @@ -547,9 +556,7 @@ protected function _addUniqueConstraint(UniqueConstraint $constraint): Table return $this; } - /** - * @return self - */ + /** @return self */ protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) { $constraint->setLocalTable($this); @@ -560,7 +567,7 @@ protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) $name = $this->_generateIdentifierName( array_merge([$this->getName()], $constraint->getLocalColumns()), 'fk', - $this->_getMaxIdentifierLength() + $this->_getMaxIdentifierLength(), ); } @@ -578,13 +585,13 @@ protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) $indexName = $this->_generateIdentifierName( array_merge([$this->getName()], $constraint->getColumns()), 'idx', - $this->_getMaxIdentifierLength() + $this->_getMaxIdentifierLength(), ); $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false); foreach ($this->_indexes as $existingIndex) { - if ($indexCandidate->isFullfilledBy($existingIndex)) { + if ($indexCandidate->isFulfilledBy($existingIndex)) { return $this; } } @@ -698,11 +705,11 @@ public function removeUniqueConstraint(string $name): void */ public function getColumns() { - $primaryKeyColumns = $this->hasPrimaryKey() ? $this->getPrimaryKeyColumns() : []; + $primaryKeyColumns = $this->getPrimaryKey() !== null ? $this->getPrimaryKeyColumns() : []; $foreignKeyColumns = $this->getForeignKeyColumns(); $remainderColumns = $this->filterColumns( array_merge(array_keys($primaryKeyColumns), array_keys($foreignKeyColumns)), - true + true, ); return array_merge($primaryKeyColumns, $foreignKeyColumns, $remainderColumns); @@ -711,10 +718,19 @@ public function getColumns() /** * Returns the foreign key columns * + * @deprecated Use {@see getForeignKey()} and {@see ForeignKeyConstraint::getLocalColumns()} instead. + * * @return Column[] */ public function getForeignKeyColumns() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5731', + '%s is deprecated. Use getForeignKey() and ForeignKeyConstraint::getLocalColumns() instead.', + __METHOD__, + ); + $foreignKeyColumns = []; foreach ($this->getForeignKeys() as $foreignKey) { @@ -789,12 +805,21 @@ public function getPrimaryKey() /** * Returns the primary key columns. * + * @deprecated Use {@see getPrimaryKey()} and {@see Index::getColumns()} instead. + * * @return Column[] * * @throws Exception */ public function getPrimaryKeyColumns() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5731', + '%s is deprecated. Use getPrimaryKey() and Index::getColumns() instead.', + __METHOD__, + ); + $primaryKey = $this->getPrimaryKey(); if ($primaryKey === null) { @@ -807,10 +832,19 @@ public function getPrimaryKeyColumns() /** * Returns whether this table has a primary key. * + * @deprecated Use {@see getPrimaryKey()} instead. + * * @return bool */ public function hasPrimaryKey() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5731', + '%s is deprecated. Use getPrimaryKey() instead.', + __METHOD__, + ); + return $this->_primaryKeyName !== null && $this->hasIndex($this->_primaryKeyName); } @@ -847,9 +881,7 @@ public function getIndex($name) return $this->_indexes[$name]; } - /** - * @return Index[] - */ + /** @return Index[] */ public function getIndexes() { return $this->_indexes; @@ -895,21 +927,27 @@ public function getOption($name) return $this->_options[$name]; } - /** - * @return mixed[] - */ + /** @return mixed[] */ public function getOptions() { return $this->_options; } /** + * @deprecated + * * @return void * * @throws SchemaException */ public function visit(Visitor $visitor) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5435', + 'Table::visit() is deprecated.', + ); + $visitor->acceptTable($this); foreach ($this->getColumns() as $column) { diff --git a/doctrine/dbal/src/Schema/TableDiff.php b/doctrine/dbal/src/Schema/TableDiff.php index 82c912f71..9aaf9e770 100644 --- a/doctrine/dbal/src/Schema/TableDiff.php +++ b/doctrine/dbal/src/Schema/TableDiff.php @@ -3,34 +3,53 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; + +use function array_filter; +use function array_values; +use function count; /** * Table Diff. */ class TableDiff { - /** @var string */ + /** + * @deprecated Use {@see getOldTable()} instead. + * + * @var string + */ public $name; - /** @var string|false */ + /** + * @deprecated Rename tables via {@link AbstractSchemaManager::renameTable()} instead. + * + * @var string|false + */ public $newName = false; /** * All added columns * + * @internal Use {@see getAddedColumns()} instead. + * * @var Column[] */ public $addedColumns; /** - * All changed columns + * All modified columns + * + * @internal Use {@see getModifiedColumns()} instead. * * @var ColumnDiff[] */ public $changedColumns = []; /** - * All removed columns + * All dropped columns + * + * @internal Use {@see getDroppedColumns()} instead. * * @var Column[] */ @@ -39,6 +58,8 @@ class TableDiff /** * Columns that are only renamed from key to column instance name. * + * @internal Use {@see getRenamedColumns()} instead. + * * @var Column[] */ public $renamedColumns = []; @@ -46,6 +67,8 @@ class TableDiff /** * All added indexes. * + * @internal Use {@see getAddedIndexes()} instead. + * * @var Index[] */ public $addedIndexes = []; @@ -53,6 +76,8 @@ class TableDiff /** * All changed indexes. * + * @internal Use {@see getModifiedIndexes()} instead. + * * @var Index[] */ public $changedIndexes = []; @@ -60,6 +85,8 @@ class TableDiff /** * All removed indexes * + * @internal Use {@see getDroppedIndexes()} instead. + * * @var Index[] */ public $removedIndexes = []; @@ -67,6 +94,8 @@ class TableDiff /** * Indexes that are only renamed but are identical otherwise. * + * @internal Use {@see getRenamedIndexes()} instead. + * * @var Index[] */ public $renamedIndexes = []; @@ -74,6 +103,8 @@ class TableDiff /** * All added foreign key definitions * + * @internal Use {@see getAddedForeignKeys()} instead. + * * @var ForeignKeyConstraint[] */ public $addedForeignKeys = []; @@ -81,6 +112,8 @@ class TableDiff /** * All changed foreign keys * + * @internal Use {@see getModifiedForeignKeys()} instead. + * * @var ForeignKeyConstraint[] */ public $changedForeignKeys = []; @@ -88,45 +121,80 @@ class TableDiff /** * All removed foreign keys * - * @var ForeignKeyConstraint[]|string[] + * @internal Use {@see getDroppedForeignKeys()} instead. + * + * @var (ForeignKeyConstraint|string)[] */ public $removedForeignKeys = []; - /** @var Table|null */ + /** + * @internal Use {@see getOldTable()} instead. + * + * @var Table|null + */ public $fromTable; /** - * Constructs an TableDiff object. + * Constructs a TableDiff object. * - * @param string $tableName - * @param Column[] $addedColumns - * @param ColumnDiff[] $changedColumns - * @param Column[] $removedColumns - * @param Index[] $addedIndexes - * @param Index[] $changedIndexes - * @param Index[] $removedIndexes + * @internal The diff can be only instantiated by a {@see Comparator}. + * + * @param string $tableName + * @param array $addedColumns + * @param array $modifiedColumns + * @param array $droppedColumns + * @param array $addedIndexes + * @param array $changedIndexes + * @param array $removedIndexes + * @param list $addedForeignKeys + * @param list $changedForeignKeys + * @param list $removedForeignKeys + * @param array $renamedColumns + * @param array $renamedIndexes */ public function __construct( $tableName, $addedColumns = [], - $changedColumns = [], - $removedColumns = [], + $modifiedColumns = [], + $droppedColumns = [], $addedIndexes = [], $changedIndexes = [], $removedIndexes = [], - ?Table $fromTable = null + ?Table $fromTable = null, + $addedForeignKeys = [], + $changedForeignKeys = [], + $removedForeignKeys = [], + $renamedColumns = [], + $renamedIndexes = [] ) { - $this->name = $tableName; - $this->addedColumns = $addedColumns; - $this->changedColumns = $changedColumns; - $this->removedColumns = $removedColumns; - $this->addedIndexes = $addedIndexes; - $this->changedIndexes = $changedIndexes; - $this->removedIndexes = $removedIndexes; - $this->fromTable = $fromTable; + $this->name = $tableName; + $this->addedColumns = $addedColumns; + $this->changedColumns = $modifiedColumns; + $this->renamedColumns = $renamedColumns; + $this->removedColumns = $droppedColumns; + $this->addedIndexes = $addedIndexes; + $this->changedIndexes = $changedIndexes; + $this->renamedIndexes = $renamedIndexes; + $this->removedIndexes = $removedIndexes; + $this->addedForeignKeys = $addedForeignKeys; + $this->changedForeignKeys = $changedForeignKeys; + $this->removedForeignKeys = $removedForeignKeys; + + if ($fromTable === null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5678', + 'Not passing the $fromTable to %s is deprecated.', + __METHOD__, + ); + } + + $this->fromTable = $fromTable; } /** + * @deprecated Use {@see getOldTable()} instead. + * * @param AbstractPlatform $platform The platform to use for retrieving this table diff's name. * * @return Identifier @@ -134,19 +202,160 @@ public function __construct( public function getName(AbstractPlatform $platform) { return new Identifier( - $this->fromTable instanceof Table ? $this->fromTable->getQuotedName($platform) : $this->name + $this->fromTable instanceof Table ? $this->fromTable->getQuotedName($platform) : $this->name, ); } /** + * @deprecated Rename tables via {@link AbstractSchemaManager::renameTable()} instead. + * * @return Identifier|false */ public function getNewName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5663', + '%s is deprecated. Rename tables via AbstractSchemaManager::renameTable() instead.', + __METHOD__, + ); + if ($this->newName === false) { return false; } return new Identifier($this->newName); } + + public function getOldTable(): ?Table + { + return $this->fromTable; + } + + /** @return list */ + public function getAddedColumns(): array + { + return array_values($this->addedColumns); + } + + /** @return list */ + public function getModifiedColumns(): array + { + return array_values($this->changedColumns); + } + + /** @return list */ + public function getDroppedColumns(): array + { + return array_values($this->removedColumns); + } + + /** @return array */ + public function getRenamedColumns(): array + { + return $this->renamedColumns; + } + + /** @return list */ + public function getAddedIndexes(): array + { + return array_values($this->addedIndexes); + } + + /** + * @internal This method exists only for compatibility with the current implementation of schema managers + * that modify the diff while processing it. + */ + public function unsetAddedIndex(Index $index): void + { + $this->addedIndexes = array_filter( + $this->addedIndexes, + static function (Index $addedIndex) use ($index): bool { + return $addedIndex !== $index; + }, + ); + } + + /** @return array */ + public function getModifiedIndexes(): array + { + return array_values($this->changedIndexes); + } + + /** @return list */ + public function getDroppedIndexes(): array + { + return array_values($this->removedIndexes); + } + + /** + * @internal This method exists only for compatibility with the current implementation of schema managers + * that modify the diff while processing it. + */ + public function unsetDroppedIndex(Index $index): void + { + $this->removedIndexes = array_filter( + $this->removedIndexes, + static function (Index $removedIndex) use ($index): bool { + return $removedIndex !== $index; + }, + ); + } + + /** @return array */ + public function getRenamedIndexes(): array + { + return $this->renamedIndexes; + } + + /** @return list */ + public function getAddedForeignKeys(): array + { + return $this->addedForeignKeys; + } + + /** @return list */ + public function getModifiedForeignKeys(): array + { + return $this->changedForeignKeys; + } + + /** @return list */ + public function getDroppedForeignKeys(): array + { + return $this->removedForeignKeys; + } + + /** + * @internal This method exists only for compatibility with the current implementation of the schema comparator. + * + * @param ForeignKeyConstraint|string $foreignKey + */ + public function unsetDroppedForeignKey($foreignKey): void + { + $this->removedForeignKeys = array_filter( + $this->removedForeignKeys, + static function ($removedForeignKey) use ($foreignKey): bool { + return $removedForeignKey !== $foreignKey; + }, + ); + } + + /** + * Returns whether the diff is empty (contains no changes). + */ + public function isEmpty(): bool + { + return count($this->addedColumns) === 0 + && count($this->changedColumns) === 0 + && count($this->removedColumns) === 0 + && count($this->renamedColumns) === 0 + && count($this->addedIndexes) === 0 + && count($this->changedIndexes) === 0 + && count($this->removedIndexes) === 0 + && count($this->renamedIndexes) === 0 + && count($this->addedForeignKeys) === 0 + && count($this->changedForeignKeys) === 0 + && count($this->removedForeignKeys) === 0; + } } diff --git a/doctrine/dbal/src/Schema/UniqueConstraint.php b/doctrine/dbal/src/Schema/UniqueConstraint.php index cb91becb2..f353f303a 100644 --- a/doctrine/dbal/src/Schema/UniqueConstraint.php +++ b/doctrine/dbal/src/Schema/UniqueConstraint.php @@ -34,7 +34,7 @@ class UniqueConstraint extends AbstractAsset implements Constraint * * @var mixed[] */ - private $options; + private array $options; /** * @param string[] $columns @@ -57,7 +57,7 @@ public function __construct(string $name, array $columns, array $flags = [], arr } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getColumns() { @@ -65,7 +65,7 @@ public function getColumns() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getQuotedColumns(AbstractPlatform $platform) { @@ -78,9 +78,7 @@ public function getQuotedColumns(AbstractPlatform $platform) return $columns; } - /** - * @return string[] - */ + /** @return string[] */ public function getUnquotedColumns(): array { return array_map([$this, 'trimQuotes'], $this->getColumns()); @@ -134,17 +132,13 @@ public function hasOption(string $name): bool return isset($this->options[strtolower($name)]); } - /** - * @return mixed - */ + /** @return mixed */ public function getOption(string $name) { return $this->options[strtolower($name)]; } - /** - * @return mixed[] - */ + /** @return mixed[] */ public function getOptions(): array { return $this->options; diff --git a/doctrine/dbal/src/Schema/View.php b/doctrine/dbal/src/Schema/View.php index ac8d6cb5c..b19f2ad1f 100644 --- a/doctrine/dbal/src/Schema/View.php +++ b/doctrine/dbal/src/Schema/View.php @@ -20,9 +20,7 @@ public function __construct($name, $sql) $this->sql = $sql; } - /** - * @return string - */ + /** @return string */ public function getSql() { return $this->sql; diff --git a/doctrine/dbal/src/Schema/Visitor/AbstractVisitor.php b/doctrine/dbal/src/Schema/Visitor/AbstractVisitor.php index 471690442..f8f3b5825 100644 --- a/doctrine/dbal/src/Schema/Visitor/AbstractVisitor.php +++ b/doctrine/dbal/src/Schema/Visitor/AbstractVisitor.php @@ -11,6 +11,8 @@ /** * Abstract Visitor with empty methods for easy extension. + * + * @deprecated */ class AbstractVisitor implements Visitor, NamespaceVisitor { @@ -19,7 +21,7 @@ public function acceptSchema(Schema $schema) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptNamespace($namespaceName) { diff --git a/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php b/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php index 730dff77a..bab0ff982 100644 --- a/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php +++ b/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php @@ -6,33 +6,40 @@ use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; +use Doctrine\Deprecations\Deprecation; use function array_merge; +/** @deprecated Use {@link CreateSchemaObjectsSQLBuilder} instead. */ class CreateSchemaSqlCollector extends AbstractVisitor { /** @var string[] */ - private $createNamespaceQueries = []; + private array $createNamespaceQueries = []; /** @var string[] */ - private $createTableQueries = []; + private array $createTableQueries = []; /** @var string[] */ - private $createSequenceQueries = []; + private array $createSequenceQueries = []; /** @var string[] */ - private $createFkConstraintQueries = []; + private array $createFkConstraintQueries = []; - /** @var AbstractPlatform */ - private $platform; + private AbstractPlatform $platform; public function __construct(AbstractPlatform $platform) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5416', + 'CreateSchemaSqlCollector is deprecated. Use CreateSchemaObjectsSQLBuilder instead.', + ); + $this->platform = $platform; } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptNamespace($namespaceName) { @@ -44,7 +51,7 @@ public function acceptNamespace($namespaceName) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptTable(Table $table) { @@ -52,7 +59,7 @@ public function acceptTable(Table $table) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { @@ -64,16 +71,14 @@ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkCons } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptSequence(Sequence $sequence) { $this->createSequenceQueries[] = $this->platform->getCreateSequenceSQL($sequence); } - /** - * @return void - */ + /** @return void */ public function resetQueries() { $this->createNamespaceQueries = []; @@ -93,7 +98,7 @@ public function getQueries() $this->createNamespaceQueries, $this->createSequenceQueries, $this->createTableQueries, - $this->createFkConstraintQueries + $this->createFkConstraintQueries, ); } } diff --git a/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php b/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php index 1b77268b4..ddec6b4ae 100644 --- a/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php +++ b/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; +use Doctrine\Deprecations\Deprecation; use SplObjectStorage; use function assert; @@ -14,29 +15,30 @@ /** * Gathers SQL statements that allow to completely drop the current schema. + * + * @deprecated Use {@link DropSchemaObjectsSQLBuilder} instead. */ class DropSchemaSqlCollector extends AbstractVisitor { - /** @var SplObjectStorage */ - private $constraints; - - /** @var SplObjectStorage */ - private $sequences; - - /** @var SplObjectStorage */ - private $tables; - - /** @var AbstractPlatform */ - private $platform; + private SplObjectStorage $constraints; + private SplObjectStorage $sequences; + private SplObjectStorage $tables; + private AbstractPlatform $platform; public function __construct(AbstractPlatform $platform) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5416', + 'DropSchemaSqlCollector is deprecated. Use DropSchemaObjectsSQLBuilder instead.', + ); + $this->platform = $platform; $this->initializeQueries(); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptTable(Table $table) { @@ -44,7 +46,7 @@ public function acceptTable(Table $table) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { @@ -56,24 +58,20 @@ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkCons } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptSequence(Sequence $sequence) { $this->sequences->attach($sequence); } - /** - * @return void - */ + /** @return void */ public function clearQueries() { $this->initializeQueries(); } - /** - * @return string[] - */ + /** @return string[] */ public function getQueries() { $sql = []; @@ -81,17 +79,20 @@ public function getQueries() foreach ($this->constraints as $fkConstraint) { assert($fkConstraint instanceof ForeignKeyConstraint); $localTable = $this->constraints[$fkConstraint]; - $sql[] = $this->platform->getDropForeignKeySQL($fkConstraint, $localTable); + $sql[] = $this->platform->getDropForeignKeySQL( + $fkConstraint->getQuotedName($this->platform), + $localTable->getQuotedName($this->platform), + ); } foreach ($this->sequences as $sequence) { assert($sequence instanceof Sequence); - $sql[] = $this->platform->getDropSequenceSQL($sequence); + $sql[] = $this->platform->getDropSequenceSQL($sequence->getQuotedName($this->platform)); } foreach ($this->tables as $table) { assert($table instanceof Table); - $sql[] = $this->platform->getDropTableSQL($table); + $sql[] = $this->platform->getDropTableSQL($table->getQuotedName($this->platform)); } return $sql; diff --git a/doctrine/dbal/src/Schema/Visitor/Graphviz.php b/doctrine/dbal/src/Schema/Visitor/Graphviz.php index 5af4678f1..5eff0d945 100644 --- a/doctrine/dbal/src/Schema/Visitor/Graphviz.php +++ b/doctrine/dbal/src/Schema/Visitor/Graphviz.php @@ -18,11 +18,10 @@ */ class Graphviz extends AbstractVisitor { - /** @var string */ - private $output = ''; + private string $output = ''; /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { @@ -33,12 +32,12 @@ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkCons 'dir' => 'back', 'arrowtail' => 'dot', 'arrowhead' => 'normal', - ] + ], ); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptSchema(Schema $schema) { @@ -51,7 +50,7 @@ public function acceptSchema(Schema $schema) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptTable(Table $table) { @@ -60,7 +59,7 @@ public function acceptTable(Table $table) [ 'label' => $this->createTableLabel($table), 'shape' => 'plaintext', - ] + ], ); } diff --git a/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php b/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php index b0548d606..443824342 100644 --- a/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php +++ b/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php @@ -4,6 +4,8 @@ /** * Visitor that can visit schema namespaces. + * + * @deprecated */ interface NamespaceVisitor { diff --git a/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php b/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php index 509eac6f0..5ed349bfd 100644 --- a/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php +++ b/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; +use Doctrine\Deprecations\Deprecation; /** * Removes assets from a schema that are not in the default namespace. @@ -16,15 +17,26 @@ * non default namespaces. * * This visitor filters all these non-default namespaced tables and sequences - * and removes them from the SChema instance. + * and removes them from the Schema instance. + * + * @deprecated Do not use namespaces if the target database platform doesn't support them. */ class RemoveNamespacedAssets extends AbstractVisitor { - /** @var Schema|null */ - private $schema; + private ?Schema $schema = null; + + public function __construct() + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5432', + 'RemoveNamespacedAssets is deprecated. Do not use namespaces' + . " if the target database platform doesn't support them.", + ); + } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptSchema(Schema $schema) { @@ -32,7 +44,7 @@ public function acceptSchema(Schema $schema) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptTable(Table $table) { @@ -48,7 +60,7 @@ public function acceptTable(Table $table) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptSequence(Sequence $sequence) { @@ -64,7 +76,7 @@ public function acceptSequence(Sequence $sequence) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { diff --git a/doctrine/dbal/src/Schema/Visitor/SchemaDiffVisitor.php b/doctrine/dbal/src/Schema/Visitor/SchemaDiffVisitor.php deleted file mode 100644 index 040b59f55..000000000 --- a/doctrine/dbal/src/Schema/Visitor/SchemaDiffVisitor.php +++ /dev/null @@ -1,50 +0,0 @@ -params[$param] = $variable; $this->types[$param] = $type; @@ -163,7 +172,8 @@ public function execute($params = null): Result Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4580', - 'Statement::execute() is deprecated, use Statement::executeQuery() or Statement::executeStatement() instead' + '%s() is deprecated, use Statement::executeQuery() or Statement::executeStatement() instead', + __METHOD__, ); if ($params !== null) { @@ -178,7 +188,7 @@ public function execute($params = null): Result try { return new Result( $this->stmt->execute($params), - $this->conn + $this->conn, ); } catch (Driver\Exception $ex) { throw $this->conn->convertExceptionDuringQuery($ex, $this->sql, $this->params, $this->types); @@ -198,6 +208,15 @@ public function execute($params = null): Result */ public function executeQuery(array $params = []): Result { + if (func_num_args() > 0) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::executeQuery() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.', + ); + } + if ($params === []) { $params = null; // Workaround as long execute() exists and used internally. } @@ -214,6 +233,15 @@ public function executeQuery(array $params = []): Result */ public function executeStatement(array $params = []): int { + if (func_num_args() > 0) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::executeStatement() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.', + ); + } + if ($params === []) { $params = null; // Workaround as long execute() exists and used internally. } diff --git a/doctrine/dbal/src/Tools/Console/Command/CommandCompatibility.php b/doctrine/dbal/src/Tools/Console/Command/CommandCompatibility.php new file mode 100644 index 000000000..562b5ce45 --- /dev/null +++ b/doctrine/dbal/src/Tools/Console/Command/CommandCompatibility.php @@ -0,0 +1,35 @@ +hasReturnType()) { + /** @internal */ + trait CommandCompatibility + { + protected function execute(InputInterface $input, OutputInterface $output): int + { + return $this->doExecute($input, $output); + } + } +} else { + /** @internal */ + trait CommandCompatibility + { + /** + * {@inheritDoc} + * + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + return $this->doExecute($input, $output); + } + } +} diff --git a/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php b/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php index 86a2842cb..e63315466 100644 --- a/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php +++ b/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php @@ -31,17 +31,26 @@ use function is_array; use function is_string; +/** @deprecated Use database documentation instead. */ class ReservedWordsCommand extends Command { + use CommandCompatibility; + /** @var array */ - private $keywordLists; + private array $keywordLists; - /** @var ConnectionProvider */ - private $connectionProvider; + private ConnectionProvider $connectionProvider; public function __construct(ConnectionProvider $connectionProvider) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5431', + 'ReservedWordsCommand is deprecated. Use database documentation instead.', + ); + parent::__construct(); + $this->connectionProvider = $connectionProvider; $this->keywordLists = [ @@ -80,7 +89,7 @@ public function setKeywordListClass($name, $class) 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'ReservedWordsCommand::setKeywordListClass() is deprecated,' - . ' use ReservedWordsCommand::setKeywordList() instead.' + . ' use ReservedWordsCommand::setKeywordList() instead.', ); $this->keywordLists[$name] = new $class(); @@ -98,10 +107,10 @@ protected function configure() 'list', 'l', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'Keyword-List name.' + 'Keyword-List name.', ), ]) - ->setHelp(<<setHelp(<<<'EOT' Checks if the current database contains tables and columns with names that are identifiers in this dialect or in other SQL dialects. @@ -126,19 +135,18 @@ protected function configure() * pgsql100 * sqlite * sqlserver -EOT - ); +EOT); } - /** - * {@inheritdoc} - * - * @return int - * - * @throws Exception - */ - protected function execute(InputInterface $input, OutputInterface $output) + /** @throws Exception */ + private function doExecute(InputInterface $input, OutputInterface $output): int { + $output->writeln( + 'The dbal:reserved-words command is deprecated.' + . ' Use the documentation on the used database platform(s) instead.', + ); + $output->writeln(''); + $conn = $this->getConnection($input); $keywordLists = $input->getOption('list'); @@ -158,7 +166,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (! isset($this->keywordLists[$keywordList])) { throw new InvalidArgumentException( "There exists no keyword list with name '" . $keywordList . "'. " . - 'Known lists: ' . implode(', ', array_keys($this->keywordLists)) + 'Known lists: ' . implode(', ', array_keys($this->keywordLists)), ); } @@ -167,10 +175,10 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->write( 'Checking keyword violations for ' . implode(', ', $keywordLists) . '...', - true + true, ); - $schema = $conn->getSchemaManager()->createSchema(); + $schema = $conn->getSchemaManager()->introspectSchema(); $visitor = new ReservedKeywordsValidator($keywords); $schema->visit($visitor); @@ -179,7 +187,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->write( 'There are ' . count($violations) . ' reserved keyword violations' . ' in your database schema:', - true + true, ); foreach ($violations as $violation) { diff --git a/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php b/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php index a91372061..4e5681e27 100644 --- a/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php +++ b/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php @@ -26,12 +26,14 @@ */ class RunSqlCommand extends Command { - /** @var ConnectionProvider */ - private $connectionProvider; + use CommandCompatibility; + + private ConnectionProvider $connectionProvider; public function __construct(ConnectionProvider $connectionProvider) { parent::__construct(); + $this->connectionProvider = $connectionProvider; } @@ -47,23 +49,16 @@ protected function configure() new InputOption('depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of result set (deprecated).'), new InputOption('force-fetch', null, InputOption::VALUE_NONE, 'Forces fetching the result.'), ]) - ->setHelp(<<setHelp(<<<'EOT' The %command.name% command executes the given SQL query and outputs the results: php %command.full_name% "SELECT * FROM users" -EOT - ); +EOT); } - /** - * {@inheritdoc} - * - * @return int - * - * @throws Exception - */ - protected function execute(InputInterface $input, OutputInterface $output) + /** @throws Exception */ + private function doExecute(InputInterface $input, OutputInterface $output): int { $conn = $this->getConnection($input); $io = new SymfonyStyle($input, $output); @@ -104,9 +99,7 @@ private function getConnection(InputInterface $input): Connection return $this->connectionProvider->getDefaultConnection(); } - /** - * @throws Exception - */ + /** @throws Exception */ private function runQuery(SymfonyStyle $io, Connection $conn, string $sql): void { $resultSet = $conn->fetchAllAssociative($sql); @@ -119,9 +112,7 @@ private function runQuery(SymfonyStyle $io, Connection $conn, string $sql): void $io->table(array_keys($resultSet[0]), $resultSet); } - /** - * @throws Exception - */ + /** @throws Exception */ private function runStatement(SymfonyStyle $io, Connection $conn, string $sql): void { $io->success(sprintf('%d rows affected.', $conn->executeStatement($sql))); diff --git a/doctrine/dbal/src/Tools/Console/ConnectionProvider.php b/doctrine/dbal/src/Tools/Console/ConnectionProvider.php index 9919e481f..b203692d9 100644 --- a/doctrine/dbal/src/Tools/Console/ConnectionProvider.php +++ b/doctrine/dbal/src/Tools/Console/ConnectionProvider.php @@ -8,8 +8,6 @@ interface ConnectionProvider { public function getDefaultConnection(): Connection; - /** - * @throws ConnectionNotFound in case a connection with the given name does not exist. - */ + /** @throws ConnectionNotFound in case a connection with the given name does not exist. */ public function getConnection(string $name): Connection; } diff --git a/doctrine/dbal/src/Tools/Console/ConnectionProvider/SingleConnectionProvider.php b/doctrine/dbal/src/Tools/Console/ConnectionProvider/SingleConnectionProvider.php index 368e0ff3c..941fbd3c2 100644 --- a/doctrine/dbal/src/Tools/Console/ConnectionProvider/SingleConnectionProvider.php +++ b/doctrine/dbal/src/Tools/Console/ConnectionProvider/SingleConnectionProvider.php @@ -10,11 +10,9 @@ class SingleConnectionProvider implements ConnectionProvider { - /** @var Connection */ - private $connection; + private Connection $connection; - /** @var string */ - private $defaultConnectionName; + private string $defaultConnectionName; public function __construct(Connection $connection, string $defaultConnectionName = 'default') { diff --git a/doctrine/dbal/src/Tools/Console/ConsoleRunner.php b/doctrine/dbal/src/Tools/Console/ConsoleRunner.php index 823a1e80a..e8fa3c60b 100644 --- a/doctrine/dbal/src/Tools/Console/ConsoleRunner.php +++ b/doctrine/dbal/src/Tools/Console/ConsoleRunner.php @@ -13,6 +13,8 @@ /** * Handles running the Console Tools inside Symfony Console context. + * + * @deprecated Use Symfony Console documentation to bootstrap a command-line application. */ class ConsoleRunner { @@ -38,9 +40,7 @@ public static function run(ConnectionProvider $connectionProvider, $commands = [ $cli->run(); } - /** - * @return void - */ + /** @return void */ public static function addCommands(Application $cli, ConnectionProvider $connectionProvider) { $cli->addCommands([ diff --git a/doctrine/dbal/src/Tools/DsnParser.php b/doctrine/dbal/src/Tools/DsnParser.php new file mode 100644 index 000000000..4ac0484d1 --- /dev/null +++ b/doctrine/dbal/src/Tools/DsnParser.php @@ -0,0 +1,218 @@ +> */ + private array $schemeMapping; + + /** @param array> $schemeMapping An array used to map DSN schemes to DBAL drivers */ + public function __construct(array $schemeMapping = []) + { + $this->schemeMapping = $schemeMapping; + } + + /** + * @psalm-return Params + * + * @throws MalformedDsnException + */ + public function parse( + #[SensitiveParameter] + string $dsn + ): array { + // (pdo-)?sqlite3?:///... => (pdo-)?sqlite3?://localhost/... or else the URL will be invalid + $url = preg_replace('#^((?:pdo-)?sqlite3?):///#', '$1://localhost/', $dsn); + assert($url !== null); + + $url = parse_url($url); + + if ($url === false) { + throw MalformedDsnException::new(); + } + + foreach ($url as $param => $value) { + if (! is_string($value)) { + continue; + } + + $url[$param] = rawurldecode($value); + } + + $params = []; + + if (isset($url['scheme'])) { + $params['driver'] = $this->parseDatabaseUrlScheme($url['scheme']); + } + + if (isset($url['host'])) { + $params['host'] = $url['host']; + } + + if (isset($url['port'])) { + $params['port'] = $url['port']; + } + + if (isset($url['user'])) { + $params['user'] = $url['user']; + } + + if (isset($url['pass'])) { + $params['password'] = $url['pass']; + } + + if (isset($params['driver']) && is_a($params['driver'], Driver::class, true)) { + $params['driverClass'] = $params['driver']; + unset($params['driver']); + } + + $params = $this->parseDatabaseUrlPath($url, $params); + $params = $this->parseDatabaseUrlQuery($url, $params); + + return $params; + } + + /** + * Parses the given connection URL and resolves the given connection parameters. + * + * Assumes that the connection URL scheme is already parsed and resolved into the given connection parameters + * via {@see parseDatabaseUrlScheme}. + * + * @see parseDatabaseUrlScheme + * + * @param mixed[] $url The URL parts to evaluate. + * @param mixed[] $params The connection parameters to resolve. + * + * @return mixed[] The resolved connection parameters. + */ + private function parseDatabaseUrlPath(array $url, array $params): array + { + if (! isset($url['path'])) { + return $params; + } + + $url['path'] = $this->normalizeDatabaseUrlPath($url['path']); + + // If we do not have a known DBAL driver, we do not know any connection URL path semantics to evaluate + // and therefore treat the path as a regular DBAL connection URL path. + if (! isset($params['driver'])) { + return $this->parseRegularDatabaseUrlPath($url, $params); + } + + if (strpos($params['driver'], 'sqlite') !== false) { + return $this->parseSqliteDatabaseUrlPath($url, $params); + } + + return $this->parseRegularDatabaseUrlPath($url, $params); + } + + /** + * Normalizes the given connection URL path. + * + * @return string The normalized connection URL path + */ + private function normalizeDatabaseUrlPath(string $urlPath): string + { + // Trim leading slash from URL path. + return substr($urlPath, 1); + } + + /** + * Parses the query part of the given connection URL and resolves the given connection parameters. + * + * @param mixed[] $url The connection URL parts to evaluate. + * @param mixed[] $params The connection parameters to resolve. + * + * @return mixed[] The resolved connection parameters. + */ + private function parseDatabaseUrlQuery(array $url, array $params): array + { + if (! isset($url['query'])) { + return $params; + } + + $query = []; + + parse_str($url['query'], $query); // simply ingest query as extra params, e.g. charset or sslmode + + return array_merge($params, $query); // parse_str wipes existing array elements + } + + /** + * Parses the given regular connection URL and resolves the given connection parameters. + * + * Assumes that the "path" URL part is already normalized via {@see normalizeDatabaseUrlPath}. + * + * @see normalizeDatabaseUrlPath + * + * @param mixed[] $url The regular connection URL parts to evaluate. + * @param mixed[] $params The connection parameters to resolve. + * + * @return mixed[] The resolved connection parameters. + */ + private function parseRegularDatabaseUrlPath(array $url, array $params): array + { + $params['dbname'] = $url['path']; + + return $params; + } + + /** + * Parses the given SQLite connection URL and resolves the given connection parameters. + * + * Assumes that the "path" URL part is already normalized via {@see normalizeDatabaseUrlPath}. + * + * @see normalizeDatabaseUrlPath + * + * @param mixed[] $url The SQLite connection URL parts to evaluate. + * @param mixed[] $params The connection parameters to resolve. + * + * @return mixed[] The resolved connection parameters. + */ + private function parseSqliteDatabaseUrlPath(array $url, array $params): array + { + if ($url['path'] === ':memory:') { + $params['memory'] = true; + + return $params; + } + + $params['path'] = $url['path']; // pdo_sqlite driver uses 'path' instead of 'dbname' key + + return $params; + } + + /** + * Parses the scheme part from given connection URL and resolves the given connection parameters. + * + * @return string The resolved driver. + */ + private function parseDatabaseUrlScheme(string $scheme): string + { + // URL schemes must not contain underscores, but dashes are ok + $driver = str_replace('-', '_', $scheme); + + // If the driver is an alias (e.g. "postgres"), map it to the actual name ("pdo-pgsql"). + // Otherwise, let checkParams decide later if the driver exists. + return $this->schemeMapping[$driver] ?? $driver; + } +} diff --git a/doctrine/dbal/src/TransactionIsolationLevel.php b/doctrine/dbal/src/TransactionIsolationLevel.php index e8dc5d918..9020343ab 100644 --- a/doctrine/dbal/src/TransactionIsolationLevel.php +++ b/doctrine/dbal/src/TransactionIsolationLevel.php @@ -26,9 +26,7 @@ final class TransactionIsolationLevel */ public const SERIALIZABLE = 4; - /** - * @codeCoverageIgnore - */ + /** @codeCoverageIgnore */ private function __construct() { } diff --git a/doctrine/dbal/src/Types/ArrayType.php b/doctrine/dbal/src/Types/ArrayType.php index b3d9d3c56..c2aa2f4de 100644 --- a/doctrine/dbal/src/Types/ArrayType.php +++ b/doctrine/dbal/src/Types/ArrayType.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; use function is_resource; use function restore_error_handler; @@ -16,11 +17,13 @@ /** * Type that maps a PHP array to a clob SQL type. + * + * @deprecated Use {@link JsonType} instead. */ class ArrayType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -28,7 +31,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -37,7 +40,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -63,7 +66,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -71,10 +74,19 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } } diff --git a/doctrine/dbal/src/Types/AsciiStringType.php b/doctrine/dbal/src/Types/AsciiStringType.php index ab1e0e061..4ea92d974 100644 --- a/doctrine/dbal/src/Types/AsciiStringType.php +++ b/doctrine/dbal/src/Types/AsciiStringType.php @@ -10,7 +10,7 @@ final class AsciiStringType extends StringType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { diff --git a/doctrine/dbal/src/Types/BigIntType.php b/doctrine/dbal/src/Types/BigIntType.php index e5d6dcbbf..8d57a1121 100644 --- a/doctrine/dbal/src/Types/BigIntType.php +++ b/doctrine/dbal/src/Types/BigIntType.php @@ -11,7 +11,7 @@ class BigIntType extends Type implements PhpIntegerMappingType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -19,7 +19,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -27,7 +27,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getBindingType() { @@ -35,7 +35,13 @@ public function getBindingType() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { diff --git a/doctrine/dbal/src/Types/BinaryType.php b/doctrine/dbal/src/Types/BinaryType.php index e030f1660..acbbd87ad 100644 --- a/doctrine/dbal/src/Types/BinaryType.php +++ b/doctrine/dbal/src/Types/BinaryType.php @@ -18,7 +18,7 @@ class BinaryType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -26,7 +26,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -50,7 +50,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -58,7 +58,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getBindingType() { diff --git a/doctrine/dbal/src/Types/BlobType.php b/doctrine/dbal/src/Types/BlobType.php index b71e7255e..cfaabec90 100644 --- a/doctrine/dbal/src/Types/BlobType.php +++ b/doctrine/dbal/src/Types/BlobType.php @@ -18,7 +18,7 @@ class BlobType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -26,7 +26,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -50,7 +50,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -58,7 +58,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getBindingType() { diff --git a/doctrine/dbal/src/Types/BooleanType.php b/doctrine/dbal/src/Types/BooleanType.php index 68a80ef3f..7dc7f3a9d 100644 --- a/doctrine/dbal/src/Types/BooleanType.php +++ b/doctrine/dbal/src/Types/BooleanType.php @@ -5,6 +5,7 @@ use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\DB2Platform; +use Doctrine\Deprecations\Deprecation; /** * Type that maps an SQL boolean to a PHP boolean. @@ -12,7 +13,7 @@ class BooleanType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -20,7 +21,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -28,7 +29,13 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : bool) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -36,7 +43,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -44,7 +51,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getBindingType() { @@ -52,10 +59,19 @@ public function getBindingType() } /** + * @deprecated + * * @return bool */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + // We require a commented boolean type in order to distinguish between // boolean and smallint as both (have to) map to the same native type. return $platform instanceof DB2Platform; diff --git a/doctrine/dbal/src/Types/ConversionException.php b/doctrine/dbal/src/Types/ConversionException.php index 278734b9b..154b06d3e 100644 --- a/doctrine/dbal/src/Types/ConversionException.php +++ b/doctrine/dbal/src/Types/ConversionException.php @@ -57,7 +57,7 @@ public static function conversionFailedFormat($value, $toType, $expectedFormat, 'Could not convert database value "' . $value . '" to Doctrine Type ' . $toType . '. Expected format: ' . $expectedFormat, 0, - $previous + $previous, ); } @@ -81,7 +81,7 @@ public static function conversionFailedInvalidType( 'Could not convert PHP value %s to type %s. Expected one of the following types: %s', var_export($value, true), $toType, - implode(', ', $possibleTypes) + implode(', ', $possibleTypes), ), 0, $previous); } @@ -89,7 +89,7 @@ public static function conversionFailedInvalidType( 'Could not convert PHP value of type %s to type %s. Expected one of the following types: %s', is_object($value) ? get_class($value) : gettype($value), $toType, - implode(', ', $possibleTypes) + implode(', ', $possibleTypes), ), 0, $previous); } @@ -108,7 +108,7 @@ public static function conversionFailedSerialization($value, $format, $error /*, "Could not convert PHP type '%s' to '%s', as an '%s' error was triggered by the serialization", $actualType, $format, - $error + $error, ), 0, func_num_args() >= 4 ? func_get_arg(3) : null); } @@ -117,7 +117,7 @@ public static function conversionFailedUnserialization(string $format, string $e return new self(sprintf( "Could not convert database value to '%s' as an error was triggered by the unserialization: '%s'", $format, - $error + $error, )); } } diff --git a/doctrine/dbal/src/Types/DateImmutableType.php b/doctrine/dbal/src/Types/DateImmutableType.php index 4fbe6a472..da96b69d5 100644 --- a/doctrine/dbal/src/Types/DateImmutableType.php +++ b/doctrine/dbal/src/Types/DateImmutableType.php @@ -4,6 +4,7 @@ use DateTimeImmutable; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; /** * Immutable type of {@see DateType}. @@ -11,7 +12,7 @@ class DateImmutableType extends DateType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -19,7 +20,13 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -34,12 +41,18 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', DateTimeImmutable::class] + ['null', DateTimeImmutable::class], ); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : DateTimeImmutable) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -53,7 +66,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedFormat( $value, $this->getName(), - $platform->getDateFormatString() + $platform->getDateFormatString(), ); } @@ -61,10 +74,19 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } } diff --git a/doctrine/dbal/src/Types/DateIntervalType.php b/doctrine/dbal/src/Types/DateIntervalType.php index 6ecd4989c..1630dc556 100644 --- a/doctrine/dbal/src/Types/DateIntervalType.php +++ b/doctrine/dbal/src/Types/DateIntervalType.php @@ -4,6 +4,7 @@ use DateInterval; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; use Throwable; use function substr; @@ -16,7 +17,7 @@ class DateIntervalType extends Type public const FORMAT = '%RP%YY%MM%DDT%HH%IM%SS'; /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -24,17 +25,23 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { $column['length'] = 255; - return $platform->getVarcharTypeDeclarationSQL($column); + return $platform->getStringTypeDeclarationSQL($column); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -46,11 +53,17 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return $value->format(self::FORMAT); } - throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateInterval']); + throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', DateInterval::class]); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : DateInterval) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -79,10 +92,19 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } } diff --git a/doctrine/dbal/src/Types/DateTimeImmutableType.php b/doctrine/dbal/src/Types/DateTimeImmutableType.php index fd7751977..a8c7fec96 100644 --- a/doctrine/dbal/src/Types/DateTimeImmutableType.php +++ b/doctrine/dbal/src/Types/DateTimeImmutableType.php @@ -4,8 +4,8 @@ use DateTimeImmutable; use Doctrine\DBAL\Platforms\AbstractPlatform; - -use function date_create_immutable; +use Doctrine\Deprecations\Deprecation; +use Exception; /** * Immutable type of {@see DateTimeType}. @@ -13,7 +13,7 @@ class DateTimeImmutableType extends DateTimeType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -21,7 +21,13 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -36,12 +42,18 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', DateTimeImmutable::class] + ['null', DateTimeImmutable::class], ); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : DateTimeImmutable) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -51,26 +63,36 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $dateTime = DateTimeImmutable::createFromFormat($platform->getDateTimeFormatString(), $value); - if ($dateTime === false) { - $dateTime = date_create_immutable($value); + if ($dateTime !== false) { + return $dateTime; } - if ($dateTime === false) { + try { + return new DateTimeImmutable($value); + } catch (Exception $e) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), - $platform->getDateTimeFormatString() + $platform->getDateTimeFormatString(), + $e, ); } - - return $dateTime; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } } diff --git a/doctrine/dbal/src/Types/DateTimeType.php b/doctrine/dbal/src/Types/DateTimeType.php index 454295d71..3ff592ae1 100644 --- a/doctrine/dbal/src/Types/DateTimeType.php +++ b/doctrine/dbal/src/Types/DateTimeType.php @@ -3,10 +3,13 @@ namespace Doctrine\DBAL\Types; use DateTime; +use DateTimeImmutable; use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; +use Exception; -use function date_create; +use function get_class; /** * Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTime object. @@ -14,7 +17,7 @@ class DateTimeType extends Type implements PhpDateTimeMappingType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -22,7 +25,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -30,7 +33,13 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -38,36 +47,69 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return $value; } + if ($value instanceof DateTimeImmutable) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/6017', + 'Passing an instance of %s is deprecated, use %s::%s() instead.', + get_class($value), + DateTimeImmutableType::class, + __FUNCTION__, + ); + } + if ($value instanceof DateTimeInterface) { return $value->format($platform->getDateTimeFormatString()); } - throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateTime']); + throw ConversionException::conversionFailedInvalidType( + $value, + $this->getName(), + ['null', DateTime::class, DateTimeImmutable::class], + ); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : DateTimeInterface) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { + if ($value instanceof DateTimeImmutable) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/6017', + 'Passing an instance of %s is deprecated, use %s::%s() instead.', + get_class($value), + DateTimeImmutableType::class, + __FUNCTION__, + ); + } + if ($value === null || $value instanceof DateTimeInterface) { return $value; } - $val = DateTime::createFromFormat($platform->getDateTimeFormatString(), $value); + $dateTime = DateTime::createFromFormat($platform->getDateTimeFormatString(), $value); - if ($val === false) { - $val = date_create($value); + if ($dateTime !== false) { + return $dateTime; } - if ($val === false) { + try { + return new DateTime($value); + } catch (Exception $e) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), - $platform->getDateTimeFormatString() + $platform->getDateTimeFormatString(), + $e, ); } - - return $val; } } diff --git a/doctrine/dbal/src/Types/DateTimeTzImmutableType.php b/doctrine/dbal/src/Types/DateTimeTzImmutableType.php index 6e707e065..e700f68d4 100644 --- a/doctrine/dbal/src/Types/DateTimeTzImmutableType.php +++ b/doctrine/dbal/src/Types/DateTimeTzImmutableType.php @@ -4,6 +4,7 @@ use DateTimeImmutable; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; /** * Immutable type of {@see DateTimeTzType}. @@ -11,7 +12,7 @@ class DateTimeTzImmutableType extends DateTimeTzType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -19,7 +20,13 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @psalm-param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -34,12 +41,18 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', DateTimeImmutable::class] + ['null', DateTimeImmutable::class], ); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : DateTimeImmutable) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -49,22 +62,31 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $dateTime = DateTimeImmutable::createFromFormat($platform->getDateTimeTzFormatString(), $value); - if ($dateTime === false) { - throw ConversionException::conversionFailedFormat( - $value, - $this->getName(), - $platform->getDateTimeTzFormatString() - ); + if ($dateTime !== false) { + return $dateTime; } - return $dateTime; + throw ConversionException::conversionFailedFormat( + $value, + $this->getName(), + $platform->getDateTimeTzFormatString(), + ); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } } diff --git a/doctrine/dbal/src/Types/DateTimeTzType.php b/doctrine/dbal/src/Types/DateTimeTzType.php index 29672397f..1980fd334 100644 --- a/doctrine/dbal/src/Types/DateTimeTzType.php +++ b/doctrine/dbal/src/Types/DateTimeTzType.php @@ -3,8 +3,12 @@ namespace Doctrine\DBAL\Types; use DateTime; +use DateTimeImmutable; use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; + +use function get_class; /** * DateTime type saving additional timezone information. @@ -25,7 +29,7 @@ class DateTimeTzType extends Type implements PhpDateTimeMappingType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -33,7 +37,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -41,7 +45,13 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -49,6 +59,17 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return $value; } + if ($value instanceof DateTimeImmutable) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/6017', + 'Passing an instance of %s is deprecated, use %s::%s() instead.', + get_class($value), + DateTimeTzImmutableType::class, + __FUNCTION__, + ); + } + if ($value instanceof DateTimeInterface) { return $value->format($platform->getDateTimeTzFormatString()); } @@ -56,28 +77,45 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', 'DateTime'] + ['null', DateTime::class], ); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : DateTimeInterface) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { + if ($value instanceof DateTimeImmutable) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/6017', + 'Passing an instance of %s is deprecated, use %s::%s() instead.', + get_class($value), + DateTimeTzImmutableType::class, + __FUNCTION__, + ); + } + if ($value === null || $value instanceof DateTimeInterface) { return $value; } - $val = DateTime::createFromFormat($platform->getDateTimeTzFormatString(), $value); - if ($val === false) { - throw ConversionException::conversionFailedFormat( - $value, - $this->getName(), - $platform->getDateTimeTzFormatString() - ); + $dateTime = DateTime::createFromFormat($platform->getDateTimeTzFormatString(), $value); + if ($dateTime !== false) { + return $dateTime; } - return $val; + throw ConversionException::conversionFailedFormat( + $value, + $this->getName(), + $platform->getDateTimeTzFormatString(), + ); } } diff --git a/doctrine/dbal/src/Types/DateType.php b/doctrine/dbal/src/Types/DateType.php index 6f86f5436..842e8bd09 100644 --- a/doctrine/dbal/src/Types/DateType.php +++ b/doctrine/dbal/src/Types/DateType.php @@ -3,8 +3,12 @@ namespace Doctrine\DBAL\Types; use DateTime; +use DateTimeImmutable; use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; + +use function get_class; /** * Type that maps an SQL DATE to a PHP Date object. @@ -12,7 +16,7 @@ class DateType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -20,7 +24,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -28,7 +32,13 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @psalm-param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -37,30 +47,58 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) } if ($value instanceof DateTimeInterface) { + if ($value instanceof DateTimeImmutable) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/6017', + 'Passing an instance of %s is deprecated, use %s::%s() instead.', + get_class($value), + DateImmutableType::class, + __FUNCTION__, + ); + } + return $value->format($platform->getDateFormatString()); } - throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateTime']); + throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', DateTime::class]); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : DateTimeInterface) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { + if ($value instanceof DateTimeImmutable) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/6017', + 'Passing an instance of %s is deprecated, use %s::%s() instead.', + get_class($value), + DateImmutableType::class, + __FUNCTION__, + ); + } + if ($value === null || $value instanceof DateTimeInterface) { return $value; } - $val = DateTime::createFromFormat('!' . $platform->getDateFormatString(), $value); - if ($val === false) { - throw ConversionException::conversionFailedFormat( - $value, - $this->getName(), - $platform->getDateFormatString() - ); + $dateTime = DateTime::createFromFormat('!' . $platform->getDateFormatString(), $value); + if ($dateTime !== false) { + return $dateTime; } - return $val; + throw ConversionException::conversionFailedFormat( + $value, + $this->getName(), + $platform->getDateFormatString(), + ); } } diff --git a/doctrine/dbal/src/Types/DecimalType.php b/doctrine/dbal/src/Types/DecimalType.php index c70067f2b..308134b08 100644 --- a/doctrine/dbal/src/Types/DecimalType.php +++ b/doctrine/dbal/src/Types/DecimalType.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\SqlitePlatform; use function is_float; use function is_int; @@ -15,7 +16,7 @@ class DecimalType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -23,7 +24,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -31,13 +32,13 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { // Some drivers starting from PHP 8.1 can represent decimals as float/int // See also: https://github.com/doctrine/dbal/pull/4818 - if (PHP_VERSION_ID >= 80100 && (is_float($value) || is_int($value))) { + if ((PHP_VERSION_ID >= 80100 || $platform instanceof SqlitePlatform) && (is_float($value) || is_int($value))) { return (string) $value; } diff --git a/doctrine/dbal/src/Types/FloatType.php b/doctrine/dbal/src/Types/FloatType.php index 98ead4a9d..e01b77413 100644 --- a/doctrine/dbal/src/Types/FloatType.php +++ b/doctrine/dbal/src/Types/FloatType.php @@ -7,7 +7,7 @@ class FloatType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -15,7 +15,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -23,7 +23,13 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : float) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { diff --git a/doctrine/dbal/src/Types/GuidType.php b/doctrine/dbal/src/Types/GuidType.php index a4974f9d0..3c8b7f4f8 100644 --- a/doctrine/dbal/src/Types/GuidType.php +++ b/doctrine/dbal/src/Types/GuidType.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; /** * Represents a GUID/UUID datatype (both are actually synonyms) in the database. @@ -10,7 +11,7 @@ class GuidType extends StringType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -18,7 +19,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -26,10 +27,19 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return ! $platform->hasNativeGuidType(); } } diff --git a/doctrine/dbal/src/Types/IntegerType.php b/doctrine/dbal/src/Types/IntegerType.php index 0df606e29..7c2d7110d 100644 --- a/doctrine/dbal/src/Types/IntegerType.php +++ b/doctrine/dbal/src/Types/IntegerType.php @@ -11,7 +11,7 @@ class IntegerType extends Type implements PhpIntegerMappingType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -19,7 +19,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -27,7 +27,13 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : int) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -35,7 +41,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getBindingType() { diff --git a/doctrine/dbal/src/Types/JsonType.php b/doctrine/dbal/src/Types/JsonType.php index d28b8b79f..27f872c88 100644 --- a/doctrine/dbal/src/Types/JsonType.php +++ b/doctrine/dbal/src/Types/JsonType.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; use JsonException; use function is_resource; @@ -19,7 +20,7 @@ class JsonType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -27,7 +28,13 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -43,7 +50,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -63,7 +70,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -71,10 +78,19 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return ! $platform->hasNativeJsonType(); } } diff --git a/doctrine/dbal/src/Types/ObjectType.php b/doctrine/dbal/src/Types/ObjectType.php index 49042c1bf..497e9c407 100644 --- a/doctrine/dbal/src/Types/ObjectType.php +++ b/doctrine/dbal/src/Types/ObjectType.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; use function is_resource; use function restore_error_handler; @@ -13,11 +14,13 @@ /** * Type that maps a PHP object to a clob SQL type. + * + * @deprecated Use {@link JsonType} instead. */ class ObjectType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -25,7 +28,11 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param mixed $value + * + * @return string */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -33,7 +40,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -55,7 +62,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -63,10 +70,19 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } } diff --git a/doctrine/dbal/src/Types/SimpleArrayType.php b/doctrine/dbal/src/Types/SimpleArrayType.php index ee9c7f2df..ee97b9b9f 100644 --- a/doctrine/dbal/src/Types/SimpleArrayType.php +++ b/doctrine/dbal/src/Types/SimpleArrayType.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; use function count; use function explode; @@ -19,7 +20,7 @@ class SimpleArrayType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -27,7 +28,11 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param mixed $value + * + * @return string|null */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -39,7 +44,11 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param mixed $value + * + * @return list */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -53,7 +62,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -61,10 +70,19 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } } diff --git a/doctrine/dbal/src/Types/SmallIntType.php b/doctrine/dbal/src/Types/SmallIntType.php index 90e634951..2c8567a11 100644 --- a/doctrine/dbal/src/Types/SmallIntType.php +++ b/doctrine/dbal/src/Types/SmallIntType.php @@ -11,7 +11,7 @@ class SmallIntType extends Type implements PhpIntegerMappingType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -19,7 +19,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -27,7 +27,13 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : int) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -35,7 +41,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getBindingType() { diff --git a/doctrine/dbal/src/Types/StringType.php b/doctrine/dbal/src/Types/StringType.php index 4e7bd55d0..1992e8fa4 100644 --- a/doctrine/dbal/src/Types/StringType.php +++ b/doctrine/dbal/src/Types/StringType.php @@ -10,15 +10,15 @@ class StringType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { - return $platform->getVarcharTypeDeclarationSQL($column); + return $platform->getStringTypeDeclarationSQL($column); } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { diff --git a/doctrine/dbal/src/Types/TextType.php b/doctrine/dbal/src/Types/TextType.php index b1e640b69..d060bb2da 100644 --- a/doctrine/dbal/src/Types/TextType.php +++ b/doctrine/dbal/src/Types/TextType.php @@ -13,7 +13,7 @@ class TextType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -21,7 +21,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -29,7 +29,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { diff --git a/doctrine/dbal/src/Types/TimeImmutableType.php b/doctrine/dbal/src/Types/TimeImmutableType.php index 8d2c1517c..9373f5913 100644 --- a/doctrine/dbal/src/Types/TimeImmutableType.php +++ b/doctrine/dbal/src/Types/TimeImmutableType.php @@ -4,6 +4,7 @@ use DateTimeImmutable; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; /** * Immutable type of {@see TimeType}. @@ -11,7 +12,7 @@ class TimeImmutableType extends TimeType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -19,7 +20,13 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -34,12 +41,18 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', DateTimeImmutable::class] + ['null', DateTimeImmutable::class], ); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : DateTimeImmutable) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -49,22 +62,31 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $dateTime = DateTimeImmutable::createFromFormat('!' . $platform->getTimeFormatString(), $value); - if ($dateTime === false) { - throw ConversionException::conversionFailedFormat( - $value, - $this->getName(), - $platform->getTimeFormatString() - ); + if ($dateTime !== false) { + return $dateTime; } - return $dateTime; + throw ConversionException::conversionFailedFormat( + $value, + $this->getName(), + $platform->getTimeFormatString(), + ); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } } diff --git a/doctrine/dbal/src/Types/TimeType.php b/doctrine/dbal/src/Types/TimeType.php index 4f2c8c631..7356fc206 100644 --- a/doctrine/dbal/src/Types/TimeType.php +++ b/doctrine/dbal/src/Types/TimeType.php @@ -3,8 +3,12 @@ namespace Doctrine\DBAL\Types; use DateTime; +use DateTimeImmutable; use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; + +use function get_class; /** * Type that maps an SQL TIME to a PHP DateTime object. @@ -12,7 +16,7 @@ class TimeType extends Type { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -20,7 +24,7 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { @@ -28,7 +32,13 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -36,31 +46,59 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return $value; } + if ($value instanceof DateTimeImmutable) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/6017', + 'Passing an instance of %s is deprecated, use %s::%s() instead.', + get_class($value), + TimeImmutableType::class, + __FUNCTION__, + ); + } + if ($value instanceof DateTimeInterface) { return $value->format($platform->getTimeFormatString()); } - throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateTime']); + throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', DateTime::class]); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : DateTimeInterface) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { + if ($value instanceof DateTimeImmutable) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/6017', + 'Passing an instance of %s is deprecated, use %s::%s() instead.', + get_class($value), + TimeImmutableType::class, + __FUNCTION__, + ); + } + if ($value === null || $value instanceof DateTimeInterface) { return $value; } - $val = DateTime::createFromFormat('!' . $platform->getTimeFormatString(), $value); - if ($val === false) { - throw ConversionException::conversionFailedFormat( - $value, - $this->getName(), - $platform->getTimeFormatString() - ); + $dateTime = DateTime::createFromFormat('!' . $platform->getTimeFormatString(), $value); + if ($dateTime !== false) { + return $dateTime; } - return $val; + throw ConversionException::conversionFailedFormat( + $value, + $this->getName(), + $platform->getTimeFormatString(), + ); } } diff --git a/doctrine/dbal/src/Types/Type.php b/doctrine/dbal/src/Types/Type.php index c2ae2be5a..7613811e9 100644 --- a/doctrine/dbal/src/Types/Type.php +++ b/doctrine/dbal/src/Types/Type.php @@ -5,6 +5,7 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; use function array_map; use function get_class; @@ -47,12 +48,9 @@ abstract class Type Types::TIME_IMMUTABLE => TimeImmutableType::class, ]; - /** @var TypeRegistry|null */ - private static $typeRegistry; + private static ?TypeRegistry $typeRegistry = null; - /** - * @internal Do not instantiate directly - use {@see Type::addType()} method instead. - */ + /** @internal Do not instantiate directly - use {@see Type::addType()} method instead. */ final public function __construct() { } @@ -102,19 +100,16 @@ abstract public function getSQLDeclaration(array $column, AbstractPlatform $plat /** * Gets the name of this type. * - * @return string + * @deprecated this method will be removed in Doctrine DBAL 4.0, + * use {@see TypeRegistry::lookupName()} instead. * - * @todo Needed? + * @return string */ abstract public function getName(); final public static function getTypeRegistry(): TypeRegistry { - if (self::$typeRegistry === null) { - self::$typeRegistry = self::createTypeRegistry(); - } - - return self::$typeRegistry; + return self::$typeRegistry ??= self::createTypeRegistry(); } private static function createTypeRegistry(): TypeRegistry @@ -143,6 +138,16 @@ public static function getType($name) return self::getTypeRegistry()->get($name); } + /** + * Finds a name for the given type. + * + * @throws Exception + */ + public static function lookupName(self $type): string + { + return self::getTypeRegistry()->lookupName($type); + } + /** * Adds a custom type to the type map. * @@ -210,7 +215,7 @@ public static function getTypesMap() static function (Type $type): string { return get_class($type); }, - self::getTypeRegistry()->getMap() + self::getTypeRegistry()->getMap(), ); } @@ -273,10 +278,19 @@ public function getMappedDatabaseTypes(AbstractPlatform $platform) * one of those types as commented, which will have Doctrine use an SQL * comment to typehint the actual Doctrine Type. * + * @deprecated + * * @return bool */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return false; } } diff --git a/doctrine/dbal/src/Types/TypeRegistry.php b/doctrine/dbal/src/Types/TypeRegistry.php index ce33b951a..9b64c6faf 100644 --- a/doctrine/dbal/src/Types/TypeRegistry.php +++ b/doctrine/dbal/src/Types/TypeRegistry.php @@ -6,8 +6,7 @@ use Doctrine\DBAL\Exception; -use function array_search; -use function in_array; +use function spl_object_id; /** * The type registry is responsible for holding a map of all known DBAL types. @@ -16,14 +15,18 @@ final class TypeRegistry { /** @var array Map of type names and their corresponding flyweight objects. */ - private $instances; + private array $instances; + /** @var array */ + private array $instancesReverseIndex; - /** - * @param array $instances - */ + /** @param array $instances */ public function __construct(array $instances = []) { - $this->instances = $instances; + $this->instances = []; + $this->instancesReverseIndex = []; + foreach ($instances as $name => $type) { + $this->register($name, $type); + } } /** @@ -33,11 +36,12 @@ public function __construct(array $instances = []) */ public function get(string $name): Type { - if (! isset($this->instances[$name])) { + $type = $this->instances[$name] ?? null; + if ($type === null) { throw Exception::unknownColumnType($name); } - return $this->instances[$name]; + return $type; } /** @@ -79,7 +83,8 @@ public function register(string $name, Type $type): void throw Exception::typeAlreadyRegistered($type); } - $this->instances[$name] = $type; + $this->instances[$name] = $type; + $this->instancesReverseIndex[spl_object_id($type)] = $name; } /** @@ -89,15 +94,18 @@ public function register(string $name, Type $type): void */ public function override(string $name, Type $type): void { - if (! isset($this->instances[$name])) { + $origType = $this->instances[$name] ?? null; + if ($origType === null) { throw Exception::typeNotFound($name); } - if (! in_array($this->findTypeName($type), [$name, null], true)) { + if (($this->findTypeName($type) ?? $name) !== $name) { throw Exception::typeAlreadyRegistered($type); } - $this->instances[$name] = $type; + unset($this->instancesReverseIndex[spl_object_id($origType)]); + $this->instances[$name] = $type; + $this->instancesReverseIndex[spl_object_id($type)] = $name; } /** @@ -114,12 +122,6 @@ public function getMap(): array private function findTypeName(Type $type): ?string { - $name = array_search($type, $this->instances, true); - - if ($name === false) { - return null; - } - - return $name; + return $this->instancesReverseIndex[spl_object_id($type)] ?? null; } } diff --git a/doctrine/dbal/src/Types/Types.php b/doctrine/dbal/src/Types/Types.php index 56bf3f51c..54b0dfecc 100644 --- a/doctrine/dbal/src/Types/Types.php +++ b/doctrine/dbal/src/Types/Types.php @@ -9,7 +9,9 @@ */ final class Types { - public const ARRAY = 'array'; + /** @deprecated Use {@link Types::JSON} instead. */ + public const ARRAY = 'array'; + public const ASCII_STRING = 'ascii_string'; public const BIGINT = 'bigint'; public const BINARY = 'binary'; @@ -27,17 +29,18 @@ final class Types public const GUID = 'guid'; public const INTEGER = 'integer'; public const JSON = 'json'; - public const OBJECT = 'object'; - public const SIMPLE_ARRAY = 'simple_array'; - public const SMALLINT = 'smallint'; - public const STRING = 'string'; - public const TEXT = 'text'; - public const TIME_MUTABLE = 'time'; - public const TIME_IMMUTABLE = 'time_immutable'; - /** - * @codeCoverageIgnore - */ + /** @deprecated Use {@link Types::JSON} instead. */ + public const OBJECT = 'object'; + + public const SIMPLE_ARRAY = 'simple_array'; + public const SMALLINT = 'smallint'; + public const STRING = 'string'; + public const TEXT = 'text'; + public const TIME_MUTABLE = 'time'; + public const TIME_IMMUTABLE = 'time_immutable'; + + /** @codeCoverageIgnore */ private function __construct() { } diff --git a/doctrine/dbal/src/Types/VarDateTimeImmutableType.php b/doctrine/dbal/src/Types/VarDateTimeImmutableType.php index f4f5e3ec3..cd6fa890b 100644 --- a/doctrine/dbal/src/Types/VarDateTimeImmutableType.php +++ b/doctrine/dbal/src/Types/VarDateTimeImmutableType.php @@ -4,8 +4,8 @@ use DateTimeImmutable; use Doctrine\DBAL\Platforms\AbstractPlatform; - -use function date_create_immutable; +use Doctrine\Deprecations\Deprecation; +use Exception; /** * Immutable type of {@see VarDateTimeType}. @@ -13,7 +13,7 @@ class VarDateTimeImmutableType extends VarDateTimeType { /** - * {@inheritdoc} + * {@inheritDoc} */ public function getName() { @@ -21,7 +21,13 @@ public function getName() } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : string) + * + * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { @@ -36,12 +42,18 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', DateTimeImmutable::class] + ['null', DateTimeImmutable::class], ); } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : DateTimeImmutable) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -49,20 +61,29 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return $value; } - $dateTime = date_create_immutable($value); - - if ($dateTime === false) { - throw ConversionException::conversionFailed($value, $this->getName()); + try { + $dateTime = new DateTimeImmutable($value); + } catch (Exception $e) { + throw ConversionException::conversionFailed($value, $this->getName(), $e); } return $dateTime; } /** - * {@inheritdoc} + * {@inheritDoc} + * + * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } } diff --git a/doctrine/dbal/src/Types/VarDateTimeType.php b/doctrine/dbal/src/Types/VarDateTimeType.php index b11ef2871..35ad40328 100644 --- a/doctrine/dbal/src/Types/VarDateTimeType.php +++ b/doctrine/dbal/src/Types/VarDateTimeType.php @@ -3,12 +3,12 @@ namespace Doctrine\DBAL\Types; use DateTime; +use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; - -use function date_create; +use Exception; /** - * Variable DateTime Type using date_create() instead of DateTime::createFromFormat(). + * Variable DateTime Type using DateTime::__construct() instead of DateTime::createFromFormat(). * * This type has performance implications as it runs twice as long as the regular * {@see DateTimeType}, however in certain PostgreSQL configurations with @@ -17,7 +17,13 @@ class VarDateTimeType extends DateTimeType { /** - * {@inheritdoc} + * {@inheritDoc} + * + * @param T $value + * + * @return (T is null ? null : DateTimeInterface) + * + * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { @@ -25,11 +31,12 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return $value; } - $val = date_create($value); - if ($val === false) { - throw ConversionException::conversionFailed($value, $this->getName()); + try { + $dateTime = new DateTime($value); + } catch (Exception $e) { + throw ConversionException::conversionFailed($value, $this->getName(), $e); } - return $val; + return $dateTime; } } diff --git a/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php b/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php index 1029372fa..bad5070ad 100644 --- a/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php +++ b/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php @@ -8,8 +8,10 @@ use function array_key_exists; use function array_reduce; +use function assert; use function debug_backtrace; use function sprintf; +use function str_replace; use function strpos; use function strrpos; use function substr; @@ -46,8 +48,8 @@ class Deprecation private const TYPE_TRIGGER_ERROR = 2; private const TYPE_PSR_LOGGER = 4; - /** @var int */ - private static $type = self::TYPE_NONE; + /** @var int-mask-of|null */ + private static $type; /** @var LoggerInterface|null */ private static $logger; @@ -56,6 +58,9 @@ class Deprecation private static $ignoredPackages = []; /** @var array */ + private static $triggeredDeprecations = []; + + /** @var array */ private static $ignoredLinks = []; /** @var bool */ @@ -68,21 +73,27 @@ class Deprecation * deprecation. It is additionally used to de-duplicate the trigger of the * same deprecation during a request. * - * @param mixed $args + * @param float|int|string $args */ public static function trigger(string $package, string $link, string $message, ...$args): void { - if (self::$type === self::TYPE_NONE) { + $type = self::$type ?? self::getTypeFromEnv(); + + if ($type === self::TYPE_NONE) { + return; + } + + if (isset(self::$ignoredLinks[$link])) { return; } - if (array_key_exists($link, self::$ignoredLinks)) { - self::$ignoredLinks[$link]++; + if (array_key_exists($link, self::$triggeredDeprecations)) { + self::$triggeredDeprecations[$link]++; } else { - self::$ignoredLinks[$link] = 1; + self::$triggeredDeprecations[$link] = 1; } - if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) { + if (self::$deduplication === true && self::$triggeredDeprecations[$link] > 1) { return; } @@ -114,19 +125,21 @@ public static function trigger(string $package, string $link, string $message, . * deprecation tracking is enabled even during deduplication, because it * needs to call {@link debug_backtrace()} * - * @param mixed $args + * @param float|int|string $args */ public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args): void { - if (self::$type === self::TYPE_NONE) { + $type = self::$type ?? self::getTypeFromEnv(); + + if ($type === self::TYPE_NONE) { return; } $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); // first check that the caller is not from a tests folder, in which case we always let deprecations pass - if (strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === false) { - $path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package . DIRECTORY_SEPARATOR; + if (isset($backtrace[1]['file'], $backtrace[0]['file']) && strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === false) { + $path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $package) . DIRECTORY_SEPARATOR; if (strpos($backtrace[0]['file'], $path) === false) { return; @@ -137,13 +150,17 @@ public static function triggerIfCalledFromOutside(string $package, string $link, } } - if (array_key_exists($link, self::$ignoredLinks)) { - self::$ignoredLinks[$link]++; + if (isset(self::$ignoredLinks[$link])) { + return; + } + + if (array_key_exists($link, self::$triggeredDeprecations)) { + self::$triggeredDeprecations[$link]++; } else { - self::$ignoredLinks[$link] = 1; + self::$triggeredDeprecations[$link] = 1; } - if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) { + if (self::$deduplication === true && self::$triggeredDeprecations[$link] > 1) { return; } @@ -157,31 +174,35 @@ public static function triggerIfCalledFromOutside(string $package, string $link, } /** - * @param array $backtrace + * @param list $backtrace */ private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void { - if ((self::$type & self::TYPE_PSR_LOGGER) > 0) { + $type = self::$type ?? self::getTypeFromEnv(); + + if (($type & self::TYPE_PSR_LOGGER) > 0) { $context = [ - 'file' => $backtrace[0]['file'], - 'line' => $backtrace[0]['line'], + 'file' => $backtrace[0]['file'] ?? null, + 'line' => $backtrace[0]['line'] ?? null, 'package' => $package, 'link' => $link, ]; + assert(self::$logger !== null); + self::$logger->notice($message, $context); } - if (! ((self::$type & self::TYPE_TRIGGER_ERROR) > 0)) { + if (! (($type & self::TYPE_TRIGGER_ERROR) > 0)) { return; } $message .= sprintf( ' (%s:%d called by %s:%d, %s, package %s)', - self::basename($backtrace[0]['file']), - $backtrace[0]['line'], - self::basename($backtrace[1]['file']), - $backtrace[1]['line'], + self::basename($backtrace[0]['file'] ?? 'native code'), + $backtrace[0]['line'] ?? 0, + self::basename($backtrace[1]['file'] ?? 'native code'), + $backtrace[1]['line'] ?? 0, $link, $package ); @@ -205,16 +226,19 @@ private static function basename(string $filename): string public static function enableTrackingDeprecations(): void { + self::$type = self::$type ?? 0; self::$type |= self::TYPE_TRACK_DEPRECATIONS; } public static function enableWithTriggerError(): void { + self::$type = self::$type ?? 0; self::$type |= self::TYPE_TRIGGER_ERROR; } public static function enableWithPsrLogger(LoggerInterface $logger): void { + self::$type = self::$type ?? 0; self::$type |= self::TYPE_PSR_LOGGER; self::$logger = $logger; } @@ -229,9 +253,10 @@ public static function disable(): void self::$type = self::TYPE_NONE; self::$logger = null; self::$deduplication = true; + self::$ignoredLinks = []; - foreach (self::$ignoredLinks as $link => $count) { - self::$ignoredLinks[$link] = 0; + foreach (self::$triggeredDeprecations as $link => $count) { + self::$triggeredDeprecations[$link] = 0; } } @@ -243,13 +268,13 @@ public static function ignorePackage(string $packageName): void public static function ignoreDeprecations(string ...$links): void { foreach ($links as $link) { - self::$ignoredLinks[$link] = 0; + self::$ignoredLinks[$link] = true; } } public static function getUniqueTriggeredDeprecationsCount(): int { - return array_reduce(self::$ignoredLinks, static function (int $carry, int $count) { + return array_reduce(self::$triggeredDeprecations, static function (int $carry, int $count) { return $carry + $count; }, 0); } @@ -261,6 +286,28 @@ public static function getUniqueTriggeredDeprecationsCount(): int */ public static function getTriggeredDeprecations(): array { - return self::$ignoredLinks; + return self::$triggeredDeprecations; + } + + /** + * @return int-mask-of + */ + private static function getTypeFromEnv(): int + { + switch ($_SERVER['DOCTRINE_DEPRECATIONS'] ?? $_ENV['DOCTRINE_DEPRECATIONS'] ?? null) { + case 'trigger': + self::$type = self::TYPE_TRIGGER_ERROR; + break; + + case 'track': + self::$type = self::TYPE_TRACK_DEPRECATIONS; + break; + + default: + self::$type = self::TYPE_NONE; + break; + } + + return self::$type; } } diff --git a/psr/cache/src/CacheException.php b/psr/cache/src/CacheException.php index e27f22f8d..bb785f46c 100644 --- a/psr/cache/src/CacheException.php +++ b/psr/cache/src/CacheException.php @@ -5,6 +5,6 @@ /** * Exception interface for all exceptions thrown by an Implementing Library. */ -interface CacheException +interface CacheException extends \Throwable { } diff --git a/psr/cache/src/CacheItemInterface.php b/psr/cache/src/CacheItemInterface.php index 63d05dd1f..2b2e4bb88 100644 --- a/psr/cache/src/CacheItemInterface.php +++ b/psr/cache/src/CacheItemInterface.php @@ -32,7 +32,7 @@ interface CacheItemInterface * @return string * The key string for this cache item. */ - public function getKey(); + public function getKey(): string; /** * Retrieves the value of the item from the cache associated with this object's key. @@ -46,7 +46,7 @@ public function getKey(); * @return mixed * The value corresponding to this cache item's key, or null if not found. */ - public function get(); + public function get(): mixed; /** * Confirms if the cache item lookup resulted in a cache hit. @@ -57,7 +57,7 @@ public function get(); * @return bool * True if the request resulted in a cache hit. False otherwise. */ - public function isHit(); + public function isHit(): bool; /** * Sets the value represented by this cache item. @@ -72,12 +72,12 @@ public function isHit(); * @return static * The invoked object. */ - public function set($value); + public function set(mixed $value): static; /** * Sets the expiration time for this cache item. * - * @param \DateTimeInterface|null $expiration + * @param ?\DateTimeInterface $expiration * The point in time after which the item MUST be considered expired. * If null is passed explicitly, a default value MAY be used. If none is set, * the value should be stored permanently or for as long as the @@ -86,7 +86,7 @@ public function set($value); * @return static * The called object. */ - public function expiresAt($expiration); + public function expiresAt(?\DateTimeInterface $expiration): static; /** * Sets the expiration time for this cache item. @@ -101,5 +101,5 @@ public function expiresAt($expiration); * @return static * The called object. */ - public function expiresAfter($time); + public function expiresAfter(int|\DateInterval|null $time): static; } diff --git a/psr/cache/src/CacheItemPoolInterface.php b/psr/cache/src/CacheItemPoolInterface.php index 035141967..4b3017c75 100644 --- a/psr/cache/src/CacheItemPoolInterface.php +++ b/psr/cache/src/CacheItemPoolInterface.php @@ -29,7 +29,7 @@ interface CacheItemPoolInterface * @return CacheItemInterface * The corresponding Cache Item. */ - public function getItem($key); + public function getItem(string $key): CacheItemInterface; /** * Returns a traversable set of cache items. @@ -41,13 +41,13 @@ public function getItem($key); * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException * MUST be thrown. * - * @return array|\Traversable - * A traversable collection of Cache Items keyed by the cache keys of + * @return iterable + * An iterable collection of Cache Items keyed by the cache keys of * each item. A Cache item will be returned for each key, even if that * key is not found. However, if no keys are specified then an empty * traversable MUST be returned instead. */ - public function getItems(array $keys = array()); + public function getItems(array $keys = []): iterable; /** * Confirms if the cache contains specified cache item. @@ -66,7 +66,7 @@ public function getItems(array $keys = array()); * @return bool * True if item exists in the cache, false otherwise. */ - public function hasItem($key); + public function hasItem(string $key): bool; /** * Deletes all items in the pool. @@ -74,7 +74,7 @@ public function hasItem($key); * @return bool * True if the pool was successfully cleared. False if there was an error. */ - public function clear(); + public function clear(): bool; /** * Removes the item from the pool. @@ -89,14 +89,14 @@ public function clear(); * @return bool * True if the item was successfully removed. False if there was an error. */ - public function deleteItem($key); + public function deleteItem(string $key): bool; /** * Removes multiple items from the pool. * * @param string[] $keys * An array of keys that should be removed from the pool. - + * * @throws InvalidArgumentException * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException * MUST be thrown. @@ -104,7 +104,7 @@ public function deleteItem($key); * @return bool * True if the items were successfully removed. False if there was an error. */ - public function deleteItems(array $keys); + public function deleteItems(array $keys): bool; /** * Persists a cache item immediately. @@ -115,7 +115,7 @@ public function deleteItems(array $keys); * @return bool * True if the item was successfully persisted. False if there was an error. */ - public function save(CacheItemInterface $item); + public function save(CacheItemInterface $item): bool; /** * Sets a cache item to be persisted later. @@ -126,7 +126,7 @@ public function save(CacheItemInterface $item); * @return bool * False if the item could not be queued or if a commit was attempted and failed. True otherwise. */ - public function saveDeferred(CacheItemInterface $item); + public function saveDeferred(CacheItemInterface $item): bool; /** * Persists any deferred cache items. @@ -134,5 +134,5 @@ public function saveDeferred(CacheItemInterface $item); * @return bool * True if all not-yet-saved items were successfully saved or there were none. False otherwise. */ - public function commit(); + public function commit(): bool; }