diff --git a/composer.json b/composer.json index f4549fb96..68da9a016 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "cweagans/composer-patches": "^1.7", "deepdiver/zipstreamer": "2.0.0", "deepdiver1975/tarstreamer": "v2.0.0", - "doctrine/dbal": "3.1.4", + "doctrine/dbal": "^3.5.1", "egulias/email-validator": "3.1.1", "fusonic/opengraph": "^2.2", "giggsey/libphonenumber-for-php": "^8.12.37", diff --git a/composer.lock b/composer.lock index 2d3cd6f1b..e28056174 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": "a1604425ae48e816f49a424526091ad7", + "content-hash": "743aa24d465bfe7a7c9d3d7b3bd26a48", "packages": [ { "name": "aws/aws-crt-php", @@ -343,79 +343,6 @@ }, "time": "2021-11-29T15:02:22+00:00" }, - { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.4", - "source": { - "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b174585d1fe49ceed21928a945138948cb394600" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", - "reference": "b174585d1fe49ceed21928a945138948cb394600", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2021-09-13T08:41:34+00:00" - }, { "name": "cweagans/composer-patches", "version": "1.7.1", @@ -577,16 +504,16 @@ }, { "name": "doctrine/cache", - "version": "2.1.1", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "331b4d5dbaeab3827976273e9356b3b453c300ce" + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/331b4d5dbaeab3827976273e9356b3b453c300ce", - "reference": "331b4d5dbaeab3827976273e9356b3b453c300ce", + "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", "shasum": "" }, "require": { @@ -596,18 +523,12 @@ "doctrine/common": ">2.2,<2.4" }, "require-dev": { - "alcaeus/mongo-php-adapter": "^1.1", "cache/integration-tests": "dev-master", - "doctrine/coding-standard": "^8.0", - "mongodb/mongodb": "^1.1", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", - "predis/predis": "~1.0", + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "psr/cache": "^1.0 || ^2.0 || ^3.0", - "symfony/cache": "^4.4 || ^5.2 || ^6.0@dev", - "symfony/var-exporter": "^4.4 || ^5.2 || ^6.0@dev" - }, - "suggest": { - "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" + "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6" }, "type": "library", "autoload": { @@ -656,7 +577,7 @@ ], "support": { "issues": "https://github.com/doctrine/cache/issues", - "source": "https://github.com/doctrine/cache/tree/2.1.1" + "source": "https://github.com/doctrine/cache/tree/2.2.0" }, "funding": [ { @@ -672,40 +593,42 @@ "type": "tidelift" } ], - "time": "2021-07-17T14:49:29+00:00" + "time": "2022-05-20T20:07:39+00:00" }, { "name": "doctrine/dbal", - "version": "3.1.4", + "version": "3.5.1", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "821b4f01a36ce63ed36c090ea74767b72db367e9" + "reference": "f38ee8aaca2d58ee88653cb34a6a3880c23f38a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/821b4f01a36ce63ed36c090ea74767b72db367e9", - "reference": "821b4f01a36ce63ed36c090ea74767b72db367e9", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/f38ee8aaca2d58ee88653cb34a6a3880c23f38a5", + "reference": "f38ee8aaca2d58ee88653cb34a6a3880c23f38a5", "shasum": "" }, "require": { - "composer/package-versions-deprecated": "^1.11.99", - "doctrine/cache": "^1.0|^2.0", - "doctrine/deprecations": "^0.5.3", - "doctrine/event-manager": "^1.0", - "php": "^7.3 || ^8.0" + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "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": "2021.1", - "phpstan/phpstan": "1.1.1", - "phpstan/phpstan-strict-rules": "^1", - "phpunit/phpunit": "9.5.10", - "psalm/plugin-phpunit": "0.16.1", - "squizlabs/php_codesniffer": "3.6.1", - "symfony/cache": "^5.2|^6.0", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", - "vimeo/psalm": "4.12.0" + "doctrine/coding-standard": "10.0.0", + "jetbrains/phpstorm-stubs": "2022.2", + "phpstan/phpstan": "1.8.10", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "9.5.25", + "psalm/plugin-phpunit": "0.17.0", + "squizlabs/php_codesniffer": "3.7.1", + "symfony/cache": "^5.4|^6.0", + "symfony/console": "^4.4|^5.4|^6.0", + "vimeo/psalm": "4.29.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -765,7 +688,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.1.4" + "source": "https://github.com/doctrine/dbal/tree/3.5.1" }, "funding": [ { @@ -781,29 +704,29 @@ "type": "tidelift" } ], - "time": "2021-11-15T16:44:33+00:00" + "time": "2022-10-24T07:26:18+00:00" }, { "name": "doctrine/deprecations", - "version": "v0.5.3", + "version": "v1.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "9504165960a1f83cc1480e2be1dd0a0478561314" + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/9504165960a1f83cc1480e2be1dd0a0478561314", - "reference": "9504165960a1f83cc1480e2be1dd0a0478561314", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", "shasum": "" }, "require": { "php": "^7.1|^8.0" }, "require-dev": { - "doctrine/coding-standard": "^6.0|^7.0|^8.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0", - "psr/log": "^1.0" + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" @@ -822,43 +745,41 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v0.5.3" + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" }, - "time": "2021-03-21T12:59:47+00:00" + "time": "2022-05-02T15:47:09+00:00" }, { "name": "doctrine/event-manager", - "version": "1.1.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f" + "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f", - "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520", + "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520", "shasum": "" }, "require": { + "doctrine/deprecations": "^0.5.3 || ^1", "php": "^7.1 || ^8.0" }, "conflict": { - "doctrine/common": "<2.9@dev" + "doctrine/common": "<2.9" }, "require-dev": { - "doctrine/coding-standard": "^6.0", - "phpunit/phpunit": "^7.0" + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "~1.4.10 || ^1.8.8", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.24" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" + "Doctrine\\Common\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -902,7 +823,7 @@ ], "support": { "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/1.1.x" + "source": "https://github.com/doctrine/event-manager/tree/1.2.0" }, "funding": [ { @@ -918,7 +839,7 @@ "type": "tidelift" } ], - "time": "2020-05-29T18:28:51+00:00" + "time": "2022-10-12T20:51:15+00:00" }, { "name": "doctrine/lexer", @@ -3311,6 +3232,55 @@ }, "time": "2021-10-28T11:13:42+00:00" }, + { + "name": "psr/cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/master" + }, + "time": "2016-08-06T20:24:11+00:00" + }, { "name": "psr/container", "version": "1.1.1", diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php index 66b569111..13ae34723 100644 --- a/composer/autoload_classmap.php +++ b/composer/autoload_classmap.php @@ -1051,16 +1051,15 @@ 'Doctrine\\Common\\Cache\\Psr6\\DoctrineProvider' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php', 'Doctrine\\Common\\Cache\\Psr6\\InvalidArgument' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php', 'Doctrine\\Common\\Cache\\Psr6\\TypedCacheItem' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/TypedCacheItem.php', - 'Doctrine\\Common\\EventArgs' => $vendorDir . '/doctrine/event-manager/lib/Doctrine/Common/EventArgs.php', - 'Doctrine\\Common\\EventManager' => $vendorDir . '/doctrine/event-manager/lib/Doctrine/Common/EventManager.php', - 'Doctrine\\Common\\EventSubscriber' => $vendorDir . '/doctrine/event-manager/lib/Doctrine/Common/EventSubscriber.php', + 'Doctrine\\Common\\EventArgs' => $vendorDir . '/doctrine/event-manager/src/EventArgs.php', + '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\\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', 'Doctrine\\DBAL\\Cache\\ArrayResult' => $vendorDir . '/doctrine/dbal/src/Cache/ArrayResult.php', 'Doctrine\\DBAL\\Cache\\CacheException' => $vendorDir . '/doctrine/dbal/src/Cache/CacheException.php', - 'Doctrine\\DBAL\\Cache\\CachingResult' => $vendorDir . '/doctrine/dbal/src/Cache/CachingResult.php', 'Doctrine\\DBAL\\Cache\\QueryCacheProfile' => $vendorDir . '/doctrine/dbal/src/Cache/QueryCacheProfile.php', 'Doctrine\\DBAL\\ColumnCase' => $vendorDir . '/doctrine/dbal/src/ColumnCase.php', 'Doctrine\\DBAL\\Configuration' => $vendorDir . '/doctrine/dbal/src/Configuration.php', @@ -1076,6 +1075,7 @@ 'Doctrine\\DBAL\\Driver\\API\\PostgreSQL\\ExceptionConverter' => $vendorDir . '/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php', 'Doctrine\\DBAL\\Driver\\API\\SQLSrv\\ExceptionConverter' => $vendorDir . '/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php', 'Doctrine\\DBAL\\Driver\\API\\SQLite\\ExceptionConverter' => $vendorDir . '/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php', + 'Doctrine\\DBAL\\Driver\\API\\SQLite\\UserDefinedFunctions' => $vendorDir . '/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php', 'Doctrine\\DBAL\\Driver\\AbstractDB2Driver' => $vendorDir . '/doctrine/dbal/src/Driver/AbstractDB2Driver.php', 'Doctrine\\DBAL\\Driver\\AbstractException' => $vendorDir . '/doctrine/dbal/src/Driver/AbstractException.php', 'Doctrine\\DBAL\\Driver\\AbstractMySQLDriver' => $vendorDir . '/doctrine/dbal/src/Driver/AbstractMySQLDriver.php', @@ -1085,6 +1085,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', @@ -1094,14 +1095,18 @@ 'Doctrine\\DBAL\\Driver\\IBMDB2\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Driver.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\CannotCopyStreamToStream' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\CannotCreateTemporaryFile' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php', - 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\CannotWriteToTemporaryFile' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotWriteToTemporaryFile.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\ConnectionError' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionError.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\ConnectionFailed' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionFailed.php', + 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\Factory' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Exception/Factory.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\PrepareFailed' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\StatementError' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Result' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Result.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Statement' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Statement.php', 'Doctrine\\DBAL\\Driver\\Middleware' => $vendorDir . '/doctrine/dbal/src/Driver/Middleware.php', + 'Doctrine\\DBAL\\Driver\\Middleware\\AbstractConnectionMiddleware' => $vendorDir . '/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php', + 'Doctrine\\DBAL\\Driver\\Middleware\\AbstractDriverMiddleware' => $vendorDir . '/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php', + 'Doctrine\\DBAL\\Driver\\Middleware\\AbstractResultMiddleware' => $vendorDir . '/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php', + 'Doctrine\\DBAL\\Driver\\Middleware\\AbstractStatementMiddleware' => $vendorDir . '/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php', 'Doctrine\\DBAL\\Driver\\Mysqli\\Connection' => $vendorDir . '/doctrine/dbal/src/Driver/Mysqli/Connection.php', 'Doctrine\\DBAL\\Driver\\Mysqli\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/Mysqli/Driver.php', 'Doctrine\\DBAL\\Driver\\Mysqli\\Exception\\ConnectionError' => $vendorDir . '/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php', @@ -1127,6 +1132,7 @@ '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', @@ -1146,11 +1152,17 @@ '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', @@ -1162,13 +1174,19 @@ 'Doctrine\\DBAL\\Event\\SchemaDropTableEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php', 'Doctrine\\DBAL\\Event\\SchemaEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaEventArgs.php', 'Doctrine\\DBAL\\Event\\SchemaIndexDefinitionEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php', + 'Doctrine\\DBAL\\Event\\TransactionBeginEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/TransactionBeginEventArgs.php', + 'Doctrine\\DBAL\\Event\\TransactionCommitEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/TransactionCommitEventArgs.php', + 'Doctrine\\DBAL\\Event\\TransactionEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/TransactionEventArgs.php', + 'Doctrine\\DBAL\\Event\\TransactionRollBackEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php', 'Doctrine\\DBAL\\Events' => $vendorDir . '/doctrine/dbal/src/Events.php', 'Doctrine\\DBAL\\Exception' => $vendorDir . '/doctrine/dbal/src/Exception.php', 'Doctrine\\DBAL\\Exception\\ConnectionException' => $vendorDir . '/doctrine/dbal/src/Exception/ConnectionException.php', 'Doctrine\\DBAL\\Exception\\ConnectionLost' => $vendorDir . '/doctrine/dbal/src/Exception/ConnectionLost.php', 'Doctrine\\DBAL\\Exception\\ConstraintViolationException' => $vendorDir . '/doctrine/dbal/src/Exception/ConstraintViolationException.php', + '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', @@ -1181,6 +1199,7 @@ 'Doctrine\\DBAL\\Exception\\NotNullConstraintViolationException' => $vendorDir . '/doctrine/dbal/src/Exception/NotNullConstraintViolationException.php', 'Doctrine\\DBAL\\Exception\\ReadOnlyException' => $vendorDir . '/doctrine/dbal/src/Exception/ReadOnlyException.php', 'Doctrine\\DBAL\\Exception\\RetryableException' => $vendorDir . '/doctrine/dbal/src/Exception/RetryableException.php', + 'Doctrine\\DBAL\\Exception\\SchemaDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Exception/SchemaDoesNotExist.php', 'Doctrine\\DBAL\\Exception\\ServerException' => $vendorDir . '/doctrine/dbal/src/Exception/ServerException.php', 'Doctrine\\DBAL\\Exception\\SyntaxErrorException' => $vendorDir . '/doctrine/dbal/src/Exception/SyntaxErrorException.php', 'Doctrine\\DBAL\\Exception\\TableExistsException' => $vendorDir . '/doctrine/dbal/src/Exception/TableExistsException.php', @@ -1191,15 +1210,21 @@ 'Doctrine\\DBAL\\Id\\TableGenerator' => $vendorDir . '/doctrine/dbal/src/Id/TableGenerator.php', 'Doctrine\\DBAL\\Id\\TableGeneratorSchemaVisitor' => $vendorDir . '/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php', 'Doctrine\\DBAL\\LockMode' => $vendorDir . '/doctrine/dbal/src/LockMode.php', + 'Doctrine\\DBAL\\Logging\\Connection' => $vendorDir . '/doctrine/dbal/src/Logging/Connection.php', 'Doctrine\\DBAL\\Logging\\DebugStack' => $vendorDir . '/doctrine/dbal/src/Logging/DebugStack.php', + 'Doctrine\\DBAL\\Logging\\Driver' => $vendorDir . '/doctrine/dbal/src/Logging/Driver.php', 'Doctrine\\DBAL\\Logging\\LoggerChain' => $vendorDir . '/doctrine/dbal/src/Logging/LoggerChain.php', + 'Doctrine\\DBAL\\Logging\\Middleware' => $vendorDir . '/doctrine/dbal/src/Logging/Middleware.php', 'Doctrine\\DBAL\\Logging\\SQLLogger' => $vendorDir . '/doctrine/dbal/src/Logging/SQLLogger.php', + 'Doctrine\\DBAL\\Logging\\Statement' => $vendorDir . '/doctrine/dbal/src/Logging/Statement.php', '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\\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', 'Doctrine\\DBAL\\Platforms\\Keywords\\KeywordList' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/KeywordList.php', + 'Doctrine\\DBAL\\Platforms\\Keywords\\MariaDBKeywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MariaDb102Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MySQL57Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MySQL80Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/MySQL80Keywords.php', @@ -1207,17 +1232,28 @@ 'Doctrine\\DBAL\\Platforms\\Keywords\\OracleKeywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/OracleKeywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQL100Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQL94Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQL94Keywords.php', + 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQLKeywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQLKeywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\ReservedKeywordsValidator' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLServer2012Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php', + 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLServerKeywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php', '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\\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', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider\\CachingCollationMetadataProvider' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider\\ConnectionCollationMetadataProvider' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\Comparator' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/Comparator.php', 'Doctrine\\DBAL\\Platforms\\OraclePlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/OraclePlatform.php', 'Doctrine\\DBAL\\Platforms\\PostgreSQL100Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php', 'Doctrine\\DBAL\\Platforms\\PostgreSQL94Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php', + 'Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php', 'Doctrine\\DBAL\\Platforms\\SQLServer2012Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/SQLServer2012Platform.php', + 'Doctrine\\DBAL\\Platforms\\SQLServerPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/SQLServerPlatform.php', + 'Doctrine\\DBAL\\Platforms\\SQLServer\\Comparator' => $vendorDir . '/doctrine/dbal/src/Platforms/SQLServer/Comparator.php', + 'Doctrine\\DBAL\\Platforms\\SQLite\\Comparator' => $vendorDir . '/doctrine/dbal/src/Platforms/SQLite/Comparator.php', 'Doctrine\\DBAL\\Platforms\\SqlitePlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/SqlitePlatform.php', 'Doctrine\\DBAL\\Platforms\\TrimMode' => $vendorDir . '/doctrine/dbal/src/Platforms/TrimMode.php', 'Doctrine\\DBAL\\Portability\\Connection' => $vendorDir . '/doctrine/dbal/src/Portability/Connection.php', @@ -1233,6 +1269,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', @@ -1244,7 +1282,20 @@ '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\\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', @@ -1269,7 +1320,6 @@ '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\\ReservedWordsCommand' => $vendorDir . '/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php', @@ -1278,7 +1328,6 @@ '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\\Dumper' => $vendorDir . '/doctrine/dbal/src/Tools/Dumper.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', @@ -2003,9 +2052,6 @@ 'PEAR' => $vendorDir . '/pear/pear-core-minimal/src/PEAR.php', 'PEAR_ErrorStack' => $vendorDir . '/pear/pear-core-minimal/src/PEAR/ErrorStack.php', 'PEAR_Exception' => $vendorDir . '/pear/pear_exception/PEAR/Exception.php', - 'PackageVersions\\FallbackVersions' => $vendorDir . '/composer/package-versions-deprecated/src/PackageVersions/FallbackVersions.php', - 'PackageVersions\\Installer' => $vendorDir . '/composer/package-versions-deprecated/src/PackageVersions/Installer.php', - 'PackageVersions\\Versions' => $vendorDir . '/composer/package-versions-deprecated/src/PackageVersions/Versions.php', 'PhpParser\\Builder' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder.php', 'PhpParser\\BuilderFactory' => $vendorDir . '/nikic/php-parser/lib/PhpParser/BuilderFactory.php', 'PhpParser\\BuilderHelpers' => $vendorDir . '/nikic/php-parser/lib/PhpParser/BuilderHelpers.php', @@ -2257,6 +2303,10 @@ 'Pimple\\Psr11\\ServiceLocator' => $vendorDir . '/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php', 'Pimple\\ServiceIterator' => $vendorDir . '/pimple/pimple/src/Pimple/ServiceIterator.php', 'Pimple\\ServiceProviderInterface' => $vendorDir . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php', + 'Psr\\Cache\\CacheException' => $vendorDir . '/psr/cache/src/CacheException.php', + 'Psr\\Cache\\CacheItemInterface' => $vendorDir . '/psr/cache/src/CacheItemInterface.php', + 'Psr\\Cache\\CacheItemPoolInterface' => $vendorDir . '/psr/cache/src/CacheItemPoolInterface.php', + 'Psr\\Cache\\InvalidArgumentException' => $vendorDir . '/psr/cache/src/InvalidArgumentException.php', 'Psr\\Container\\ContainerExceptionInterface' => $vendorDir . '/psr/container/src/ContainerExceptionInterface.php', 'Psr\\Container\\ContainerInterface' => $vendorDir . '/psr/container/src/ContainerInterface.php', 'Psr\\Container\\NotFoundExceptionInterface' => $vendorDir . '/psr/container/src/NotFoundExceptionInterface.php', diff --git a/composer/autoload_psr4.php b/composer/autoload_psr4.php index 59242d02e..e845bb85e 100644 --- a/composer/autoload_psr4.php +++ b/composer/autoload_psr4.php @@ -55,8 +55,8 @@ 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), 'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'), 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), + 'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'), 'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), - 'PackageVersions\\' => array($vendorDir . '/composer/package-versions-deprecated/src/PackageVersions'), 'Opis\\Closure\\' => array($vendorDir . '/opis/closure/src'), 'OpenStack\\' => array($vendorDir . '/php-opencloud/openstack/src'), 'Nextcloud\\LogNormalizer\\' => array($vendorDir . '/nextcloud/lognormalizer/src'), @@ -85,7 +85,7 @@ 'Doctrine\\DBAL\\' => array($vendorDir . '/doctrine/dbal/src'), 'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'), 'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'), - 'Doctrine\\Common\\' => array($vendorDir . '/doctrine/event-manager/lib/Doctrine/Common'), + 'Doctrine\\Common\\' => array($vendorDir . '/doctrine/event-manager/src'), 'Cose\\' => array($vendorDir . '/web-auth/cose-lib/src'), 'CBOR\\' => array($vendorDir . '/spomky-labs/cbor-php/src'), 'Brick\\Math\\' => array($vendorDir . '/brick/math/src'), diff --git a/composer/autoload_static.php b/composer/autoload_static.php index e0442213d..93fcf1dfa 100644 --- a/composer/autoload_static.php +++ b/composer/autoload_static.php @@ -204,8 +204,8 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Psr\\Http\\Client\\' => 16, 'Psr\\EventDispatcher\\' => 20, 'Psr\\Container\\' => 14, + 'Psr\\Cache\\' => 10, 'PhpParser\\' => 10, - 'PackageVersions\\' => 16, ), 'O' => array ( @@ -486,13 +486,13 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 array ( 0 => __DIR__ . '/..' . '/psr/container/src', ), - 'PhpParser\\' => + 'Psr\\Cache\\' => array ( - 0 => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser', + 0 => __DIR__ . '/..' . '/psr/cache/src', ), - 'PackageVersions\\' => + 'PhpParser\\' => array ( - 0 => __DIR__ . '/..' . '/composer/package-versions-deprecated/src/PackageVersions', + 0 => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser', ), 'Opis\\Closure\\' => array ( @@ -609,7 +609,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 ), 'Doctrine\\Common\\' => array ( - 0 => __DIR__ . '/..' . '/doctrine/event-manager/lib/Doctrine/Common', + 0 => __DIR__ . '/..' . '/doctrine/event-manager/src', ), 'Cose\\' => array ( @@ -1718,16 +1718,15 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\Common\\Cache\\Psr6\\DoctrineProvider' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php', 'Doctrine\\Common\\Cache\\Psr6\\InvalidArgument' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php', 'Doctrine\\Common\\Cache\\Psr6\\TypedCacheItem' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/TypedCacheItem.php', - 'Doctrine\\Common\\EventArgs' => __DIR__ . '/..' . '/doctrine/event-manager/lib/Doctrine/Common/EventArgs.php', - 'Doctrine\\Common\\EventManager' => __DIR__ . '/..' . '/doctrine/event-manager/lib/Doctrine/Common/EventManager.php', - 'Doctrine\\Common\\EventSubscriber' => __DIR__ . '/..' . '/doctrine/event-manager/lib/Doctrine/Common/EventSubscriber.php', + 'Doctrine\\Common\\EventArgs' => __DIR__ . '/..' . '/doctrine/event-manager/src/EventArgs.php', + '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\\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', 'Doctrine\\DBAL\\Cache\\ArrayResult' => __DIR__ . '/..' . '/doctrine/dbal/src/Cache/ArrayResult.php', 'Doctrine\\DBAL\\Cache\\CacheException' => __DIR__ . '/..' . '/doctrine/dbal/src/Cache/CacheException.php', - 'Doctrine\\DBAL\\Cache\\CachingResult' => __DIR__ . '/..' . '/doctrine/dbal/src/Cache/CachingResult.php', 'Doctrine\\DBAL\\Cache\\QueryCacheProfile' => __DIR__ . '/..' . '/doctrine/dbal/src/Cache/QueryCacheProfile.php', 'Doctrine\\DBAL\\ColumnCase' => __DIR__ . '/..' . '/doctrine/dbal/src/ColumnCase.php', 'Doctrine\\DBAL\\Configuration' => __DIR__ . '/..' . '/doctrine/dbal/src/Configuration.php', @@ -1743,6 +1742,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Driver\\API\\PostgreSQL\\ExceptionConverter' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php', 'Doctrine\\DBAL\\Driver\\API\\SQLSrv\\ExceptionConverter' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php', 'Doctrine\\DBAL\\Driver\\API\\SQLite\\ExceptionConverter' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php', + 'Doctrine\\DBAL\\Driver\\API\\SQLite\\UserDefinedFunctions' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php', 'Doctrine\\DBAL\\Driver\\AbstractDB2Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/AbstractDB2Driver.php', 'Doctrine\\DBAL\\Driver\\AbstractException' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/AbstractException.php', 'Doctrine\\DBAL\\Driver\\AbstractMySQLDriver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/AbstractMySQLDriver.php', @@ -1752,6 +1752,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', @@ -1761,14 +1762,18 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Driver\\IBMDB2\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Driver.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\CannotCopyStreamToStream' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\CannotCreateTemporaryFile' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php', - 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\CannotWriteToTemporaryFile' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotWriteToTemporaryFile.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\ConnectionError' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionError.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\ConnectionFailed' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionFailed.php', + 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\Factory' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Exception/Factory.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\PrepareFailed' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Exception\\StatementError' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Result.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Statement.php', 'Doctrine\\DBAL\\Driver\\Middleware' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Middleware.php', + 'Doctrine\\DBAL\\Driver\\Middleware\\AbstractConnectionMiddleware' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php', + 'Doctrine\\DBAL\\Driver\\Middleware\\AbstractDriverMiddleware' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php', + 'Doctrine\\DBAL\\Driver\\Middleware\\AbstractResultMiddleware' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php', + 'Doctrine\\DBAL\\Driver\\Middleware\\AbstractStatementMiddleware' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php', 'Doctrine\\DBAL\\Driver\\Mysqli\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Mysqli/Connection.php', 'Doctrine\\DBAL\\Driver\\Mysqli\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Mysqli/Driver.php', 'Doctrine\\DBAL\\Driver\\Mysqli\\Exception\\ConnectionError' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php', @@ -1794,6 +1799,7 @@ 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', @@ -1813,11 +1819,17 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 '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', @@ -1829,13 +1841,19 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Event\\SchemaDropTableEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php', 'Doctrine\\DBAL\\Event\\SchemaEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaEventArgs.php', 'Doctrine\\DBAL\\Event\\SchemaIndexDefinitionEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php', + 'Doctrine\\DBAL\\Event\\TransactionBeginEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/TransactionBeginEventArgs.php', + 'Doctrine\\DBAL\\Event\\TransactionCommitEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/TransactionCommitEventArgs.php', + 'Doctrine\\DBAL\\Event\\TransactionEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/TransactionEventArgs.php', + 'Doctrine\\DBAL\\Event\\TransactionRollBackEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php', 'Doctrine\\DBAL\\Events' => __DIR__ . '/..' . '/doctrine/dbal/src/Events.php', 'Doctrine\\DBAL\\Exception' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception.php', 'Doctrine\\DBAL\\Exception\\ConnectionException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ConnectionException.php', 'Doctrine\\DBAL\\Exception\\ConnectionLost' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ConnectionLost.php', 'Doctrine\\DBAL\\Exception\\ConstraintViolationException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ConstraintViolationException.php', + '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', @@ -1848,6 +1866,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Exception\\NotNullConstraintViolationException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/NotNullConstraintViolationException.php', 'Doctrine\\DBAL\\Exception\\ReadOnlyException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ReadOnlyException.php', 'Doctrine\\DBAL\\Exception\\RetryableException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/RetryableException.php', + 'Doctrine\\DBAL\\Exception\\SchemaDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/SchemaDoesNotExist.php', 'Doctrine\\DBAL\\Exception\\ServerException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ServerException.php', 'Doctrine\\DBAL\\Exception\\SyntaxErrorException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/SyntaxErrorException.php', 'Doctrine\\DBAL\\Exception\\TableExistsException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/TableExistsException.php', @@ -1858,15 +1877,21 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Id\\TableGenerator' => __DIR__ . '/..' . '/doctrine/dbal/src/Id/TableGenerator.php', 'Doctrine\\DBAL\\Id\\TableGeneratorSchemaVisitor' => __DIR__ . '/..' . '/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php', 'Doctrine\\DBAL\\LockMode' => __DIR__ . '/..' . '/doctrine/dbal/src/LockMode.php', + 'Doctrine\\DBAL\\Logging\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/Connection.php', 'Doctrine\\DBAL\\Logging\\DebugStack' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/DebugStack.php', + 'Doctrine\\DBAL\\Logging\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/Driver.php', 'Doctrine\\DBAL\\Logging\\LoggerChain' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/LoggerChain.php', + 'Doctrine\\DBAL\\Logging\\Middleware' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/Middleware.php', 'Doctrine\\DBAL\\Logging\\SQLLogger' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/SQLLogger.php', + 'Doctrine\\DBAL\\Logging\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/Statement.php', '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\\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', 'Doctrine\\DBAL\\Platforms\\Keywords\\KeywordList' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/KeywordList.php', + 'Doctrine\\DBAL\\Platforms\\Keywords\\MariaDBKeywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MariaDb102Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MySQL57Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MySQL80Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/MySQL80Keywords.php', @@ -1874,17 +1899,28 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Platforms\\Keywords\\OracleKeywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/OracleKeywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQL100Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQL94Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQL94Keywords.php', + 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQLKeywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQLKeywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\ReservedKeywordsValidator' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLServer2012Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php', + 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLServerKeywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php', '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\\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', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider\\CachingCollationMetadataProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider\\ConnectionCollationMetadataProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\Comparator' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/Comparator.php', 'Doctrine\\DBAL\\Platforms\\OraclePlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/OraclePlatform.php', 'Doctrine\\DBAL\\Platforms\\PostgreSQL100Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php', 'Doctrine\\DBAL\\Platforms\\PostgreSQL94Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php', + 'Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php', 'Doctrine\\DBAL\\Platforms\\SQLServer2012Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SQLServer2012Platform.php', + 'Doctrine\\DBAL\\Platforms\\SQLServerPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SQLServerPlatform.php', + 'Doctrine\\DBAL\\Platforms\\SQLServer\\Comparator' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SQLServer/Comparator.php', + 'Doctrine\\DBAL\\Platforms\\SQLite\\Comparator' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SQLite/Comparator.php', 'Doctrine\\DBAL\\Platforms\\SqlitePlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SqlitePlatform.php', 'Doctrine\\DBAL\\Platforms\\TrimMode' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/TrimMode.php', 'Doctrine\\DBAL\\Portability\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Portability/Connection.php', @@ -1900,6 +1936,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', @@ -1911,7 +1949,20 @@ 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\\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', @@ -1936,7 +1987,6 @@ 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\\ReservedWordsCommand' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php', @@ -1945,7 +1995,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 '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\\Dumper' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Dumper.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', @@ -2670,9 +2719,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'PEAR' => __DIR__ . '/..' . '/pear/pear-core-minimal/src/PEAR.php', 'PEAR_ErrorStack' => __DIR__ . '/..' . '/pear/pear-core-minimal/src/PEAR/ErrorStack.php', 'PEAR_Exception' => __DIR__ . '/..' . '/pear/pear_exception/PEAR/Exception.php', - 'PackageVersions\\FallbackVersions' => __DIR__ . '/..' . '/composer/package-versions-deprecated/src/PackageVersions/FallbackVersions.php', - 'PackageVersions\\Installer' => __DIR__ . '/..' . '/composer/package-versions-deprecated/src/PackageVersions/Installer.php', - 'PackageVersions\\Versions' => __DIR__ . '/..' . '/composer/package-versions-deprecated/src/PackageVersions/Versions.php', 'PhpParser\\Builder' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder.php', 'PhpParser\\BuilderFactory' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/BuilderFactory.php', 'PhpParser\\BuilderHelpers' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/BuilderHelpers.php', @@ -2924,6 +2970,10 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Pimple\\Psr11\\ServiceLocator' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php', 'Pimple\\ServiceIterator' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/ServiceIterator.php', 'Pimple\\ServiceProviderInterface' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php', + 'Psr\\Cache\\CacheException' => __DIR__ . '/..' . '/psr/cache/src/CacheException.php', + 'Psr\\Cache\\CacheItemInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemInterface.php', + 'Psr\\Cache\\CacheItemPoolInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemPoolInterface.php', + 'Psr\\Cache\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/cache/src/InvalidArgumentException.php', 'Psr\\Container\\ContainerExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerExceptionInterface.php', 'Psr\\Container\\ContainerInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerInterface.php', 'Psr\\Container\\NotFoundExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/NotFoundExceptionInterface.php', diff --git a/composer/installed.json b/composer/installed.json index 811e97f79..4c562354e 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -355,82 +355,6 @@ }, "install-path": "../christophwurst/id3parser" }, - { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.4", - "version_normalized": "1.11.99.4", - "source": { - "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b174585d1fe49ceed21928a945138948cb394600" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", - "reference": "b174585d1fe49ceed21928a945138948cb394600", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "time": "2021-09-13T08:41:34+00:00", - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "install-path": "./package-versions-deprecated" - }, { "name": "cweagans/composer-patches", "version": "1.7.1", @@ -601,17 +525,17 @@ }, { "name": "doctrine/cache", - "version": "2.1.1", - "version_normalized": "2.1.1.0", + "version": "2.2.0", + "version_normalized": "2.2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "331b4d5dbaeab3827976273e9356b3b453c300ce" + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/331b4d5dbaeab3827976273e9356b3b453c300ce", - "reference": "331b4d5dbaeab3827976273e9356b3b453c300ce", + "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", "shasum": "" }, "require": { @@ -621,20 +545,14 @@ "doctrine/common": ">2.2,<2.4" }, "require-dev": { - "alcaeus/mongo-php-adapter": "^1.1", "cache/integration-tests": "dev-master", - "doctrine/coding-standard": "^8.0", - "mongodb/mongodb": "^1.1", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", - "predis/predis": "~1.0", + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "psr/cache": "^1.0 || ^2.0 || ^3.0", - "symfony/cache": "^4.4 || ^5.2 || ^6.0@dev", - "symfony/var-exporter": "^4.4 || ^5.2 || ^6.0@dev" + "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6" }, - "suggest": { - "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" - }, - "time": "2021-07-17T14:49:29+00:00", + "time": "2022-05-20T20:07:39+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -683,7 +601,7 @@ ], "support": { "issues": "https://github.com/doctrine/cache/issues", - "source": "https://github.com/doctrine/cache/tree/2.1.1" + "source": "https://github.com/doctrine/cache/tree/2.2.0" }, "funding": [ { @@ -703,42 +621,44 @@ }, { "name": "doctrine/dbal", - "version": "3.1.4", - "version_normalized": "3.1.4.0", + "version": "3.5.1", + "version_normalized": "3.5.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "821b4f01a36ce63ed36c090ea74767b72db367e9" + "reference": "f38ee8aaca2d58ee88653cb34a6a3880c23f38a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/821b4f01a36ce63ed36c090ea74767b72db367e9", - "reference": "821b4f01a36ce63ed36c090ea74767b72db367e9", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/f38ee8aaca2d58ee88653cb34a6a3880c23f38a5", + "reference": "f38ee8aaca2d58ee88653cb34a6a3880c23f38a5", "shasum": "" }, "require": { - "composer/package-versions-deprecated": "^1.11.99", - "doctrine/cache": "^1.0|^2.0", - "doctrine/deprecations": "^0.5.3", - "doctrine/event-manager": "^1.0", - "php": "^7.3 || ^8.0" + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "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": "2021.1", - "phpstan/phpstan": "1.1.1", - "phpstan/phpstan-strict-rules": "^1", - "phpunit/phpunit": "9.5.10", - "psalm/plugin-phpunit": "0.16.1", - "squizlabs/php_codesniffer": "3.6.1", - "symfony/cache": "^5.2|^6.0", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", - "vimeo/psalm": "4.12.0" + "doctrine/coding-standard": "10.0.0", + "jetbrains/phpstorm-stubs": "2022.2", + "phpstan/phpstan": "1.8.10", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "9.5.25", + "psalm/plugin-phpunit": "0.17.0", + "squizlabs/php_codesniffer": "3.7.1", + "symfony/cache": "^5.4|^6.0", + "symfony/console": "^4.4|^5.4|^6.0", + "vimeo/psalm": "4.29.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, - "time": "2021-11-15T16:44:33+00:00", + "time": "2022-10-24T07:26:18+00:00", "bin": [ "bin/doctrine-dbal" ], @@ -795,7 +715,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.1.4" + "source": "https://github.com/doctrine/dbal/tree/3.5.1" }, "funding": [ { @@ -815,31 +735,31 @@ }, { "name": "doctrine/deprecations", - "version": "v0.5.3", - "version_normalized": "0.5.3.0", + "version": "v1.0.0", + "version_normalized": "1.0.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "9504165960a1f83cc1480e2be1dd0a0478561314" + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/9504165960a1f83cc1480e2be1dd0a0478561314", - "reference": "9504165960a1f83cc1480e2be1dd0a0478561314", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", "shasum": "" }, "require": { "php": "^7.1|^8.0" }, "require-dev": { - "doctrine/coding-standard": "^6.0|^7.0|^8.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0", - "psr/log": "^1.0" + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, - "time": "2021-03-21T12:59:47+00:00", + "time": "2022-05-02T15:47:09+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -855,46 +775,44 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v0.5.3" + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" }, "install-path": "../doctrine/deprecations" }, { "name": "doctrine/event-manager", - "version": "1.1.1", - "version_normalized": "1.1.1.0", + "version": "1.2.0", + "version_normalized": "1.2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f" + "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f", - "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520", + "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520", "shasum": "" }, "require": { + "doctrine/deprecations": "^0.5.3 || ^1", "php": "^7.1 || ^8.0" }, "conflict": { - "doctrine/common": "<2.9@dev" + "doctrine/common": "<2.9" }, "require-dev": { - "doctrine/coding-standard": "^6.0", - "phpunit/phpunit": "^7.0" + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "~1.4.10 || ^1.8.8", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.24" }, - "time": "2020-05-29T18:28:51+00:00", + "time": "2022-10-12T20:51:15+00:00", "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "installation-source": "dist", "autoload": { "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" + "Doctrine\\Common\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -938,7 +856,7 @@ ], "support": { "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/1.1.x" + "source": "https://github.com/doctrine/event-manager/tree/1.2.0" }, "funding": [ { @@ -3455,6 +3373,58 @@ }, "install-path": "../pimple/pimple" }, + { + "name": "psr/cache", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-08-06T20:24:11+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/master" + }, + "install-path": "../psr/cache" + }, { "name": "psr/container", "version": "1.1.1", diff --git a/composer/installed.php b/composer/installed.php index 75343ccc3..5d83d68e2 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'nextcloud/3rdparty', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'c1fe9577fcc7006273d35848598df01eaa708b4f', + 'reference' => '02ba5bba999b61d40c79a6ed56683b69edf66619', 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), @@ -64,15 +64,6 @@ 'aliases' => array(), 'dev_requirement' => false, ), - 'composer/package-versions-deprecated' => array( - 'pretty_version' => '1.11.99.4', - 'version' => '1.11.99.4', - 'reference' => 'b174585d1fe49ceed21928a945138948cb394600', - 'type' => 'composer-plugin', - 'install_path' => __DIR__ . '/./package-versions-deprecated', - 'aliases' => array(), - 'dev_requirement' => false, - ), 'cweagans/composer-patches' => array( 'pretty_version' => '1.7.1', 'version' => '1.7.1.0', @@ -101,36 +92,36 @@ 'dev_requirement' => false, ), 'doctrine/cache' => array( - 'pretty_version' => '2.1.1', - 'version' => '2.1.1.0', - 'reference' => '331b4d5dbaeab3827976273e9356b3b453c300ce', + 'pretty_version' => '2.2.0', + 'version' => '2.2.0.0', + 'reference' => '1ca8f21980e770095a31456042471a57bc4c68fb', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/cache', 'aliases' => array(), 'dev_requirement' => false, ), 'doctrine/dbal' => array( - 'pretty_version' => '3.1.4', - 'version' => '3.1.4.0', - 'reference' => '821b4f01a36ce63ed36c090ea74767b72db367e9', + 'pretty_version' => '3.5.1', + 'version' => '3.5.1.0', + 'reference' => 'f38ee8aaca2d58ee88653cb34a6a3880c23f38a5', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/dbal', 'aliases' => array(), 'dev_requirement' => false, ), 'doctrine/deprecations' => array( - 'pretty_version' => 'v0.5.3', - 'version' => '0.5.3.0', - 'reference' => '9504165960a1f83cc1480e2be1dd0a0478561314', + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'reference' => '0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/deprecations', 'aliases' => array(), 'dev_requirement' => false, ), 'doctrine/event-manager' => array( - 'pretty_version' => '1.1.1', - 'version' => '1.1.1.0', - 'reference' => '41370af6a30faa9dc0368c4a6814d596e81aba7f', + 'pretty_version' => '1.2.0', + 'version' => '1.2.0.0', + 'reference' => '95aa4cb529f1e96576f3fda9f5705ada4056a520', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/event-manager', 'aliases' => array(), @@ -337,7 +328,7 @@ 'nextcloud/3rdparty' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'c1fe9577fcc7006273d35848598df01eaa708b4f', + 'reference' => '02ba5bba999b61d40c79a6ed56683b69edf66619', 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), @@ -361,12 +352,6 @@ 'aliases' => array(), 'dev_requirement' => false, ), - 'ocramius/package-versions' => array( - 'dev_requirement' => false, - 'replaced' => array( - 0 => '1.11.99', - ), - ), 'opis/closure' => array( 'pretty_version' => '3.6.3', 'version' => '3.6.3.0', @@ -487,6 +472,15 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'psr/cache' => array( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'reference' => 'd11b50ad223250cf17b86e38383413f5a6764bf8', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/cache', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'psr/container' => array( 'pretty_version' => '1.1.1', 'version' => '1.1.1.0', diff --git a/composer/package-versions-deprecated/CHANGELOG.md b/composer/package-versions-deprecated/CHANGELOG.md deleted file mode 100644 index a838c56ad..000000000 --- a/composer/package-versions-deprecated/CHANGELOG.md +++ /dev/null @@ -1,120 +0,0 @@ -# CHANGELOG - -## 1.1.3 - 2017-09-06 - -This release fixes a bug that caused PackageVersions to prevent -the `composer remove` and `composer update` commands to fail when -this package is removed. - -In addition to that, mutation testing has been added to the suite, -ensuring that the package is accurately and extensively tested. - -Total issues resolved: **3** - -- [40: Mutation testing, PHP 7.1 testing](https://github.com/Ocramius/PackageVersions/pull/40) thanks to @Ocramius -- [41: Removing this package on install results in file access error](https://github.com/Ocramius/PackageVersions/issues/41) thanks to @Xerkus -- [46: #41 Avoid issues when the package is scheduled for removal](https://github.com/Ocramius/PackageVersions/pull/46) thanks to @Jean85 - -## 1.1.2 - 2016-12-30 - -This release fixes a bug that caused PackageVersions to be enabled -even when it was part of a globally installed package. - -Total issues resolved: **3** - -- [35: remove all temp directories](https://github.com/Ocramius/PackageVersions/pull/35) -- [38: Interferes with other projects when installed globally](https://github.com/Ocramius/PackageVersions/issues/38) -- [39: Ignore the global plugin when updating local projects](https://github.com/Ocramius/PackageVersions/pull/39) - -## 1.1.1 - 2016-07-25 - -This release removes the [`"files"`](https://getcomposer.org/doc/04-schema.md#files) directive from -[`composer.json`](https://github.com/Ocramius/PackageVersions/commit/86f2636f7c5e7b56fa035fa3826d5fcf80b6dc72), -as it is no longer needed for `composer install --classmap-authoritative`. -Also, that directive was causing issues with HHVM installations, since -PackageVersions is not compatible with it. - -Total issues resolved: **1** - -- [34: Fatal error during travis build after update to 1.1.0](https://github.com/Ocramius/PackageVersions/issues/34) - -## 1.1.0 - 2016-07-22 - -This release introduces support for running `composer install --classmap-authoritative` -and `composer install --no-scripts`. Please note that performance -while using these modes may be degraded, but the package will -still work. - -Additionally, the package was tuned to prevent the plugin from -running twice at installation. - -Total issues resolved: **10** - -- [18: Fails when using composer install --no-scripts](https://github.com/Ocramius/PackageVersions/issues/18) -- [20: CS (spacing)](https://github.com/Ocramius/PackageVersions/pull/20) -- [22: Document the way the require-dev section is treated](https://github.com/Ocramius/PackageVersions/issues/22) -- [23: Underline that composer.lock is used as source of information](https://github.com/Ocramius/PackageVersions/pull/23) -- [27: Fix incompatibility with --classmap-authoritative](https://github.com/Ocramius/PackageVersions/pull/27) -- [29: mention optimize-autoloader composer.json config option in README](https://github.com/Ocramius/PackageVersions/pull/29) -- [30: The version class is generated twice during composer update](https://github.com/Ocramius/PackageVersions/issues/30) -- [31: Remove double registration of the event listeners](https://github.com/Ocramius/PackageVersions/pull/31) -- [32: Update the usage of mock APIs to use the new API](https://github.com/Ocramius/PackageVersions/pull/32) -- [33: Fix for #18 - support running with --no-scripts flag](https://github.com/Ocramius/PackageVersions/pull/33) - -## 1.0.4 - 2016-04-23 - -This release includes a fix/workaround for composer/composer#5237, -which causes `ocramius/package-versions` to sometimes generate a -`Versions` class with malformed name (something like -`Versions_composer_tmp0`) when running `composer require `. - -Total issues resolved: **2** - -- [16: Workaround for composer/composer#5237 - class parsing](https://github.com/Ocramius/PackageVersions/pull/16) -- [17: Weird Class name being generated](https://github.com/Ocramius/PackageVersions/issues/17) - -## 1.0.3 - 2016-02-26 - -This release fixes an issue related to concurrent autoloader -re-generation caused by multiple composer plugins being installed. -The issue was solved by removing autoloader re-generation from this -package, but it may still affect other packages. - -It is now recommended that you run `composer dump-autoload --optimize` -after installation when using this particular package. -Please note that `composer (install|update) -o` is not sufficient -to avoid autoload overhead when using this particular package. - -Total issues resolved: **1** - -- [15: Remove autoload re-dump optimization](https://github.com/Ocramius/PackageVersions/pull/15) - -## 1.0.2 - 2016-02-24 - -This release fixes issues related to installing the component without -any dev dependencies or with packages that don't have a source or dist -reference, which is usual with packages defined directly in the -`composer.json`. - -Total issues resolved: **3** - -- [11: fix composer install --no-dev PHP7](https://github.com/Ocramius/PackageVersions/pull/11) -- [12: Packages don't always have a source/reference](https://github.com/Ocramius/PackageVersions/issues/12) -- [13: Fix #12 - support dist and missing package version references](https://github.com/Ocramius/PackageVersions/pull/13) - -## 1.0.1 - 2016-02-01 - -This release fixes an issue related with composer updates to -already installed versions. -Using `composer require` within a package that already used -`ocramius/package-versions` caused the installation to be unable -to write the `PackageVersions\Versions` class to a file. - -Total issues resolved: **6** - -- [2: remove unused use statement](https://github.com/Ocramius/PackageVersions/pull/2) -- [3: Remove useless files from dist package](https://github.com/Ocramius/PackageVersions/pull/3) -- [5: failed to open stream: phar error: write operations disabled by the php.ini setting phar.readonly](https://github.com/Ocramius/PackageVersions/issues/5) -- [6: Fix/#5 use composer vendor dir](https://github.com/Ocramius/PackageVersions/pull/6) -- [7: Hotfix - #5 generate package versions also when in phar context](https://github.com/Ocramius/PackageVersions/pull/7) -- [8: Versions class should be ignored by VCS, as it is an install-time artifact](https://github.com/Ocramius/PackageVersions/pull/8) diff --git a/composer/package-versions-deprecated/CONTRIBUTING.md b/composer/package-versions-deprecated/CONTRIBUTING.md deleted file mode 100644 index 718061758..000000000 --- a/composer/package-versions-deprecated/CONTRIBUTING.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: Contributing ---- - -# Contributing - - * Coding standard for the project is [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) - * The project will follow strict [object calisthenics](http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php) - * Any contribution must provide tests for additional introduced conditions - * Any un-confirmed issue needs a failing test case before being accepted - * Pull requests must be sent from a new hotfix/feature branch, not from `master`. - -## Installation - -To install the project and run the tests, you need to clone it first: - -```sh -$ git clone git://github.com/Ocramius/PackageVersions.git -``` - -You will then need to run a composer installation: - -```sh -$ cd PackageVersions -$ curl -s https://getcomposer.org/installer | php -$ php composer.phar update -``` - -## Testing - -The PHPUnit version to be used is the one installed as a dev- dependency via composer: - -```sh -$ ./vendor/bin/phpunit -``` - -Accepted coverage for new contributions is 80%. Any contribution not satisfying this requirement -won't be merged. - diff --git a/composer/package-versions-deprecated/README.md b/composer/package-versions-deprecated/README.md deleted file mode 100644 index 7fe2097d4..000000000 --- a/composer/package-versions-deprecated/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Package Versions - -**`composer/package-versions-deprecated` is a fully-compatible fork of [`ocramius/package-versions`](https://github.com/Ocramius/PackageVersions)** which provides compatibility with Composer 1 and 2 on PHP 7+. It replaces ocramius/package-versions so if you have a dependency requiring it and you want to use Composer v2 but can not upgrade to PHP 7.4 just yet, you can require this package instead. - -If you have a direct dependency on ocramius/package-versions, we recommend instead that once you migrated to Composer 2 you also migrate to use the `Composer\InstalledVersions` class which offers the functionality present here out of the box. diff --git a/composer/package-versions-deprecated/SECURITY.md b/composer/package-versions-deprecated/SECURITY.md deleted file mode 100644 index da9c516dd..000000000 --- a/composer/package-versions-deprecated/SECURITY.md +++ /dev/null @@ -1,5 +0,0 @@ -## Security contact information - -To report a security vulnerability, please use the -[Tidelift security contact](https://tidelift.com/security). -Tidelift will coordinate the fix and disclosure. diff --git a/composer/package-versions-deprecated/composer.json b/composer/package-versions-deprecated/composer.json deleted file mode 100644 index d5a40daac..000000000 --- a/composer/package-versions-deprecated/composer.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "composer/package-versions-deprecated", - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "type": "composer-plugin", - "license": "MIT", - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "require": { - "php": "^7 || ^8", - "composer-plugin-api": "^1.1.0 || ^2.0" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "phpunit/phpunit": "^6.5 || ^7", - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13" - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "autoload-dev": { - "psr-4": { - "PackageVersionsTest\\": "test/PackageVersionsTest" - } - }, - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "scripts": { - "post-update-cmd": "PackageVersions\\Installer::dumpVersionsClass", - "post-install-cmd": "PackageVersions\\Installer::dumpVersionsClass" - } -} diff --git a/composer/package-versions-deprecated/composer.lock b/composer/package-versions-deprecated/composer.lock deleted file mode 100644 index b711f6b13..000000000 --- a/composer/package-versions-deprecated/composer.lock +++ /dev/null @@ -1,2603 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "6bfe0a7d7a51c4bdf14a2d7ea1d22d11", - "packages": [], - "packages-dev": [ - { - "name": "composer/ca-bundle", - "version": "1.2.7", - "source": { - "type": "git", - "url": "https://github.com/composer/ca-bundle.git", - "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/95c63ab2117a72f48f5a55da9740a3273d45b7fd", - "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "ext-pcre": "*", - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", - "psr/log": "^1.0", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\CaBundle\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", - "keywords": [ - "cabundle", - "cacert", - "certificate", - "ssl", - "tls" - ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-04-08T08:27:21+00:00" - }, - { - "name": "composer/composer", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/composer/composer.git", - "reference": "a8c105da344dd84ebd5d11be7943a45b09dc076f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/a8c105da344dd84ebd5d11be7943a45b09dc076f", - "reference": "a8c105da344dd84ebd5d11be7943a45b09dc076f", - "shasum": "" - }, - "require": { - "composer/ca-bundle": "^1.0", - "composer/semver": "^1.0", - "composer/spdx-licenses": "^1.2", - "composer/xdebug-handler": "^1.1", - "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0", - "php": "^5.3.2 || ^7.0", - "psr/log": "^1.0", - "seld/jsonlint": "^1.4", - "seld/phar-utils": "^1.0", - "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" - }, - "conflict": { - "symfony/console": "2.8.38" - }, - "require-dev": { - "phpspec/prophecy": "^1.10", - "symfony/phpunit-bridge": "^3.4" - }, - "suggest": { - "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", - "ext-zip": "Enabling the zip extension allows you to unzip archives", - "ext-zlib": "Allow gzip compression of HTTP requests" - }, - "bin": [ - "bin/composer" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\": "src/Composer" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", - "homepage": "https://getcomposer.org/", - "keywords": [ - "autoload", - "dependency", - "package" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/composer/issues", - "source": "https://github.com/composer/composer/tree/master" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-03-29T14:59:26+00:00" - }, - { - "name": "composer/semver", - "version": "1.5.1", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/c6bea70230ef4dd483e6bbcab6005f682ed3a8de", - "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/1.5.1" - }, - "time": "2020-01-13T12:06:48+00:00" - }, - { - "name": "composer/spdx-licenses", - "version": "1.5.3", - "source": { - "type": "git", - "url": "https://github.com/composer/spdx-licenses.git", - "reference": "0c3e51e1880ca149682332770e25977c70cf9dae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/0c3e51e1880ca149682332770e25977c70cf9dae", - "reference": "0c3e51e1880ca149682332770e25977c70cf9dae", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Spdx\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "SPDX licenses list and validation library.", - "keywords": [ - "license", - "spdx", - "validator" - ], - "time": "2020-02-14T07:44:31+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", - "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/master" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - } - ], - "time": "2020-03-01T12:26:26+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", - "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "doctrine/coding-standard": "^6.0", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.13", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-shim": "^0.11", - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/master" - }, - "time": "2019-10-21T16:45:58+00:00" - }, - { - "name": "justinrainbow/json-schema", - "version": "5.2.9", - "source": { - "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "44c6787311242a979fa15c704327c20e7221a0e4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/44c6787311242a979fa15c704327c20e7221a0e4", - "reference": "44c6787311242a979fa15c704327c20e7221a0e4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", - "json-schema/json-schema-test-suite": "1.2.0", - "phpunit/phpunit": "^4.8.35" - }, - "bin": [ - "bin/validate-json" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "JsonSchema\\": "src/JsonSchema/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" - } - ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", - "keywords": [ - "json", - "schema" - ], - "support": { - "issues": "https://github.com/justinrainbow/json-schema/issues", - "source": "https://github.com/justinrainbow/json-schema/tree/5.2.9" - }, - "time": "2019-09-25T14:49:45+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.9.5", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "replace": { - "myclabs/deep-copy": "self.version" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.9.5" - }, - "time": "2020-01-17T21:11:47+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" - }, - { - "name": "phar-io/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "~6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "time": "2018-08-07T13:53:10+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.1.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", - "shasum": "" - }, - "require": { - "ext-filter": "^7.1", - "php": "^7.2", - "phpdocumentor/reflection-common": "^2.0", - "phpdocumentor/type-resolver": "^1.0", - "webmozart/assert": "^1" - }, - "require-dev": { - "doctrine/instantiator": "^1", - "mockery/mockery": "^1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2020-02-22T12:28:44+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", - "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", - "shasum": "" - }, - "require": { - "php": "^7.2", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "^7.2", - "mockery/mockery": "~1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/master" - }, - "time": "2020-02-18T18:59:58+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.10.3", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" - }, - "time": "2020-03-05T15:02:03+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "6.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.1", - "phpunit/php-file-iterator": "^2.0", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.1 || ^4.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "suggest": { - "ext-xdebug": "^2.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2018-10-31T16:06:48+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2018-09-13T20:33:42+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "2.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2019-06-07T04:22:29+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2019-09-17T06:23:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "7.5.20", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9467db479d1b0487c99733bb1e7944d32deded2c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c", - "reference": "9467db479d1b0487c99733bb1e7944d32deded2c", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.2", - "phar-io/version": "^2.0", - "php": "^7.1", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.7", - "phpunit/php-file-iterator": "^2.0.1", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", - "sebastian/environment": "^4.0", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpunit/phpunit-mock-objects": "*" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.5-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2020-01-08T08:45:45+00:00" - }, - { - "name": "psr/container", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "time": "2017-02-14T16:28:37+00:00" - }, - { - "name": "psr/log", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2020-03-23T09:12:05+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" - }, - { - "name": "sebastian/comparator", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "shasum": "" - }, - "require": { - "php": "^7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2018-07-12T15:12:46+00:00" - }, - { - "name": "sebastian/diff", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "time": "2019-02-04T06:01:07+00:00" - }, - { - "name": "sebastian/environment", - "version": "4.2.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", - "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2019-11-20T08:46:58+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2019-09-14T09:02:43+00:00" - }, - { - "name": "sebastian/global-state", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2017-04-27T15:39:26+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "seld/jsonlint", - "version": "1.7.2", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", - "shasum": "" - }, - "require": { - "php": "^5.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "bin": [ - "bin/jsonlint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "JSON Linter", - "keywords": [ - "json", - "linter", - "parser", - "validator" - ], - "support": { - "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.7.2" - }, - "time": "2019-10-24T14:27:39+00:00" - }, - { - "name": "seld/phar-utils", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8800503d56b9867d43d9c303b9cbcc26016e82f0", - "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Seld\\PharUtils\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "PHAR file format utilities, for when PHP phars you up", - "keywords": [ - "phar" - ], - "support": { - "issues": "https://github.com/Seldaek/phar-utils/issues", - "source": "https://github.com/Seldaek/phar-utils/tree/1.1.0" - }, - "time": "2020-02-14T15:25:33+00:00" - }, - { - "name": "symfony/console", - "version": "v5.0.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "5fa1caadc8cdaa17bcfb25219f3b53fe294a9935" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5fa1caadc8cdaa17bcfb25219f3b53fe294a9935", - "reference": "5fa1caadc8cdaa17bcfb25219f3b53fe294a9935", - "shasum": "" - }, - "require": { - "php": "^7.2.5", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/service-contracts": "^1.1|^2" - }, - "conflict": { - "symfony/dependency-injection": "<4.4", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/event-dispatcher": "^4.4|^5.0", - "symfony/lock": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", - "symfony/var-dumper": "^4.4|^5.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/console/tree/v5.0.7" - }, - "time": "2020-03-30T11:42:42+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v5.0.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "ca3b87dd09fff9b771731637f5379965fbfab420" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/ca3b87dd09fff9b771731637f5379965fbfab420", - "reference": "ca3b87dd09fff9b771731637f5379965fbfab420", - "shasum": "" - }, - "require": { - "php": "^7.2.5", - "symfony/polyfill-ctype": "~1.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.0.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-03-27T16:56:45+00:00" - }, - { - "name": "symfony/finder", - "version": "v5.0.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "600a52c29afc0d1caa74acbec8d3095ca7e9910d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/600a52c29afc0d1caa74acbec8d3095ca7e9910d", - "reference": "600a52c29afc0d1caa74acbec8d3095ca7e9910d", - "shasum": "" - }, - "require": { - "php": "^7.2.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/5.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-03-27T16:56:45+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.15-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.15.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-02-27T09:26:54+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.15-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.15.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-03-09T19:04:49+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", - "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.15-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.15.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-02-27T09:26:54+00:00" - }, - { - "name": "symfony/process", - "version": "v5.0.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "c5ca4a0fc16a0c888067d43fbcfe1f8a53d8e70e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c5ca4a0fc16a0c888067d43fbcfe1f8a53d8e70e", - "reference": "c5ca4a0fc16a0c888067d43fbcfe1f8a53d8e70e", - "shasum": "" - }, - "require": { - "php": "^7.2.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v5.0.7" - }, - "time": "2020-03-27T16:56:45+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v2.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "144c5e51266b281231e947b51223ba14acf1a749" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/144c5e51266b281231e947b51223ba14acf1a749", - "reference": "144c5e51266b281231e947b51223ba14acf1a749", - "shasum": "" - }, - "require": { - "php": "^7.2.5", - "psr/container": "^1.0" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.0.1" - }, - "time": "2019-11-18T17:27:11+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-06-13T22:48:21+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.8.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6", - "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "vimeo/psalm": "<3.9.1" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" - }, - "type": "library", - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozart/assert/issues", - "source": "https://github.com/webmozart/assert/tree/master" - }, - "time": "2020-04-18T12:12:48+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": { - "composer/composer": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": "^7", - "composer-plugin-api": "^1.1.0 || ^2.0" - }, - "platform-dev": { - "ext-zip": "^1.13" - }, - "plugin-api-version": "1.1.0" -} diff --git a/composer/package-versions-deprecated/src/PackageVersions/FallbackVersions.php b/composer/package-versions-deprecated/src/PackageVersions/FallbackVersions.php deleted file mode 100644 index 18e5fe64f..000000000 --- a/composer/package-versions-deprecated/src/PackageVersions/FallbackVersions.php +++ /dev/null @@ -1,128 +0,0 @@ - - */ - private static function getVersions(array $packageData): Generator - { - foreach ($packageData as $package) { - yield $package['name'] => $package['version'] . '@' . ( - $package['source']['reference'] ?? $package['dist']['reference'] ?? '' - ); - } - - yield self::ROOT_PACKAGE_NAME => self::ROOT_PACKAGE_NAME; - } -} diff --git a/composer/package-versions-deprecated/src/PackageVersions/Installer.php b/composer/package-versions-deprecated/src/PackageVersions/Installer.php deleted file mode 100644 index 05bdac9a7..000000000 --- a/composer/package-versions-deprecated/src/PackageVersions/Installer.php +++ /dev/null @@ -1,290 +0,0 @@ - - * @internal - */ - const VERSIONS = %s; - - private function __construct() - { - } - - /** - * @psalm-pure - * - * @psalm-suppress ImpureMethodCall we know that {@see InstalledVersions} interaction does not - * cause any side effects here. - */ - public static function rootPackageName() : string - { - if (!self::composer2ApiUsable()) { - return self::ROOT_PACKAGE_NAME; - } - - return InstalledVersions::getRootPackage()['name']; - } - - /** - * @throws OutOfBoundsException If a version cannot be located. - * - * @psalm-param key-of $packageName - * @psalm-pure - * - * @psalm-suppress ImpureMethodCall we know that {@see InstalledVersions} interaction does not - * cause any side effects here. - */ - public static function getVersion(string $packageName): string - { - if (self::composer2ApiUsable()) { - return InstalledVersions::getPrettyVersion($packageName) - . '@' . InstalledVersions::getReference($packageName); - } - - if (isset(self::VERSIONS[$packageName])) { - return self::VERSIONS[$packageName]; - } - - throw new OutOfBoundsException( - 'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files' - ); - } - - private static function composer2ApiUsable(): bool - { - if (!class_exists(InstalledVersions::class, false)) { - return false; - } - - if (method_exists(InstalledVersions::class, 'getAllRawData')) { - $rawData = InstalledVersions::getAllRawData(); - if (count($rawData) === 1 && count($rawData[0]) === 0) { - return false; - } - } else { - $rawData = InstalledVersions::getRawData(); - if ($rawData === null || $rawData === []) { - return false; - } - } - - return true; - } -} - -PHP; - - public function activate(Composer $composer, IOInterface $io) - { - // Nothing to do here, as all features are provided through event listeners - } - - public function deactivate(Composer $composer, IOInterface $io) - { - // Nothing to do here, as all features are provided through event listeners - } - - public function uninstall(Composer $composer, IOInterface $io) - { - // Nothing to do here, as all features are provided through event listeners - } - - /** - * {@inheritDoc} - */ - public static function getSubscribedEvents(): array - { - return [ScriptEvents::POST_AUTOLOAD_DUMP => 'dumpVersionsClass']; - } - - /** - * @throws RuntimeException - */ - public static function dumpVersionsClass(Event $composerEvent) - { - $composer = $composerEvent->getComposer(); - $rootPackage = $composer->getPackage(); - $versions = iterator_to_array(self::getVersions($composer->getLocker(), $rootPackage)); - - if (! array_key_exists('composer/package-versions-deprecated', $versions)) { - //plugin must be globally installed - we only want to generate versions for projects which specifically - //require composer/package-versions-deprecated - return; - } - - $versionClass = self::generateVersionsClass($rootPackage->getName(), $versions); - - self::writeVersionClassToFile($versionClass, $composer, $composerEvent->getIO()); - } - - /** - * @param string[] $versions - */ - private static function generateVersionsClass(string $rootPackageName, array $versions): string - { - return sprintf( - self::$generatedClassTemplate, - 'fin' . 'al ' . 'cla' . 'ss ' . 'Versions', // note: workaround for regex-based code parsers :-( - $rootPackageName, - var_export($versions, true) - ); - } - - /** - * @throws RuntimeException - */ - private static function writeVersionClassToFile(string $versionClassSource, Composer $composer, IOInterface $io) - { - $installPath = self::locateRootPackageInstallPath($composer->getConfig(), $composer->getPackage()) - . '/src/PackageVersions/Versions.php'; - - $installDir = dirname($installPath); - if (! file_exists($installDir)) { - $io->write('composer/package-versions-deprecated: Package not found (probably scheduled for removal); generation of version class skipped.'); - - return; - } - - if (! is_writable($installDir)) { - $io->write( - sprintf( - 'composer/package-versions-deprecated: %s is not writable; generation of version class skipped.', - $installDir - ) - ); - - return; - } - - $io->write('composer/package-versions-deprecated: Generating version class...'); - - $installPathTmp = $installPath . '_' . uniqid('tmp', true); - file_put_contents($installPathTmp, $versionClassSource); - chmod($installPathTmp, 0664); - rename($installPathTmp, $installPath); - - $io->write('composer/package-versions-deprecated: ...done generating version class'); - } - - /** - * @throws RuntimeException - */ - private static function locateRootPackageInstallPath( - Config $composerConfig, - RootPackageInterface $rootPackage - ): string { - if (self::getRootPackageAlias($rootPackage)->getName() === 'composer/package-versions-deprecated') { - return dirname($composerConfig->get('vendor-dir')); - } - - return $composerConfig->get('vendor-dir') . '/composer/package-versions-deprecated'; - } - - private static function getRootPackageAlias(RootPackageInterface $rootPackage): PackageInterface - { - $package = $rootPackage; - - while ($package instanceof AliasPackage) { - $package = $package->getAliasOf(); - } - - return $package; - } - - /** - * @return Generator&string[] - * - * @psalm-return Generator - */ - private static function getVersions(Locker $locker, RootPackageInterface $rootPackage): Generator - { - $lockData = $locker->getLockData(); - - $lockData['packages-dev'] = $lockData['packages-dev'] ?? []; - - $packages = $lockData['packages']; - if (getenv('COMPOSER_DEV_MODE') !== '0') { - $packages = array_merge($packages, $lockData['packages-dev']); - } - foreach ($packages as $package) { - yield $package['name'] => $package['version'] . '@' . ( - $package['source']['reference'] ?? $package['dist']['reference'] ?? '' - ); - } - - foreach ($rootPackage->getReplaces() as $replace) { - $version = $replace->getPrettyConstraint(); - if ($version === 'self.version') { - $version = $rootPackage->getPrettyVersion(); - } - - yield $replace->getTarget() => $version . '@' . $rootPackage->getSourceReference(); - } - - yield $rootPackage->getName() => $rootPackage->getPrettyVersion() . '@' . $rootPackage->getSourceReference(); - } -} diff --git a/composer/package-versions-deprecated/src/PackageVersions/Versions.php b/composer/package-versions-deprecated/src/PackageVersions/Versions.php deleted file mode 100644 index 9cbf18772..000000000 --- a/composer/package-versions-deprecated/src/PackageVersions/Versions.php +++ /dev/null @@ -1,92 +0,0 @@ -pool; } + public function reset(): void + { + if ($this->pool instanceof ResetInterface) { + $this->pool->reset(); + } + + $this->setNamespace($this->getNamespace()); + } + /** * {@inheritdoc} */ diff --git a/doctrine/dbal/README.md b/doctrine/dbal/README.md index 8a1757886..768107751 100644 --- a/doctrine/dbal/README.md +++ b/doctrine/dbal/README.md @@ -1,11 +1,11 @@ # Doctrine DBAL -| [4.0-dev][4.0] | [3.1][3.1] | [2.13][2.13] | -|:----------------:|:----------:|:----------:| -| [![GitHub Actions][GA 4.0 image]][GA 4.0] | [![GitHub Actions][GA 3.1 image]][GA 3.1] | [![GitHub Actions][GA 2.13 image]][GA 2.13] | -| [![AppVeyor][AppVeyor 4.0 image]][AppVeyor 4.0] | [![AppVeyor][AppVeyor 3.1 image]][AppVeyor 3.1] | [![AppVeyor][AppVeyor 2.13 image]][AppVeyor 2.13] | -| [![Code Coverage][Coverage image]][CodeCov 4.0] | [![Code Coverage][Coverage 3.1 image]][CodeCov 3.1] | [![Code Coverage][Coverage 2.13 image]][CodeCov 2.13] | -| N/A | [![Code Coverage][TypeCov 3.1 image]][TypeCov 3.1] | N/A | +| [4.0-dev][4.0] | [3.5][3.5] | +|:-----------------------------------------------:|:---------------------------------------------------:| +| [![GitHub Actions][GA 4.0 image]][GA 4.0] | [![GitHub Actions][GA 3.5 image]][GA 3.5] | +| [![AppVeyor][AppVeyor 4.0 image]][AppVeyor 4.0] | [![AppVeyor][AppVeyor 3.5 image]][AppVeyor 3.5] | +| [![Code Coverage][Coverage image]][CodeCov 4.0] | [![Code Coverage][Coverage 3.5 image]][CodeCov 3.5] | +| N/A | [![Code Coverage][TypeCov 3.5 image]][TypeCov 3.5] | Powerful ***D***ata***B***ase ***A***bstraction ***L***ayer with many features for database schema introspection and schema management. @@ -23,20 +23,12 @@ Powerful ***D***ata***B***ase ***A***bstraction ***L***ayer with many features f [GA 4.0]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.0.x [GA 4.0 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg - [Coverage 3.1 image]: https://codecov.io/gh/doctrine/dbal/branch/3.1.x/graph/badge.svg - [3.1]: https://github.com/doctrine/dbal/tree/3.1.x - [CodeCov 3.1]: https://codecov.io/gh/doctrine/dbal/branch/3.1.x - [AppVeyor 3.1]: https://ci.appveyor.com/project/doctrine/dbal/branch/3.1.x - [AppVeyor 3.1 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/3.1.x?svg=true - [GA 3.1]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A3.1.x - [GA 3.1 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=3.1.x - - [Coverage 2.13 image]: https://codecov.io/gh/doctrine/dbal/branch/2.13.x/graph/badge.svg - [2.13]: https://github.com/doctrine/dbal/tree/2.13.x - [CodeCov 2.13]: https://codecov.io/gh/doctrine/dbal/branch/2.13.x - [AppVeyor 2.13]: https://ci.appveyor.com/project/doctrine/dbal/branch/2.13.x - [AppVeyor 2.13 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/2.13.x?svg=true - [GA 2.13]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A2.13.x - [GA 2.13 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=2.13.x - [TypeCov 3.1]: https://shepherd.dev/github/doctrine/dbal - [TypeCov 3.1 image]: https://shepherd.dev/github/doctrine/dbal/coverage.svg + [Coverage 3.5 image]: https://codecov.io/gh/doctrine/dbal/branch/3.5.x/graph/badge.svg + [3.5]: https://github.com/doctrine/dbal/tree/3.5.x + [CodeCov 3.5]: https://codecov.io/gh/doctrine/dbal/branch/3.5.x + [AppVeyor 3.5]: https://ci.appveyor.com/project/doctrine/dbal/branch/3.5.x + [AppVeyor 3.5 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/3.5.x?svg=true + [GA 3.5]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A3.5.x + [GA 3.5 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=3.5.x + [TypeCov 3.5]: https://shepherd.dev/github/doctrine/dbal + [TypeCov 3.5 image]: https://shepherd.dev/github/doctrine/dbal/coverage.svg diff --git a/doctrine/dbal/composer.json b/doctrine/dbal/composer.json index 2374d79c2..e1e1aab90 100644 --- a/doctrine/dbal/composer.json +++ b/doctrine/dbal/composer.json @@ -31,30 +31,36 @@ {"name": "Jonathan Wage", "email": "jonwage@gmail.com"} ], "require": { - "php": "^7.3 || ^8.0", - "composer/package-versions-deprecated": "^1.11.99", - "doctrine/cache": "^1.0|^2.0", - "doctrine/deprecations": "^0.5.3", - "doctrine/event-manager": "^1.0" + "php": "^7.4 || ^8.0", + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "doctrine/event-manager": "^1|^2", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" }, "require-dev": { - "doctrine/coding-standard": "9.0.0", - "jetbrains/phpstorm-stubs": "2021.1", - "phpstan/phpstan": "1.1.1", - "phpstan/phpstan-strict-rules": "^1", - "phpunit/phpunit": "9.5.10", - "psalm/plugin-phpunit": "0.16.1", - "squizlabs/php_codesniffer": "3.6.1", - "symfony/cache": "^5.2|^6.0", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", - "vimeo/psalm": "4.12.0" + "doctrine/coding-standard": "10.0.0", + "jetbrains/phpstorm-stubs": "2022.2", + "phpstan/phpstan": "1.8.10", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "9.5.25", + "psalm/plugin-phpunit": "0.17.0", + "squizlabs/php_codesniffer": "3.7.1", + "symfony/cache": "^5.4|^6.0", + "symfony/console": "^4.4|^5.4|^6.0", + "vimeo/psalm": "4.29.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, "bin": ["bin/doctrine-dbal"], "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true, + "composer/package-versions-deprecated": true + } }, "autoload": { "psr-4": { "Doctrine\\DBAL\\": "src" } diff --git a/doctrine/dbal/src/ArrayParameters/Exception.php b/doctrine/dbal/src/ArrayParameters/Exception.php index cce84b25b..7fc0f7060 100644 --- a/doctrine/dbal/src/ArrayParameters/Exception.php +++ b/doctrine/dbal/src/ArrayParameters/Exception.php @@ -4,9 +4,7 @@ use Throwable; -/** - * @internal - */ +/** @internal */ interface Exception extends Throwable { } diff --git a/doctrine/dbal/src/ArrayParameters/Exception/MissingNamedParameter.php b/doctrine/dbal/src/ArrayParameters/Exception/MissingNamedParameter.php index 73803e2bd..cd9b9756a 100644 --- a/doctrine/dbal/src/ArrayParameters/Exception/MissingNamedParameter.php +++ b/doctrine/dbal/src/ArrayParameters/Exception/MissingNamedParameter.php @@ -7,15 +7,13 @@ use function sprintf; -/** - * @psalm-immutable - */ +/** @psalm-immutable */ class MissingNamedParameter extends LogicException implements Exception { public static function new(string $name): self { return new self( - sprintf('Named parameter "%s" does not have a bound value.', $name) + sprintf('Named parameter "%s" does not have a bound value.', $name), ); } } diff --git a/doctrine/dbal/src/ArrayParameters/Exception/MissingPositionalParameter.php b/doctrine/dbal/src/ArrayParameters/Exception/MissingPositionalParameter.php index 3bfb0ea48..f89a98b79 100644 --- a/doctrine/dbal/src/ArrayParameters/Exception/MissingPositionalParameter.php +++ b/doctrine/dbal/src/ArrayParameters/Exception/MissingPositionalParameter.php @@ -17,7 +17,7 @@ class MissingPositionalParameter extends LogicException implements Exception public static function new(int $index): self { return new self( - sprintf('Positional parameter at index %d does not have a bound value.', $index) + sprintf('Positional parameter at index %d does not have a bound value.', $index), ); } } diff --git a/doctrine/dbal/src/Cache/ArrayResult.php b/doctrine/dbal/src/Cache/ArrayResult.php index e2dc6e115..93b93d176 100644 --- a/doctrine/dbal/src/Cache/ArrayResult.php +++ b/doctrine/dbal/src/Cache/ArrayResult.php @@ -9,23 +9,16 @@ use function count; use function reset; -/** - * @internal The class is internal to the caching layer implementation. - */ +/** @internal The class is internal to the caching layer implementation. */ final class ArrayResult implements Result { - /** @var mixed[] */ - private $data; + /** @var list> */ + private array $data; - /** @var int */ - private $columnCount = 0; + private int $columnCount = 0; + private int $num = 0; - /** @var int */ - private $num = 0; - - /** - * @param mixed[] $data - */ + /** @param list> $data */ public function __construct(array $data) { $this->data = $data; @@ -111,9 +104,7 @@ public function free(): void $this->data = []; } - /** - * @return mixed|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/CachingResult.php b/doctrine/dbal/src/Cache/CachingResult.php deleted file mode 100644 index c060673c5..000000000 --- a/doctrine/dbal/src/Cache/CachingResult.php +++ /dev/null @@ -1,184 +0,0 @@ ->|null */ - private $data; - - /** - * @param string $cacheKey - * @param string $realKey - * @param int $lifetime - */ - public function __construct(Result $result, Cache $cache, $cacheKey, $realKey, $lifetime) - { - $this->result = $result; - $this->cache = $cache; - $this->cacheKey = $cacheKey; - $this->realKey = $realKey; - $this->lifetime = $lifetime; - } - - /** - * {@inheritdoc} - */ - public function fetchNumeric() - { - $row = $this->fetch(); - - if ($row === false) { - return false; - } - - return array_values($row); - } - - /** - * {@inheritdoc} - */ - public function fetchAssociative() - { - return $this->fetch(); - } - - /** - * {@inheritdoc} - */ - public function fetchOne() - { - return FetchUtils::fetchOne($this); - } - - /** - * {@inheritdoc} - */ - public function fetchAllNumeric(): array - { - return array_map('array_values', $this->fetchAllAssociative()); - } - - /** - * {@inheritdoc} - */ - public function fetchAllAssociative(): array - { - $data = $this->result->fetchAllAssociative(); - - $this->store($data); - - return $data; - } - - /** - * {@inheritdoc} - */ - public function fetchFirstColumn(): array - { - return FetchUtils::fetchFirstColumn($this); - } - - public function rowCount(): int - { - return $this->result->rowCount(); - } - - public function columnCount(): int - { - return $this->result->columnCount(); - } - - public function free(): void - { - $this->data = null; - } - - /** - * @return array|false - * - * @throws Exception - */ - private function fetch() - { - if ($this->data === null) { - $this->data = []; - } - - $row = $this->result->fetchAssociative(); - - if ($row !== false) { - $this->data[] = $row; - - return $row; - } - - $this->saveToCache(); - - return false; - } - - /** - * @param array> $data - */ - private function store(array $data): void - { - $this->data = $data; - - $this->saveToCache(); - } - - private function saveToCache(): void - { - if ($this->data === null) { - return; - } - - $data = $this->cache->fetch($this->cacheKey); - - if ($data === false) { - $data = []; - } - - $data[$this->realKey] = $this->data; - - $this->cache->save($this->cacheKey, $data, $this->lifetime); - } -} diff --git a/doctrine/dbal/src/Cache/QueryCacheProfile.php b/doctrine/dbal/src/Cache/QueryCacheProfile.php index 21bd144dc..e1476112e 100644 --- a/doctrine/dbal/src/Cache/QueryCacheProfile.php +++ b/doctrine/dbal/src/Cache/QueryCacheProfile.php @@ -3,11 +3,18 @@ namespace Doctrine\DBAL\Cache; use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Cache\Psr6\CacheAdapter; +use Doctrine\Common\Cache\Psr6\DoctrineProvider; use Doctrine\DBAL\Types\Type; +use Doctrine\Deprecations\Deprecation; +use Psr\Cache\CacheItemPoolInterface; +use TypeError; +use function get_class; use function hash; use function serialize; use function sha1; +use function sprintf; /** * Query Cache Profile handles the data relevant for query caching. @@ -16,8 +23,7 @@ */ class QueryCacheProfile { - /** @var Cache|null */ - private $resultCacheDriver; + private ?CacheItemPoolInterface $resultCache = null; /** @var int */ private $lifetime; @@ -26,27 +32,60 @@ class QueryCacheProfile private $cacheKey; /** - * @param int $lifetime - * @param string|null $cacheKey + * @param int $lifetime + * @param string|null $cacheKey + * @param CacheItemPoolInterface|Cache|null $resultCache */ - public function __construct($lifetime = 0, $cacheKey = null, ?Cache $resultCache = null) + public function __construct($lifetime = 0, $cacheKey = null, ?object $resultCache = null) { - $this->lifetime = $lifetime; - $this->cacheKey = $cacheKey; - $this->resultCacheDriver = $resultCache; + $this->lifetime = $lifetime; + $this->cacheKey = $cacheKey; + if ($resultCache instanceof CacheItemPoolInterface) { + $this->resultCache = $resultCache; + } elseif ($resultCache instanceof Cache) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4620', + 'Passing an instance of %s to %s as $resultCache is deprecated. Pass an instance of %s instead.', + Cache::class, + __METHOD__, + CacheItemPoolInterface::class, + ); + + $this->resultCache = CacheAdapter::wrap($resultCache); + } elseif ($resultCache !== null) { + throw new TypeError(sprintf( + '$resultCache: Expected either null or an instance of %s or %s, got %s.', + CacheItemPoolInterface::class, + Cache::class, + get_class($resultCache), + )); + } + } + + public function getResultCache(): ?CacheItemPoolInterface + { + return $this->resultCache; } /** + * @deprecated Use {@see getResultCache()} instead. + * * @return Cache|null */ public function getResultCacheDriver() { - return $this->resultCacheDriver; + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4620', + '%s is deprecated, call getResultCache() instead.', + __METHOD__, + ); + + return $this->resultCache !== null ? DoctrineProvider::wrap($this->resultCache) : null; } - /** - * @return int - */ + /** @return int */ public function getLifetime() { return $this->lifetime; @@ -97,12 +136,26 @@ public function generateCacheKeys($sql, $params, $types, array $connectionParams return [$cacheKey, $realCacheKey]; } + public function setResultCache(CacheItemPoolInterface $cache): QueryCacheProfile + { + return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache); + } + /** + * @deprecated Use {@see setResultCache()} instead. + * * @return QueryCacheProfile */ public function setResultCacheDriver(Cache $cache) { - return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache); + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4620', + '%s is deprecated, call setResultCache() instead.', + __METHOD__, + ); + + return new QueryCacheProfile($this->lifetime, $this->cacheKey, CacheAdapter::wrap($cache)); } /** @@ -112,7 +165,7 @@ public function setResultCacheDriver(Cache $cache) */ public function setCacheKey($cacheKey) { - return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCacheDriver); + return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCache); } /** @@ -122,6 +175,6 @@ public function setCacheKey($cacheKey) */ public function setLifetime($lifetime) { - return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCacheDriver); + return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCache); } } diff --git a/doctrine/dbal/src/Configuration.php b/doctrine/dbal/src/Configuration.php index 109289f5a..114058271 100644 --- a/doctrine/dbal/src/Configuration.php +++ b/doctrine/dbal/src/Configuration.php @@ -3,8 +3,14 @@ namespace Doctrine\DBAL; use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Cache\Psr6\CacheAdapter; +use Doctrine\Common\Cache\Psr6\DoctrineProvider; use Doctrine\DBAL\Driver\Middleware; use Doctrine\DBAL\Logging\SQLLogger; +use Doctrine\Deprecations\Deprecation; +use Psr\Cache\CacheItemPoolInterface; + +use function func_num_args; /** * Configuration container for the Doctrine DBAL. @@ -12,7 +18,7 @@ class Configuration { /** @var Middleware[] */ - private $middlewares = []; + private array $middlewares = []; /** * The SQL logger in use. If null, SQL logging is disabled. @@ -23,6 +29,13 @@ class Configuration /** * The cache driver implementation that is used for query result caching. + */ + private ?CacheItemPoolInterface $resultCache = null; + + /** + * The cache driver implementation that is used for query result caching. + * + * @deprecated Use {@see $resultCache} instead. * * @var Cache|null */ @@ -42,36 +55,97 @@ class Configuration */ protected $autoCommit = true; + 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; } /** * Gets the cache driver implementation that is used for query result caching. */ + public function getResultCache(): ?CacheItemPoolInterface + { + return $this->resultCache; + } + + /** + * Gets the cache driver implementation that is used for query result caching. + * + * @deprecated Use {@see getResultCache()} instead. + */ public function getResultCacheImpl(): ?Cache { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4620', + '%s is deprecated, call getResultCache() instead.', + __METHOD__, + ); + return $this->resultCacheImpl; } /** * Sets the cache driver implementation that is used for query result caching. */ + public function setResultCache(CacheItemPoolInterface $cache): void + { + $this->resultCacheImpl = DoctrineProvider::wrap($cache); + $this->resultCache = $cache; + } + + /** + * Sets the cache driver implementation that is used for query result caching. + * + * @deprecated Use {@see setResultCache()} instead. + */ public function setResultCacheImpl(Cache $cacheImpl): void { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4620', + '%s is deprecated, call setResultCache() instead.', + __METHOD__, + ); + $this->resultCacheImpl = $cacheImpl; + $this->resultCache = CacheAdapter::wrap($cacheImpl); } /** @@ -79,6 +153,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; } @@ -130,9 +220,7 @@ public function setMiddlewares(array $middlewares): self return $this; } - /** - * @return Middleware[] - */ + /** @return Middleware[] */ public function getMiddlewares(): array { return $this->middlewares; diff --git a/doctrine/dbal/src/Connection.php b/doctrine/dbal/src/Connection.php index 7b3686c6a..364df9a9c 100644 --- a/doctrine/dbal/src/Connection.php +++ b/doctrine/dbal/src/Connection.php @@ -6,12 +6,14 @@ use Doctrine\Common\EventManager; use Doctrine\DBAL\Cache\ArrayResult; use Doctrine\DBAL\Cache\CacheException; -use Doctrine\DBAL\Cache\CachingResult; use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Driver\API\ExceptionConverter; use Doctrine\DBAL\Driver\Connection as DriverConnection; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\Statement as DriverStatement; +use Doctrine\DBAL\Event\TransactionBeginEventArgs; +use Doctrine\DBAL\Event\TransactionCommitEventArgs; +use Doctrine\DBAL\Event\TransactionRollBackEventArgs; use Doctrine\DBAL\Exception\ConnectionLost; use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Exception\InvalidArgumentException; @@ -22,22 +24,27 @@ use Doctrine\DBAL\SQL\Parser; use Doctrine\DBAL\Types\Type; use Doctrine\Deprecations\Deprecation; +use LogicException; use Throwable; use Traversable; use function array_key_exists; use function assert; use function count; +use function get_class; use function implode; use function is_int; use function is_string; use function key; +use function method_exists; +use function sprintf; /** * A database abstraction-level connection that implements features like events, transaction isolation levels, * configuration, emulated transaction nesting, lazy connecting and more. * * @psalm-import-type Params from DriverManager + * @psalm-consistent-constructor */ class Connection { @@ -51,8 +58,15 @@ class Connection */ public const PARAM_STR_ARRAY = ParameterType::STRING + self::ARRAY_PARAM_OFFSET; + /** + * Represents an array of ascii strings to be expanded by Doctrine SQL parsing. + */ + public const PARAM_ASCII_STR_ARRAY = ParameterType::ASCII + self::ARRAY_PARAM_OFFSET; + /** * 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; @@ -66,11 +80,15 @@ class Connection /** @var Configuration */ protected $_config; - /** @var EventManager */ + /** + * @deprecated + * + * @var EventManager + */ protected $_eventManager; /** - * @deprecated Use {@link createExpressionBuilder()} instead. + * @deprecated Use {@see createExpressionBuilder()} instead. * * @var ExpressionBuilder */ @@ -78,58 +96,46 @@ 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. * * @var array - * @phpstan-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. * - * @deprecated Use {@link createSchemaManager()} instead. + * @deprecated Use {@see createSchemaManager()} instead. * * @var AbstractSchemaManager|null */ @@ -144,10 +150,8 @@ class Connection /** * Flag that indicates whether the current transaction is marked for rollback only. - * - * @var bool */ - private $isRollbackOnly = false; + private bool $isRollbackOnly = false; /** * Initializes a new instance of the Connection class. @@ -172,26 +176,29 @@ public function __construct( $this->_driver = $driver; $this->params = $params; + // Create default config and event manager if none given + $config ??= new Configuration(); + $eventManager ??= new EventManager(); + + $this->_config = $config; + $this->_eventManager = $eventManager; + if (isset($params['platform'])) { if (! $params['platform'] instanceof Platforms\AbstractPlatform) { throw Exception::invalidPlatformType($params['platform']); } - $this->platform = $params['platform']; - } - - // Create default config and event manager if none given - if ($config === null) { - $config = new Configuration(); - } + 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.', + ); - if ($eventManager === null) { - $eventManager = new EventManager(); + $this->platform = $params['platform']; + $this->platform->setEventManager($this->_eventManager); } - $this->_config = $config; - $this->_eventManager = $eventManager; - $this->_expr = $this->createExpressionBuilder(); $this->autoCommit = $config->getAutoCommit(); @@ -204,7 +211,6 @@ public function __construct( * * @return array * @psalm-return Params - * @phpstan-return array */ public function getParams() { @@ -254,10 +260,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; } @@ -289,7 +304,7 @@ public function createExpressionBuilder(): ExpressionBuilder /** * Gets the ExpressionBuilder for the connection. * - * @deprecated Use {@link createExpressionBuilder()} instead. + * @deprecated Use {@see createExpressionBuilder()} instead. * * @return ExpressionBuilder */ @@ -299,7 +314,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; @@ -308,6 +323,8 @@ public function getExpressionBuilder() /** * Establishes the connection with the database. * + * @internal This method will be made protected in DBAL 4.0. + * * @return bool TRUE if the connection was successfully established, FALSE if * the connection is already open. * @@ -315,6 +332,12 @@ public function getExpressionBuilder() */ public function connect() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4966', + 'Public access to Connection::connect() is deprecated.', + ); + if ($this->_conn !== null) { return false; } @@ -330,6 +353,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); } @@ -390,6 +420,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; @@ -438,6 +477,13 @@ private function getServerVersion() } } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4750', + 'Not implementing the ServerInfoAwareConnection interface in %s is deprecated', + get_class($connection), + ); + // Unable to detect platform version. return null; } @@ -503,11 +549,7 @@ public function setAutoCommit($autoCommit) */ public function fetchAssociative(string $query, array $params = [], array $types = []) { - try { - return $this->executeQuery($query, $params, $types)->fetchAssociative(); - } catch (Driver\Exception $e) { - throw $this->convertExceptionDuringQuery($e, $query, $params, $types); - } + return $this->executeQuery($query, $params, $types)->fetchAssociative(); } /** @@ -518,17 +560,13 @@ public function fetchAssociative(string $query, array $params = [], array $types * @param list|array $params Query parameters * @param array|array $types Parameter types * - * @return list< mixed>|false False is returned if no rows are found. + * @return list|false False is returned if no rows are found. * * @throws Exception */ public function fetchNumeric(string $query, array $params = [], array $types = []) { - try { - return $this->executeQuery($query, $params, $types)->fetchNumeric(); - } catch (Driver\Exception $e) { - throw $this->convertExceptionDuringQuery($e, $query, $params, $types); - } + return $this->executeQuery($query, $params, $types)->fetchNumeric(); } /** @@ -545,11 +583,7 @@ public function fetchNumeric(string $query, array $params = [], array $types = [ */ public function fetchOne(string $query, array $params = [], array $types = []) { - try { - return $this->executeQuery($query, $params, $types)->fetchOne(); - } catch (Driver\Exception $e) { - throw $this->convertExceptionDuringQuery($e, $query, $params, $types); - } + return $this->executeQuery($query, $params, $types)->fetchOne(); } /** @@ -611,7 +645,7 @@ private function addCriteriaCondition( * @param array $criteria Deletion criteria * @param array|array $types Parameter types * - * @return int The number of affected rows. + * @return int|string The number of affected rows. * * @throws Exception */ @@ -628,7 +662,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, ); } @@ -646,9 +680,9 @@ public function close() /** * Sets the transaction isolation level. * - * @param int $level The level to set. + * @param TransactionIsolationLevel::* $level The level to set. * - * @return int + * @return int|string * * @throws Exception */ @@ -662,17 +696,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(); } /** @@ -685,7 +715,7 @@ public function getTransactionIsolation() * @param array $criteria Update criteria * @param array|array $types Parameter types * - * @return int The number of affected rows. + * @return int|string The number of affected rows. * * @throws Exception */ @@ -720,7 +750,7 @@ public function update($table, array $data, array $criteria, array $types = []) * @param array $data Column-value pairs * @param array|array $types Parameter types * - * @return int The number of affected rows. + * @return int|string The number of affected rows. * * @throws Exception */ @@ -744,7 +774,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, ); } @@ -756,7 +786,7 @@ public function insert($table, array $data, array $types = []) * * @return array|array */ - private function extractTypeValues(array $columnList, array $types) + private function extractTypeValues(array $columnList, array $types): array { $typeValues = []; @@ -787,6 +817,9 @@ public function quoteIdentifier($str) } /** + * The usage of this method is discouraged. Use prepared statements + * or {@see AbstractPlatform::quoteStringLiteral()} instead. + * * @param mixed $value * @param int|string|Type|null $type * @@ -814,11 +847,7 @@ public function quote($value, $type = ParameterType::STRING) */ public function fetchAllNumeric(string $query, array $params = [], array $types = []): array { - try { - return $this->executeQuery($query, $params, $types)->fetchAllNumeric(); - } catch (Driver\Exception $e) { - throw $this->convertExceptionDuringQuery($e, $query, $params, $types); - } + return $this->executeQuery($query, $params, $types)->fetchAllNumeric(); } /** @@ -834,11 +863,7 @@ public function fetchAllNumeric(string $query, array $params = [], array $types */ public function fetchAllAssociative(string $query, array $params = [], array $types = []): array { - try { - return $this->executeQuery($query, $params, $types)->fetchAllAssociative(); - } catch (Driver\Exception $e) { - throw $this->convertExceptionDuringQuery($e, $query, $params, $types); - } + return $this->executeQuery($query, $params, $types)->fetchAllAssociative(); } /** @@ -889,11 +914,7 @@ public function fetchAllAssociativeIndexed(string $query, array $params = [], ar */ public function fetchFirstColumn(string $query, array $params = [], array $types = []): array { - try { - return $this->executeQuery($query, $params, $types)->fetchFirstColumn(); - } catch (Driver\Exception $e) { - throw $this->convertExceptionDuringQuery($e, $query, $params, $types); - } + return $this->executeQuery($query, $params, $types)->fetchFirstColumn(); } /** @@ -909,15 +930,7 @@ public function fetchFirstColumn(string $query, array $params = [], array $types */ public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable { - try { - $result = $this->executeQuery($query, $params, $types); - - while (($row = $result->fetchNumeric()) !== false) { - yield $row; - } - } catch (Driver\Exception $e) { - throw $this->convertExceptionDuringQuery($e, $query, $params, $types); - } + return $this->executeQuery($query, $params, $types)->iterateNumeric(); } /** @@ -934,15 +947,7 @@ public function iterateNumeric(string $query, array $params = [], array $types = */ public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable { - try { - $result = $this->executeQuery($query, $params, $types); - - while (($row = $result->fetchAssociative()) !== false) { - yield $row; - } - } catch (Driver\Exception $e) { - throw $this->convertExceptionDuringQuery($e, $query, $params, $types); - } + return $this->executeQuery($query, $params, $types)->iterateAssociative(); } /** @@ -993,15 +998,7 @@ public function iterateAssociativeIndexed(string $query, array $params = [], arr */ public function iterateColumn(string $query, array $params = [], array $types = []): Traversable { - try { - $result = $this->executeQuery($query, $params, $types); - - while (($value = $result->fetchOne()) !== false) { - yield $value; - } - } catch (Driver\Exception $e) { - throw $this->convertExceptionDuringQuery($e, $query, $params, $types); - } + return $this->executeQuery($query, $params, $types)->iterateColumn(); } /** @@ -1013,7 +1010,15 @@ public function iterateColumn(string $query, array $params = [], array $types = */ public function prepare(string $sql): Statement { - return new Statement($sql, $this); + $connection = $this->getWrappedConnection(); + + try { + $statement = $connection->prepare($sql); + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $sql); + } + + return new Statement($this, $statement, $sql); } /** @@ -1052,12 +1057,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); } @@ -1084,7 +1087,7 @@ public function executeQuery( */ public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp): Result { - $resultCache = $qcp->getResultCacheDriver() ?? $this->_config->getResultCacheImpl(); + $resultCache = $qcp->getResultCache() ?? $this->_config->getResultCache(); if ($resultCache === null) { throw CacheException::noResultDriverConfigured(); @@ -1095,29 +1098,31 @@ public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp) [$cacheKey, $realKey] = $qcp->generateCacheKeys($sql, $params, $types, $connectionParams); - // fetch the row pointers entry - $data = $resultCache->fetch($cacheKey); + $item = $resultCache->getItem($cacheKey); - if ($data !== false) { - // is the real key part of this row pointers map or is the cache only pointing to other cache keys? - if (isset($data[$realKey])) { - $result = new ArrayResult($data[$realKey]); - } elseif (array_key_exists($realKey, $data)) { - $result = new ArrayResult([]); + if ($item->isHit()) { + $value = $item->get(); + if (isset($value[$realKey])) { + return new Result(new ArrayResult($value[$realKey]), $this); } + } else { + $value = []; } - if (! isset($result)) { - $result = new CachingResult( - $this->executeQuery($sql, $params, $types), - $resultCache, - $cacheKey, - $realKey, - $qcp->getLifetime() - ); + $data = $this->fetchAllAssociative($sql, $params, $types); + + $value[$realKey] = $data; + + $item->set($value); + + $lifetime = $qcp->getLifetime(); + if ($lifetime > 0) { + $item->expiresAfter($lifetime); } - return new Result($result, $this); + $resultCache->save($item); + + return new Result(new ArrayResult($data), $this); } /** @@ -1136,7 +1141,7 @@ public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp) * @param list|array $params Statement parameters * @param array|array $types Parameter types * - * @return int The number of affected rows. + * @return int|string The number of affected rows. * * @throws Exception */ @@ -1157,15 +1162,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); @@ -1204,6 +1204,14 @@ public function getTransactionNestingLevel() */ public function lastInsertId($name = null) { + if ($name !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4687', + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', + ); + } + try { return $this->getWrappedConnection()->lastInsertId($name); } catch (Driver\Exception $e) { @@ -1219,11 +1227,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) { @@ -1251,6 +1261,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(); } @@ -1273,10 +1295,9 @@ public function getNestTransactionsWithSavepoints() } /** - * Returns the savepoint name to use for nested transactions are false if they are not supported - * "savepointFormat" parameter is not set + * Returns the savepoint name to use for nested transactions. * - * @return mixed A string with the savepoint name or false. + * @return string */ protected function _getNestedTransactionSavePointName() { @@ -1315,6 +1336,29 @@ 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, + ); + } + + $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; @@ -1339,31 +1383,27 @@ 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; + $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; } @@ -1373,6 +1413,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. * @@ -1440,6 +1502,19 @@ public function rollBack() --$this->transactionNestingLevel; } + $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; } @@ -1474,6 +1549,8 @@ public function createSavepoint($savepoint) */ public function releaseSavepoint($savepoint) { + $logger = $this->_config->getSQLLogger(); + $platform = $this->getDatabasePlatform(); if (! $platform->supportsSavepoints()) { @@ -1481,10 +1558,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(); } /** @@ -1510,12 +1601,21 @@ public function rollbackSavepoint($savepoint) /** * Gets the wrapped driver connection. * + * @deprecated Use {@link getNativeConnection()} to access the native connection. + * * @return DriverConnection * * @throws Exception */ public function getWrappedConnection() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4966', + 'Connection::getWrappedConnection() is deprecated.' + . ' Use Connection::getNativeConnection() to access the native connection.', + ); + $this->connect(); assert($this->_conn !== null); @@ -1523,6 +1623,22 @@ public function getWrappedConnection() return $this->_conn; } + /** @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), + )); + } + + return $this->_conn->getNativeConnection(); + } + /** * Creates a SchemaManager that can be used to inspect or change the * database schema through the connection. @@ -1533,7 +1649,7 @@ public function createSchemaManager(): AbstractSchemaManager { return $this->_driver->getSchemaManager( $this, - $this->getDatabasePlatform() + $this->getDatabasePlatform(), ); } @@ -1541,7 +1657,7 @@ public function createSchemaManager(): AbstractSchemaManager * Gets the SchemaManager that can be used to inspect or change the * database schema through the connection. * - * @deprecated Use {@link createSchemaManager()} instead. + * @deprecated Use {@see createSchemaManager()} instead. * * @return AbstractSchemaManager * @@ -1552,14 +1668,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(); } /** @@ -1637,7 +1749,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))) { @@ -1647,11 +1759,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 { @@ -1660,10 +1782,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); } } } @@ -1719,9 +1851,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); @@ -1735,11 +1865,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); @@ -1761,7 +1888,11 @@ private function needsArrayParameterConversion(array $params, array $types): boo } foreach ($types as $type) { - if ($type === self::PARAM_INT_ARRAY || $type === self::PARAM_STR_ARRAY) { + if ( + $type === self::PARAM_INT_ARRAY + || $type === self::PARAM_STR_ARRAY + || $type === self::PARAM_ASCII_STR_ARRAY + ) { return true; } } @@ -1773,11 +1904,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(); 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..c65eb8ae9 100644 --- a/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php +++ b/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php @@ -13,6 +13,7 @@ use Doctrine\DBAL\Events; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Statement; +use Doctrine\Deprecations\Deprecation; use InvalidArgumentException; use function array_rand; @@ -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); } diff --git a/doctrine/dbal/src/Driver.php b/doctrine/dbal/src/Driver.php index 50a6005f9..efc1792b0 100644 --- a/doctrine/dbal/src/Driver.php +++ b/doctrine/dbal/src/Driver.php @@ -37,6 +37,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/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/ExceptionConverter.php index b848b0e28..a7bf27139 100644 --- a/doctrine/dbal/src/Driver/API/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/ExceptionConverter.php @@ -14,12 +14,12 @@ interface ExceptionConverter * Converts a given driver-level exception into a DBAL-level driver exception. * * Implementors should use the vendor-specific error code and SQLSTATE of the exception - * and instantiate the most appropriate specialized {@link DriverException} subclass. + * and instantiate the most appropriate specialized {@see DriverException} subclass. * * @param Exception $exception The driver exception to convert. * @param Query|null $query The SQL query that triggered the exception, if any. * - * @return DriverException An instance of {@link DriverException} or one of its subclasses. + * @return DriverException An instance of {@see DriverException} or one of its subclasses. */ public function convert(Exception $exception, ?Query $query): DriverException; } diff --git a/doctrine/dbal/src/Driver/API/IBMDB2/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/IBMDB2/ExceptionConverter.php index 20831e0fd..dbde0b10f 100644 --- a/doctrine/dbal/src/Driver/API/IBMDB2/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/IBMDB2/ExceptionConverter.php @@ -6,16 +6,60 @@ use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface; 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\NonUniqueFieldNameException; +use Doctrine\DBAL\Exception\NotNullConstraintViolationException; +use Doctrine\DBAL\Exception\SyntaxErrorException; +use Doctrine\DBAL\Exception\TableExistsException; +use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Query; /** * @internal + * + * @link https://www.ibm.com/docs/en/db2/11.5?topic=messages-sql */ final class ExceptionConverter implements ExceptionConverterInterface { public function convert(Exception $exception, ?Query $query): DriverException { + switch ($exception->getCode()) { + case -104: + return new SyntaxErrorException($exception, $query); + + case -203: + return new NonUniqueFieldNameException($exception, $query); + + case -204: + return new TableNotFoundException($exception, $query); + + case -206: + return new InvalidFieldNameException($exception, $query); + + case -407: + return new NotNullConstraintViolationException($exception, $query); + + case -530: + case -531: + case -532: + case -20356: + return new ForeignKeyConstraintViolationException($exception, $query); + + case -601: + return new TableExistsException($exception, $query); + + case -803: + return new UniqueConstraintViolationException($exception, $query); + + case -1336: + case -30082: + return new ConnectionException($exception, $query); + } + return new DriverException($exception, $query); } } diff --git a/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php index 9abc0c168..87d50aff9 100644 --- a/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php @@ -8,6 +8,7 @@ use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Exception\ConnectionException; use Doctrine\DBAL\Exception\ConnectionLost; +use Doctrine\DBAL\Exception\DatabaseDoesNotExist; use Doctrine\DBAL\Exception\DeadlockException; use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; @@ -21,9 +22,7 @@ use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Query; -/** - * @internal - */ +/** @internal */ final class ExceptionConverter implements ExceptionConverterInterface { /** @@ -33,6 +32,9 @@ final class ExceptionConverter implements ExceptionConverterInterface public function convert(Exception $exception, ?Query $query): DriverException { switch ($exception->getCode()) { + case 1008: + return new DatabaseDoesNotExist($exception, $query); + case 1213: return new DeadlockException($exception, $query); @@ -95,6 +97,7 @@ public function convert(Exception $exception, ?Query $query): DriverException case 1429: case 2002: case 2005: + case 2054: return new ConnectionException($exception, $query); case 2006: diff --git a/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php index f25e58d6c..4703a57d5 100644 --- a/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php @@ -7,6 +7,8 @@ use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface; use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Exception\ConnectionException; +use Doctrine\DBAL\Exception\DatabaseDoesNotExist; +use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException; use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; use Doctrine\DBAL\Exception\InvalidFieldNameException; @@ -18,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()) { @@ -57,6 +55,14 @@ public function convert(Exception $exception, ?Query $query): DriverException case 1400: return new NotNullConstraintViolationException($exception, $query); + case 1918: + return new DatabaseDoesNotExist($exception, $query); + + case 2289: + case 2443: + case 4080: + return new DatabaseObjectNotFoundException($exception, $query); + case 2266: case 2291: case 2292: diff --git a/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php index b9530d0b2..2baca1ee2 100644 --- a/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php @@ -7,12 +7,14 @@ use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface; use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Exception\ConnectionException; +use Doctrine\DBAL\Exception\DatabaseDoesNotExist; use Doctrine\DBAL\Exception\DeadlockException; use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; use Doctrine\DBAL\Exception\InvalidFieldNameException; use Doctrine\DBAL\Exception\NonUniqueFieldNameException; use Doctrine\DBAL\Exception\NotNullConstraintViolationException; +use Doctrine\DBAL\Exception\SchemaDoesNotExist; use Doctrine\DBAL\Exception\SyntaxErrorException; use Doctrine\DBAL\Exception\TableExistsException; use Doctrine\DBAL\Exception\TableNotFoundException; @@ -21,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()) { @@ -54,6 +52,12 @@ public function convert(Exception $exception, ?Query $query): DriverException case '23505': return new UniqueConstraintViolationException($exception, $query); + case '3D000': + return new DatabaseDoesNotExist($exception, $query); + + case '3F000': + return new SchemaDoesNotExist($exception, $query); + case '42601': return new SyntaxErrorException($exception, $query); @@ -73,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/SQLSrv/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php index 3fc3ff081..d0e8e9f46 100644 --- a/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php @@ -6,16 +6,64 @@ use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface; use Doctrine\DBAL\Driver\Exception; +use Doctrine\DBAL\Exception\ConnectionException; +use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException; use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; +use Doctrine\DBAL\Exception\InvalidFieldNameException; +use Doctrine\DBAL\Exception\NonUniqueFieldNameException; +use Doctrine\DBAL\Exception\NotNullConstraintViolationException; +use Doctrine\DBAL\Exception\SyntaxErrorException; +use Doctrine\DBAL\Exception\TableExistsException; +use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Query; /** * @internal + * + * @link https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors */ final class ExceptionConverter implements ExceptionConverterInterface { public function convert(Exception $exception, ?Query $query): DriverException { + switch ($exception->getCode()) { + case 102: + return new SyntaxErrorException($exception, $query); + + case 207: + return new InvalidFieldNameException($exception, $query); + + case 208: + return new TableNotFoundException($exception, $query); + + case 209: + return new NonUniqueFieldNameException($exception, $query); + + case 515: + return new NotNullConstraintViolationException($exception, $query); + + case 547: + case 4712: + return new ForeignKeyConstraintViolationException($exception, $query); + + case 2601: + case 2627: + return new UniqueConstraintViolationException($exception, $query); + + case 2714: + return new TableExistsException($exception, $query); + + case 3701: + case 15151: + return new DatabaseObjectNotFoundException($exception, $query); + + case 11001: + case 18456: + return new ConnectionException($exception, $query); + } + return new DriverException($exception, $query); } } 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 new file mode 100644 index 000000000..3779c8bab --- /dev/null +++ b/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php @@ -0,0 +1,80 @@ + ['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(). + * + * @param int $a + * @param int $b + */ + public static function mod($a, $b): int + { + return $a % $b; + } + + /** + * User-defined function that implements LOCATE(). + * + * @param string $str + * @param string $substr + * @param int $offset + */ + 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) { + $offset -= 1; + } + + $pos = strpos($str, $substr, $offset); + + if ($pos !== false) { + return $pos + 1; + } + + return 0; + } +} diff --git a/doctrine/dbal/src/Driver/AbstractDB2Driver.php b/doctrine/dbal/src/Driver/AbstractDB2Driver.php index d8c707bb0..3a08312d6 100644 --- a/doctrine/dbal/src/Driver/AbstractDB2Driver.php +++ b/doctrine/dbal/src/Driver/AbstractDB2Driver.php @@ -9,9 +9,12 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Schema\DB2SchemaManager; +use Doctrine\Deprecations\Deprecation; + +use function assert; /** - * Abstract base implementation of the {@link Driver} interface for IBM DB2 based drivers. + * Abstract base implementation of the {@see Driver} interface for IBM DB2 based drivers. */ abstract class AbstractDB2Driver implements Driver { @@ -25,9 +28,20 @@ public function getDatabasePlatform() /** * {@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); } diff --git a/doctrine/dbal/src/Driver/AbstractException.php b/doctrine/dbal/src/Driver/AbstractException.php index d38669359..d4d20aa34 100644 --- a/doctrine/dbal/src/Driver/AbstractException.php +++ b/doctrine/dbal/src/Driver/AbstractException.php @@ -8,7 +8,7 @@ use Throwable; /** - * Base implementation of the {@link Exception} interface. + * Base implementation of the {@see Exception} interface. * * @internal * @@ -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. diff --git a/doctrine/dbal/src/Driver/AbstractMySQLDriver.php b/doctrine/dbal/src/Driver/AbstractMySQLDriver.php index 31949b9c1..d8f3fb35c 100644 --- a/doctrine/dbal/src/Driver/AbstractMySQLDriver.php +++ b/doctrine/dbal/src/Driver/AbstractMySQLDriver.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Driver\API\ExceptionConverter; use Doctrine\DBAL\Driver\API\MySQL; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MySQL57Platform; @@ -13,13 +14,15 @@ use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Schema\MySQLSchemaManager; use Doctrine\DBAL\VersionAwarePlatformDriver; +use Doctrine\Deprecations\Deprecation; +use function assert; use function preg_match; use function stripos; use function version_compare; /** - * Abstract base implementation of the {@link Driver} interface for MySQL based drivers. + * Abstract base implementation of the {@see Driver} interface for MySQL based drivers. */ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver { @@ -46,6 +49,13 @@ public function createDatabasePlatformForVersion($version) } } + 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.', + ); + return $this->getDatabasePlatform(); } @@ -63,12 +73,12 @@ private function getOracleMysqlVersionNumber(string $versionString): string preg_match( '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $versionString, - $versionParts + $versionParts, ) === 0 ) { throw Exception::invalidPlatformVersionSpecified( $versionString, - '..' + '..', ); } @@ -76,8 +86,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; @@ -97,12 +107,12 @@ private function getMariaDbMysqlVersionNumber(string $versionString): string 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-)?..', ); } @@ -112,7 +122,7 @@ private function getMariaDbMysqlVersionNumber(string $versionString): string /** * {@inheritdoc} * - * @return MySQLPlatform + * @return AbstractMySQLPlatform */ public function getDatabasePlatform() { @@ -122,10 +132,21 @@ public function getDatabasePlatform() /** * {@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 80e4a02c8..d912109a0 100644 --- a/doctrine/dbal/src/Driver/AbstractOracleDriver.php +++ b/doctrine/dbal/src/Driver/AbstractOracleDriver.php @@ -10,9 +10,12 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Schema\OracleSchemaManager; +use Doctrine\Deprecations\Deprecation; + +use function assert; /** - * Abstract base implementation of the {@link Driver} interface for Oracle based drivers. + * Abstract base implementation of the {@see Driver} interface for Oracle based drivers. */ abstract class AbstractOracleDriver implements Driver { @@ -26,9 +29,20 @@ public function getDatabasePlatform() /** * {@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); } 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 6b11d6178..69e4baff3 100644 --- a/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php +++ b/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php @@ -9,14 +9,17 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\PostgreSQL100Platform; use Doctrine\DBAL\Platforms\PostgreSQL94Platform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Schema\PostgreSQLSchemaManager; use Doctrine\DBAL\VersionAwarePlatformDriver; +use Doctrine\Deprecations\Deprecation; +use function assert; use function preg_match; use function version_compare; /** - * Abstract base implementation of the {@link Driver} interface for PostgreSQL based drivers. + * Abstract base implementation of the {@see Driver} interface for PostgreSQL based drivers. */ abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver { @@ -28,7 +31,7 @@ public function createDatabasePlatformForVersion($version) if (preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts) === 0) { throw Exception::invalidPlatformVersionSpecified( $version, - '..' + '..', ); } @@ -41,6 +44,13 @@ public function createDatabasePlatformForVersion($version) return new PostgreSQL100Platform(); } + Deprecation::trigger( + '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.', + ); + return new PostgreSQL94Platform(); } @@ -54,9 +64,20 @@ public function getDatabasePlatform() /** * {@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 b3d0928bb..c5d79feef 100644 --- a/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php +++ b/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php @@ -8,10 +8,14 @@ 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; /** - * Abstract base implementation of the {@link Driver} interface for Microsoft SQL Server based drivers. + * Abstract base implementation of the {@see Driver} interface for Microsoft SQL Server based drivers. */ abstract class AbstractSQLServerDriver implements Driver { @@ -25,9 +29,20 @@ public function getDatabasePlatform() /** * {@inheritdoc} + * + * @deprecated Use {@link SQLServerPlatform::createSchemaManager()} instead. */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + 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 12b4cfa68..3c9d2c714 100644 --- a/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php +++ b/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php @@ -9,9 +9,12 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\SqliteSchemaManager; +use Doctrine\Deprecations\Deprecation; + +use function assert; /** - * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SQLite based drivers. + * Abstract base implementation of the {@see Doctrine\DBAL\Driver} interface for SQLite based drivers. */ abstract class AbstractSQLiteDriver implements Driver { @@ -25,9 +28,20 @@ public function getDatabasePlatform() /** * {@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..a5eee7484 --- /dev/null +++ b/doctrine/dbal/src/Driver/AbstractSQLiteDriver/Middleware/EnableForeignKeys.php @@ -0,0 +1,28 @@ +exec('PRAGMA foreign_keys=ON'); + + return $connection; + } + }; + } +} diff --git a/doctrine/dbal/src/Driver/Connection.php b/doctrine/dbal/src/Driver/Connection.php index 9febe72a5..2f460fd10 100644 --- a/doctrine/dbal/src/Driver/Connection.php +++ b/doctrine/dbal/src/Driver/Connection.php @@ -7,6 +7,8 @@ /** * Connection interface. * Driver connections must implement this interface. + * + * @method resource|object getNativeConnection() */ interface Connection { @@ -27,6 +29,9 @@ public function query(string $sql): Result; /** * Quotes a string for use in a query. * + * The usage of this method is discouraged. Use prepared statements + * or {@see AbstractPlatform::quoteStringLiteral()} instead. + * * @param mixed $value * @param int $type * 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 c8e36da3e..6a2aa802e 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Connection.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Connection.php @@ -2,25 +2,23 @@ namespace Doctrine\DBAL\Driver\IBMDB2; -use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionError; -use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed; use Doctrine\DBAL\Driver\IBMDB2\Exception\PrepareFailed; +use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError; use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\Statement as DriverStatement; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use stdClass; use function assert; use function db2_autocommit; use function db2_commit; -use function db2_connect; use function db2_escape_string; use function db2_exec; use function db2_last_insert_id; use function db2_num_rows; -use function db2_pconnect; use function db2_prepare; use function db2_rollback; use function db2_server_info; @@ -33,33 +31,16 @@ final class Connection implements ServerInfoAwareConnection { /** @var resource */ - private $conn; + private $connection; /** * @internal The connection can be only instantiated by its driver. * - * @param array $driverOptions - * - * @throws Exception + * @param resource $connection */ - public function __construct( - string $database, - bool $persistent, - string $username, - string $password, - array $driverOptions = [] - ) { - if ($persistent) { - $conn = db2_pconnect($database, $username, $password, $driverOptions); - } else { - $conn = db2_connect($database, $username, $password, $driverOptions); - } - - if ($conn === false) { - throw ConnectionFailed::new(); - } - - $this->conn = $conn; + public function __construct($connection) + { + $this->connection = $connection; } /** @@ -67,7 +48,7 @@ public function __construct( */ public function getServerVersion() { - $serverInfo = db2_server_info($this->conn); + $serverInfo = db2_server_info($this->connection); assert($serverInfo instanceof stdClass); return $serverInfo->DBMS_VER; @@ -75,7 +56,7 @@ public function getServerVersion() public function prepare(string $sql): DriverStatement { - $stmt = @db2_prepare($this->conn, $sql); + $stmt = @db2_prepare($this->connection, $sql); if ($stmt === false) { throw PrepareFailed::new(error_get_last()); @@ -105,10 +86,10 @@ public function quote($value, $type = ParameterType::STRING) public function exec(string $sql): int { - $stmt = @db2_exec($this->conn, $sql); + $stmt = @db2_exec($this->connection, $sql); if ($stmt === false) { - throw ConnectionError::new($this->conn); + throw StatementError::new(); } return db2_num_rows($stmt); @@ -119,47 +100,52 @@ public function exec(string $sql): int */ public function lastInsertId($name = null) { - return db2_last_insert_id($this->conn); + 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 db2_last_insert_id($this->connection) ?? false; } - /** - * {@inheritdoc} - */ - public function beginTransaction() + public function beginTransaction(): bool { - $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_OFF); + $result = db2_autocommit($this->connection, DB2_AUTOCOMMIT_OFF); assert(is_bool($result)); return $result; } - /** - * {@inheritdoc} - */ - public function commit() + public function commit(): bool { - if (! db2_commit($this->conn)) { - throw ConnectionError::new($this->conn); + if (! db2_commit($this->connection)) { + throw ConnectionError::new($this->connection); } - $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON); + $result = db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); assert(is_bool($result)); return $result; } - /** - * {@inheritdoc} - */ - public function rollBack() + public function rollBack(): bool { - if (! db2_rollback($this->conn)) { - throw ConnectionError::new($this->conn); + if (! db2_rollback($this->connection)) { + throw ConnectionError::new($this->connection); } - $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON); + $result = db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); assert(is_bool($result)); return $result; } + + /** @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..6379efcfe 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php +++ b/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php @@ -13,8 +13,7 @@ */ final class DataSourceName { - /** @var string */ - private $string; + private string $string; private function __construct(string $string) { diff --git a/doctrine/dbal/src/Driver/IBMDB2/Driver.php b/doctrine/dbal/src/Driver/IBMDB2/Driver.php index c08165520..142b369b3 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Driver.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Driver.php @@ -3,6 +3,10 @@ namespace Doctrine\DBAL\Driver\IBMDB2; use Doctrine\DBAL\Driver\AbstractDB2Driver; +use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed; + +use function db2_connect; +use function db2_pconnect; final class Driver extends AbstractDB2Driver { @@ -13,12 +17,22 @@ final class Driver extends AbstractDB2Driver */ public function connect(array $params) { - return new Connection( - DataSourceName::fromConnectionParameters($params)->toString(), - isset($params['persistent']) && $params['persistent'] === true, - $params['user'] ?? '', - $params['password'] ?? '', - $params['driverOptions'] ?? [] - ); + $dataSourceName = DataSourceName::fromConnectionParameters($params)->toString(); + + $username = $params['user'] ?? ''; + $password = $params['password'] ?? ''; + $driverOptions = $params['driverOptions'] ?? []; + + if (! empty($params['persistent'])) { + $connection = db2_pconnect($dataSourceName, $username, $password, $driverOptions); + } else { + $connection = db2_connect($dataSourceName, $username, $password, $driverOptions); + } + + if ($connection === false) { + throw ConnectionFailed::new(); + } + + return new Connection($connection); } } 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/CannotWriteToTemporaryFile.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotWriteToTemporaryFile.php deleted file mode 100644 index f2287b300..000000000 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotWriteToTemporaryFile.php +++ /dev/null @@ -1,29 +0,0 @@ - */ - private $lobs = []; + private array $lobs = []; /** * @internal The statement can be only instantiated by its driver connection. @@ -59,37 +58,54 @@ public function __construct($stmt) /** * {@inheritdoc} */ - public function bindValue($param, $value, $type = ParameterType::STRING) + 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} + * + * @deprecated Use {@see bindValue()} instead. */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + 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); break; case ParameterType::LARGE_OBJECT: - if (isset($this->lobs[$param])) { - [, $handle] = $this->lobs[$param]; - fclose($handle); - } - - $handle = $this->createTemporaryFile(); - $path = stream_get_meta_data($handle)['uri']; - - $this->bind($param, $path, DB2_PARAM_FILE, DB2_BINARY); - - $this->lobs[$param] = [&$variable, $handle]; + $this->lobs[$param] = &$variable; break; default: @@ -108,9 +124,9 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le */ private function bind($position, &$variable, int $parameterType, int $dataType): void { - $this->bindParam[$position] =& $variable; + $this->parameters[$position] =& $variable; - if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) { + if (! db2_bind_param($this->stmt, $position, '', $parameterType, $dataType)) { throw StatementError::new($this->stmt); } } @@ -120,29 +136,20 @@ private function bind($position, &$variable, int $parameterType, int $dataType): */ public function execute($params = null): ResultInterface { - if ($params === null) { - ksort($this->bindParam); - - $params = []; - - foreach ($this->bindParam as $value) { - $params[] = $value; - } + 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 ($this->lobs as [$source, $target]) { - if (is_resource($source)) { - $this->copyStreamToStream($source, $target); - - continue; - } - - $this->writeStringToStream($source, $target); - } + $handles = $this->bindLobs(); - $result = db2_execute($this->stmt, $params); + $result = @db2_execute($this->stmt, $params ?? $this->parameters); - foreach ($this->lobs as [, $handle]) { + foreach ($handles as $handle) { fclose($handle); } @@ -155,6 +162,31 @@ public function execute($params = null): ResultInterface return new Result($this->stmt); } + /** + * @return list + * + * @throws Exception + */ + private function bindLobs(): array + { + $handles = []; + + foreach ($this->lobs as $param => $value) { + if (is_resource($value)) { + $handle = $handles[] = $this->createTemporaryFile(); + $path = stream_get_meta_data($handle)['uri']; + + $this->copyStreamToStream($value, $handle); + + $this->bind($param, $path, DB2_PARAM_FILE, DB2_BINARY); + } else { + $this->bind($param, $value, DB2_PARAM_IN, DB2_CHAR); + } + } + + return $handles; + } + /** * @return resource * @@ -183,16 +215,4 @@ private function copyStreamToStream($source, $target): void throw CannotCopyStreamToStream::new(error_get_last()); } } - - /** - * @param resource $target - * - * @throws Exception - */ - private function writeStringToStream(string $string, $target): void - { - if (@fwrite($target, $string) === false) { - throw CannotWriteToTemporaryFile::new(error_get_last()); - } - } } diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php new file mode 100644 index 000000000..6dab1cf13 --- /dev/null +++ b/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php @@ -0,0 +1,113 @@ +wrappedConnection = $wrappedConnection; + } + + public function prepare(string $sql): Statement + { + return $this->wrappedConnection->prepare($sql); + } + + public function query(string $sql): Result + { + return $this->wrappedConnection->query($sql); + } + + /** + * {@inheritdoc} + */ + public function quote($value, $type = ParameterType::STRING) + { + return $this->wrappedConnection->quote($value, $type); + } + + public function exec(string $sql): int + { + return $this->wrappedConnection->exec($sql); + } + + /** + * {@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->wrappedConnection->lastInsertId($name); + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + return $this->wrappedConnection->beginTransaction(); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->wrappedConnection->commit(); + } + + /** + * {@inheritdoc} + */ + public function rollBack() + { + return $this->wrappedConnection->rollBack(); + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + if (! $this->wrappedConnection instanceof ServerInfoAwareConnection) { + throw new LogicException('The underlying connection is not a ServerInfoAwareConnection'); + } + + return $this->wrappedConnection->getServerVersion(); + } + + /** @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), + )); + } + + return $this->wrappedConnection->getNativeConnection(); + } +} diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php new file mode 100644 index 000000000..6914176f4 --- /dev/null +++ b/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php @@ -0,0 +1,70 @@ +wrappedDriver = $wrappedDriver; + } + + /** + * {@inheritdoc} + */ + public function connect(array $params) + { + return $this->wrappedDriver->connect($params); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return $this->wrappedDriver->getDatabasePlatform(); + } + + /** + * {@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); + } + + public function getExceptionConverter(): ExceptionConverter + { + return $this->wrappedDriver->getExceptionConverter(); + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ($this->wrappedDriver instanceof VersionAwarePlatformDriver) { + return $this->wrappedDriver->createDatabasePlatformForVersion($version); + } + + return $this->wrappedDriver->getDatabasePlatform(); + } +} diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php new file mode 100644 index 000000000..de90207b0 --- /dev/null +++ b/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php @@ -0,0 +1,78 @@ +wrappedResult = $result; + } + + /** + * {@inheritdoc} + */ + public function fetchNumeric() + { + return $this->wrappedResult->fetchNumeric(); + } + + /** + * {@inheritdoc} + */ + public function fetchAssociative() + { + return $this->wrappedResult->fetchAssociative(); + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + return $this->wrappedResult->fetchOne(); + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + return $this->wrappedResult->fetchAllNumeric(); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + return $this->wrappedResult->fetchAllAssociative(); + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + return $this->wrappedResult->fetchFirstColumn(); + } + + public function rowCount(): int + { + return $this->wrappedResult->rowCount(); + } + + public function columnCount(): int + { + return $this->wrappedResult->columnCount(); + } + + public function free(): void + { + $this->wrappedResult->free(); + } +} diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php new file mode 100644 index 000000000..70e8df637 --- /dev/null +++ b/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php @@ -0,0 +1,71 @@ +wrappedStatement = $wrappedStatement; + } + + /** + * {@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} + * + * @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} + */ + public function execute($params = null): Result + { + return $this->wrappedStatement->execute($params); + } +} diff --git a/doctrine/dbal/src/Driver/Mysqli/Connection.php b/doctrine/dbal/src/Driver/Mysqli/Connection.php index 736345362..78112d825 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Connection.php +++ b/doctrine/dbal/src/Driver/Mysqli/Connection.php @@ -2,21 +2,15 @@ namespace Doctrine\DBAL\Driver\Mysqli; -use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError; -use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionFailed; use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\Statement as DriverStatement; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use mysqli; use mysqli_sql_exception; -use function assert; -use function floor; -use function mysqli_init; -use function stripos; - final class Connection implements ServerInfoAwareConnection { /** @@ -24,50 +18,12 @@ final class Connection implements ServerInfoAwareConnection */ public const OPTION_FLAGS = 'flags'; - /** @var mysqli */ - private $conn; - - /** - * @internal The connection can be only instantiated by its driver. - * - * @param iterable $preInitializers - * @param iterable $postInitializers - * - * @throws Exception - */ - public function __construct( - ?string $host = null, - ?string $username = null, - ?string $password = null, - ?string $database = null, - ?int $port = null, - ?string $socket = null, - int $flags = 0, - iterable $preInitializers = [], - iterable $postInitializers = [] - ) { - $connection = mysqli_init(); - assert($connection !== false); - - foreach ($preInitializers as $initializer) { - $initializer->initialize($connection); - } - - try { - $success = @$connection->real_connect($host, $username, $password, $database, $port, $socket, $flags); - } catch (mysqli_sql_exception $e) { - throw ConnectionFailed::upcast($e); - } + private mysqli $connection; - if (! $success) { - throw ConnectionFailed::new($connection); - } - - foreach ($postInitializers as $initializer) { - $initializer->initialize($connection); - } - - $this->conn = $connection; + /** @internal The connection can be only instantiated by its driver. */ + public function __construct(mysqli $connection) + { + $this->connection = $connection; } /** @@ -75,38 +31,38 @@ public function __construct( * * Could be used if part of your application is not using DBAL. * - * @return mysqli + * @deprecated Call {@see getNativeConnection()} instead. */ - public function getWrappedResourceHandle() + public function getWrappedResourceHandle(): mysqli { - return $this->conn; + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5037', + '%s is deprecated, call getNativeConnection() instead.', + __METHOD__, + ); + + return $this->getNativeConnection(); } - /** - * {@inheritdoc} - * - * The server version detection includes a special case for MariaDB - * to support '5.5.5-' prefixed versions introduced in Maria 10+ - * - * @link https://jira.mariadb.org/browse/MDEV-4088 - */ - public function getServerVersion() + public function getServerVersion(): string { - $serverInfos = $this->conn->get_server_info(); - if (stripos($serverInfos, 'mariadb') !== false) { - return $serverInfos; - } - - $majorVersion = floor($this->conn->server_version / 10000); - $minorVersion = floor(($this->conn->server_version - $majorVersion * 10000) / 100); - $patchVersion = floor($this->conn->server_version - $majorVersion * 10000 - $minorVersion * 100); - - return $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + return $this->connection->get_server_info(); } public function prepare(string $sql): DriverStatement { - return new Statement($this->conn, $sql); + try { + $stmt = $this->connection->prepare($sql); + } catch (mysqli_sql_exception $e) { + throw ConnectionError::upcast($e); + } + + if ($stmt === false) { + throw ConnectionError::new($this->connection); + } + + return new Statement($stmt); } public function query(string $sql): ResultInterface @@ -119,22 +75,22 @@ public function query(string $sql): ResultInterface */ public function quote($value, $type = ParameterType::STRING) { - return "'" . $this->conn->escape_string($value) . "'"; + return "'" . $this->connection->escape_string($value) . "'"; } public function exec(string $sql): int { try { - $result = $this->conn->query($sql); + $result = $this->connection->query($sql); } catch (mysqli_sql_exception $e) { throw ConnectionError::upcast($e); } if ($result === false) { - throw ConnectionError::new($this->conn); + throw ConnectionError::new($this->connection); } - return $this->conn->affected_rows; + return $this->connection->affected_rows; } /** @@ -142,40 +98,44 @@ public function exec(string $sql): int */ public function lastInsertId($name = null) { - return $this->conn->insert_id; + 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->connection->insert_id; } - /** - * {@inheritdoc} - */ - public function beginTransaction() + public function beginTransaction(): bool { - $this->conn->query('START TRANSACTION'); + $this->connection->begin_transaction(); return true; } - /** - * {@inheritdoc} - */ - public function commit() + public function commit(): bool { try { - return $this->conn->commit(); + return $this->connection->commit(); } catch (mysqli_sql_exception $e) { return false; } } - /** - * {@inheritdoc} - */ - public function rollBack() + public function rollBack(): bool { try { - return $this->conn->rollback(); + return $this->connection->rollback(); } catch (mysqli_sql_exception $e) { return false; } } + + public function getNativeConnection(): mysqli + { + return $this->connection; + } } diff --git a/doctrine/dbal/src/Driver/Mysqli/Driver.php b/doctrine/dbal/src/Driver/Mysqli/Driver.php index d459863d8..af305230f 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Driver.php +++ b/doctrine/dbal/src/Driver/Mysqli/Driver.php @@ -3,10 +3,13 @@ namespace Doctrine\DBAL\Driver\Mysqli; use Doctrine\DBAL\Driver\AbstractMySQLDriver; +use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionFailed; use Doctrine\DBAL\Driver\Mysqli\Exception\HostRequired; use Doctrine\DBAL\Driver\Mysqli\Initializer\Charset; use Doctrine\DBAL\Driver\Mysqli\Initializer\Options; use Doctrine\DBAL\Driver\Mysqli\Initializer\Secure; +use mysqli; +use mysqli_sql_exception; use function count; @@ -47,17 +50,35 @@ public function connect(array $params) $preInitializers = $this->withSecure($preInitializers, $params); $postInitializers = $this->withCharset($postInitializers, $params); - return new Connection( - $host, - $params['user'] ?? null, - $params['password'] ?? null, - $params['dbname'] ?? null, - $params['port'] ?? null, - $params['unix_socket'] ?? null, - $flags, - $preInitializers, - $postInitializers - ); + $connection = new mysqli(); + + foreach ($preInitializers as $initializer) { + $initializer->initialize($connection); + } + + try { + $success = @$connection->real_connect( + $host, + $params['user'] ?? null, + $params['password'] ?? null, + $params['dbname'] ?? null, + $params['port'] ?? null, + $params['unix_socket'] ?? null, + $flags, + ); + } catch (mysqli_sql_exception $e) { + throw ConnectionFailed::upcast($e); + } + + if (! $success) { + throw ConnectionFailed::new($connection); + } + + foreach ($postInitializers as $initializer) { + $initializer->initialize($connection); + } + + return new Connection($connection); } /** @@ -95,7 +116,7 @@ private function withSecure(array $initializers, array $params): array $params['ssl_cert'] ?? '', $params['ssl_ca'] ?? '', $params['ssl_capath'] ?? '', - $params['ssl_cipher'] ?? '' + $params['ssl_cipher'] ?? '', ); } diff --git a/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php b/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php index faceeb086..44a8cab99 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php +++ b/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php @@ -9,6 +9,8 @@ use mysqli_sql_exception; use ReflectionProperty; +use function assert; + /** * @internal * @@ -18,7 +20,10 @@ final class ConnectionFailed extends AbstractException { public static function new(mysqli $connection): self { - return new self($connection->connect_error, 'HY000', $connection->connect_errno); + $error = $connection->connect_error; + assert($error !== null); + + return new self($error, 'HY000', $connection->connect_errno); } public static function upcast(mysqli_sql_exception $exception): self 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..643475e58 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php +++ b/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php @@ -9,20 +9,11 @@ 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; + private string $key; + private string $cert; + private string $ca; + private string $capath; + private string $cipher; public function __construct(string $key, string $cert, string $ca, string $capath, string $cipher) { diff --git a/doctrine/dbal/src/Driver/Mysqli/Result.php b/doctrine/dbal/src/Driver/Mysqli/Result.php index 230471526..c096e6d76 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Result.php +++ b/doctrine/dbal/src/Driver/Mysqli/Result.php @@ -10,25 +10,21 @@ use Doctrine\DBAL\Driver\Result as ResultInterface; use mysqli_sql_exception; use mysqli_stmt; -use stdClass; +use function array_column; use function array_combine; use function array_fill; -use function array_map; use function count; 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 @@ -36,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. @@ -58,11 +54,7 @@ public function __construct(mysqli_stmt $statement) $this->hasColumns = true; - $fields = $meta->fetch_fields(); - - $this->columnNames = array_map(static function (stdClass $field): string { - return $field->name; - }, $fields); + $this->columnNames = array_column($meta->fetch_fields(), 'name'); $meta->free(); @@ -84,10 +76,8 @@ public function __construct(mysqli_stmt $statement) // to the length of the ones fetched during the previous execution. $this->boundValues = array_fill(0, count($this->columnNames), null); - $refs = []; - foreach ($this->boundValues as &$value) { - $refs[] =& $value; - } + // The following is necessary as PHP cannot handle references to properties properly + $refs = &$this->boundValues; if (! $this->statement->bind_result(...$refs)) { throw StatementError::new($this->statement); diff --git a/doctrine/dbal/src/Driver/Mysqli/Statement.php b/doctrine/dbal/src/Driver/Mysqli/Statement.php index 249cb2c87..7b7c93129 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Statement.php +++ b/doctrine/dbal/src/Driver/Mysqli/Statement.php @@ -4,14 +4,13 @@ use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Driver\Exception\UnknownParameterType; -use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError; use Doctrine\DBAL\Driver\Mysqli\Exception\FailedReadingStreamOffset; use Doctrine\DBAL\Driver\Mysqli\Exception\NonStreamResourceUsedAsLargeObject; use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError; use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; -use mysqli; +use Doctrine\Deprecations\Deprecation; use mysqli_sql_exception; use mysqli_stmt; @@ -20,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; @@ -28,7 +28,7 @@ final class Statement implements StatementInterface { /** @var string[] */ - protected static $_paramTypeMap = [ + private static array $paramTypeMap = [ ParameterType::ASCII => 's', ParameterType::STRING => 's', ParameterType::BINARY => 's', @@ -38,66 +38,61 @@ final class Statement implements StatementInterface ParameterType::LARGE_OBJECT => 'b', ]; - /** @var mysqli */ - protected $_conn; - - /** @var mysqli_stmt */ - protected $_stmt; + private mysqli_stmt $stmt; /** @var mixed[] */ - protected $_bindedValues; + private array $boundValues; - /** @var string */ - protected $types; + private string $types; /** * Contains ref values for bindValue(). * * @var mixed[] */ - protected $_values = []; + private array $values = []; - /** - * @internal The statement can be only instantiated by its driver connection. - * - * @param string $prepareString - * - * @throws Exception - */ - public function __construct(mysqli $conn, $prepareString) + /** @internal The statement can be only instantiated by its driver connection. */ + public function __construct(mysqli_stmt $stmt) { - $this->_conn = $conn; - - try { - $stmt = $conn->prepare($prepareString); - } catch (mysqli_sql_exception $e) { - throw ConnectionError::upcast($e); - } - - if ($stmt === false) { - throw ConnectionError::new($this->_conn); - } - - $this->_stmt = $stmt; + $this->stmt = $stmt; - $paramCount = $this->_stmt->param_count; - $this->types = str_repeat('s', $paramCount); - $this->_bindedValues = array_fill(1, $paramCount, null); + $paramCount = $this->stmt->param_count; + $this->types = str_repeat('s', $paramCount); + $this->boundValues = array_fill(1, $paramCount, null); } /** * {@inheritdoc} + * + * @deprecated Use {@see bindValue()} instead. */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + 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::$paramTypeMap[$type])) { throw UnknownParameterType::new($type); } - $this->_bindedValues[$param] =& $variable; - $this->types[$param - 1] = self::$_paramTypeMap[$type]; + $this->boundValues[$param] =& $variable; + $this->types[$param - 1] = self::$paramTypeMap[$type]; return true; } @@ -105,17 +100,26 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le /** * {@inheritdoc} */ - public function bindValue($param, $value, $type = ParameterType::STRING) + 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::$paramTypeMap[$type])) { throw UnknownParameterType::new($type); } - $this->_values[$param] = $value; - $this->_bindedValues[$param] =& $this->_values[$param]; - $this->types[$param - 1] = self::$_paramTypeMap[$type]; + $this->values[$param] = $value; + $this->boundValues[$param] =& $this->values[$param]; + $this->types[$param - 1] = self::$paramTypeMap[$type]; return true; } @@ -125,25 +129,34 @@ public function bindValue($param, $value, $type = ParameterType::STRING) */ 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); + throw StatementError::new($this->stmt); } - } elseif (count($this->_bindedValues) > 0) { + } elseif (count($this->boundValues) > 0) { $this->bindTypedParameters(); } try { - $result = $this->_stmt->execute(); + $result = $this->stmt->execute(); } catch (mysqli_sql_exception $e) { throw StatementError::upcast($e); } if (! $result) { - throw StatementError::new($this->_stmt); + throw StatementError::new($this->stmt); } - return new Result($this->_stmt); + return new Result($this->stmt); } /** @@ -156,14 +169,14 @@ private function bindTypedParameters(): void $streams = $values = []; $types = $this->types; - foreach ($this->_bindedValues as $parameter => $value) { + foreach ($this->boundValues as $parameter => $value) { assert(is_int($parameter)); if (! isset($types[$parameter - 1])) { - $types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING]; + $types[$parameter - 1] = self::$paramTypeMap[ParameterType::STRING]; } - if ($types[$parameter - 1] === static::$_paramTypeMap[ParameterType::LARGE_OBJECT]) { + if ($types[$parameter - 1] === self::$paramTypeMap[ParameterType::LARGE_OBJECT]) { if (is_resource($value)) { if (get_resource_type($value) !== 'stream') { throw NonStreamResourceUsedAsLargeObject::new($parameter); @@ -174,14 +187,14 @@ private function bindTypedParameters(): void continue; } - $types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING]; + $types[$parameter - 1] = self::$paramTypeMap[ParameterType::STRING]; } $values[$parameter] = $value; } - if (! $this->_stmt->bind_param($types, ...$values)) { - throw StatementError::new($this->_stmt); + if (! $this->stmt->bind_param($types, ...$values)) { + throw StatementError::new($this->stmt); } $this->sendLongData($streams); @@ -204,8 +217,8 @@ private function sendLongData(array $streams): void throw FailedReadingStreamOffset::new($paramNr); } - if (! $this->_stmt->send_long_data($paramNr - 1, $chunk)) { - throw StatementError::new($this->_stmt); + if (! $this->stmt->send_long_data($paramNr - 1, $chunk)) { + throw StatementError::new($this->stmt); } } } @@ -215,18 +228,9 @@ private function sendLongData(array $streams): void * Binds a array of values to bound parameters. * * @param mixed[] $values - * - * @return bool */ - private function bindUntypedValues(array $values) + private function bindUntypedValues(array $values): bool { - $params = []; - $types = str_repeat('s', count($values)); - - foreach ($values as &$v) { - $params[] =& $v; - } - - return $this->_stmt->bind_param($types, ...$params); + return $this->stmt->bind_param(str_repeat('s', count($values)), ...$values); } } diff --git a/doctrine/dbal/src/Driver/OCI8/Connection.php b/doctrine/dbal/src/Driver/OCI8/Connection.php index b05892f96..da4f61aeb 100644 --- a/doctrine/dbal/src/Driver/OCI8/Connection.php +++ b/doctrine/dbal/src/Driver/OCI8/Connection.php @@ -3,79 +3,53 @@ namespace Doctrine\DBAL\Driver\OCI8; use Doctrine\DBAL\Driver\Exception; -use Doctrine\DBAL\Driver\OCI8\Exception\ConnectionFailed; use Doctrine\DBAL\Driver\OCI8\Exception\Error; use Doctrine\DBAL\Driver\OCI8\Exception\SequenceDoesNotExist; use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\Statement as DriverStatement; use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\SQL\Parser; +use Doctrine\Deprecations\Deprecation; use function addcslashes; use function assert; use function is_float; use function is_int; +use function is_resource; use function oci_commit; -use function oci_connect; -use function oci_pconnect; +use function oci_parse; use function oci_rollback; use function oci_server_version; use function preg_match; use function str_replace; -use const OCI_NO_AUTO_COMMIT; - final class Connection implements ServerInfoAwareConnection { /** @var resource */ - protected $dbh; + private $connection; - /** @var ExecutionMode */ - private $executionMode; + private Parser $parser; + private ExecutionMode $executionMode; /** - * Creates a Connection to an Oracle Database using oci8 extension. - * * @internal The connection can be only instantiated by its driver. * - * @param string $username - * @param string $password - * @param string $db - * @param string $charset - * @param int $sessionMode - * @param bool $persistent - * - * @throws Exception + * @param resource $connection */ - public function __construct( - $username, - $password, - $db, - $charset = '', - $sessionMode = OCI_NO_AUTO_COMMIT, - $persistent = false - ) { - $dbh = $persistent - ? @oci_pconnect($username, $password, $db, $charset, $sessionMode) - : @oci_connect($username, $password, $db, $charset, $sessionMode); - - if ($dbh === false) { - throw ConnectionFailed::new(); - } - - $this->dbh = $dbh; + public function __construct($connection) + { + $this->connection = $connection; + $this->parser = new Parser(false); $this->executionMode = new ExecutionMode(); } - /** - * {@inheritdoc} - */ - public function getServerVersion() + public function getServerVersion(): string { - $version = oci_server_version($this->dbh); + $version = oci_server_version($this->connection); if ($version === false) { - throw Error::new($this->dbh); + throw Error::new($this->connection); } $result = preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', $version, $matches); @@ -84,11 +58,23 @@ public function getServerVersion() return $matches[1]; } + /** @throws Parser\Exception */ public function prepare(string $sql): DriverStatement { - return new Statement($this->dbh, $sql, $this->executionMode); + $visitor = new ConvertPositionalToNamedPlaceholders(); + + $this->parser->parse($sql, $visitor); + + $statement = oci_parse($this->connection, $visitor->getSQL()); + assert(is_resource($statement)); + + return new Statement($this->connection, $statement, $visitor->getParameterMap(), $this->executionMode); } + /** + * @throws Exception + * @throws Parser\Exception + */ public function query(string $sql): ResultInterface { return $this->prepare($sql)->execute(); @@ -108,6 +94,10 @@ public function quote($value, $type = ParameterType::STRING) return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; } + /** + * @throws Exception + * @throws Parser\Exception + */ public function exec(string $sql): int { return $this->prepare($sql)->execute()->rowCount(); @@ -119,6 +109,8 @@ public function exec(string $sql): int * @param string|null $name * * @return int|false + * + * @throws Parser\Exception */ public function lastInsertId($name = null) { @@ -126,6 +118,12 @@ public function lastInsertId($name = null) return false; } + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4687', + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', + ); + $result = $this->query('SELECT ' . $name . '.CURRVAL FROM DUAL')->fetchOne(); if ($result === false) { @@ -135,23 +133,17 @@ public function lastInsertId($name = null) return (int) $result; } - /** - * {@inheritdoc} - */ - public function beginTransaction() + public function beginTransaction(): bool { $this->executionMode->disableAutoCommit(); return true; } - /** - * {@inheritdoc} - */ - public function commit() + public function commit(): bool { - if (! oci_commit($this->dbh)) { - throw Error::new($this->dbh); + if (! oci_commit($this->connection)) { + throw Error::new($this->connection); } $this->executionMode->enableAutoCommit(); @@ -159,17 +151,20 @@ public function commit() return true; } - /** - * {@inheritdoc} - */ - public function rollBack() + public function rollBack(): bool { - if (! oci_rollback($this->dbh)) { - throw Error::new($this->dbh); + if (! oci_rollback($this->connection)) { + throw Error::new($this->connection); } $this->executionMode->enableAutoCommit(); return true; } + + /** @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 351ffb87a..48ef0bbe4 100644 --- a/doctrine/dbal/src/Driver/OCI8/Driver.php +++ b/doctrine/dbal/src/Driver/OCI8/Driver.php @@ -3,6 +3,10 @@ namespace Doctrine\DBAL\Driver\OCI8; use Doctrine\DBAL\Driver\AbstractOracleDriver; +use Doctrine\DBAL\Driver\OCI8\Exception\ConnectionFailed; + +use function oci_connect; +use function oci_pconnect; use const OCI_NO_AUTO_COMMIT; @@ -18,25 +22,23 @@ final class Driver extends AbstractOracleDriver */ public function connect(array $params) { - return new Connection( - $params['user'] ?? '', - $params['password'] ?? '', - $this->_constructDsn($params), - $params['charset'] ?? '', - $params['sessionMode'] ?? OCI_NO_AUTO_COMMIT, - $params['persistent'] ?? false - ); - } + $username = $params['user'] ?? ''; + $password = $params['password'] ?? ''; + $charset = $params['charset'] ?? ''; + $sessionMode = $params['sessionMode'] ?? OCI_NO_AUTO_COMMIT; - /** - * Constructs the Oracle DSN. - * - * @param mixed[] $params - * - * @return string The DSN. - */ - protected function _constructDsn(array $params) - { - return $this->getEasyConnectString($params); + $connectionString = $this->getEasyConnectString($params); + + if (! empty($params['persistent'])) { + $connection = @oci_pconnect($username, $password, $connectionString, $charset, $sessionMode); + } else { + $connection = @oci_connect($username, $password, $connectionString, $charset, $sessionMode); + } + + if ($connection === false) { + throw ConnectionFailed::new(); + } + + return new Connection($connection); } } 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..30896a23c --- /dev/null +++ b/doctrine/dbal/src/Driver/OCI8/Middleware/InitializeSession.php @@ -0,0 +1,36 @@ +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 56602a364..e707f721c 100644 --- a/doctrine/dbal/src/Driver/OCI8/Statement.php +++ b/doctrine/dbal/src/Driver/OCI8/Statement.php @@ -2,21 +2,18 @@ namespace Doctrine\DBAL\Driver\OCI8; -use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Driver\OCI8\Exception\Error; use Doctrine\DBAL\Driver\OCI8\Exception\UnknownParameterIndex; use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\SQL\Parser; +use Doctrine\Deprecations\Deprecation; -use function assert; +use function func_num_args; use function is_int; -use function is_resource; use function oci_bind_by_name; use function oci_execute; use function oci_new_descriptor; -use function oci_parse; use const OCI_B_BIN; use const OCI_B_BLOB; @@ -29,88 +26,96 @@ final class Statement implements StatementInterface { /** @var resource */ - protected $_dbh; + private $connection; /** @var resource */ - protected $_sth; + private $statement; - /** @var ExecutionMode */ - private $executionMode; + /** @var array */ + private array $parameterMap; - /** @var string[] */ - protected $_paramMap = []; + private ExecutionMode $executionMode; /** - * Holds references to bound parameter values. - * - * This is a new requirement for PHP7's oci8 extension that prevents bound values from being garbage collected. - * - * @var mixed[] - */ - private $boundValues = []; - - /** - * Creates a new OCI8Statement that uses the given connection handle and SQL statement. - * * @internal The statement can be only instantiated by its driver connection. * - * @param resource $dbh The connection handle. - * @param string $query The SQL query. - * - * @throws Exception + * @param resource $connection + * @param resource $statement + * @param array $parameterMap */ - public function __construct($dbh, $query, ExecutionMode $executionMode) + public function __construct($connection, $statement, array $parameterMap, ExecutionMode $executionMode) { - $parser = new Parser(false); - $visitor = new ConvertPositionalToNamedPlaceholders(); - - $parser->parse($query, $visitor); - - $stmt = oci_parse($dbh, $visitor->getSQL()); - assert(is_resource($stmt)); - - $this->_sth = $stmt; - $this->_dbh = $dbh; - $this->_paramMap = $visitor->getParameterMap(); + $this->connection = $connection; + $this->statement = $statement; + $this->parameterMap = $parameterMap; $this->executionMode = $executionMode; } /** * {@inheritdoc} */ - public function bindValue($param, $value, $type = ParameterType::STRING) + public function bindValue($param, $value, $type = ParameterType::STRING): bool { - return $this->bindParam($param, $value, $type, null); + 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} + * + * @deprecated Use {@see bindValue()} instead. */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + 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->_paramMap[$param])) { + if (! isset($this->parameterMap[$param])) { throw UnknownParameterIndex::new($param); } - $param = $this->_paramMap[$param]; + $param = $this->parameterMap[$param]; } if ($type === ParameterType::LARGE_OBJECT) { - $lob = oci_new_descriptor($this->_dbh, OCI_D_LOB); - $lob->writeTemporary($variable, OCI_TEMP_BLOB); + if ($variable !== null) { + $lob = oci_new_descriptor($this->connection, OCI_D_LOB); + $lob->writeTemporary($variable, OCI_TEMP_BLOB); - $variable =& $lob; + $variable =& $lob; + } else { + $type = ParameterType::STRING; + } } - $this->boundValues[$param] =& $variable; - return oci_bind_by_name( - $this->_sth, + $this->statement, $param, $variable, $length ?? -1, - $this->convertParameterType($type) + $this->convertParameterType($type), ); } @@ -137,11 +142,18 @@ private function convertParameterType(int $type): int 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); } } } @@ -152,11 +164,11 @@ public function execute($params = null): ResultInterface $mode = OCI_NO_AUTO_COMMIT; } - $ret = @oci_execute($this->_sth, $mode); + $ret = @oci_execute($this->statement, $mode); if (! $ret) { - throw Error::new($this->_sth); + throw Error::new($this->statement); } - return new Result($this->_sth); + return new Result($this->statement); } } diff --git a/doctrine/dbal/src/Driver/PDO/Connection.php b/doctrine/dbal/src/Driver/PDO/Connection.php index 169016937..e0b6221f4 100644 --- a/doctrine/dbal/src/Driver/PDO/Connection.php +++ b/doctrine/dbal/src/Driver/PDO/Connection.php @@ -2,11 +2,11 @@ namespace Doctrine\DBAL\Driver\PDO; -use Doctrine\DBAL\Driver\Exception as ExceptionInterface; use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use PDO; use PDOException; use PDOStatement; @@ -15,27 +15,14 @@ final class Connection implements ServerInfoAwareConnection { - /** @var PDO */ - private $connection; + private PDO $connection; - /** - * @internal The connection can be only instantiated by its driver. - * - * @param string $dsn - * @param string|null $user - * @param string|null $password - * @param mixed[]|null $options - * - * @throws ExceptionInterface - */ - public function __construct($dsn, $user = null, $password = null, ?array $options = null) + /** @internal The connection can be only instantiated by its driver. */ + public function __construct(PDO $connection) { - try { - $this->connection = new PDO($dsn, (string) $user, (string) $password, (array) $options); - $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } catch (PDOException $exception) { - throw Exception::new($exception); - } + $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $this->connection = $connection; } public function exec(string $sql): int @@ -70,7 +57,7 @@ public function prepare(string $sql): StatementInterface $stmt = $this->connection->prepare($sql); assert($stmt instanceof PDOStatement); - return $this->createStatement($stmt); + return new Statement($stmt); } catch (PDOException $exception) { throw Exception::new($exception); } @@ -106,46 +93,48 @@ public function lastInsertId($name = null) return $this->connection->lastInsertId(); } + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4687', + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', + ); + return $this->connection->lastInsertId($name); } catch (PDOException $exception) { throw Exception::new($exception); } } - /** - * Creates a wrapped statement - */ - protected function createStatement(PDOStatement $stmt): Statement - { - return new Statement($stmt); - } - - /** - * {@inheritDoc} - */ - public function beginTransaction() + public function beginTransaction(): bool { return $this->connection->beginTransaction(); } - /** - * {@inheritDoc} - */ - public function commit() + public function commit(): bool { return $this->connection->commit(); } - /** - * {@inheritDoc} - */ - public function rollBack() + public function rollBack(): bool { return $this->connection->rollBack(); } - public function getWrappedConnection(): PDO + public function getNativeConnection(): PDO { return $this->connection; } + + /** @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__, + ); + + return $this->getNativeConnection(); + } } diff --git a/doctrine/dbal/src/Driver/PDO/Exception.php b/doctrine/dbal/src/Driver/PDO/Exception.php index 49f55951d..fbb81253b 100644 --- a/doctrine/dbal/src/Driver/PDO/Exception.php +++ b/doctrine/dbal/src/Driver/PDO/Exception.php @@ -18,6 +18,8 @@ public static function new(PDOException $exception): self { if ($exception->errorInfo !== null) { [$sqlState, $code] = $exception->errorInfo; + + $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 50c05a40f..505b0fa69 100644 --- a/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php @@ -4,7 +4,9 @@ use Doctrine\DBAL\Driver\AbstractMySQLDriver; use Doctrine\DBAL\Driver\PDO\Connection; +use Doctrine\DBAL\Driver\PDO\Exception; use PDO; +use PDOException; final class Driver extends AbstractMySQLDriver { @@ -21,22 +23,26 @@ public function connect(array $params) $driverOptions[PDO::ATTR_PERSISTENT] = true; } - return new Connection( - $this->constructPdoDsn($params), - $params['user'] ?? '', - $params['password'] ?? '', - $driverOptions - ); + try { + $pdo = new PDO( + $this->constructPdoDsn($params), + $params['user'] ?? '', + $params['password'] ?? '', + $driverOptions, + ); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + + return new Connection($pdo); } /** * Constructs the MySQL PDO DSN. * * @param mixed[] $params - * - * @return string The DSN. */ - protected function constructPdoDsn(array $params) + private function constructPdoDsn(array $params): string { $dsn = 'mysql:'; if (isset($params['host']) && $params['host'] !== '') { diff --git a/doctrine/dbal/src/Driver/PDO/OCI/Driver.php b/doctrine/dbal/src/Driver/PDO/OCI/Driver.php index a2c1f4652..fb9944bb8 100644 --- a/doctrine/dbal/src/Driver/PDO/OCI/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/OCI/Driver.php @@ -4,7 +4,9 @@ use Doctrine\DBAL\Driver\AbstractOracleDriver; use Doctrine\DBAL\Driver\PDO\Connection; +use Doctrine\DBAL\Driver\PDO\Exception; use PDO; +use PDOException; final class Driver extends AbstractOracleDriver { @@ -21,22 +23,26 @@ public function connect(array $params) $driverOptions[PDO::ATTR_PERSISTENT] = true; } - return new Connection( - $this->constructPdoDsn($params), - $params['user'] ?? '', - $params['password'] ?? '', - $driverOptions - ); + try { + $pdo = new PDO( + $this->constructPdoDsn($params), + $params['user'] ?? '', + $params['password'] ?? '', + $driverOptions, + ); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + + return new Connection($pdo); } /** * Constructs the Oracle PDO DSN. * * @param mixed[] $params - * - * @return string The DSN. */ - private function constructPdoDsn(array $params) + private function constructPdoDsn(array $params): string { $dsn = 'oci:dbname=' . $this->getEasyConnectString($params); diff --git a/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php b/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php index e9620bde7..caa9cd606 100644 --- a/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php @@ -4,9 +4,10 @@ use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver; use Doctrine\DBAL\Driver\PDO\Connection; +use Doctrine\DBAL\Driver\PDO\Exception; +use Doctrine\Deprecations\Deprecation; use PDO; - -use function defined; +use PDOException; final class Driver extends AbstractPostgreSQLDriver { @@ -23,24 +24,27 @@ public function connect(array $params) $driverOptions[PDO::ATTR_PERSISTENT] = true; } - $connection = new Connection( - $this->_constructPdoDsn($params), - $params['user'] ?? '', - $params['password'] ?? '', - $driverOptions, - ); + try { + $pdo = new PDO( + $this->constructPdoDsn($params), + $params['user'] ?? '', + $params['password'] ?? '', + $driverOptions, + ); + } catch (PDOException $exception) { + throw Exception::new($exception); + } if ( - defined('PDO::PGSQL_ATTR_DISABLE_PREPARES') - && (! isset($driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES]) - || $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] === true - ) + ! isset($driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES]) + || $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] === true ) { - $connection->getWrappedConnection()->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true); + $pdo->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true); } + $connection = new Connection($pdo); + /* defining client_encoding via SET NAMES to avoid inconsistent DSN support - * - the 'client_encoding' connection param only works with postgres >= 9.1 * - passing client_encoding via the 'options' param breaks pgbouncer support */ if (isset($params['charset'])) { @@ -54,10 +58,8 @@ public function connect(array $params) * Constructs the Postgres PDO DSN. * * @param mixed[] $params - * - * @return string The DSN. */ - private function _constructPdoDsn(array $params) + private function constructPdoDsn(array $params): string { $dsn = 'pgsql:'; @@ -72,11 +74,25 @@ private function _constructPdoDsn(array $params) 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 888716d81..88957d23c 100644 --- a/doctrine/dbal/src/Driver/PDO/Result.php +++ b/doctrine/dbal/src/Driver/PDO/Result.php @@ -9,17 +9,11 @@ use PDOException; use PDOStatement; -use function assert; -use function is_array; - 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; @@ -118,13 +112,9 @@ private function fetch(int $mode) private function fetchAll(int $mode): array { try { - $data = $this->statement->fetchAll($mode); + return $this->statement->fetchAll($mode); } catch (PDOException $exception) { throw Exception::new($exception); } - - assert(is_array($data)); - - return $data; } } diff --git a/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php b/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php index a64f4245a..9015f5558 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php +++ b/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php @@ -2,96 +2,69 @@ namespace Doctrine\DBAL\Driver\PDO\SQLSrv; +use Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware; use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection; -use Doctrine\DBAL\Driver\Result; -use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\Statement as StatementInterface; -use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use PDO; -final class Connection implements ServerInfoAwareConnection +final class Connection extends AbstractConnectionMiddleware { - /** @var PDOConnection */ - private $connection; + private PDOConnection $connection; public function __construct(PDOConnection $connection) { + parent::__construct($connection); + $this->connection = $connection; } public function prepare(string $sql): StatementInterface { return new Statement( - $this->connection->prepare($sql) + $this->connection->prepare($sql), ); } - public function query(string $sql): Result - { - return $this->connection->query($sql); - } - - /** - * {@inheritDoc} - */ - public function quote($value, $type = ParameterType::STRING) - { - return $this->connection->quote($value, $type); - } - - public function exec(string $sql): int - { - return $this->connection->exec($sql); - } - /** * {@inheritDoc} */ public function lastInsertId($name = null) { if ($name === null) { - return $this->connection->lastInsertId($name); + return parent::lastInsertId($name); } - return $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') - ->execute([$name]) - ->fetchOne(); - } - - /** - * {@inheritDoc} - */ - public function beginTransaction() - { - return $this->connection->beginTransaction(); - } + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4687', + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', + ); - /** - * {@inheritDoc} - */ - public function commit() - { - return $this->connection->commit(); - } + $statement = $this->prepare( + 'SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?', + ); + $statement->bindValue(1, $name); - /** - * {@inheritDoc} - */ - public function rollBack() - { - return $this->connection->rollBack(); + return $statement->execute() + ->fetchOne(); } - /** - * {@inheritDoc} - */ - public function getServerVersion() + public function getNativeConnection(): PDO { - return $this->connection->getServerVersion(); + return $this->connection->getNativeConnection(); } + /** @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__, + ); + 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 c48425049..eba640e6e 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Driver\AbstractSQLServerDriver\Exception\PortWithoutHost; use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection; +use Doctrine\DBAL\Driver\PDO\Exception as PDOException; use PDO; use function is_int; @@ -20,12 +21,12 @@ final class Driver extends AbstractSQLServerDriver */ public function connect(array $params) { - $pdoOptions = $dsnOptions = []; + $driverOptions = $dsnOptions = []; if (isset($params['driverOptions'])) { foreach ($params['driverOptions'] as $option => $value) { if (is_int($option)) { - $pdoOptions[$option] = $value; + $driverOptions[$option] = $value; } else { $dsnOptions[$option] = $value; } @@ -33,17 +34,21 @@ public function connect(array $params) } if (! empty($params['persistent'])) { - $pdoOptions[PDO::ATTR_PERSISTENT] = true; + $driverOptions[PDO::ATTR_PERSISTENT] = true; } - return new Connection( - new PDOConnection( - $this->_constructPdoDsn($params, $dsnOptions), + try { + $pdo = new PDO( + $this->constructDsn($params, $dsnOptions), $params['user'] ?? '', $params['password'] ?? '', - $pdoOptions - ) - ); + $driverOptions, + ); + } catch (\PDOException $exception) { + throw PDOException::new($exception); + } + + return new Connection(new PDOConnection($pdo)); } /** @@ -52,11 +57,9 @@ public function connect(array $params) * @param mixed[] $params * @param string[] $connectionOptions * - * @return string The DSN. - * * @throws Exception */ - private function _constructPdoDsn(array $params, array $connectionOptions) + private function constructDsn(array $params, array $connectionOptions): string { $dsn = 'sqlsrv:server='; diff --git a/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php b/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php index 2f48874e0..edabd410a 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php +++ b/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php @@ -2,53 +2,72 @@ namespace Doctrine\DBAL\Driver\PDO\SQLSrv; +use Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware; use Doctrine\DBAL\Driver\PDO\Statement as PDOStatement; -use Doctrine\DBAL\Driver\Result; -use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; use Doctrine\Deprecations\Deprecation; use PDO; use function func_num_args; -final class Statement implements StatementInterface +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); + $this->statement = $statement; } /** * {@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. */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null) - { + public function bindParam( + $param, + &$variable, + $type = ParameterType::STRING, + $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; @@ -65,16 +84,17 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le /** * {@inheritdoc} */ - public function bindValue($param, $value, $type = ParameterType::STRING) + public function bindValue($param, $value, $type = ParameterType::STRING): bool { - return $this->bindParam($param, $value, $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.', + ); + } - /** - * {@inheritdoc} - */ - public function execute($params = null): Result - { - return $this->statement->execute($params); + 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 d85ba28fa..4840b6788 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php @@ -3,20 +3,15 @@ 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\Platforms\SqlitePlatform; - -use function array_merge; +use Doctrine\DBAL\Driver\PDO\Exception; +use Doctrine\Deprecations\Deprecation; +use PDO; +use PDOException; final class Driver extends AbstractSQLiteDriver { - /** @var mixed[] */ - protected $_userDefinedFunctions = [ - 'sqrt' => ['callback' => [SqlitePlatform::class, 'udfSqrt'], 'numArgs' => 1], - 'mod' => ['callback' => [SqlitePlatform::class, 'udfMod'], 'numArgs' => 2], - 'locate' => ['callback' => [SqlitePlatform::class, 'udfLocate'], 'numArgs' => -1], - ]; - /** * {@inheritdoc} * @@ -24,40 +19,46 @@ final class Driver extends AbstractSQLiteDriver */ public function connect(array $params) { - $driverOptions = $params['driverOptions'] ?? []; + $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']); } - $connection = new Connection( - $this->_constructPdoDsn($params), - $params['user'] ?? '', - $params['password'] ?? '', - $driverOptions - ); - - $pdo = $connection->getWrappedConnection(); - - foreach ($this->_userDefinedFunctions as $fn => $data) { - $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); + try { + $pdo = new PDO( + $this->constructPdoDsn($params), + $params['user'] ?? '', + $params['password'] ?? '', + $driverOptions, + ); + } catch (PDOException $exception) { + throw Exception::new($exception); } - return $connection; + UserDefinedFunctions::register( + [$pdo, 'sqliteCreateFunction'], + $userDefinedFunctions, + ); + + return new Connection($pdo); } /** * Constructs the Sqlite PDO DSN. * * @param mixed[] $params - * - * @return string The DSN. */ - protected function _constructPdoDsn(array $params) + private function constructPdoDsn(array $params): string { $dsn = 'sqlite:'; if (isset($params['path'])) { diff --git a/doctrine/dbal/src/Driver/PDO/Statement.php b/doctrine/dbal/src/Driver/PDO/Statement.php index 1461239e6..aaf494ccb 100644 --- a/doctrine/dbal/src/Driver/PDO/Statement.php +++ b/doctrine/dbal/src/Driver/PDO/Statement.php @@ -28,12 +28,9 @@ final class Statement implements StatementInterface 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; @@ -44,6 +41,15 @@ public function __construct(PDOStatement $stmt) */ 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.', + ); + } + $type = $this->convertParamType($type); try { @@ -56,28 +62,55 @@ 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. - * - * @return bool */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null) - { + public function bindParam( + $param, + &$variable, + $type = ParameterType::STRING, + $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); try { - return $this->stmt->bindParam($param, $variable, $type, ...array_slice(func_get_args(), 3)); + return $this->stmt->bindParam( + $param, + $variable, + $type, + $length ?? 0, + ...array_slice(func_get_args(), 4), + ); } catch (PDOException $exception) { throw Exception::new($exception); } @@ -88,6 +121,15 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le */ 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) { diff --git a/doctrine/dbal/src/Driver/SQLSrv/Connection.php b/doctrine/dbal/src/Driver/SQLSrv/Connection.php index e37ea9006..ba1c58c17 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Connection.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Connection.php @@ -2,20 +2,18 @@ namespace Doctrine\DBAL\Driver\SQLSrv; -use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\SQLSrv\Exception\Error; use Doctrine\DBAL\Driver\Statement as DriverStatement; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use function is_float; use function is_int; use function sprintf; use function sqlsrv_begin_transaction; use function sqlsrv_commit; -use function sqlsrv_configure; -use function sqlsrv_connect; use function sqlsrv_query; use function sqlsrv_rollback; use function sqlsrv_rows_affected; @@ -25,29 +23,16 @@ final class Connection implements ServerInfoAwareConnection { /** @var resource */ - protected $conn; + private $connection; /** * @internal The connection can be only instantiated by its driver. * - * @param string $serverName - * @param mixed[] $connectionOptions - * - * @throws Exception + * @param resource $connection */ - public function __construct($serverName, $connectionOptions) + public function __construct($connection) { - if (! sqlsrv_configure('WarningsReturnAsErrors', 0)) { - throw Error::new(); - } - - $conn = sqlsrv_connect($serverName, $connectionOptions); - - if ($conn === false) { - throw Error::new(); - } - - $this->conn = $conn; + $this->connection = $connection; } /** @@ -55,14 +40,14 @@ public function __construct($serverName, $connectionOptions) */ public function getServerVersion() { - $serverInfo = sqlsrv_server_info($this->conn); + $serverInfo = sqlsrv_server_info($this->connection); return $serverInfo['SQLServerVersion']; } public function prepare(string $sql): DriverStatement { - return new Statement($this->conn, $sql); + return new Statement($this->connection, $sql); } public function query(string $sql): ResultInterface @@ -88,7 +73,7 @@ public function quote($value, $type = ParameterType::STRING) public function exec(string $sql): int { - $stmt = sqlsrv_query($this->conn, $sql); + $stmt = sqlsrv_query($this->connection, $sql); if ($stmt === false) { throw Error::new(); @@ -109,6 +94,12 @@ public function exec(string $sql): int 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.', + ); + $result = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') ->execute([$name]); } else { @@ -118,39 +109,36 @@ public function lastInsertId($name = null) return $result->fetchOne(); } - /** - * {@inheritDoc} - */ - public function beginTransaction() + public function beginTransaction(): bool { - if (! sqlsrv_begin_transaction($this->conn)) { + if (! sqlsrv_begin_transaction($this->connection)) { throw Error::new(); } return true; } - /** - * {@inheritDoc} - */ - public function commit() + public function commit(): bool { - if (! sqlsrv_commit($this->conn)) { + if (! sqlsrv_commit($this->connection)) { throw Error::new(); } return true; } - /** - * {@inheritDoc} - */ - public function rollBack() + public function rollBack(): bool { - if (! sqlsrv_rollback($this->conn)) { + if (! sqlsrv_rollback($this->connection)) { throw Error::new(); } return true; } + + /** @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 efa9ccfe9..085b22412 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Driver.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Driver.php @@ -4,6 +4,10 @@ use Doctrine\DBAL\Driver\AbstractSQLServerDriver; use Doctrine\DBAL\Driver\AbstractSQLServerDriver\Exception\PortWithoutHost; +use Doctrine\DBAL\Driver\SQLSrv\Exception\Error; + +use function sqlsrv_configure; +use function sqlsrv_connect; /** * Driver for ext/sqlsrv. @@ -51,6 +55,16 @@ public function connect(array $params) $driverOptions['ReturnDatesAsStrings'] = 1; } - return new Connection($serverName, $driverOptions); + if (! sqlsrv_configure('WarningsReturnAsErrors', 0)) { + throw Error::new(); + } + + $connection = sqlsrv_connect($serverName, $driverOptions); + + if ($connection === false) { + throw Error::new(); + } + + return new Connection($connection); } } 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 8267bc0de..1244ba51a 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. @@ -47,16 +47,16 @@ final class Statement implements StatementInterface /** * References to the variables bound as statement parameters. * - * @var mixed + * @var array */ - private $variables = []; + private array $variables = []; /** * Bound parameter types. * - * @var int[] + * @var array */ - private $types = []; + private array $types = []; /** * Append to any INSERT query to retrieve the last insert id. @@ -84,10 +84,19 @@ public function __construct($conn, $sql) /** * {@inheritdoc} */ - public function bindValue($param, $value, $type = ParameterType::STRING) + 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; @@ -96,11 +105,29 @@ public function bindValue($param, $value, $type = ParameterType::STRING) /** * {@inheritdoc} + * + * @deprecated Use {@see bindValue()} instead. */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + 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; @@ -116,18 +143,23 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le 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..de9a8e031 --- /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..a2276d528 --- /dev/null +++ b/doctrine/dbal/src/Driver/SQLite3/Driver.php @@ -0,0 +1,46 @@ +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..a51bd4e8b --- /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..878627a6a --- /dev/null +++ b/doctrine/dbal/src/Driver/SQLite3/Statement.php @@ -0,0 +1,119 @@ + 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; + } + + /** @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->statement->bindValue($param, $value, $this->convertParamType($type)); + } + + /** @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.', + ); + } + + 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()); + } + + 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/ServerInfoAwareConnection.php b/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php index 622f98c6c..5687ab0bb 100644 --- a/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php +++ b/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php @@ -4,11 +4,14 @@ /** * Contract for a connection that is able to provide information about the server it is connected to. + * + * @deprecated The methods defined in this interface will be made part of the {@see Driver} interface + * in the next major release. */ interface ServerInfoAwareConnection extends Connection { /** - * Returns the version number of the database server connected to. + * Returns information about the version of the database server connected to. * * @return string * diff --git a/doctrine/dbal/src/Driver/Statement.php b/doctrine/dbal/src/Driver/Statement.php index 334634a8c..b273128f1 100644 --- a/doctrine/dbal/src/Driver/Statement.php +++ b/doctrine/dbal/src/Driver/Statement.php @@ -20,7 +20,7 @@ interface Statement * 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. * @param mixed $value The value to bind to the parameter. - * @param int $type Explicit data type for the parameter using the {@link ParameterType} + * @param int $type Explicit data type for the parameter using the {@see ParameterType} * constants. * * @return bool TRUE on success or FALSE on failure. @@ -31,7 +31,7 @@ public function bindValue($param, $value, $type = ParameterType::STRING); /** * Binds a PHP variable to a corresponding named (not supported by mysqli driver, see comment below) or question - * mark placeholder in the SQL statement that was use to prepare the statement. Unlike {@link bindValue()}, + * mark placeholder in the SQL statement that was use to prepare the statement. Unlike {@see bindValue()}, * the variable is bound as a reference and will only be evaluated at the time * that PDOStatement->execute() is called. * @@ -43,11 +43,13 @@ 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. * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. - * @param int $type Explicit data type for the parameter using the {@link ParameterType} + * @param int $type Explicit data type for the parameter using the {@see ParameterType} * constants. * @param int|null $length You must specify maxlength when using an OUT bind * so that PHP allocates enough memory to hold the returned value. @@ -62,7 +64,7 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le * Executes a prepared statement * * If the prepared statement included parameter markers, you must either: - * call {@link bindParam()} to bind PHP variables to the parameter markers: + * call {@see bindParam()} to bind PHP variables to the parameter markers: * bound variables pass their value as input and receive the output value, * if any, of their associated parameter markers or pass an array of input-only * parameter values. diff --git a/doctrine/dbal/src/DriverManager.php b/doctrine/dbal/src/DriverManager.php index 698e0438d..5c30088ec 100644 --- a/doctrine/dbal/src/DriverManager.php +++ b/doctrine/dbal/src/DriverManager.php @@ -7,7 +7,9 @@ use Doctrine\DBAL\Driver\Mysqli; use Doctrine\DBAL\Driver\OCI8; use Doctrine\DBAL\Driver\PDO; +use Doctrine\DBAL\Driver\SQLite3; use Doctrine\DBAL\Driver\SQLSrv; +use Doctrine\Deprecations\Deprecation; use function array_keys; use function array_merge; @@ -24,7 +26,7 @@ use function substr; /** - * Factory for creating {@link Connection} instances. + * Factory for creating {@see Connection} instances. * * @psalm-type OverrideParams = array{ * charset?: string, @@ -40,10 +42,12 @@ * platform?: Platforms\AbstractPlatform, * port?: int, * user?: string, + * unix_socket?: string, * } * @psalm-type Params = array{ * charset?: string, * dbname?: string, + * defaultTableOptions?: array, * default_dbname?: string, * driver?: key-of, * driverClass?: class-string, @@ -60,10 +64,12 @@ * port?: int, * primary?: OverrideParams, * replica?: array, + * serverVersion?: string, * sharding?: array, * slaves?: array, * user?: string, * wrapperClass?: class-string, + * unix_socket?: string, * } */ final class DriverManager @@ -71,7 +77,7 @@ final class DriverManager /** * List of supported drivers and their mappings to the driver classes. * - * To add your own driver use the 'driverClass' parameter to {@link DriverManager::getConnection()}. + * To add your own driver use the 'driverClass' parameter to {@see DriverManager::getConnection()}. */ private const DRIVER_MAP = [ 'pdo_mysql' => PDO\MySQL\Driver::class, @@ -83,14 +89,17 @@ final class DriverManager 'pdo_sqlsrv' => PDO\SQLSrv\Driver::class, 'mysqli' => Mysqli\Driver::class, 'sqlsrv' => SQLSrv\Driver::class, + 'sqlite3' => SQLite3\Driver::class, ]; /** * List of URL schemes from a database URL and their mappings to driver. * + * @deprecated Use actual driver names instead. + * * @var string[] */ - private static $driverSchemeAliases = [ + private static array $driverSchemeAliases = [ 'db2' => 'ibm_db2', 'mssql' => 'pdo_sqlsrv', 'mysql' => 'pdo_mysql', @@ -118,7 +127,7 @@ private function __construct() * * $params must contain at least one of the following. * - * Either 'driver' with one of the array keys of {@link DRIVER_MAP}, + * Either 'driver' with one of the array keys of {@see DRIVER_MAP}, * OR 'driverClass' that contains the full class name (with namespace) of the * driver class to instantiate. * @@ -141,9 +150,8 @@ private function __construct() * driverClass: * The driver class to use. * - * @param array $params - * @param Configuration|null $config The configuration to use. - * @param EventManager|null $eventManager The event manager to use. + * @param Configuration|null $config The configuration to use. + * @param EventManager|null $eventManager The event manager to use. * @psalm-param array{ * charset?: string, * dbname?: string, @@ -168,7 +176,6 @@ private function __construct() * user?: string, * wrapperClass?: class-string, * } $params - * @phpstan-param array $params * * @psalm-return ($params is array{wrapperClass:mixed} ? T : Connection) * @@ -182,15 +189,9 @@ public static function getConnection( ?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'])) { @@ -340,7 +341,7 @@ private static function parseDatabaseUrl(array $params): array * 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 {@link parseDatabaseUrlScheme}. + * via {@see parseDatabaseUrlScheme}. * * @see parseDatabaseUrlScheme * @@ -394,7 +395,7 @@ private static function parseDatabaseUrlQuery(array $url, array $params): array /** * Parses the given regular connection URL and resolves the given connection parameters. * - * Assumes that the "path" URL part is already normalized via {@link normalizeDatabaseUrlPath}. + * Assumes that the "path" URL part is already normalized via {@see normalizeDatabaseUrlPath}. * * @see normalizeDatabaseUrlPath * @@ -413,7 +414,7 @@ private static function parseRegularDatabaseUrlPath(array $url, array $params): /** * Parses the given SQLite connection URL and resolves the given connection parameters. * - * Assumes that the "path" URL part is already normalized via {@link normalizeDatabaseUrlPath}. + * Assumes that the "path" URL part is already normalized via {@see normalizeDatabaseUrlPath}. * * @see normalizeDatabaseUrlPath * @@ -455,11 +456,25 @@ private static function parseDatabaseUrlScheme(?string $scheme, array $params): // 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"). + // 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; + if (isset(self::$driverSchemeAliases[$driver])) { + $actualDriver = self::$driverSchemeAliases[$driver]; + + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5697', + 'Relying on driver name aliases is deprecated. Use %s instead of %s.', + str_replace('_', '-', $actualDriver), + $driver, + ); + + $driver = $actualDriver; + } + + // The requested driver from the URL scheme takes precedence over the + // default driver from the connection parameters. + $params['driver'] = $driver; return $params; } 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..6256cdb5b 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); diff --git a/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php b/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php index f7a4e9129..0f77e9ffc 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; diff --git a/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php b/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php new file mode 100644 index 000000000..f2ee05a32 --- /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 e61a48d06..9f3ff6ea7 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php @@ -12,21 +12,18 @@ use function is_array; /** - * Event Arguments used when SQL queries for adding table columns are generated inside {@link AbstractPlatform}. + * 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 e9ebb66ce..9ba37aade 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php @@ -11,21 +11,18 @@ use function is_array; /** - * Event Arguments used when SQL queries for changing table columns are generated inside {@link AbstractPlatform}. + * 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 9c2b9b31e..07c065a9a 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php @@ -10,18 +10,17 @@ use function is_array; /** - * Event Arguments used when SQL queries for creating tables are generated inside {@link AbstractPlatform}. + * 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 d32af5975..4122b418c 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php @@ -11,21 +11,18 @@ use function is_array; /** - * Event Arguments used when SQL queries for removing table columns are generated inside {@link AbstractPlatform}. + * 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 6d52b6c0d..21d3c1645 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php @@ -11,28 +11,23 @@ use function is_array; /** - * Event Arguments used when SQL queries for renaming table columns are generated inside {@link AbstractPlatform}. + * 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 58efa7b50..04fcbde9c 100644 --- a/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php @@ -6,12 +6,13 @@ use Doctrine\DBAL\Schema\Column; /** - * Event Arguments used when the portable column definition is generated inside {@link AbstractPlatform}. + * 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 246d2de07..54f134d14 100644 --- a/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php @@ -11,21 +11,18 @@ use function is_array; /** - * Event Arguments used when SQL queries for creating table columns are generated inside {@link AbstractPlatform}. + * 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 e9829a645..a7d548deb 100644 --- a/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php @@ -10,24 +10,24 @@ use function is_array; /** - * Event Arguments used when SQL queries for creating tables are generated inside {@link AbstractPlatform}. + * 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 072e1efb9..f45e3a15a 100644 --- a/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php @@ -7,15 +7,16 @@ use InvalidArgumentException; /** - * Event Arguments used when the SQL query for dropping tables are generated inside {@link AbstractPlatform}. + * 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 82c17a26a..dbee55a0e 100644 --- a/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php @@ -6,25 +6,25 @@ use Doctrine\DBAL\Schema\Index; /** - * Event Arguments used when the portable index definition is generated inside {@link AbstractSchemaManager}. + * 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 new file mode 100644 index 000000000..be4ccdf15 --- /dev/null +++ b/doctrine/dbal/src/Event/TransactionBeginEventArgs.php @@ -0,0 +1,10 @@ +connection = $connection; + } + + public function getConnection(): Connection + { + return $this->connection; + } +} diff --git a/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php b/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php new file mode 100644 index 000000000..9e6e650d8 --- /dev/null +++ b/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php @@ -0,0 +1,10 @@ +|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,15 +89,19 @@ 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; } $type = $this->originalTypes[$key]; - if ($type !== Connection::PARAM_INT_ARRAY && $type !== Connection::PARAM_STR_ARRAY) { + if ( + $type !== Connection::PARAM_INT_ARRAY + && $type !== Connection::PARAM_STR_ARRAY + && $type !== Connection::PARAM_ASCII_STR_ARRAY + ) { $this->appendTypedParameter([$value], $type); return; @@ -115,9 +116,7 @@ private function acceptParameter($key, $value): void $this->appendTypedParameter($value, $type - Connection::ARRAY_PARAM_OFFSET); } - /** - * @return array - */ + /** @return array */ public function getTypes(): array { return $this->convertedTypes; @@ -131,10 +130,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..4909bc623 100644 --- a/doctrine/dbal/src/FetchMode.php +++ b/doctrine/dbal/src/FetchMode.php @@ -9,8 +9,10 @@ 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 4058bcb6f..7d7f210e2 100644 --- a/doctrine/dbal/src/Id/TableGenerator.php +++ b/doctrine/dbal/src/Id/TableGenerator.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception; use Doctrine\DBAL\LockMode; +use Doctrine\Deprecations\Deprecation; use Throwable; use function array_change_key_case; @@ -51,17 +52,18 @@ * * If no row is present for a given sequence a new one will be created with the * default values 'value' = 1 and 'increment_by' = 1 + * + * @deprecated */ class TableGenerator { - /** @var Connection */ - private $conn; + private Connection $conn; /** @var string */ private $generatorTableName; /** @var mixed[][] */ - private $sequences = []; + private array $sequences = []; /** * @param string $generatorTableName @@ -70,6 +72,12 @@ class TableGenerator */ public function __construct(Connection $conn, $generatorTableName = 'sequences') { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4681', + 'The TableGenerator class is is deprecated.', + ); + if ($conn->getDriver() instanceof Driver\PDO\SQLite\Driver) { throw new Exception('Cannot use TableGenerator with SQLite.'); } @@ -77,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; @@ -139,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; } @@ -151,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 3ec22f37c..9b4724ddf 100644 --- a/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php +++ b/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php @@ -9,17 +9,23 @@ use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\Deprecations\Deprecation; +/** @deprecated */ class TableGeneratorSchemaVisitor implements Visitor { /** @var string */ private $generatorTableName; - /** - * @param string $generatorTableName - */ + /** @param string $generatorTableName */ public function __construct($generatorTableName = 'sequences') { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4681', + 'The TableGeneratorSchemaVisitor class is is deprecated.', + ); + $this->generatorTableName = $generatorTableName; } diff --git a/doctrine/dbal/src/Logging/Connection.php b/doctrine/dbal/src/Logging/Connection.php new file mode 100644 index 000000000..5ab111ec2 --- /dev/null +++ b/doctrine/dbal/src/Logging/Connection.php @@ -0,0 +1,82 @@ +logger = $logger; + } + + public function __destruct() + { + $this->logger->info('Disconnecting'); + } + + public function prepare(string $sql): DriverStatement + { + return new Statement( + parent::prepare($sql), + $this->logger, + $sql, + ); + } + + public function query(string $sql): Result + { + $this->logger->debug('Executing query: {sql}', ['sql' => $sql]); + + return parent::query($sql); + } + + public function exec(string $sql): int + { + $this->logger->debug('Executing statement: {sql}', ['sql' => $sql]); + + return parent::exec($sql); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + $this->logger->debug('Beginning transaction'); + + return parent::beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() + { + $this->logger->debug('Committing transaction'); + + return parent::commit(); + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + $this->logger->debug('Rolling back transaction'); + + return parent::rollBack(); + } +} diff --git a/doctrine/dbal/src/Logging/DebugStack.php b/doctrine/dbal/src/Logging/DebugStack.php index 6a9fab5a9..e8539e584 100644 --- a/doctrine/dbal/src/Logging/DebugStack.php +++ b/doctrine/dbal/src/Logging/DebugStack.php @@ -2,10 +2,14 @@ namespace Doctrine\DBAL\Logging; +use Doctrine\Deprecations\Deprecation; + use function microtime; /** * Includes executed SQLs in a Debug Stack. + * + * @deprecated */ class DebugStack implements SQLLogger { @@ -29,6 +33,15 @@ class DebugStack implements SQLLogger /** @var int */ public $currentQuery = 0; + public function __construct() + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4967', + 'DebugStack is deprecated.', + ); + } + /** * {@inheritdoc} */ diff --git a/doctrine/dbal/src/Logging/Driver.php b/doctrine/dbal/src/Logging/Driver.php new file mode 100644 index 000000000..10cea8f03 --- /dev/null +++ b/doctrine/dbal/src/Logging/Driver.php @@ -0,0 +1,53 @@ +logger = $logger; + } + + /** + * {@inheritDoc} + */ + public function connect(array $params) + { + $this->logger->info('Connecting with parameters {params}', ['params' => $this->maskPassword($params)]); + + return new Connection( + parent::connect($params), + $this->logger, + ); + } + + /** + * @param array $params Connection parameters + * + * @return array + */ + private function maskPassword(array $params): array + { + if (isset($params['password'])) { + $params['password'] = ''; + } + + if (isset($params['url'])) { + $params['url'] = ''; + } + + return $params; + } +} diff --git a/doctrine/dbal/src/Logging/LoggerChain.php b/doctrine/dbal/src/Logging/LoggerChain.php index 9b44dc0e3..3c62a1d8e 100644 --- a/doctrine/dbal/src/Logging/LoggerChain.php +++ b/doctrine/dbal/src/Logging/LoggerChain.php @@ -2,19 +2,27 @@ namespace Doctrine\DBAL\Logging; +use Doctrine\Deprecations\Deprecation; + /** * Chains multiple SQLLogger. + * + * @deprecated */ 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', + ); + $this->loggers = $loggers; } diff --git a/doctrine/dbal/src/Logging/Middleware.php b/doctrine/dbal/src/Logging/Middleware.php new file mode 100644 index 000000000..da0c1b90f --- /dev/null +++ b/doctrine/dbal/src/Logging/Middleware.php @@ -0,0 +1,24 @@ +logger = $logger; + } + + public function wrap(DriverInterface $driver): DriverInterface + { + return new Driver($driver, $this->logger); + } +} diff --git a/doctrine/dbal/src/Logging/SQLLogger.php b/doctrine/dbal/src/Logging/SQLLogger.php index a0bdf1bf6..dab4a3a7d 100644 --- a/doctrine/dbal/src/Logging/SQLLogger.php +++ b/doctrine/dbal/src/Logging/SQLLogger.php @@ -6,6 +6,9 @@ /** * Interface for SQL loggers. + * + * @deprecated Use {@see \Doctrine\DBAL\Logging\Middleware} or implement + * {@see \Doctrine\DBAL\Driver\Middleware} instead. */ interface SQLLogger { diff --git a/doctrine/dbal/src/Logging/Statement.php b/doctrine/dbal/src/Logging/Statement.php new file mode 100644 index 000000000..95d603586 --- /dev/null +++ b/doctrine/dbal/src/Logging/Statement.php @@ -0,0 +1,100 @@ +|array */ + private array $params = []; + + /** @var array|array */ + private array $types = []; + + /** @internal This statement can be only instantiated by its connection. */ + public function __construct(StatementInterface $statement, LoggerInterface $logger, string $sql) + { + parent::__construct($statement); + + $this->logger = $logger; + $this->sql = $sql; + } + + /** + * {@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; + + return parent::bindParam($param, $variable, $type, ...array_slice(func_get_args(), 3)); + } + + /** + * {@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; + + return parent::bindValue($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null): ResultInterface + { + $this->logger->debug('Executing statement: {sql} (parameters: {params}, types: {types})', [ + 'sql' => $this->sql, + 'params' => $params ?? $this->params, + 'types' => $this->types, + ]); + + return parent::execute($params); + } +} diff --git a/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php b/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php new file mode 100644 index 000000000..cdbdbec78 --- /dev/null +++ b/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php @@ -0,0 +1,1424 @@ + 0) { + $query .= sprintf(' OFFSET %d', $offset); + } + } elseif ($offset > 0) { + // 2^64-1 is the maximum of unsigned BIGINT, the biggest limit possible + $query .= sprintf(' LIMIT 18446744073709551615 OFFSET %d', $offset); + } + + return $query; + } + + /** + * {@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 '`'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos === false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + return sprintf('CONCAT(%s)', implode(', ', func_get_args())); + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $function = $operator === '+' ? 'DATE_ADD' : 'DATE_SUB'; + + return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + public function getCurrentDatabaseExpression(): string + { + return 'DATABASE()'; + } + + /** + * {@inheritDoc} + */ + public function getLengthExpression($column) + { + return 'CHAR_LENGTH(' . $column . ')'; + } + + /** + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. + */ + public function getListDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + /** + * @deprecated + * + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + return 'SHOW INDEX FROM ' . $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 + * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table". + */ + public function getListTableIndexesSQL($table, $database = null) + { + if ($database !== null) { + return 'SELECT 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 WHERE TABLE_NAME = ' . $this->quoteStringLiteral($table) . + ' AND TABLE_SCHEMA = ' . $this->quoteStringLiteral($database) . + ' ORDER BY SEQ_IN_INDEX ASC'; + } + + return 'SHOW INDEX FROM ' . $table; + } + + /** + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. + */ + public function getListViewsSQL($database) + { + return 'SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = ' . $this->quoteStringLiteral($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 + * + * @return string + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + // The schema name is passed multiple times as a literal 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 + return 'SELECT k.CONSTRAINT_NAME, k.COLUMN_NAME, k.REFERENCED_TABLE_NAME, ' . + 'k.REFERENCED_COLUMN_NAME /*!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 */ ' . + 'WHERE k.TABLE_NAME = ' . $this->quoteStringLiteral($table) . ' ' . + 'AND k.TABLE_SCHEMA = ' . $this->getDatabaseNameSQL($database) . ' /*!50116 ' . + 'AND c.CONSTRAINT_SCHEMA = ' . $this->getDatabaseNameSQL($database) . ' */' . + 'ORDER BY k.ORDINAL_POSITION'; + } + + /** + * {@inheritDoc} + */ + 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} + */ + 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) . ')'; + } + + /** + * Gets the SQL snippet used to declare a CLOB column type. + * TINYTEXT : 2 ^ 8 - 1 = 255 + * TEXT : 2 ^ 16 - 1 = 65535 + * MEDIUMTEXT : 2 ^ 24 - 1 = 16777215 + * LONGTEXT : 2 ^ 32 - 1 = 4294967295 + * + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $column) + { + if (! empty($column['length']) && is_numeric($column['length'])) { + $length = $column['length']; + + if ($length <= static::LENGTH_LIMIT_TINYTEXT) { + return 'TINYTEXT'; + } + + if ($length <= static::LENGTH_LIMIT_TEXT) { + return 'TEXT'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) { + return 'MEDIUMTEXT'; + } + } + + return 'LONGTEXT'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $column) + { + if (isset($column['version']) && $column['version'] === true) { + return 'TIMESTAMP'; + } + + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $column) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $column) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $column) + { + return 'TINYINT(1)'; + } + + /** + * {@inheritDoc} + * + * @deprecated + * + * MySQL prefers "autoincrement" identity columns since sequences can only + * be emulated with a table. + */ + public function prefersIdentityColumns() + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/1519', + 'AbstractMySQLPlatform::prefersIdentityColumns() is deprecated.', + ); + + return true; + } + + /** + * {@inheritDoc} + * + * MySQL supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function supportsInlineColumnComments() + { + return true; + } + + /** + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SHOW FULL TABLES WHERE Table_type = 'BASE 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) + { + return 'SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ' . + 'COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, ' . + 'CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation ' . + 'FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ' . $this->getDatabaseNameSQL($database) . + ' AND TABLE_NAME = ' . $this->quoteStringLiteral($table) . + ' ORDER BY ORDINAL_POSITION ASC'; + } + + /** @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( + <<<'SQL' +SELECT 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 +WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = %s AND TABLE_NAME = %s +SQL + , + $this->getDatabaseNameSQL($database), + $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} + */ + protected function _getCreateTableSQL($name, array $columns, array $options = []) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $constraintName => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); + } + } + + // add all indexes + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $indexName => $definition) { + $queryFields .= ', ' . $this->getIndexDeclarationSQL($indexName, $definition); + } + } + + // attach all primary keys + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE '; + + if (! empty($options['temporary'])) { + $query .= 'TEMPORARY '; + } + + $query .= 'TABLE ' . $name . ' (' . $queryFields . ') '; + $query .= $this->buildTableOptions($options); + $query .= $this->buildPartitionOptions($options); + + $sql = [$query]; + + // Propagate foreign key constraints only for InnoDB. + 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.', + ); + } + } + + return $sql; + } + + /** + * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function getDefaultValueDeclarationSQL($column) + { + // Unset the default value if the given column definition does not allow default values. + if ($column['type'] instanceof TextType || $column['type'] instanceof BlobType) { + $column['default'] = null; + } + + return parent::getDefaultValueDeclarationSQL($column); + } + + /** + * Build SQL for table options + * + * @param mixed[] $options + */ + private function buildTableOptions(array $options): string + { + if (isset($options['table_options'])) { + return $options['table_options']; + } + + $tableOptions = []; + + // Charset + if (! isset($options['charset'])) { + $options['charset'] = 'utf8'; + } + + $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']; + } + + // Collation + if (! isset($options['collation'])) { + $options['collation'] = $options['charset'] . '_unicode_ci'; + } + + $tableOptions[] = $this->getColumnCollationDeclarationSQL($options['collation']); + + // Engine + if (! isset($options['engine'])) { + $options['engine'] = 'InnoDB'; + } + + $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); + + // Auto increment + if (isset($options['auto_increment'])) { + $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); + } + + // Comment + if (isset($options['comment'])) { + $tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($options['comment'])); + } + + // Row format + if (isset($options['row_format'])) { + $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); + } + + return implode(' ', $tableOptions); + } + + /** + * Build SQL for partition options. + * + * @param mixed[] $options + */ + private function buildPartitionOptions(array $options): string + { + return isset($options['partition_options']) + ? ' ' . $options['partition_options'] + : ''; + } + + private function engineSupportsForeignKeys(string $engine): bool + { + return strcasecmp(trim($engine), 'InnoDB') === 0; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = []; + $queryParts = []; + $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->getAddedColumns() as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnProperties = array_merge($column->toArray(), [ + 'comment' => $this->getColumnComment($column), + ]); + + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL( + $column->getQuotedName($this), + $columnProperties, + ); + } + + foreach ($diff->getDroppedColumns() as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP ' . $column->getQuotedName($this); + } + + foreach ($diff->getModifiedColumns() as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $newColumn = $columnDiff->getNewColumn(); + + $newColumnProperties = array_merge($newColumn->toArray(), [ + 'comment' => $this->getColumnComment($newColumn), + ]); + + $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); + + $queryParts[] = 'CHANGE ' . $oldColumn->getQuotedName($this) . ' ' + . $this->getColumnDeclarationSQL($newColumn->getQuotedName($this), $newColumnProperties); + } + + foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $columnProperties = array_merge($column->toArray(), [ + 'comment' => $this->getColumnComment($column), + ]); + + $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnProperties); + } + + $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($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 ($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($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->getOldTable() ?? $diff->getName($this))->getQuotedName($this) . ' ' + . implode(', ', $queryParts); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff), + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $sql = []; + + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); + + foreach ($diff->getModifiedIndexes() as $changedIndex) { + $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $changedIndex)); + } + + foreach ($diff->getDroppedIndexes() as $droppedIndex) { + $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $droppedIndex)); + + foreach ($diff->getAddedIndexes() as $addedIndex) { + if ($droppedIndex->getColumns() !== $addedIndex->getColumns()) { + continue; + } + + $indexClause = 'INDEX ' . $addedIndex->getName(); + + if ($addedIndex->isPrimary()) { + $indexClause = 'PRIMARY KEY'; + } elseif ($addedIndex->isUnique()) { + $indexClause = 'UNIQUE INDEX ' . $addedIndex->getName(); + } + + $query = 'ALTER TABLE ' . $tableNameSQL . ' DROP INDEX ' . $droppedIndex->getName() . ', '; + $query .= 'ADD ' . $indexClause; + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addedIndex) . ')'; + + $sql[] = $query; + + $diff->unsetAddedIndex($addedIndex); + $diff->unsetDroppedIndex($droppedIndex); + + break; + } + } + + $engine = 'INNODB'; + + $table = $diff->getOldTable(); + + if ($table !== null && $table->hasOption('engine')) { + $engine = strtoupper(trim($table->getOption('engine'))); + } + + // Suppress foreign key constraint propagation on non-supporting engines. + if ($engine !== 'INNODB') { + $diff->addedForeignKeys = []; + $diff->changedForeignKeys = []; + $diff->removedForeignKeys = []; + } + + $sql = array_merge( + $sql, + $this->getPreAlterTableAlterIndexForeignKeySQL($diff), + parent::getPreAlterTableIndexForeignKeySQL($diff), + $this->getPreAlterTableRenameIndexForeignKeySQL($diff), + ); + + return $sql; + } + + /** + * @return string[] + * + * @throws Exception + */ + private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index): array + { + if (! $index->isPrimary()) { + return []; + } + + $table = $diff->getOldTable(); + + if ($table === null) { + return []; + } + + $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 (! $table->hasColumn($columnName)) { + continue; + } + + $column = $table->getColumn($columnName); + + if ($column->getAutoincrement() !== true) { + continue; + } + + $column->setAutoincrement(false); + + $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 + $column->setAutoincrement(true); + } + + return $sql; + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return string[] + * + * @throws Exception + */ + private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff): array + { + $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); + } + + if (count($primaryKeyColumns) === 0) { + return []; + } + + $sql = []; + + $tableNameSQL = $table->getQuotedName($this); + + foreach ($diff->getModifiedIndexes() as $changedIndex) { + // Changed primary key + if (! $changedIndex->isPrimary()) { + continue; + } + + foreach ($primaryKeyColumns as $column) { + // Check if an autoincrement column was dropped from the primary key. + if (! $column->getAutoincrement() || in_array($column->getName(), $changedIndex->getColumns(), true)) { + continue; + } + + // The autoincrement attribute needs to be removed from the dropped column + // before we can drop and recreate the primary key. + $column->setAutoincrement(false); + + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + // Restore the autoincrement attribute as it might be needed later on + // by other parts of the table alteration. + $column->setAutoincrement(true); + } + } + + return $sql; + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return string[] + */ + protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + $sql = []; + + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (in_array($foreignKey, $diff->getModifiedForeignKeys(), true)) { + continue; + } + + $sql[] = $this->getDropForeignKeySQL($foreignKey->getQuotedName($this), $tableNameSQL); + } + + return $sql; + } + + /** + * Returns the remaining foreign key constraints that require one of the renamed indexes. + * + * "Remaining" here refers to the diff between the foreign keys currently defined in the associated + * table and the foreign keys to be removed. + * + * @param TableDiff $diff The table diff to evaluate. + * + * @return ForeignKeyConstraint[] + */ + private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff): array + { + if (count($diff->getRenamedIndexes()) === 0) { + return []; + } + + $table = $diff->getOldTable(); + + if ($table === null) { + return []; + } + + $foreignKeys = []; + /** @var ForeignKeyConstraint[] $remainingForeignKeys */ + $remainingForeignKeys = array_diff_key( + $table->getForeignKeys(), + $diff->getDroppedForeignKeys(), + ); + + foreach ($remainingForeignKeys as $foreignKey) { + foreach ($diff->getRenamedIndexes() as $index) { + if ($foreignKey->intersectsIndexColumns($index)) { + $foreignKeys[] = $foreignKey; + + break; + } + } + } + + return $foreignKeys; + } + + /** + * {@inheritdoc} + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + return array_merge( + parent::getPostAlterTableIndexForeignKeySQL($diff), + $this->getPostAlterTableRenameIndexForeignKeySQL($diff), + ); + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return string[] + */ + protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + $sql = []; + $newName = $diff->getNewName(); + + if ($newName !== false) { + $tableNameSQL = $newName->getQuotedName($this); + } else { + $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); + } + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (in_array($foreignKey, $diff->getModifiedForeignKeys(), true)) { + continue; + } + + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } elseif ($index->hasFlag('fulltext')) { + $type .= 'FULLTEXT '; + } elseif ($index->hasFlag('spatial')) { + $type .= 'SPATIAL '; + } + + return $type; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $column) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $column) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $column) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritdoc} + */ + public function getFloatDeclarationSQL(array $column) + { + return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($column); + } + + /** + * {@inheritdoc} + */ + public function getDecimalTypeDeclarationSQL(array $column) + { + return parent::getDecimalTypeDeclarationSQL($column) . $this->getUnsignedDeclaration($column); + } + + /** + * Get unsigned declaration for a column. + * + * @param mixed[] $columnDef + */ + private function getUnsignedDeclaration(array $columnDef): string + { + return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : ''; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $column) + { + $autoinc = ''; + if (! empty($column['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } + + return $this->getUnsignedDeclaration($column) . $autoinc; + } + + /** + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function getColumnCharsetDeclarationSQL($charset) + { + return 'CHARACTER SET ' . $charset; + } + + /** + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + return $query; + } + + /** + * {@inheritDoc} + */ + 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 . '.', + ); + } + + 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 . '.', + ); + } + + if ($index instanceof Index && $index->isPrimary()) { + // MySQL primary keys are always named "PRIMARY", + // so we cannot use them in statements because of them being keyword. + return $this->getDropPrimaryKeySQL($table); + } + + return 'DROP INDEX ' . $indexName . ' ON ' . $table; + } + + /** + * @param string $table + * + * @return string + */ + protected function getDropPrimaryKeySQL($table) + { + return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } + + /** + * The `ALTER TABLE ... DROP CONSTRAINT` syntax is only available as of MySQL 8.0.19. + * + * @link https://dev.mysql.com/doc/refman/8.0/en/alter-table.html + */ + public function getDropUniqueConstraintSQL(string $name, string $tableName): string + { + return $this->getDropIndexSQL($name, $tableName); + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4749', + 'AbstractMySQLPlatform::getName() is deprecated. Identify platforms by their class.', + ); + + return 'mysql'; + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'LOCK IN SHARE MODE'; + } + + /** + * {@inheritDoc} + */ + 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', + ]; + } + + /** + * {@inheritDoc} + * + * @deprecated + */ + public function getVarcharMaxLength() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'AbstractMySQLPlatform::getVarcharMaxLength() is deprecated.', + ); + + return 65535; + } + + /** + * {@inheritdoc} + * + * @deprecated + */ + public function getBinaryMaxLength() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'AbstractMySQLPlatform::getBinaryMaxLength() is deprecated.', + ); + + return 65535; + } + + /** + * {@inheritDoc} + * + * @deprecated Implement {@see createReservedKeywordsList()} instead. + */ + protected function getReservedKeywordsClass() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'AbstractMySQLPlatform::getReservedKeywordsClass() is deprecated,' + . ' use AbstractMySQLPlatform::createReservedKeywordsList() instead.', + ); + + return Keywords\MySQLKeywords::class; + } + + /** + * {@inheritDoc} + * + * MySQL commits a transaction implicitly when DROP TABLE is executed, however not + * if DROP TEMPORARY TABLE is executed. + */ + 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 . '.', + ); + } + + return 'DROP TEMPORARY TABLE ' . $table; + } + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + * TINYBLOB : 2 ^ 8 - 1 = 255 + * BLOB : 2 ^ 16 - 1 = 65535 + * MEDIUMBLOB : 2 ^ 24 - 1 = 16777215 + * LONGBLOB : 2 ^ 32 - 1 = 4294967295 + * + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $column) + { + if (! empty($column['length']) && is_numeric($column['length'])) { + $length = $column['length']; + + if ($length <= static::LENGTH_LIMIT_TINYBLOB) { + return 'TINYBLOB'; + } + + if ($length <= static::LENGTH_LIMIT_BLOB) { + return 'BLOB'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) { + return 'MEDIUMBLOB'; + } + } + + return 'LONGBLOB'; + } + + /** + * {@inheritdoc} + */ + public function quoteStringLiteral($str) + { + $str = str_replace('\\', '\\\\', $str); // MySQL requires backslashes to be escaped + + return parent::quoteStringLiteral($str); + } + + /** + * {@inheritdoc} + */ + public function getDefaultTransactionIsolationLevel() + { + return TransactionIsolationLevel::REPEATABLE_READ; + } + + public function supportsColumnLengthIndexes(): bool + { + return true; + } + + private function getDatabaseNameSQL(?string $databaseName): string + { + if ($databaseName !== null) { + return $this->quoteStringLiteral($databaseName); + } + + 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 f22fab5bc..28a0eb580 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; @@ -80,11 +83,17 @@ abstract class AbstractPlatform * Contains a list of all columns that should generate parseable column comments for type-detection * in reverse engineering scenarios. * + * @deprecated This property is deprecated and will be removed in Doctrine DBAL 4.0. + * * @var string[]|null */ protected $doctrineTypeComments; - /** @var EventManager|null */ + /** + * @deprecated + * + * @var EventManager|null + */ protected $_eventManager; /** @@ -94,27 +103,41 @@ abstract class AbstractPlatform */ protected $_keywords; - public function __construct() - { - } - /** * 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; } @@ -173,10 +196,8 @@ abstract protected function initializeDoctrineTypeMappings(); /** * Initializes Doctrine Type Mappings with the platform defaults * and with all additional type mappings. - * - * @return void */ - private function initializeAllDoctrineTypeMappings() + private function initializeAllDoctrineTypeMappings(): void { $this->initializeDoctrineTypeMappings(); @@ -195,20 +216,25 @@ private function initializeAllDoctrineTypeMappings() */ 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; @@ -221,7 +247,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); } /** @@ -233,8 +271,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; @@ -249,14 +290,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); } /** @@ -274,7 +315,7 @@ public function getGuidTypeDeclarationSQL(array $column) $column['length'] = 36; $column['fixed'] = true; - return $this->getVarcharTypeDeclarationSQL($column); + return $this->getStringTypeDeclarationSQL($column); } /** @@ -300,7 +341,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.'); } @@ -315,7 +356,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.'); } @@ -341,6 +382,8 @@ abstract public function getBlobTypeDeclarationSQL(array $column); /** * Gets the name of the platform. * + * @deprecated Identify platforms by their class. + * * @return string */ abstract public function getName(); @@ -396,7 +439,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.', ); } @@ -424,10 +467,19 @@ public function hasDoctrineTypeMappingFor($dbType) /** * Initializes the Doctrine Type comments instance variable for in_array() checks. * + * @deprecated This API will be removed in Doctrine DBAL 4.0. + * * @return void */ protected function initializeCommentedDoctrineTypes() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5058', + '%s is deprecated and will be removed in Doctrine DBAL 4.0.', + __METHOD__, + ); + $this->doctrineTypeComments = []; foreach (Type::getTypesMap() as $typeName => $className) { @@ -444,17 +496,24 @@ protected function initializeCommentedDoctrineTypes() /** * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type? * + * @deprecated Use {@link Type::requiresSQLCommentHint()} instead. + * * @return bool */ public function isCommentedDoctrineType(Type $doctrineType) { + Deprecation::triggerIfCalledFromOutside( + '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__, + ); + if ($this->doctrineTypeComments === null) { $this->initializeCommentedDoctrineTypes(); } - assert(is_array($this->doctrineTypeComments)); - - return in_array($doctrineType->getName(), $this->doctrineTypeComments, true); + return $doctrineType->requiresSQLCommentHint($this); } /** @@ -466,6 +525,13 @@ public function isCommentedDoctrineType(Type $doctrineType) */ public function markDoctrineTypeCommented($doctrineType) { + Deprecation::triggerIfCalledFromOutside( + '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__, + ); + if ($this->doctrineTypeComments === null) { $this->initializeCommentedDoctrineTypes(); } @@ -478,23 +544,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 ($this->isCommentedDoctrineType($column->getType())) { + if ($column->getType()->requiresSQLCommentHint($this)) { $comment .= $this->getDoctrineTypeComment($column->getType()); } @@ -504,88 +588,161 @@ 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 '"'; } /** * Gets the string portion that starts an SQL comment. * + * @deprecated + * * @return string */ public function getSqlCommentStartString() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getSqlCommentStartString() is deprecated.', + ); + return '--'; } /** * Gets the string portion that ends an SQL comment. * + * @deprecated + * * @return string */ public function getSqlCommentEndString() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getSqlCommentEndString() is deprecated.', + ); + return "\n"; } /** * Gets the maximum length of a char column. + * + * @deprecated */ public function getCharMaxLength(): int { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'AbstractPlatform::getCharMaxLength() is deprecated.', + ); + return $this->getVarcharMaxLength(); } /** * Gets the maximum length of a varchar column. * + * @deprecated + * * @return int */ public function getVarcharMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'AbstractPlatform::getVarcharMaxLength() is deprecated.', + ); + return 4000; } /** * Gets the default length of a varchar column. * + * @deprecated + * * @return int */ 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.', + ); + return 255; } /** * Gets the maximum length of a binary column. * + * @deprecated + * * @return int */ public function getBinaryMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'AbstractPlatform::getBinaryMaxLength() is deprecated.', + ); + return 4000; } /** * Gets the default length of a binary column. * + * @deprecated + * * @return int */ 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 255; } /** * Gets all SQL wildcard characters of the platform. * + * @deprecated Use {@see AbstractPlatform::getLikeWildcardCharacters()} instead. + * * @return string[] */ public function getWildcards() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getWildcards() is deprecated.' + . ' Use AbstractPlatform::getLikeWildcardCharacters() instead.', + ); + return ['%', '_']; } @@ -604,12 +761,20 @@ public function getRegexpExpression() /** * Returns the SQL snippet to get the average value of a column. * + * @deprecated Use AVG() in SQL instead. + * * @param string $column The column to use. * * @return string Generated SQL including an AVG aggregate function. */ public function getAvgExpression($column) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getAvgExpression() is deprecated. Use AVG() in SQL instead.', + ); + return 'AVG(' . $column . ')'; } @@ -618,48 +783,80 @@ public function getAvgExpression($column) * * If a '*' is used instead of a column the number of selected rows is returned. * + * @deprecated Use COUNT() in SQL instead. + * * @param string|int $column The column to use. * * @return string Generated SQL including a COUNT aggregate function. */ public function getCountExpression($column) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getCountExpression() is deprecated. Use COUNT() in SQL instead.', + ); + return 'COUNT(' . $column . ')'; } /** * Returns the SQL snippet to get the highest value of a column. * + * @deprecated Use MAX() in SQL instead. + * * @param string $column The column to use. * * @return string Generated SQL including a MAX aggregate function. */ public function getMaxExpression($column) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getMaxExpression() is deprecated. Use MAX() in SQL instead.', + ); + return 'MAX(' . $column . ')'; } /** * Returns the SQL snippet to get the lowest value of a column. * + * @deprecated Use MIN() in SQL instead. + * * @param string $column The column to use. * * @return string Generated SQL including a MIN aggregate function. */ public function getMinExpression($column) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getMinExpression() is deprecated. Use MIN() in SQL instead.', + ); + return 'MIN(' . $column . ')'; } /** * Returns the SQL snippet to get the total sum of a column. * + * @deprecated Use SUM() in SQL instead. + * * @param string $column The column to use. * * @return string Generated SQL including a SUM aggregate function. */ public function getSumExpression($column) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getSumExpression() is deprecated. Use SUM() in SQL instead.', + ); + return 'SUM(' . $column . ')'; } @@ -670,17 +867,25 @@ public function getSumExpression($column) * * Note: Not SQL92, but common functionality. * + * @deprecated + * * @param string $column * * @return string */ public function getMd5Expression($column) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getMd5Expression() is deprecated.', + ); + return 'MD5(' . $column . ')'; } /** - * Returns the SQL snippet to get the length of a text column. + * Returns the SQL snippet to get the length of a text column in characters. * * @param string $column * @@ -694,25 +899,41 @@ public function getLengthExpression($column) /** * Returns the SQL snippet to get the squared value of a column. * + * @deprecated Use SQRT() in SQL instead. + * * @param string $column The column to use. * * @return string Generated SQL including an SQRT aggregate function. */ public function getSqrtExpression($column) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getSqrtExpression() is deprecated. Use SQRT() in SQL instead.', + ); + return 'SQRT(' . $column . ')'; } /** * Returns the SQL snippet to round a numeric column to the number of decimals specified. * - * @param string $column - * @param int $decimals + * @deprecated Use ROUND() in SQL instead. + * + * @param string $column + * @param string|int $decimals * * @return string */ public function getRoundExpression($column, $decimals = 0) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getRoundExpression() is deprecated. Use ROUND() in SQL instead.', + ); + return 'ROUND(' . $column . ', ' . $decimals . ')'; } @@ -770,24 +991,40 @@ public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = f /** * Returns the SQL snippet to trim trailing space characters from the expression. * + * @deprecated Use RTRIM() in SQL instead. + * * @param string $str Literal string or column name. * * @return string */ public function getRtrimExpression($str) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getRtrimExpression() is deprecated. Use RTRIM() in SQL instead.', + ); + return 'RTRIM(' . $str . ')'; } /** * Returns the SQL snippet to trim leading space characters from the expression. * + * @deprecated Use LTRIM() in SQL instead. + * * @param string $str Literal string or column name. * * @return string */ public function getLtrimExpression($str) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getLtrimExpression() is deprecated. Use LTRIM() in SQL instead.', + ); + return 'LTRIM(' . $str . ')'; } @@ -795,12 +1032,20 @@ public function getLtrimExpression($str) * Returns the SQL snippet to change all characters from the expression to uppercase, * according to the current character set mapping. * + * @deprecated Use UPPER() in SQL instead. + * * @param string $str Literal string or column name. * * @return string */ public function getUpperExpression($str) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getUpperExpression() is deprecated. Use UPPER() in SQL instead.', + ); + return 'UPPER(' . $str . ')'; } @@ -808,21 +1053,29 @@ public function getUpperExpression($str) * Returns the SQL snippet to change all characters from the expression to lowercase, * according to the current character set mapping. * + * @deprecated Use LOWER() in SQL instead. + * * @param string $str Literal string or column name. * * @return string */ public function getLowerExpression($str) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getLowerExpression() is deprecated. Use LOWER() in SQL instead.', + ); + return 'LOWER(' . $str . ')'; } /** * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str. * - * @param string $str Literal string. - * @param string $substr Literal string to find. - * @param int|false $startPos Position to start at, beginning of string by default. + * @param string $str Literal string. + * @param string $substr Literal string to find. + * @param string|int|false $startPos Position to start at, beginning of string by default. * * @return string * @@ -836,10 +1089,18 @@ public function getLocateExpression($str, $substr, $startPos = false) /** * Returns the SQL snippet to get the current system date. * + * @deprecated Generate dates within the application. + * * @return string */ public function getNowExpression() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4753', + 'AbstractPlatform::getNowExpression() is deprecated. Generate dates within the application.', + ); + return 'NOW()'; } @@ -850,9 +1111,9 @@ public function getNowExpression() * * SQLite only supports the 2 parameter variant of this function. * - * @param string $string An sql string literal or column name/alias. - * @param int $start Where to start the substring portion. - * @param int|null $length The substring portion length. + * @param string $string An sql string literal or column name/alias. + * @param string|int $start Where to start the substring portion. + * @param string|int|null $length The substring portion length. * * @return string */ @@ -888,36 +1149,60 @@ public function getConcatExpression() * ->where($e->eq('id', $e->not('null')); * * + * @deprecated Use NOT() in SQL instead. + * * @param string $expression * * @return string The logical expression. */ public function getNotExpression($expression) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getNotExpression() is deprecated. Use NOT() in SQL instead.', + ); + return 'NOT(' . $expression . ')'; } /** * Returns the SQL that checks if an expression is null. * + * @deprecated Use IS NULL in SQL instead. + * * @param string $expression The expression that should be compared to null. * * @return string The logical expression. */ public function getIsNullExpression($expression) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getIsNullExpression() is deprecated. Use IS NULL in SQL instead.', + ); + return $expression . ' IS NULL'; } /** * Returns the SQL that checks if an expression is not null. * + * @deprecated Use IS NOT NULL in SQL instead. + * * @param string $expression The expression that should be compared to null. * * @return string The logical expression. */ public function getIsNotNullExpression($expression) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getIsNotNullExpression() is deprecated. Use IS NOT NULL in SQL instead.', + ); + return $expression . ' IS NOT NULL'; } @@ -930,6 +1215,8 @@ public function getIsNotNullExpression($expression) * http://www.w3schools.com/sql/sql_between.asp. If you want complete database * independence you should avoid using between(). * + * @deprecated Use BETWEEN in SQL instead. + * * @param string $expression The value to compare to. * @param string $value1 The lower value to compare with. * @param string $value2 The higher value to compare with. @@ -938,52 +1225,90 @@ public function getIsNotNullExpression($expression) */ public function getBetweenExpression($expression, $value1, $value2) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getBetweenExpression() is deprecated. Use BETWEEN in SQL instead.', + ); + return $expression . ' BETWEEN ' . $value1 . ' AND ' . $value2; } /** * Returns the SQL to get the arccosine of a value. * + * @deprecated Use ACOS() in SQL instead. + * * @param string $value * * @return string */ public function getAcosExpression($value) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getAcosExpression() is deprecated. Use ACOS() in SQL instead.', + ); + return 'ACOS(' . $value . ')'; } /** * Returns the SQL to get the sine of a value. * + * @deprecated Use SIN() in SQL instead. + * * @param string $value * * @return string */ public function getSinExpression($value) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getSinExpression() is deprecated. Use SIN() in SQL instead.', + ); + return 'SIN(' . $value . ')'; } /** * Returns the SQL to get the PI value. * + * @deprecated Use PI() in SQL instead. + * * @return string */ public function getPiExpression() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getPiExpression() is deprecated. Use PI() in SQL instead.', + ); + return 'PI()'; } /** * Returns the SQL to get the cosine of a value. * + * @deprecated Use COS() in SQL instead. + * * @param string $value * * @return string */ public function getCosExpression($value) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getCosExpression() is deprecated. Use COS() in SQL instead.', + ); + return 'COS(' . $value . ')'; } @@ -1007,8 +1332,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 * @@ -1016,14 +1341,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 * @@ -1031,14 +1364,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 * @@ -1046,14 +1387,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 * @@ -1061,14 +1410,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 * @@ -1076,14 +1433,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 * @@ -1091,14 +1456,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 * @@ -1106,14 +1479,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 * @@ -1121,14 +1502,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 * @@ -1136,14 +1525,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 * @@ -1151,14 +1548,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 * @@ -1166,14 +1571,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 * @@ -1181,14 +1594,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 * @@ -1196,14 +1617,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 * @@ -1211,14 +1640,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 * @@ -1226,14 +1663,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 * @@ -1241,17 +1686,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 DATE_INTERVAL_UNIT_* constants. * * @return string * @@ -1309,6 +1763,7 @@ public function getForUpdateSQL() * * @param string $fromClause The FROM clause to append the hint for the given lock mode to * @param int $lockMode One of the Doctrine\DBAL\LockMode::* constants + * @psalm-param LockMode::* $lockMode */ public function appendLockHint(string $fromClause, int $lockMode): string { @@ -1349,26 +1804,6 @@ public function getWriteLockSQL() return $this->getForUpdateSQL(); } - /** - * Returns the SQL snippet to drop an existing database. - * - * @param string $name The name of the database that should be dropped. - * - * @return string - */ - public function getDropDatabaseSQL($name) - { - return 'DROP DATABASE ' . $name; - } - - /** - * Returns the SQL snippet to drop a schema. - */ - public function getDropSchemaSQL(string $schemaName): string - { - return 'DROP SCHEMA ' . $schemaName; - } - /** * Returns the SQL snippet to drop an existing table. * @@ -1383,16 +1818,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); @@ -1419,14 +1868,25 @@ 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); } /** * Returns the SQL to drop an index from a table. * - * @param Index|string $index - * @param Table|string $table + * @param Index|string $index + * @param Table|string|null $table * * @return string * @@ -1435,10 +1895,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 . '.', ); } @@ -1448,6 +1915,8 @@ public function getDropIndexSQL($index, $table = null) /** * Returns the SQL to drop a constraint. * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + * * @param Constraint|string $constraint * @param Table|string $table * @@ -1455,11 +1924,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); } @@ -1479,11 +1962,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); } @@ -1493,13 +1991,22 @@ public function getDropForeignKeySQL($foreignKey, $table) return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey; } + /** + * Returns the SQL to drop a unique constraint. + */ + public function getDropUniqueConstraintSQL(string $name, string $tableName): string + { + return $this->getDropConstraintSQL($name, $tableName); + } + /** * Returns the SQL statement(s) to create a table with the specified name, columns and constraints * 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 @@ -1508,10 +2015,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()); } @@ -1522,7 +2073,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; @@ -1539,7 +2090,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) { @@ -1555,6 +2106,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); @@ -1566,26 +2124,23 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE } } - $name = $column->getQuotedName($this); - - $columnData = 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'] = 255; - } + $columnData = $this->columnToArray($column); if (in_array($column->getName(), $options['primary'], true)) { $columnData['primary'] = true; } - $columns[$name] = $columnData; + $columns[$columnData['name']] = $columnData; } 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); @@ -1616,6 +2171,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); @@ -1623,7 +2230,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), ); } @@ -1643,7 +2250,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), ); } @@ -1706,7 +2313,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); } } @@ -1714,14 +2321,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. * @@ -1746,9 +2361,41 @@ public function getAlterSequenceSQL(Sequence $sequence) throw Exception::notSupported(__METHOD__); } + /** + * Returns the SQL snippet to drop an existing sequence. + * + * @param Sequence|string $sequence + * + * @return string + * + * @throws Exception If not supported on this platform. + */ + public function getDropSequenceSQL($sequence) + { + if (! $this->supportsSequences()) { + throw Exception::notSupported(__METHOD__); + } + + 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); + } + + return 'DROP SEQUENCE ' . $sequence; + } + /** * Returns the SQL to create a constraint on a table on this platform. * + * @deprecated Use {@see getCreateIndexSQL()}, {@see getCreateForeignKeySQL()} + * or {@see getCreateUniqueConstraintSQL()} instead. + * * @param Table|string $table * * @return string @@ -1758,6 +2405,13 @@ public function getAlterSequenceSQL(Sequence $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); } @@ -1773,7 +2427,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) { @@ -1802,6 +2456,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); } @@ -1809,7 +2470,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()) { @@ -1856,6 +2521,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); } @@ -1873,7 +2545,33 @@ public function getCreatePrimaryKeySQL(Index $index, $table) */ public function getCreateSchemaSQL($schemaName) { - throw Exception::notSupported(__METHOD__); + if (! $this->supportsSchemas()) { + throw Exception::notSupported(__METHOD__); + } + + return 'CREATE SCHEMA ' . $schemaName; + } + + /** + * Returns the SQL to create a unique constraint on a table on this platform. + */ + public function getCreateUniqueConstraintSQL(UniqueConstraint $constraint, string $tableName): string + { + return $this->getCreateConstraintSQL($constraint, $tableName); + } + + /** + * Returns the SQL snippet to drop a schema. + * + * @throws Exception If not supported on this platform. + */ + public function getDropSchemaSQL(string $schemaName): string + { + if (! $this->supportsSchemas()) { + throw Exception::notSupported(__METHOD__); + } + + return 'DROP SCHEMA ' . $schemaName; } /** @@ -1925,6 +2623,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); } @@ -1936,7 +2641,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. */ @@ -1945,6 +2650,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 * @@ -1960,6 +2673,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); @@ -1983,6 +2703,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); @@ -2006,6 +2733,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); @@ -2030,6 +2764,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); @@ -2053,6 +2794,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); @@ -2061,72 +2809,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), ); } @@ -2240,10 +2988,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; @@ -2265,12 +3032,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']; + } + + 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.', + ); + } - return 'NUMERIC(' . $column['precision'] . ', ' . $column['scale'] . ')'; + $scale = 0; + } else { + $scale = $column['scale']; + } + + return 'NUMERIC(' . $precision . ', ' . $scale . ')'; } /** @@ -2312,7 +3104,7 @@ public function getDefaultValueDeclarationSQL($column) } if ($type instanceof Types\BooleanType) { - return " DEFAULT '" . $this->convertBooleans($default) . "'"; + return ' DEFAULT ' . $this->convertBooleans($default); } return ' DEFAULT ' . $this->quoteStringLiteral($default); @@ -2402,21 +3194,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)); } @@ -2424,10 +3234,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) { @@ -2452,11 +3271,19 @@ public function getColumnsFieldDeclarationListSQL(array $columns): string * SQL error for any database that does not support temporary tables, or that * requires a different SQL command from "CREATE TEMPORARY TABLE". * + * @deprecated + * * @return string The string required to be placed between "CREATE" and "TABLE" * to generate a temporary table, if possible. */ public function getTemporaryTableSQL() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getTemporaryTableSQL() is deprecated.', + ); + return 'TEMPORARY'; } @@ -2573,11 +3400,19 @@ public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint * of a column declaration to be used in statements like CREATE TABLE. * + * @deprecated Use UNIQUE in SQL instead. + * * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint * of a column declaration. */ public function getUniqueFieldDeclarationSQL() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getUniqueFieldDeclarationSQL() is deprecated. Use UNIQUE in SQL instead.', + ); + return 'UNIQUE'; } @@ -2606,17 +3441,25 @@ public function getColumnCharsetDeclarationSQL($charset) */ public function getColumnCollationDeclarationSQL($collation) { - return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : ''; + return $this->supportsColumnCollation() ? 'COLLATE ' . $this->quoteSingleIdentifier($collation) : ''; } /** * Whether the platform prefers identity columns (eg. autoincrement) for ID generation. * Subclasses should override this method to return TRUE if they prefer identity columns. * + * @deprecated + * * @return bool */ public function prefersIdentityColumns() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/1519', + 'AbstractPlatform::prefersIdentityColumns() is deprecated.', + ); + return false; } @@ -2740,6 +3583,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. @@ -2752,7 +3597,7 @@ public function getListDatabasesSQL() /** * Returns the SQL statement for retrieving the namespaces defined in the database. * - * @deprecated Use {@link AbstractSchemaManager::listSchemaNames()} instead. + * @deprecated Use {@see AbstractSchemaManager::listSchemaNames()} instead. * * @return string * @@ -2764,13 +3609,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 @@ -2783,6 +3630,8 @@ public function getListSequencesSQL($database) } /** + * @deprecated + * * @param string $table * * @return string @@ -2795,6 +3644,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 * @@ -2808,6 +3659,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. @@ -2818,18 +3671,28 @@ public function getListTablesSQL() } /** + * @deprecated + * * @return string * * @throws Exception If not supported on this platform. */ public function getListUsersSQL() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::getListUsersSQL() is deprecated.', + ); + throw Exception::notSupported(__METHOD__); } /** * 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 @@ -2842,6 +3705,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 @@ -2864,6 +3729,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 @@ -2880,64 +3747,66 @@ public function getListTableForeignKeysSQL($table) * @param string $sql * * @return string - * - * @throws Exception If not supported on this platform. */ public function getCreateViewSQL($name, $sql) { - throw Exception::notSupported(__METHOD__); + return 'CREATE VIEW ' . $name . ' AS ' . $sql; } /** * @param string $name * * @return string - * - * @throws Exception If not supported on this platform. */ public function getDropViewSQL($name) { - throw Exception::notSupported(__METHOD__); + return 'DROP VIEW ' . $name; } /** - * Returns the SQL snippet to drop an existing sequence. - * - * @param Sequence|string $sequence + * @param string $sequence * * @return string * * @throws Exception If not supported on this platform. */ - public function getDropSequenceSQL($sequence) + public function getSequenceNextValSQL($sequence) { throw Exception::notSupported(__METHOD__); } /** - * @param string $sequence + * Returns the SQL to create a new database. + * + * @param string $name The name of the database that should be created. * * @return string * * @throws Exception If not supported on this platform. */ - public function getSequenceNextValSQL($sequence) + public function getCreateDatabaseSQL($name) { - throw Exception::notSupported(__METHOD__); + if (! $this->supportsCreateDropDatabase()) { + throw Exception::notSupported(__METHOD__); + } + + return 'CREATE DATABASE ' . $name; } /** - * Returns the SQL to create a new database. + * Returns the SQL snippet to drop an existing database. * - * @param string $name The name of the database that should be created. + * @param string $name The name of the database that should be dropped. * * @return string - * - * @throws Exception If not supported on this platform. */ - public function getCreateDatabaseSQL($name) + public function getDropDatabaseSQL($name) { - throw Exception::notSupported(__METHOD__); + if (! $this->supportsCreateDropDatabase()) { + throw Exception::notSupported(__METHOD__); + } + + return 'DROP DATABASE ' . $name; } /** @@ -3026,7 +3895,7 @@ public function getFloatDeclarationSQL(array $column) * * @see TransactionIsolationLevel * - * @return int The default isolation level. + * @return TransactionIsolationLevel::* The default isolation level. */ public function getDefaultTransactionIsolationLevel() { @@ -3065,16 +3934,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. @@ -3092,10 +3972,18 @@ public function getIdentitySequenceName($tableName, $columnName) /** * Whether the platform supports indexes. * + * @deprecated + * * @return bool */ public function supportsIndexes() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsIndexes() is deprecated.', + ); + return true; } @@ -3120,20 +4008,36 @@ public function supportsColumnLengthIndexes(): bool /** * Whether the platform supports altering tables. * + * @deprecated All platforms must implement altering tables. + * * @return bool */ public function supportsAlterTable() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsAlterTable() is deprecated. All platforms must implement altering tables.', + ); + return true; } /** * Whether the platform supports transactions. * + * @deprecated + * * @return bool */ public function supportsTransactions() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsTransactions() is deprecated.', + ); + return true; } @@ -3160,20 +4064,36 @@ public function supportsReleaseSavepoints() /** * Whether the platform supports primary key constraints. * + * @deprecated + * * @return bool */ public function supportsPrimaryConstraints() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsPrimaryConstraints() is deprecated.', + ); + return true; } /** * 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; } @@ -3190,19 +4110,29 @@ public function supportsSchemas() /** * Whether this platform can emulate schemas. * + * @deprecated + * * Platforms that either support or emulate schemas don't automatically - * filter a schema for the namespaced elements in {@link AbstractManager::createSchema()}. + * filter a schema for the namespaced elements in {@see AbstractManager::introspectSchema()}. * * @return bool */ public function canEmulateSchemas() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4805', + 'AbstractPlatform::canEmulateSchemas() is deprecated.', + ); + return false; } /** * Returns the default schema name. * + * @deprecated + * * @return string * * @throws Exception If not supported on this platform. @@ -3217,20 +4147,37 @@ 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::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + return true; } /** * Whether the platform supports getting the affected rows of a recent update/delete type query. * + * @deprecated + * * @return bool */ public function supportsGettingAffectedRows() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsGettingAffectedRows() is deprecated.', + ); + return true; } @@ -3257,30 +4204,56 @@ 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; } /** * Whether this platform supports views. * + * @deprecated All platforms must implement support for views. + * * @return bool */ public function supportsViews() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsViews() is deprecated. All platforms must implement support for views.', + ); + return true; } @@ -3345,23 +4318,21 @@ public function getTimeFormatString() * @param int|null $limit * @param int $offset * - * @return string - * * @throws Exception */ - final public function modifyLimitQuery($query, $limit, $offset = 0) + 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(), )); } @@ -3384,11 +4355,11 @@ final public function modifyLimitQuery($query, $limit, $offset = 0) protected function doModifyLimitQuery($query, $limit, $offset) { if ($limit !== null) { - $query .= ' LIMIT ' . $limit; + $query .= sprintf(' LIMIT %d', $limit); } if ($offset > 0) { - $query .= ' OFFSET ' . $offset; + $query .= sprintf(' OFFSET %d', $offset); } return $query; @@ -3397,10 +4368,19 @@ protected function doModifyLimitQuery($query, $limit, $offset) /** * Whether the database platform support offsets in modify limit clauses. * + * @deprecated All platforms must implement support for offsets in modify limit clauses. + * * @return bool */ public function supportsLimitOffset() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4724', + 'AbstractPlatform::supportsViews() is deprecated.' + . ' All platforms must implement support for offsets in modify limit clauses.', + ); + return true; } @@ -3496,19 +4476,12 @@ public function rollbackSavePoint($savepoint) /** * Returns the keyword list instance of this platform. * - * @return KeywordList - * * @throws Exception If no keyword list is specified. */ - final public function getReservedKeywordsList() + 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(); } /** @@ -3532,7 +4505,7 @@ protected function createReservedKeywordsList(): KeywordList /** * Returns the class name of the reserved keywords list. * - * @deprecated Implement {@link createReservedKeywordsList()} instead. + * @deprecated Implement {@see createReservedKeywordsList()} instead. * * @return string * @psalm-return class-string @@ -3545,7 +4518,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__); @@ -3571,10 +4544,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 "'"; } @@ -3591,13 +4573,26 @@ final public function escapeStringForLike(string $inputString, string $escapeCha return preg_replace( '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u', addcslashes($escapeChar, '\\') . '$1', - $inputString + $inputString, ); } /** - * @internal + * @return array An associative array with the name of the properties + * of the column being declared as array indexes. */ + private function columnToArray(Column $column): array + { + $name = $column->getQuotedName($this); + + return array_merge($column->toArray(), [ + 'name' => $name, + 'version' => $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false, + 'comment' => $this->getColumnComment($column), + ]); + } + + /** @internal */ public function createSQLParser(): Parser { return new Parser(false); @@ -3607,4 +4602,50 @@ protected function getLikeWildcardCharacters(): string { return '%_'; } + + /** + * Compares the definitions of the given columns in the context of this platform. + * + * @throws Exception + */ + public function columnsEqual(Column $column1, Column $column2): bool + { + $column1Array = $this->columnToArray($column1); + $column2Array = $this->columnToArray($column2); + + // ignore explicit columnDefinition since it's not set on the Column generated by the SchemaManager + unset($column1Array['columnDefinition']); + unset($column2Array['columnDefinition']); + + if ( + $this->getColumnDeclarationSQL('', $column1Array) + !== $this->getColumnDeclarationSQL('', $column2Array) + ) { + return false; + } + + // If the platform supports inline comments, all comparison is already done above + if ($this->supportsInlineColumnComments()) { + return true; + } + + if ($column1->getComment() !== $column2->getComment()) { + return false; + } + + return $column1->getType() === $column2->getType(); + } + + /** + * 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/DB2Platform.php b/doctrine/dbal/src/Platforms/DB2Platform.php index 036310545..c40a03e6d 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; @@ -23,24 +25,51 @@ class DB2Platform extends AbstractPlatform { + /** + * {@inheritdoc} + * + * @deprecated + */ public function getCharMaxLength(): int { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'DB2Platform::getCharMaxLength() is deprecated.', + ); + return 254; } /** * {@inheritdoc} + * + * @deprecated */ public function getBinaryMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'DB2Platform::getBinaryMaxLength() is deprecated.', + ); + return 32704; } /** * {@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; } @@ -69,7 +98,7 @@ public function getBlobTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function initializeDoctrineTypeMappings() + protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ 'bigint' => 'bigint', @@ -95,6 +124,13 @@ public function initializeDoctrineTypeMappings() */ 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__, + ); + if ($doctrineType->getName() === Types::BOOLEAN) { // We require a commented boolean type in order to distinguish between boolean and smallint // as both (have to) map to the same native type. @@ -107,8 +143,17 @@ 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)'); } @@ -116,8 +161,17 @@ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) /** * {@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'; } @@ -135,6 +189,12 @@ public function getClobTypeDeclarationSQL(array $column) */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4749', + 'DB2Platform::getName() is deprecated. Identify platforms by their class.', + ); + return 'db2'; } @@ -266,6 +326,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 @@ -323,15 +385,19 @@ 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 = 'T' AND CREATOR = CURRENT_USER"; } /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { @@ -339,6 +405,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) @@ -363,6 +431,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) @@ -398,54 +468,25 @@ public function getListTableForeignKeysSQL($table) /** * {@inheritDoc} - */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; - } - - /** - * {@inheritDoc} - */ - public function getDropViewSQL($name) - { - return 'DROP VIEW ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getCreateDatabaseSQL($name) - { - return 'CREATE DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getDropDatabaseSQL($name) - { - return 'DROP DATABASE ' . $name; - } - - /** - * {@inheritDoc} + * + * @deprecated */ public function supportsCreateDropDatabase() { - return false; - } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); - /** - * {@inheritDoc} - */ - public function supportsReleaseSavepoints() - { return false; } /** * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsCommentOnStatement() { @@ -478,6 +519,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) { @@ -515,8 +558,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; } @@ -542,13 +587,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; } @@ -556,27 +601,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; } @@ -591,12 +637,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); @@ -604,33 +650,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 @@ -652,46 +715,44 @@ 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; } } /** * Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff. * - * @param ColumnDiff $columnDiff The column diff to evaluate. - * * @return string[] */ - private function getAlterColumnClausesSQL(ColumnDiff $columnDiff) + 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; @@ -709,34 +770,34 @@ private function getAlterColumnClausesSQL(ColumnDiff $columnDiff) */ 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)); } /** @@ -754,6 +815,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) { @@ -817,7 +880,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), ); } @@ -845,6 +908,14 @@ public function getSubstringExpression($string, $start, $length = null) return 'SUBSTR(' . $string . ', ' . $start . ', ' . $length . ')'; } + /** + * {@inheritDoc} + */ + public function getLengthExpression($column) + { + return 'LENGTH(' . $column . ', CODEUNITS32)'; + } + public function getCurrentDatabaseExpression(): string { return 'CURRENT_USER'; @@ -860,9 +931,17 @@ public function supportsIdentityColumns() /** * {@inheritDoc} + * + * @deprecated */ public function prefersIdentityColumns() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/1519', + 'DB2Platform::prefersIdentityColumns() is deprecated.', + ); + return true; } @@ -899,7 +978,7 @@ public function supportsSavepoints() /** * {@inheritDoc} * - * @deprecated Implement {@link createReservedKeywordsList()} instead. + * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { @@ -907,12 +986,13 @@ protected function getReservedKeywordsClass() 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'DB2Platform::getReservedKeywordsClass() is deprecated,' - . ' use DB2Platform::createReservedKeywordsList() instead.' + . ' use DB2Platform::createReservedKeywordsList() instead.', ); 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( @@ -922,7 +1002,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..ee8cb1d67 100644 --- a/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Platforms\Keywords; +use Doctrine\Deprecations\Deprecation; + /** * DB2 Keywords. */ @@ -9,9 +11,17 @@ class DB2Keywords extends KeywordList { /** * {@inheritdoc} + * + * @deprecated */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5433', + 'DB2Keywords::getName() is deprecated.', + ); + return 'DB2'; } diff --git a/doctrine/dbal/src/Platforms/Keywords/KeywordList.php b/doctrine/dbal/src/Platforms/Keywords/KeywordList.php index 852a58aba..584277395 100644 --- a/doctrine/dbal/src/Platforms/Keywords/KeywordList.php +++ b/doctrine/dbal/src/Platforms/Keywords/KeywordList.php @@ -8,11 +8,13 @@ /** * Abstract interface for a SQL reserved keyword dictionary. + * + * @psalm-consistent-constructor */ 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. @@ -30,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())); @@ -48,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 new file mode 100644 index 000000000..06c561254 --- /dev/null +++ b/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php @@ -0,0 +1,276 @@ +keywordLists = $keywordLists; } - /** - * @return string[] - */ + /** @return string[] */ public function getViolations() { return $this->violations; @@ -43,7 +47,7 @@ public function getViolations() * * @return string[] */ - private function isReservedWord($word) + private function isReservedWord($word): array { if ($word[0] === '`') { $word = str_replace('`', '', $word); @@ -64,10 +68,8 @@ private function isReservedWord($word) /** * @param string $asset * @param string[] $violatedPlatforms - * - * @return void */ - private function addViolation($asset, $violatedPlatforms) + private function addViolation($asset, $violatedPlatforms): void { if (count($violatedPlatforms) === 0) { return; @@ -83,7 +85,7 @@ public function acceptColumn(Table $table, Column $column) { $this->addViolation( 'Table ' . $table->getName() . ' column ' . $column->getName(), - $this->isReservedWord($column->getName()) + $this->isReservedWord($column->getName()), ); } @@ -122,7 +124,7 @@ 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/SQLServer2012Keywords.php b/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php index 24e190bf5..ebc45c402 100644 --- a/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php @@ -4,210 +4,9 @@ /** * Microsoft SQL Server 2012 reserved keyword dictionary. + * + * @deprecated Use {@see SQLServerKeywords} instead. */ -class SQLServer2012Keywords extends KeywordList +class SQLServer2012Keywords extends SQLServerKeywords { - /** - * {@inheritdoc} - */ - public function getName() - { - return 'SQLServer'; - } - - /** - * {@inheritdoc} - * - * @link http://msdn.microsoft.com/en-us/library/aa238507%28v=sql.80%29.aspx - */ - protected function getKeywords() - { - return [ - 'ADD', - 'ALL', - 'ALTER', - 'AND', - 'ANY', - 'AS', - 'ASC', - 'AUTHORIZATION', - 'BACKUP', - 'BEGIN', - 'BETWEEN', - 'BREAK', - 'BROWSE', - 'BULK', - 'BY', - 'CASCADE', - 'CASE', - 'CHECK', - 'CHECKPOINT', - 'CLOSE', - 'CLUSTERED', - 'COALESCE', - 'COLLATE', - 'COLUMN', - 'COMMIT', - 'COMPUTE', - 'CONSTRAINT', - 'CONTAINS', - 'CONTAINSTABLE', - 'CONTINUE', - 'CONVERT', - 'CREATE', - 'CROSS', - 'CURRENT', - 'CURRENT_DATE', - 'CURRENT_TIME', - 'CURRENT_TIMESTAMP', - 'CURRENT_USER', - 'CURSOR', - 'DATABASE', - 'DBCC', - 'DEALLOCATE', - 'DECLARE', - 'DEFAULT', - 'DELETE', - 'DENY', - 'DESC', - 'DISK', - 'DISTINCT', - 'DISTRIBUTED', - 'DOUBLE', - 'DROP', - 'DUMP', - 'ELSE', - 'END', - 'ERRLVL', - 'ESCAPE', - 'EXCEPT', - 'EXEC', - 'EXECUTE', - 'EXISTS', - 'EXIT', - 'EXTERNAL', - 'FETCH', - 'FILE', - 'FILLFACTOR', - 'FOR', - 'FOREIGN', - 'FREETEXT', - 'FREETEXTTABLE', - 'FROM', - 'FULL', - 'FUNCTION', - 'GOTO', - 'GRANT', - 'GROUP', - 'HAVING', - 'HOLDLOCK', - 'IDENTITY', - 'IDENTITY_INSERT', - 'IDENTITYCOL', - 'IF', - 'IN', - 'INDEX', - 'INNER', - 'INSERT', - 'INTERSECT', - 'INTO', - 'IS', - 'JOIN', - 'KEY', - 'KILL', - 'LEFT', - 'LIKE', - 'LINENO', - 'LOAD', - 'MERGE', - 'NATIONAL', - 'NOCHECK ', - 'NONCLUSTERED', - 'NOT', - 'NULL', - 'NULLIF', - 'OF', - 'OFF', - 'OFFSETS', - 'ON', - 'OPEN', - 'OPENDATASOURCE', - 'OPENQUERY', - 'OPENROWSET', - 'OPENXML', - 'OPTION', - 'OR', - 'ORDER', - 'OUTER', - 'OVER', - 'PERCENT', - 'PIVOT', - 'PLAN', - 'PRECISION', - 'PRIMARY', - 'PRINT', - 'PROC', - 'PROCEDURE', - 'PUBLIC', - 'RAISERROR', - 'READ', - 'READTEXT', - 'RECONFIGURE', - 'REFERENCES', - 'REPLICATION', - 'RESTORE', - 'RESTRICT', - 'RETURN', - 'REVERT', - 'REVOKE', - 'RIGHT', - 'ROLLBACK', - 'ROWCOUNT', - 'ROWGUIDCOL', - 'RULE', - 'SAVE', - 'SCHEMA', - 'SECURITYAUDIT', - 'SELECT', - 'SEMANTICKEYPHRASETABLE', - 'SEMANTICSIMILARITYDETAILSTABLE', - 'SEMANTICSIMILARITYTABLE', - 'SESSION_USER', - 'SET', - 'SETUSER', - 'SHUTDOWN', - 'SOME', - 'STATISTICS', - 'SYSTEM_USER', - 'TABLE', - 'TABLESAMPLE', - 'TEXTSIZE', - 'THEN', - 'TO', - 'TOP', - 'TRAN', - 'TRANSACTION', - 'TRIGGER', - 'TRUNCATE', - 'TRY_CONVERT', - 'TSEQUAL', - 'UNION', - 'UNIQUE', - 'UNPIVOT', - 'UPDATE', - 'UPDATETEXT', - 'USE', - 'USER', - 'VALUES', - 'VARYING', - 'VIEW', - 'WAITFOR', - 'WHEN', - 'WHERE', - 'WHILE', - 'WITH', - 'WITHIN GROUP', - 'WRITETEXT', - ]; - } } diff --git a/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php b/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php new file mode 100644 index 000000000..48953baa2 --- /dev/null +++ b/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php @@ -0,0 +1,224 @@ +doctrineTypeMapping['json'] = Types::JSON; + } +} diff --git a/doctrine/dbal/src/Platforms/MariaDb1027Platform.php b/doctrine/dbal/src/Platforms/MariaDb1027Platform.php index bb8e0b88d..93c7d3488 100644 --- a/doctrine/dbal/src/Platforms/MariaDb1027Platform.php +++ b/doctrine/dbal/src/Platforms/MariaDb1027Platform.php @@ -2,45 +2,14 @@ namespace Doctrine\DBAL\Platforms; -use Doctrine\DBAL\Types\Types; -use Doctrine\Deprecations\Deprecation; - /** * Provides the behavior, features and SQL dialect of the MariaDB 10.2 (10.2.7 GA) database platform. * * 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. */ -class MariaDb1027Platform extends MySQLPlatform +class MariaDb1027Platform extends MariaDBPlatform { - /** - * {@inheritdoc} - * - * @link https://mariadb.com/kb/en/library/json-data-type/ - */ - public function getJsonTypeDeclarationSQL(array $column): string - { - return 'LONGTEXT'; - } - - /** - * @deprecated Implement {@link 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.' - ); - - return Keywords\MariaDb102Keywords::class; - } - - protected function initializeDoctrineTypeMappings(): void - { - parent::initializeDoctrineTypeMappings(); - - $this->doctrineTypeMapping['json'] = Types::JSON; - } } diff --git a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider.php b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider.php new file mode 100644 index 000000000..d52ca74a2 --- /dev/null +++ b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider.php @@ -0,0 +1,11 @@ + */ + private $cache = []; + + public function __construct(CollationMetadataProvider $collationMetadataProvider) + { + $this->collationMetadataProvider = $collationMetadataProvider; + } + + public function getCollationCharset(string $collation): ?string + { + if (array_key_exists($collation, $this->cache)) { + return $this->cache[$collation]; + } + + return $this->cache[$collation] = $this->collationMetadataProvider->getCollationCharset($collation); + } +} diff --git a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php new file mode 100644 index 000000000..8dc2421a0 --- /dev/null +++ b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php @@ -0,0 +1,41 @@ +connection = $connection; + } + + /** @throws Exception */ + public function getCollationCharset(string $collation): ?string + { + $charset = $this->connection->fetchOne( + <<<'SQL' +SELECT CHARACTER_SET_NAME +FROM information_schema.COLLATIONS +WHERE COLLATION_NAME = ?; +SQL + , + [$collation], + ); + + if ($charset !== false) { + return $charset; + } + + return null; + } +} diff --git a/doctrine/dbal/src/Platforms/MySQL/Comparator.php b/doctrine/dbal/src/Platforms/MySQL/Comparator.php new file mode 100644 index 000000000..5951a5744 --- /dev/null +++ b/doctrine/dbal/src/Platforms/MySQL/Comparator.php @@ -0,0 +1,85 @@ +collationMetadataProvider = $collationMetadataProvider; + } + + /** + * {@inheritDoc} + */ + public function diffTable(Table $fromTable, Table $toTable) + { + return parent::diffTable( + $this->normalizeColumns($fromTable), + $this->normalizeColumns($toTable), + ); + } + + private function normalizeColumns(Table $table): Table + { + $tableOptions = array_intersect_key($table->getOptions(), [ + 'charset' => null, + 'collation' => null, + ]); + + $table = clone $table; + + foreach ($table->getColumns() as $column) { + $originalOptions = $column->getPlatformOptions(); + $normalizedOptions = $this->normalizeOptions($originalOptions); + + $overrideOptions = array_diff_assoc($normalizedOptions, $tableOptions); + + if ($overrideOptions === $originalOptions) { + continue; + } + + $column->setPlatformOptions($overrideOptions); + } + + return $table; + } + + /** + * @param array $options + * + * @return array + */ + private function normalizeOptions(array $options): array + { + if (isset($options['collation']) && ! isset($options['charset'])) { + $charset = $this->collationMetadataProvider->getCollationCharset($options['collation']); + + if ($charset !== null) { + $options['charset'] = $charset; + } + } + + return $options; + } +} diff --git a/doctrine/dbal/src/Platforms/MySQL57Platform.php b/doctrine/dbal/src/Platforms/MySQL57Platform.php index f29204cf4..ea11ee636 100644 --- a/doctrine/dbal/src/Platforms/MySQL57Platform.php +++ b/doctrine/dbal/src/Platforms/MySQL57Platform.php @@ -10,14 +10,26 @@ /** * Provides the behavior, features and SQL dialect of the MySQL 5.7 (5.7.9 GA) database platform. + * + * @deprecated This class will be merged with {@see MySQLPlatform} in 4.0 because support for MySQL + * releases prior to 5.7 will be dropped. */ class MySQL57Platform extends MySQLPlatform { /** * {@inheritdoc} + * + * @deprecated */ public function hasNativeJsonType() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + return true; } @@ -61,7 +73,7 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) /** * {@inheritdoc} * - * @deprecated Implement {@link createReservedKeywordsList()} instead. + * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { @@ -69,7 +81,7 @@ 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; diff --git a/doctrine/dbal/src/Platforms/MySQL80Platform.php b/doctrine/dbal/src/Platforms/MySQL80Platform.php index 808e93449..dd6599de1 100644 --- a/doctrine/dbal/src/Platforms/MySQL80Platform.php +++ b/doctrine/dbal/src/Platforms/MySQL80Platform.php @@ -12,7 +12,7 @@ class MySQL80Platform extends MySQL57Platform /** * {@inheritdoc} * - * @deprecated Implement {@link createReservedKeywordsList()} instead. + * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { @@ -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/MySQLPlatform.php b/doctrine/dbal/src/Platforms/MySQLPlatform.php index 379616f22..ae2cc9f2b 100644 --- a/doctrine/dbal/src/Platforms/MySQLPlatform.php +++ b/doctrine/dbal/src/Platforms/MySQLPlatform.php @@ -2,1193 +2,10 @@ namespace Doctrine\DBAL\Platforms; -use Doctrine\DBAL\Exception; -use Doctrine\DBAL\Schema\ForeignKeyConstraint; -use Doctrine\DBAL\Schema\Identifier; -use Doctrine\DBAL\Schema\Index; -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\Deprecations\Deprecation; -use InvalidArgumentException; - -use function array_diff_key; -use function array_merge; -use function array_unique; -use function array_values; -use function count; -use function func_get_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 strtoupper; -use function trim; - /** - * The MySQLPlatform provides the behavior, features and SQL dialect of the - * MySQL database platform. This platform represents a MySQL 5.0 or greater platform that - * uses the InnoDB storage engine. + * Provides the behavior, features and SQL dialect of the Oracle MySQL database platform + * of the oldest supported version. */ -class MySQLPlatform extends AbstractPlatform +class MySQLPlatform extends AbstractMySQLPlatform { - public const LENGTH_LIMIT_TINYTEXT = 255; - public const LENGTH_LIMIT_TEXT = 65535; - public const LENGTH_LIMIT_MEDIUMTEXT = 16777215; - - public const LENGTH_LIMIT_TINYBLOB = 255; - public const LENGTH_LIMIT_BLOB = 65535; - public const LENGTH_LIMIT_MEDIUMBLOB = 16777215; - - /** - * {@inheritDoc} - */ - protected function doModifyLimitQuery($query, $limit, $offset) - { - if ($limit !== null) { - $query .= ' LIMIT ' . $limit; - - if ($offset > 0) { - $query .= ' OFFSET ' . $offset; - } - } elseif ($offset > 0) { - // 2^64-1 is the maximum of unsigned BIGINT, the biggest limit possible - $query .= ' LIMIT 18446744073709551615 OFFSET ' . $offset; - } - - return $query; - } - - /** - * {@inheritDoc} - */ - public function getIdentifierQuoteCharacter() - { - return '`'; - } - - /** - * {@inheritDoc} - */ - public function getRegexpExpression() - { - return 'RLIKE'; - } - - /** - * {@inheritDoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) - { - if ($startPos === false) { - return 'LOCATE(' . $substr . ', ' . $str . ')'; - } - - return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')'; - } - - /** - * {@inheritDoc} - */ - public function getConcatExpression() - { - return sprintf('CONCAT(%s)', implode(', ', func_get_args())); - } - - /** - * {@inheritdoc} - */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) - { - $function = $operator === '+' ? 'DATE_ADD' : 'DATE_SUB'; - - return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')'; - } - - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) - { - return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; - } - - public function getCurrentDatabaseExpression(): string - { - return 'DATABASE()'; - } - - /** - * {@inheritDoc} - */ - public function getListDatabasesSQL() - { - return 'SHOW DATABASES'; - } - - /** - * {@inheritDoc} - */ - public function getListTableConstraintsSQL($table) - { - return 'SHOW INDEX FROM ' . $table; - } - - /** - * {@inheritDoc} - * - * Two approaches to listing the table indexes. The information_schema is - * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table". - */ - public function getListTableIndexesSQL($table, $database = null) - { - if ($database !== null) { - $database = $this->quoteStringLiteral($database); - $table = $this->quoteStringLiteral($table); - - return 'SELECT 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 WHERE TABLE_NAME = ' . $table . - ' AND TABLE_SCHEMA = ' . $database . - ' ORDER BY SEQ_IN_INDEX ASC'; - } - - return 'SHOW INDEX FROM ' . $table; - } - - /** - * {@inheritDoc} - */ - public function getListViewsSQL($database) - { - $database = $this->quoteStringLiteral($database); - - return 'SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = ' . $database; - } - - /** - * @param string $table - * @param string|null $database - * - * @return string - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - $table = $this->quoteStringLiteral($table); - - if ($database !== null) { - $database = $this->quoteStringLiteral($database); - } - - $sql = 'SELECT DISTINCT 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 = ' . $table . ' */ WHERE k.table_name = ' . $table; - - $databaseNameSql = $database ?? 'DATABASE()'; - - return $sql . ' AND k.table_schema = ' . $databaseNameSql - . ' /*!50116 AND c.constraint_schema = ' . $databaseNameSql . ' */' - . ' AND k.`REFERENCED_COLUMN_NAME` is not NULL' - . ' ORDER BY k.`ORDINAL_POSITION`'; - } - - /** - * {@inheritDoc} - */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; - } - - /** - * {@inheritDoc} - */ - public function getDropViewSQL($name) - { - return 'DROP VIEW ' . $name; - } - - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') - : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); - } - - /** - * {@inheritdoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed - ? 'BINARY(' . ($length > 0 ? $length : 255) . ')' - : 'VARBINARY(' . ($length > 0 ? $length : 255) . ')'; - } - - /** - * Gets the SQL snippet used to declare a CLOB column type. - * TINYTEXT : 2 ^ 8 - 1 = 255 - * TEXT : 2 ^ 16 - 1 = 65535 - * MEDIUMTEXT : 2 ^ 24 - 1 = 16777215 - * LONGTEXT : 2 ^ 32 - 1 = 4294967295 - * - * {@inheritDoc} - */ - public function getClobTypeDeclarationSQL(array $column) - { - if (! empty($column['length']) && is_numeric($column['length'])) { - $length = $column['length']; - - if ($length <= static::LENGTH_LIMIT_TINYTEXT) { - return 'TINYTEXT'; - } - - if ($length <= static::LENGTH_LIMIT_TEXT) { - return 'TEXT'; - } - - if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) { - return 'MEDIUMTEXT'; - } - } - - return 'LONGTEXT'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTypeDeclarationSQL(array $column) - { - if (isset($column['version']) && $column['version'] === true) { - return 'TIMESTAMP'; - } - - return 'DATETIME'; - } - - /** - * {@inheritDoc} - */ - public function getDateTypeDeclarationSQL(array $column) - { - return 'DATE'; - } - - /** - * {@inheritDoc} - */ - public function getTimeTypeDeclarationSQL(array $column) - { - return 'TIME'; - } - - /** - * {@inheritDoc} - */ - public function getBooleanTypeDeclarationSQL(array $column) - { - return 'TINYINT(1)'; - } - - /** - * {@inheritDoc} - * - * MySQL prefers "autoincrement" identity columns since sequences can only - * be emulated with a table. - */ - public function prefersIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - * - * MySQL supports this through AUTO_INCREMENT columns. - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsInlineColumnComments() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsColumnCollation() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getListTablesSQL() - { - return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"; - } - - /** - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - $table = $this->quoteStringLiteral($table); - - if ($database !== null) { - $database = $this->quoteStringLiteral($database); - } else { - $database = 'DATABASE()'; - } - - return 'SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ' . - 'COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, ' . - 'CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation ' . - 'FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ' . $database . ' AND TABLE_NAME = ' . $table . - ' ORDER BY ORDINAL_POSITION ASC'; - } - - public function getListTableMetadataSQL(string $table, ?string $database = null): string - { - return sprintf( - <<<'SQL' -SELECT ENGINE, AUTO_INCREMENT, TABLE_COLLATION, TABLE_COMMENT, CREATE_OPTIONS -FROM information_schema.TABLES -WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = %s AND TABLE_NAME = %s -SQL - , - $database !== null ? $this->quoteStringLiteral($database) : 'DATABASE()', - $this->quoteStringLiteral($table) - ); - } - - /** - * {@inheritDoc} - */ - public function getCreateDatabaseSQL($name) - { - return 'CREATE DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getDropDatabaseSQL($name) - { - return 'DROP DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) - { - $queryFields = $this->getColumnDeclarationListSQL($columns); - - if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $constraintName => $definition) { - $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); - } - } - - // add all indexes - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $indexName => $definition) { - $queryFields .= ', ' . $this->getIndexDeclarationSQL($indexName, $definition); - } - } - - // attach all primary keys - if (isset($options['primary']) && ! empty($options['primary'])) { - $keyColumns = array_unique(array_values($options['primary'])); - $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; - } - - $query = 'CREATE '; - - if (! empty($options['temporary'])) { - $query .= 'TEMPORARY '; - } - - $query .= 'TABLE ' . $name . ' (' . $queryFields . ') '; - $query .= $this->buildTableOptions($options); - $query .= $this->buildPartitionOptions($options); - - $sql = [$query]; - $engine = 'INNODB'; - - if (isset($options['engine'])) { - $engine = strtoupper(trim($options['engine'])); - } - - // Propagate foreign key constraints only for InnoDB. - if (isset($options['foreignKeys']) && $engine === 'INNODB') { - foreach ((array) $options['foreignKeys'] as $definition) { - $sql[] = $this->getCreateForeignKeySQL($definition, $name); - } - } - - return $sql; - } - - /** - * {@inheritdoc} - */ - public function getDefaultValueDeclarationSQL($column) - { - // Unset the default value if the given column definition does not allow default values. - if ($column['type'] instanceof TextType || $column['type'] instanceof BlobType) { - $column['default'] = null; - } - - return parent::getDefaultValueDeclarationSQL($column); - } - - /** - * Build SQL for table options - * - * @param mixed[] $options - * - * @return string - */ - private function buildTableOptions(array $options) - { - if (isset($options['table_options'])) { - return $options['table_options']; - } - - $tableOptions = []; - - // Charset - if (! isset($options['charset'])) { - $options['charset'] = 'utf8'; - } - - $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']); - - // Collate - if (! isset($options['collate'])) { - $options['collate'] = $options['charset'] . '_unicode_ci'; - } - - $tableOptions[] = $this->getColumnCollationDeclarationSQL($options['collate']); - - // Engine - if (! isset($options['engine'])) { - $options['engine'] = 'InnoDB'; - } - - $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); - - // Auto increment - if (isset($options['auto_increment'])) { - $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); - } - - // Comment - if (isset($options['comment'])) { - $tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($options['comment'])); - } - - // Row format - if (isset($options['row_format'])) { - $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); - } - - return implode(' ', $tableOptions); - } - - /** - * Build SQL for partition options. - * - * @param mixed[] $options - * - * @return string - */ - private function buildPartitionOptions(array $options) - { - return isset($options['partition_options']) - ? ' ' . $options['partition_options'] - : ''; - } - - /** - * {@inheritDoc} - */ - public function getAlterTableSQL(TableDiff $diff) - { - $columnSql = []; - $queryParts = []; - $newName = $diff->getNewName(); - - if ($newName !== false) { - $queryParts[] = 'RENAME TO ' . $newName->getQuotedName($this); - } - - foreach ($diff->addedColumns as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - - $columnArray = array_merge($column->toArray(), [ - 'comment' => $this->getColumnComment($column), - ]); - - $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); - } - - foreach ($diff->removedColumns as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - - $queryParts[] = 'DROP ' . $column->getQuotedName($this); - } - - foreach ($diff->changedColumns as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - - $column = $columnDiff->column; - $columnArray = $column->toArray(); - - // 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; - } - - $columnArray['comment'] = $this->getColumnComment($column); - $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' - . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); - } - - foreach ($diff->renamedColumns 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); - } - - if (isset($diff->addedIndexes['primary'])) { - $keyColumns = array_unique(array_values($diff->addedIndexes['primary']->getColumns())); - $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; - unset($diff->addedIndexes['primary']); - } elseif (isset($diff->changedIndexes['primary'])) { - // 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())); - $queryParts[] = 'DROP PRIMARY KEY'; - $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; - unset($diff->changedIndexes['primary']); - break; - } - } - } - - $sql = []; - $tableSql = []; - - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - if (count($queryParts) > 0) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' - . implode(', ', $queryParts); - } - - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) - ); - } - - return array_merge($sql, $tableSql, $columnSql); - } - - /** - * {@inheritDoc} - */ - protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) - { - $sql = []; - $table = $diff->getName($this)->getQuotedName($this); - - foreach ($diff->changedIndexes 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->addedIndexes as $addKey => $addIndex) { - if ($remIndex->getColumns() !== $addIndex->getColumns()) { - continue; - } - - $indexClause = 'INDEX ' . $addIndex->getName(); - - if ($addIndex->isPrimary()) { - $indexClause = 'PRIMARY KEY'; - } elseif ($addIndex->isUnique()) { - $indexClause = 'UNIQUE INDEX ' . $addIndex->getName(); - } - - $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', '; - $query .= 'ADD ' . $indexClause; - $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex) . ')'; - - $sql[] = $query; - - unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]); - - break; - } - } - - $engine = 'INNODB'; - - if ($diff->fromTable instanceof Table && $diff->fromTable->hasOption('engine')) { - $engine = strtoupper(trim($diff->fromTable->getOption('engine'))); - } - - // Suppress foreign key constraint propagation on non-supporting engines. - if ($engine !== 'INNODB') { - $diff->addedForeignKeys = []; - $diff->changedForeignKeys = []; - $diff->removedForeignKeys = []; - } - - $sql = array_merge( - $sql, - $this->getPreAlterTableAlterIndexForeignKeySQL($diff), - parent::getPreAlterTableIndexForeignKeySQL($diff), - $this->getPreAlterTableRenameIndexForeignKeySQL($diff) - ); - - return $sql; - } - - /** - * @return string[] - * - * @throws Exception - */ - private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index) - { - $sql = []; - - if (! $index->isPrimary() || ! $diff->fromTable instanceof Table) { - return $sql; - } - - $tableName = $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)) { - continue; - } - - $column = $diff->fromTable->getColumn($columnName); - - if ($column->getAutoincrement() !== true) { - continue; - } - - $column->setAutoincrement(false); - - $sql[] = 'ALTER TABLE ' . $tableName . ' MODIFY ' . - $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); - - // original autoincrement information might be needed later on by other parts of the table alteration - $column->setAutoincrement(true); - } - - return $sql; - } - - /** - * @param TableDiff $diff The table diff to gather the SQL for. - * - * @return string[] - * - * @throws Exception - */ - private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff) - { - $sql = []; - $table = $diff->getName($this)->getQuotedName($this); - - foreach ($diff->changedIndexes as $changedIndex) { - // Changed primary key - if (! $changedIndex->isPrimary() || ! ($diff->fromTable instanceof Table)) { - continue; - } - - foreach ($diff->fromTable->getPrimaryKeyColumns() as $columnName => $column) { - // Check if an autoincrement column was dropped from the primary key. - if (! $column->getAutoincrement() || in_array($columnName, $changedIndex->getColumns(), true)) { - continue; - } - - // The autoincrement attribute needs to be removed from the dropped column - // before we can drop and recreate the primary key. - $column->setAutoincrement(false); - - $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' . - $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); - - // Restore the autoincrement attribute as it might be needed later on - // by other parts of the table alteration. - $column->setAutoincrement(true); - } - } - - return $sql; - } - - /** - * @param TableDiff $diff The table diff to gather the SQL for. - * - * @return string[] - */ - protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) - { - $sql = []; - $tableName = $diff->getName($this)->getQuotedName($this); - - foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { - if (in_array($foreignKey, $diff->changedForeignKeys, true)) { - continue; - } - - $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); - } - - return $sql; - } - - /** - * Returns the remaining foreign key constraints that require one of the renamed indexes. - * - * "Remaining" here refers to the diff between the foreign keys currently defined in the associated - * table and the foreign keys to be removed. - * - * @param TableDiff $diff The table diff to evaluate. - * - * @return ForeignKeyConstraint[] - */ - private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff) - { - if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) { - return []; - } - - $foreignKeys = []; - /** @var ForeignKeyConstraint[] $remainingForeignKeys */ - $remainingForeignKeys = array_diff_key( - $diff->fromTable->getForeignKeys(), - $diff->removedForeignKeys - ); - - foreach ($remainingForeignKeys as $foreignKey) { - foreach ($diff->renamedIndexes as $index) { - if ($foreignKey->intersectsIndexColumns($index)) { - $foreignKeys[] = $foreignKey; - - break; - } - } - } - - return $foreignKeys; - } - - /** - * {@inheritdoc} - */ - protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) - { - return array_merge( - parent::getPostAlterTableIndexForeignKeySQL($diff), - $this->getPostAlterTableRenameIndexForeignKeySQL($diff) - ); - } - - /** - * @param TableDiff $diff The table diff to gather the SQL for. - * - * @return string[] - */ - protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) - { - $sql = []; - $newName = $diff->getNewName(); - - if ($newName !== false) { - $tableName = $newName->getQuotedName($this); - } else { - $tableName = $diff->getName($this)->getQuotedName($this); - } - - foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { - if (in_array($foreignKey, $diff->changedForeignKeys, true)) { - continue; - } - - $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); - } - - return $sql; - } - - /** - * {@inheritDoc} - */ - protected function getCreateIndexSQLFlags(Index $index) - { - $type = ''; - if ($index->isUnique()) { - $type .= 'UNIQUE '; - } elseif ($index->hasFlag('fulltext')) { - $type .= 'FULLTEXT '; - } elseif ($index->hasFlag('spatial')) { - $type .= 'SPATIAL '; - } - - return $type; - } - - /** - * {@inheritDoc} - */ - public function getIntegerTypeDeclarationSQL(array $column) - { - return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getBigIntTypeDeclarationSQL(array $column) - { - return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getSmallIntTypeDeclarationSQL(array $column) - { - return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritdoc} - */ - public function getFloatDeclarationSQL(array $column) - { - return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($column); - } - - /** - * {@inheritdoc} - */ - public function getDecimalTypeDeclarationSQL(array $column) - { - return parent::getDecimalTypeDeclarationSQL($column) . $this->getUnsignedDeclaration($column); - } - - /** - * Get unsigned declaration for a column. - * - * @param mixed[] $columnDef - * - * @return string - */ - private function getUnsignedDeclaration(array $columnDef) - { - return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : ''; - } - - /** - * {@inheritDoc} - */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) - { - $autoinc = ''; - if (! empty($column['autoincrement'])) { - $autoinc = ' AUTO_INCREMENT'; - } - - return $this->getUnsignedDeclaration($column) . $autoinc; - } - - /** - * {@inheritDoc} - */ - public function getColumnCharsetDeclarationSQL($charset) - { - return 'CHARACTER SET ' . $charset; - } - - /** - * {@inheritDoc} - */ - public function getColumnCollationDeclarationSQL($collation) - { - return 'COLLATE ' . $this->quoteSingleIdentifier($collation); - } - - /** - * {@inheritDoc} - */ - public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) - { - $query = ''; - if ($foreignKey->hasOption('match')) { - $query .= ' MATCH ' . $foreignKey->getOption('match'); - } - - $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); - - return $query; - } - - /** - * {@inheritDoc} - */ - public function getDropIndexSQL($index, $table = null) - { - if ($index instanceof Index) { - $indexName = $index->getQuotedName($this); - } elseif (is_string($index)) { - $indexName = $index; - } else { - throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.' - ); - } - - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } elseif (! is_string($table)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' - ); - } - - if ($index instanceof Index && $index->isPrimary()) { - // MySQL primary keys are always named "PRIMARY", - // so we cannot use them in statements because of them being keyword. - return $this->getDropPrimaryKeySQL($table); - } - - return 'DROP INDEX ' . $indexName . ' ON ' . $table; - } - - /** - * @param string $table - * - * @return string - */ - protected function getDropPrimaryKeySQL($table) - { - return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; - } - - /** - * {@inheritDoc} - */ - public function getSetTransactionIsolationSQL($level) - { - return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return 'mysql'; - } - - /** - * {@inheritDoc} - */ - public function getReadLockSQL() - { - return 'LOCK IN SHARE MODE'; - } - - /** - * {@inheritDoc} - */ - 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', - ]; - } - - /** - * {@inheritDoc} - */ - public function getVarcharMaxLength() - { - return 65535; - } - - /** - * {@inheritdoc} - */ - public function getBinaryMaxLength() - { - return 65535; - } - - /** - * {@inheritDoc} - * - * @deprecated Implement {@link createReservedKeywordsList()} instead. - */ - protected function getReservedKeywordsClass() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'MySQLPlatform::getReservedKeywordsClass() is deprecated,' - . ' use MySQLPlatform::createReservedKeywordsList() instead.' - ); - - return Keywords\MySQLKeywords::class; - } - - /** - * {@inheritDoc} - * - * MySQL commits a transaction implicitly when DROP TABLE is executed, however not - * if DROP TEMPORARY TABLE is executed. - */ - public function getDropTemporaryTableSQL($table) - { - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } elseif (! is_string($table)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' - ); - } - - return 'DROP TEMPORARY TABLE ' . $table; - } - - /** - * Gets the SQL Snippet used to declare a BLOB column type. - * TINYBLOB : 2 ^ 8 - 1 = 255 - * BLOB : 2 ^ 16 - 1 = 65535 - * MEDIUMBLOB : 2 ^ 24 - 1 = 16777215 - * LONGBLOB : 2 ^ 32 - 1 = 4294967295 - * - * {@inheritDoc} - */ - public function getBlobTypeDeclarationSQL(array $column) - { - if (! empty($column['length']) && is_numeric($column['length'])) { - $length = $column['length']; - - if ($length <= static::LENGTH_LIMIT_TINYBLOB) { - return 'TINYBLOB'; - } - - if ($length <= static::LENGTH_LIMIT_BLOB) { - return 'BLOB'; - } - - if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) { - return 'MEDIUMBLOB'; - } - } - - return 'LONGBLOB'; - } - - /** - * {@inheritdoc} - */ - public function quoteStringLiteral($str) - { - $str = str_replace('\\', '\\\\', $str); // MySQL requires backslashes to be escaped aswell. - - return parent::quoteStringLiteral($str); - } - - /** - * {@inheritdoc} - */ - public function getDefaultTransactionIsolationLevel() - { - return TransactionIsolationLevel::REPEATABLE_READ; - } - - public function supportsColumnLengthIndexes(): bool - { - return true; - } } diff --git a/doctrine/dbal/src/Platforms/OraclePlatform.php b/doctrine/dbal/src/Platforms/OraclePlatform.php index 7be7621d3..306173e99 100644 --- a/doctrine/dbal/src/Platforms/OraclePlatform.php +++ b/doctrine/dbal/src/Platforms/OraclePlatform.php @@ -2,10 +2,12 @@ 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; @@ -35,6 +37,8 @@ class OraclePlatform extends AbstractPlatform /** * Assertion for Oracle identifiers. * + * @deprecated + * * @link http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm * * @param string $identifier @@ -63,12 +67,20 @@ public function getSubstringExpression($string, $start, $length = null) } /** + * @deprecated Generate dates within the application. + * * @param string $type * * @return string */ 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.', + ); + switch ($type) { case 'date': case 'time': @@ -167,6 +179,26 @@ public function getBitOrComparisonExpression($value1, $value2) . '+' . $value2 . ')'; } + /** + * {@inheritDoc} + */ + 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); + } + + return 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $index->getQuotedName($this) + . ' PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; + } + /** * {@inheritDoc} * @@ -195,10 +227,8 @@ public function getAlterSequenceSQL(Sequence $sequence) /** * Cache definition for sequences - * - * @return string */ - private function getSequenceCacheSQL(Sequence $sequence) + private function getSequenceCacheSQL(Sequence $sequence): string { if ($sequence->getCache() === 0) { return ' NOCACHE'; @@ -327,8 +357,17 @@ 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)'); } @@ -336,16 +375,33 @@ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) /** * {@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} + * + * @deprecated */ public function getBinaryMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'OraclePlatform::getBinaryMaxLength() is deprecated.', + ); + return 2000; } @@ -359,6 +415,8 @@ public function getClobTypeDeclarationSQL(array $column) /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListDatabasesSQL() { @@ -367,6 +425,8 @@ public function getListDatabasesSQL() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListSequencesSQL($database) { @@ -401,16 +461,16 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] $sql = array_merge($sql, $this->getCreateAutoincrementSql($columnName, $name)); } - if (isset($indexes) && ! empty($indexes)) { - foreach ($indexes as $index) { - $sql[] = $this->getCreateIndexSQL($index, $name); - } + foreach ($indexes as $index) { + $sql[] = $this->getCreateIndexSQL($index, $name); } return $sql; } /** + * @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 @@ -443,6 +503,7 @@ public function getListTableIndexesSQL($table, $database = null) SELECT ucon.constraint_type FROM user_constraints ucon WHERE ucon.index_name = uind_col.index_name + AND ucon.table_name = uind_col.table_name ) AS is_primary FROM user_ind_columns uind_col WHERE uind_col.table_name = " . $table . ' @@ -450,6 +511,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() @@ -459,6 +522,8 @@ public function getListTablesSQL() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { @@ -466,22 +531,8 @@ public function getListViewsSQL($database) } /** - * {@inheritDoc} - */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; - } - - /** - * {@inheritDoc} - */ - public function getDropViewSQL($name) - { - return 'DROP VIEW ' . $name; - } - - /** + * @internal The method should be only used from within the OraclePlatform class hierarchy. + * * @param string $name * @param string $table * @param int $start @@ -518,7 +569,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); @@ -549,6 +600,8 @@ public function getCreateAutoincrementSql($name, $table, $start = 1) } /** + * @internal The method should be only used from within the OracleSchemaManager class hierarchy. + * * Returns the SQL statements to drop the autoincrement for the given table name. * * @param string $table The table name to drop the autoincrement for. @@ -561,7 +614,7 @@ public function getDropAutoincrementSql($table) $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table); $identitySequenceName = $this->getIdentitySequenceName( $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(), - '' + '', ); return [ @@ -578,10 +631,8 @@ public function getDropAutoincrementSql($table) * to reflect Oracle's internal auto uppercasing strategy of unquoted identifiers. * * @param string $name The identifier to normalize. - * - * @return Identifier The normalized identifier. */ - private function normalizeIdentifier($name) + private function normalizeIdentifier($name): Identifier { $identifier = new Identifier($name); @@ -609,12 +660,8 @@ private function addSuffix(string $identifier, string $suffix): string * * Quotes the autoincrement primary key identifier name * if the given table name is quoted by intention. - * - * @param Identifier $table The table identifier to return the autoincrement primary key identifier name for. - * - * @return string */ - private function getAutoincrementIdentifierName(Identifier $table) + private function getAutoincrementIdentifierName(Identifier $table): string { $identifierName = $this->addSuffix($table->getName(), '_AI_PK'); @@ -624,6 +671,8 @@ private function getAutoincrementIdentifierName(Identifier $table) } /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * * {@inheritDoc} */ public function getListTableForeignKeysSQL($table) @@ -656,6 +705,8 @@ public function getListTableForeignKeysSQL($table) } /** + * @deprecated + * * {@inheritDoc} */ public function getListTableConstraintsSQL($table) @@ -667,6 +718,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) @@ -706,32 +759,35 @@ public function getListTableColumnsSQL($table, $database = null) $colCommentsOwnerCondition, $tabColumnsTableName, $table, - $tabColumnsOwnerCondition + $tabColumnsOwnerCondition, ); } - /** - * {@inheritDoc} - */ - public function getDropSequenceSQL($sequence) - { - if ($sequence instanceof Sequence) { - $sequence = $sequence->getQuotedName($this); - } - - return 'DROP SEQUENCE ' . $sequence; - } - /** * {@inheritDoc} */ 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); } @@ -743,6 +799,8 @@ public function getDropForeignKeySQL($foreignKey, $table) /** * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { @@ -761,6 +819,8 @@ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey /** * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getForeignKeyReferentialActionSQL($action) { @@ -783,6 +843,14 @@ public function getForeignKeyReferentialActionSQL($action) } } + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE USER ' . $name; + } + /** * {@inheritDoc} */ @@ -802,7 +870,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; } @@ -815,49 +885,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) { @@ -865,30 +934,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; } @@ -897,8 +965,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 = []; @@ -909,17 +976,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), ); } @@ -928,6 +1002,8 @@ public function getAlterTableSQL(TableDiff $diff) /** * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getColumnDeclarationSQL($name, array $column) { @@ -942,11 +1018,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; @@ -970,14 +1064,25 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) /** * {@inheritdoc} + * + * @deprecated */ public function usesSequenceEmulatedIdentityColumns() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + return true; } /** * {@inheritdoc} + * + * @internal The method should be only used from within the OraclePlatform class hierarchy. */ public function getIdentitySequenceName($tableName, $columnName) { @@ -997,6 +1102,8 @@ public function getIdentitySequenceName($tableName, $columnName) /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsCommentOnStatement() { @@ -1008,6 +1115,12 @@ public function supportsCommentOnStatement() */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4749', + 'OraclePlatform::getName() is deprecated. Identify platforms by their class.', + ); + return 'oracle'; } @@ -1164,7 +1277,7 @@ public function releaseSavePoint($savepoint) /** * {@inheritDoc} * - * @deprecated Implement {@link createReservedKeywordsList()} instead. + * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { @@ -1172,7 +1285,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; @@ -1186,6 +1299,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'; @@ -1194,7 +1308,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(), ); } @@ -1205,7 +1319,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 c9ca509d0..aa023ba79 100644 --- a/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php +++ b/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php @@ -9,36 +9,22 @@ /** * Provides the behavior, features and SQL dialect of the PostgreSQL 10.0 database platform. + * + * @deprecated This class will be merged with {@see PostgreSQLPlatform} in 4.0 because support for Postgres + * releases prior to 10.0 will be dropped. */ class PostgreSQL100Platform extends PostgreSQL94Platform { - /** - * @deprecated Implement {@link 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/PostgreSQL94Platform.php b/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php index bf21477c6..2c974c36d 100644 --- a/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php +++ b/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php @@ -2,1306 +2,11 @@ namespace Doctrine\DBAL\Platforms; -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\Sequence; -use Doctrine\DBAL\Schema\TableDiff; -use Doctrine\DBAL\Types\BigIntType; -use Doctrine\DBAL\Types\BinaryType; -use Doctrine\DBAL\Types\BlobType; -use Doctrine\DBAL\Types\IntegerType; -use Doctrine\DBAL\Types\Type; -use Doctrine\Deprecations\Deprecation; -use UnexpectedValueException; - -use function array_diff; -use function array_merge; -use function array_unique; -use function array_values; -use function count; -use function explode; -use function implode; -use function in_array; -use function is_array; -use function is_bool; -use function is_numeric; -use function is_string; -use function sprintf; -use function strpos; -use function strtolower; -use function trim; - /** * Provides the behavior, features and SQL dialect of the PostgreSQL 9.4+ database platform. + * + * @deprecated Use {@see PostgreSQLPlatform} instead. */ -class PostgreSQL94Platform extends AbstractPlatform +class PostgreSQL94Platform extends PostgreSQLPlatform { - /** @var bool */ - private $useBooleanTrueFalseStrings = true; - - /** @var string[][] PostgreSQL booleans literals */ - private $booleanLiterals = [ - 'true' => [ - 't', - 'true', - 'y', - 'yes', - 'on', - '1', - ], - 'false' => [ - 'f', - 'false', - 'n', - 'no', - 'off', - '0', - ], - ]; - - /** - * PostgreSQL has different behavior with some drivers - * with regard to how booleans have to be handled. - * - * Enables use of 'true'/'false' or otherwise 1 and 0 instead. - * - * @param bool $flag - * - * @return void - */ - public function setUseBooleanTrueFalseStrings($flag) - { - $this->useBooleanTrueFalseStrings = (bool) $flag; - } - - /** - * {@inheritDoc} - */ - public function getSubstringExpression($string, $start, $length = null) - { - if ($length === null) { - return 'SUBSTRING(' . $string . ' FROM ' . $start . ')'; - } - - return 'SUBSTRING(' . $string . ' FROM ' . $start . ' FOR ' . $length . ')'; - } - - /** - * {@inheritDoc} - */ - public function getNowExpression() - { - return 'LOCALTIMESTAMP(0)'; - } - - /** - * {@inheritDoc} - */ - public function getRegexpExpression() - { - return 'SIMILAR TO'; - } - - /** - * {@inheritDoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) - { - if ($startPos !== false) { - $str = $this->getSubstringExpression($str, $startPos); - - return 'CASE WHEN (POSITION(' . $substr . ' IN ' . $str . ') = 0) THEN 0' - . ' ELSE (POSITION(' . $substr . ' IN ' . $str . ') + ' . ($startPos - 1) . ') END'; - } - - return 'POSITION(' . $substr . ' IN ' . $str . ')'; - } - - /** - * {@inheritdoc} - */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) - { - if ($unit === DateIntervalUnit::QUARTER) { - $interval *= 3; - $unit = DateIntervalUnit::MONTH; - } - - return '(' . $date . ' ' . $operator . ' (' . $interval . " || ' " . $unit . "')::interval)"; - } - - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) - { - return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; - } - - public function getCurrentDatabaseExpression(): string - { - return 'CURRENT_DATABASE()'; - } - - /** - * {@inheritDoc} - */ - public function supportsSequences() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsSchemas() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function getDefaultSchemaName() - { - return 'public'; - } - - /** - * {@inheritDoc} - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function supportsPartialIndexes() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function usesSequenceEmulatedIdentityColumns() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function getIdentitySequenceName($tableName, $columnName) - { - return $tableName . '_' . $columnName . '_seq'; - } - - /** - * {@inheritDoc} - */ - public function supportsCommentOnStatement() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function hasNativeGuidType() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getListDatabasesSQL() - { - return 'SELECT datname FROM pg_database'; - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link PostgreSQLSchemaManager::listSchemaNames()} instead. - */ - public function getListNamespacesSQL() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4503', - 'PostgreSQL94Platform::getListNamespacesSQL() is deprecated,' - . ' use PostgreSQLSchemaManager::listSchemaNames() instead.' - ); - - return "SELECT schema_name AS nspname - FROM information_schema.schemata - WHERE schema_name NOT LIKE 'pg\_%' - AND schema_name != 'information_schema'"; - } - - /** - * {@inheritDoc} - */ - public function getListSequencesSQL($database) - { - return "SELECT sequence_name AS relname, - sequence_schema AS schemaname - FROM information_schema.sequences - WHERE sequence_schema NOT LIKE 'pg\_%' - AND sequence_schema != 'information_schema'"; - } - - /** - * {@inheritDoc} - */ - public function getListTablesSQL() - { - return "SELECT quote_ident(table_name) AS table_name, - table_schema AS schema_name - FROM information_schema.tables - WHERE table_schema NOT LIKE 'pg\_%' - AND table_schema != 'information_schema' - AND table_name != 'geometry_columns' - AND table_name != 'spatial_ref_sys' - AND table_type != 'VIEW'"; - } - - /** - * {@inheritDoc} - */ - public function getListViewsSQL($database) - { - return 'SELECT quote_ident(table_name) AS viewname, - table_schema AS schemaname, - view_definition AS definition - FROM information_schema.views - WHERE view_definition IS NOT NULL'; - } - - /** - * @param string $table - * @param string|null $database - * - * @return string - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - return 'SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef - FROM pg_catalog.pg_constraint r - WHERE r.conrelid = - ( - SELECT c.oid - FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n - WHERE ' . $this->getTableWhereClause($table) . " AND n.oid = c.relnamespace - ) - AND r.contype = 'f'"; - } - - /** - * {@inheritDoc} - */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; - } - - /** - * {@inheritDoc} - */ - public function getDropViewSQL($name) - { - return 'DROP VIEW ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getListTableConstraintsSQL($table) - { - $table = new Identifier($table); - $table = $this->quoteStringLiteral($table->getName()); - - return sprintf( - <<<'SQL' -SELECT - quote_ident(relname) as relname -FROM - pg_class -WHERE oid IN ( - SELECT indexrelid - FROM pg_index, pg_class - WHERE pg_class.relname = %s - AND pg_class.oid = pg_index.indrelid - AND (indisunique = 't' OR indisprimary = 't') - ) -SQL - , - $table - ); - } - - /** - * {@inheritDoc} - * - * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html - */ - public function getListTableIndexesSQL($table, $database = null) - { - return 'SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary, - pg_index.indkey, pg_index.indrelid, - pg_get_expr(indpred, indrelid) AS where - FROM pg_class, pg_index - WHERE oid IN ( - SELECT indexrelid - FROM pg_index si, pg_class sc, pg_namespace sn - WHERE ' . $this->getTableWhereClause($table, 'sc', 'sn') . ' - AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid - ) AND pg_index.indexrelid = oid'; - } - - /** - * @param string $table - * @param string $classAlias - * @param string $namespaceAlias - * - * @return string - */ - private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n') - { - $whereClause = $namespaceAlias . ".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND "; - if (strpos($table, '.') !== false) { - [$schema, $table] = explode('.', $table); - $schema = $this->quoteStringLiteral($schema); - } else { - $schema = 'ANY(current_schemas(false))'; - } - - $table = new Identifier($table); - $table = $this->quoteStringLiteral($table->getName()); - - return $whereClause . sprintf( - '%s.relname = %s AND %s.nspname = %s', - $classAlias, - $table, - $namespaceAlias, - $schema - ); - } - - /** - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - return "SELECT - 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, pg_class c, pg_type t, pg_namespace n - WHERE " . $this->getTableWhereClause($table, 'c', 'n') . ' - AND a.attnum > 0 - AND a.attrelid = c.oid - AND a.atttypid = t.oid - AND n.oid = c.relnamespace - ORDER BY a.attnum'; - } - - /** - * {@inheritDoc} - */ - public function getCreateDatabaseSQL($name) - { - return 'CREATE DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) - { - $query = ''; - - if ($foreignKey->hasOption('match')) { - $query .= ' MATCH ' . $foreignKey->getOption('match'); - } - - $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); - - if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) { - $query .= ' DEFERRABLE'; - } else { - $query .= ' NOT DEFERRABLE'; - } - - if ( - ($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false) - || ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) - ) { - $query .= ' INITIALLY DEFERRED'; - } else { - $query .= ' INITIALLY IMMEDIATE'; - } - - return $query; - } - - /** - * {@inheritDoc} - */ - public function getAlterTableSQL(TableDiff $diff) - { - $sql = []; - $commentsSQL = []; - $columnSql = []; - - foreach ($diff->addedColumns as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - - $query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - - $comment = $this->getColumnComment($column); - - if ($comment === null || $comment === '') { - continue; - } - - $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), - $column->getQuotedName($this), - $comment - ); - } - - foreach ($diff->removedColumns as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - - $query = 'DROP ' . $column->getQuotedName($this); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - } - - foreach ($diff->changedColumns as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - - if ($this->isUnchangedBinaryColumn($columnDiff)) { - continue; - } - - $oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this); - $column = $columnDiff->column; - - if ( - $columnDiff->hasChanged('type') - || $columnDiff->hasChanged('precision') - || $columnDiff->hasChanged('scale') - || $columnDiff->hasChanged('fixed') - ) { - $type = $column->getType(); - - // SERIAL/BIGSERIAL are not "real" types and we can't alter a column to that type - $columnDefinition = $column->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; - } - - if ($columnDiff->hasChanged('default') || $this->typeChangeBreaksDefaultValue($columnDiff)) { - $defaultClause = $column->getDefault() === null - ? ' DROP DEFAULT' - : ' SET' . $this->getDefaultValueDeclarationSQL($column->toArray()); - $query = 'ALTER ' . $oldColumnName . $defaultClause; - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $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->hasChanged('autoincrement')) { - if ($column->getAutoincrement()) { - // add autoincrement - $seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName); - - $sql[] = 'CREATE SEQUENCE ' . $seqName; - $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ') FROM ' - . $diff->getName($this)->getQuotedName($this) . '))'; - $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; - } - } - - $newComment = $this->getColumnComment($column); - $oldComment = $this->getOldColumnComment($columnDiff); - - if ( - $columnDiff->hasChanged('comment') - || ($columnDiff->fromColumn !== null && $oldComment !== $newComment) - ) { - $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), - $column->getQuotedName($this), - $newComment - ); - } - - if (! $columnDiff->hasChanged('length')) { - continue; - } - - $query = 'ALTER ' . $oldColumnName . ' TYPE ' - . $column->getType()->getSQLDeclaration($column->toArray(), $this); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - } - - foreach ($diff->renamedColumns 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); - } - - $tableSql = []; - - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - $sql = array_merge($sql, $commentsSQL); - - $newName = $diff->getNewName(); - - if ($newName !== false) { - $sql[] = sprintf( - 'ALTER TABLE %s RENAME TO %s', - $diff->getName($this)->getQuotedName($this), - $newName->getQuotedName($this) - ); - } - - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) - ); - } - - return array_merge($sql, $tableSql, $columnSql); - } - - /** - * Checks whether a given column diff is a logically unchanged binary type column. - * - * Used to determine whether a column alteration for a binary type column can be skipped. - * Doctrine's {@link BinaryType} and {@link BlobType} are mapped to the same database column type on this platform - * as this platform does not have a native VARBINARY/BINARY column type. Therefore the comparator - * might detect differences for binary type columns which do not have to be propagated - * to database as there actually is no difference at database level. - * - * @param ColumnDiff $columnDiff The column diff to check against. - * - * @return bool True if the given column diff is an unchanged binary type column, false otherwise. - */ - private function isUnchangedBinaryColumn(ColumnDiff $columnDiff) - { - $columnType = $columnDiff->column->getType(); - - if (! $columnType instanceof BinaryType && ! $columnType instanceof BlobType) { - return false; - } - - $fromColumn = $columnDiff->fromColumn instanceof Column ? $columnDiff->fromColumn : null; - - if ($fromColumn !== null) { - $fromColumnType = $fromColumn->getType(); - - if (! $fromColumnType instanceof BinaryType && ! $fromColumnType instanceof BlobType) { - return false; - } - - return count(array_diff($columnDiff->changedProperties, ['type', 'length', 'fixed'])) === 0; - } - - if ($columnDiff->hasChanged('type')) { - return false; - } - - return count(array_diff($columnDiff->changedProperties, ['length', 'fixed'])) === 0; - } - - /** - * {@inheritdoc} - */ - protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) - { - if (strpos($tableName, '.') !== false) { - [$schema] = explode('.', $tableName); - $oldIndexName = $schema . '.' . $oldIndexName; - } - - return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)]; - } - - /** - * {@inheritdoc} - */ - public function getCommentOnColumnSQL($tableName, $columnName, $comment) - { - $tableName = new Identifier($tableName); - $columnName = new Identifier($columnName); - $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); - - return sprintf( - 'COMMENT ON COLUMN %s.%s IS %s', - $tableName->getQuotedName($this), - $columnName->getQuotedName($this), - $comment - ); - } - - /** - * {@inheritDoc} - */ - public function getCreateSequenceSQL(Sequence $sequence) - { - return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . - ' INCREMENT BY ' . $sequence->getAllocationSize() . - ' MINVALUE ' . $sequence->getInitialValue() . - ' START ' . $sequence->getInitialValue() . - $this->getSequenceCacheSQL($sequence); - } - - /** - * {@inheritDoc} - */ - public function getAlterSequenceSQL(Sequence $sequence) - { - return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . - ' INCREMENT BY ' . $sequence->getAllocationSize() . - $this->getSequenceCacheSQL($sequence); - } - - /** - * Cache definition for sequences - * - * @return string - */ - private function getSequenceCacheSQL(Sequence $sequence) - { - if ($sequence->getCache() > 1) { - return ' CACHE ' . $sequence->getCache(); - } - - return ''; - } - - /** - * {@inheritDoc} - */ - public function getDropSequenceSQL($sequence) - { - if ($sequence instanceof Sequence) { - $sequence = $sequence->getQuotedName($this); - } - - return 'DROP SEQUENCE ' . $sequence . ' CASCADE'; - } - - /** - * {@inheritDoc} - */ - public function getCreateSchemaSQL($schemaName) - { - return 'CREATE SCHEMA ' . $schemaName; - } - - /** - * {@inheritDoc} - */ - public function getDropForeignKeySQL($foreignKey, $table) - { - return $this->getDropConstraintSQL($foreignKey, $table); - } - - /** - * {@inheritDoc} - */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) - { - $queryFields = $this->getColumnDeclarationListSQL($columns); - - if (isset($options['primary']) && ! empty($options['primary'])) { - $keyColumns = array_unique(array_values($options['primary'])); - $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; - } - - $query = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; - - $sql = [$query]; - - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $index) { - $sql[] = $this->getCreateIndexSQL($index, $name); - } - } - - if (isset($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $uniqueConstraint) { - $sql[] = $this->getCreateConstraintSQL($uniqueConstraint, $name); - } - } - - if (isset($options['foreignKeys'])) { - foreach ((array) $options['foreignKeys'] as $definition) { - $sql[] = $this->getCreateForeignKeySQL($definition, $name); - } - } - - return $sql; - } - - /** - * Converts a single boolean value. - * - * First converts the value to its native PHP boolean type - * and passes it to the given callback function to be reconverted - * into any custom representation. - * - * @param mixed $value The value to convert. - * @param callable $callback The callback function to use for converting the real boolean value. - * - * @return mixed - * - * @throws UnexpectedValueException - */ - private function convertSingleBooleanValue($value, $callback) - { - if ($value === null) { - return $callback(null); - } - - if (is_bool($value) || is_numeric($value)) { - return $callback((bool) $value); - } - - if (! is_string($value)) { - return $callback(true); - } - - /** - * Better safe than sorry: http://php.net/in_array#106319 - */ - if (in_array(strtolower(trim($value)), $this->booleanLiterals['false'], true)) { - return $callback(false); - } - - if (in_array(strtolower(trim($value)), $this->booleanLiterals['true'], true)) { - return $callback(true); - } - - throw new UnexpectedValueException("Unrecognized boolean literal '${value}'"); - } - - /** - * Converts one or multiple boolean values. - * - * First converts the value(s) to their native PHP boolean type - * and passes them to the given callback function to be reconverted - * into any custom representation. - * - * @param mixed $item The value(s) to convert. - * @param callable $callback The callback function to use for converting the real boolean value(s). - * - * @return mixed - */ - private function doConvertBooleans($item, $callback) - { - if (is_array($item)) { - foreach ($item as $key => $value) { - $item[$key] = $this->convertSingleBooleanValue($value, $callback); - } - - return $item; - } - - return $this->convertSingleBooleanValue($item, $callback); - } - - /** - * {@inheritDoc} - * - * Postgres wants boolean values converted to the strings 'true'/'false'. - */ - public function convertBooleans($item) - { - if (! $this->useBooleanTrueFalseStrings) { - return parent::convertBooleans($item); - } - - return $this->doConvertBooleans( - $item, - /** - * @param mixed $value - */ - static function ($value) { - if ($value === null) { - return 'NULL'; - } - - return $value === true ? 'true' : 'false'; - } - ); - } - - /** - * {@inheritDoc} - */ - public function convertBooleansToDatabaseValue($item) - { - if (! $this->useBooleanTrueFalseStrings) { - return parent::convertBooleansToDatabaseValue($item); - } - - return $this->doConvertBooleans( - $item, - /** - * @param mixed $value - */ - static function ($value): ?int { - return $value === null ? null : (int) $value; - } - ); - } - - /** - * {@inheritDoc} - */ - public function convertFromBoolean($item) - { - if ($item !== null && in_array(strtolower($item), $this->booleanLiterals['false'], true)) { - return false; - } - - return parent::convertFromBoolean($item); - } - - /** - * {@inheritDoc} - */ - public function getSequenceNextValSQL($sequence) - { - return "SELECT NEXTVAL('" . $sequence . "')"; - } - - /** - * {@inheritDoc} - */ - public function getSetTransactionIsolationSQL($level) - { - return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' - . $this->_getTransactionIsolationLevelSQL($level); - } - - /** - * {@inheritDoc} - */ - public function getBooleanTypeDeclarationSQL(array $column) - { - return 'BOOLEAN'; - } - - /** - * {@inheritDoc} - */ - public function getIntegerTypeDeclarationSQL(array $column) - { - if (! empty($column['autoincrement'])) { - return 'SERIAL'; - } - - return 'INT'; - } - - /** - * {@inheritDoc} - */ - public function getBigIntTypeDeclarationSQL(array $column) - { - if (! empty($column['autoincrement'])) { - return 'BIGSERIAL'; - } - - return 'BIGINT'; - } - - /** - * {@inheritDoc} - */ - public function getSmallIntTypeDeclarationSQL(array $column) - { - if (! empty($column['autoincrement'])) { - return 'SMALLSERIAL'; - } - - return 'SMALLINT'; - } - - /** - * {@inheritDoc} - */ - public function getGuidTypeDeclarationSQL(array $column) - { - return 'UUID'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTypeDeclarationSQL(array $column) - { - return 'TIMESTAMP(0) WITHOUT TIME ZONE'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTzTypeDeclarationSQL(array $column) - { - return 'TIMESTAMP(0) WITH TIME ZONE'; - } - - /** - * {@inheritDoc} - */ - public function getDateTypeDeclarationSQL(array $column) - { - return 'DATE'; - } - - /** - * {@inheritDoc} - */ - public function getTimeTypeDeclarationSQL(array $column) - { - return 'TIME(0) WITHOUT TIME ZONE'; - } - - /** - * {@inheritDoc} - */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) - { - return ''; - } - - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') - : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); - } - - /** - * {@inheritdoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) - { - return 'BYTEA'; - } - - /** - * {@inheritDoc} - */ - public function getClobTypeDeclarationSQL(array $column) - { - return 'TEXT'; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return 'postgresql'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTzFormatString() - { - return 'Y-m-d H:i:sO'; - } - - /** - * {@inheritDoc} - */ - public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) - { - return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; - } - - /** - * {@inheritDoc} - */ - public function getTruncateTableSQL($tableName, $cascade = false) - { - $tableIdentifier = new Identifier($tableName); - $sql = 'TRUNCATE ' . $tableIdentifier->getQuotedName($this); - - if ($cascade) { - $sql .= ' CASCADE'; - } - - return $sql; - } - - /** - * {@inheritDoc} - */ - public function getReadLockSQL() - { - return 'FOR SHARE'; - } - - /** - * {@inheritDoc} - */ - 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', - ]; - } - - /** - * {@inheritDoc} - */ - public function getVarcharMaxLength() - { - return 65535; - } - - /** - * {@inheritdoc} - */ - public function getBinaryMaxLength() - { - return 0; - } - - /** - * {@inheritdoc} - */ - public function getBinaryDefaultLength() - { - return 0; - } - - /** - * {@inheritdoc} - */ - public function hasNativeJsonType() - { - return true; - } - - /** - * {@inheritDoc} - * - * @deprecated Implement {@link createReservedKeywordsList()} instead. - */ - protected function getReservedKeywordsClass() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'PostgreSQL94Platform::getReservedKeywordsClass() is deprecated,' - . ' use PostgreSQL94Platform::createReservedKeywordsList() instead.' - ); - - return Keywords\PostgreSQL94Keywords::class; - } - - /** - * {@inheritDoc} - */ - public function getBlobTypeDeclarationSQL(array $column) - { - return 'BYTEA'; - } - - /** - * {@inheritdoc} - */ - public function getDefaultValueDeclarationSQL($column) - { - if ($this->isSerialColumn($column)) { - return ''; - } - - return parent::getDefaultValueDeclarationSQL($column); - } - - /** - * {@inheritdoc} - */ - public function supportsColumnCollation() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function getColumnCollationDeclarationSQL($collation) - { - return 'COLLATE ' . $this->quoteSingleIdentifier($collation); - } - - /** - * {@inheritdoc} - */ - public function getJsonTypeDeclarationSQL(array $column) - { - if (! empty($column['jsonb'])) { - return 'JSONB'; - } - - return 'JSON'; - } - - /** - * @param mixed[] $column - */ - private function isSerialColumn(array $column): bool - { - return isset($column['type'], $column['autoincrement']) - && $column['autoincrement'] === true - && $this->isNumericType($column['type']); - } - - /** - * 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'); - } - - $oldTypeIsNumeric = $this->isNumericType($columnDiff->fromColumn->getType()); - $newTypeIsNumeric = $this->isNumericType($columnDiff->column->getType()); - - // default should not be changed when switching between numeric types and the default comes from a sequence - return $columnDiff->hasChanged('type') - && ! ($oldTypeIsNumeric && $newTypeIsNumeric && $columnDiff->column->getAutoincrement()); - } - - private function isNumericType(Type $type): bool - { - return $type instanceof IntegerType || $type instanceof BigIntType; - } - - private function getOldColumnComment(ColumnDiff $columnDiff): ?string - { - return $columnDiff->fromColumn !== null ? $this->getColumnComment($columnDiff->fromColumn) : null; - } - - public function getListTableMetadataSQL(string $table, ?string $schema = null): string - { - if ($schema !== null) { - $table = $schema . '.' . $table; - } - - return sprintf( - <<<'SQL' -SELECT obj_description(%s::regclass) AS table_comment; -SQL - , - $this->quoteStringLiteral($table) - ); - } } diff --git a/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php b/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php new file mode 100644 index 000000000..c3e41d730 --- /dev/null +++ b/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php @@ -0,0 +1,1362 @@ + [ + 't', + 'true', + 'y', + 'yes', + 'on', + '1', + ], + 'false' => [ + 'f', + 'false', + 'n', + 'no', + 'off', + '0', + ], + ]; + + /** + * PostgreSQL has different behavior with some drivers + * with regard to how booleans have to be handled. + * + * Enables use of 'true'/'false' or otherwise 1 and 0 instead. + * + * @param bool $flag + * + * @return void + */ + public function setUseBooleanTrueFalseStrings($flag) + { + $this->useBooleanTrueFalseStrings = (bool) $flag; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($string, $start, $length = null) + { + if ($length === null) { + return 'SUBSTRING(' . $string . ' FROM ' . $start . ')'; + } + + return 'SUBSTRING(' . $string . ' FROM ' . $start . ' FOR ' . $length . ')'; + } + + /** + * {@inheritDoc} + * + * @deprecated Generate dates within the application. + */ + public function getNowExpression() + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4753', + 'PostgreSQLPlatform::getNowExpression() is deprecated. Generate dates within the application.', + ); + + return 'LOCALTIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'SIMILAR TO'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos !== false) { + $str = $this->getSubstringExpression($str, $startPos); + + return 'CASE WHEN (POSITION(' . $substr . ' IN ' . $str . ') = 0) THEN 0' + . ' ELSE (POSITION(' . $substr . ' IN ' . $str . ') + ' . $startPos . ' - 1) END'; + } + + return 'POSITION(' . $substr . ' IN ' . $str . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + if ($unit === DateIntervalUnit::QUARTER) { + $interval *= 3; + $unit = DateIntervalUnit::MONTH; + } + + return '(' . $date . ' ' . $operator . ' (' . $interval . " || ' " . $unit . "')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; + } + + public function getCurrentDatabaseExpression(): string + { + return 'CURRENT_DATABASE()'; + } + + /** + * {@inheritDoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritdoc} + * + * @deprecated + */ + public function getDefaultSchemaName() + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + + return 'public'; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function supportsPartialIndexes() + { + return true; + } + + /** + * {@inheritdoc} + * + * @deprecated + */ + public function usesSequenceEmulatedIdentityColumns() + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + + return true; + } + + /** + * {@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() + { + return true; + } + + /** + * {@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() + { + return 'SELECT datname FROM pg_database'; + } + + /** + * {@inheritDoc} + * + * @deprecated Use {@see PostgreSQLSchemaManager::listSchemaNames()} instead. + */ + public function getListNamespacesSQL() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'PostgreSQLPlatform::getListNamespacesSQL() is deprecated,' + . ' use PostgreSQLSchemaManager::listSchemaNames() instead.', + ); + + return "SELECT schema_name AS nspname + FROM information_schema.schemata + WHERE schema_name NOT LIKE 'pg\_%' + AND schema_name != 'information_schema'"; + } + + /** + * {@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, + 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'"; + } + + /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT quote_ident(table_name) AS table_name, + table_schema AS schema_name + FROM information_schema.tables + WHERE table_schema NOT LIKE 'pg\_%' + AND table_schema != 'information_schema' + AND table_name != 'geometry_columns' + AND table_name != 'spatial_ref_sys' + AND table_type != 'VIEW'"; + } + + /** + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. + */ + public function getListViewsSQL($database) + { + return 'SELECT quote_ident(table_name) AS viewname, + table_schema AS schemaname, + view_definition AS definition + FROM information_schema.views + WHERE view_definition IS NOT 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 + * + * @return string + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + return 'SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef + FROM pg_catalog.pg_constraint r + WHERE r.conrelid = + ( + SELECT c.oid + FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n + WHERE ' . $this->getTableWhereClause($table) . " AND n.oid = c.relnamespace + ) + AND r.contype = 'f'"; + } + + /** + * @deprecated + * + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + $table = new Identifier($table); + $table = $this->quoteStringLiteral($table->getName()); + + return sprintf( + <<<'SQL' +SELECT + quote_ident(relname) as relname +FROM + pg_class +WHERE oid IN ( + SELECT indexrelid + FROM pg_index, pg_class + WHERE pg_class.relname = %s + AND pg_class.oid = pg_index.indrelid + AND (indisunique = 't' OR indisprimary = 't') + ) +SQL + , + $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 + */ + public function getListTableIndexesSQL($table, $database = null) + { + return 'SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary, + pg_index.indkey, pg_index.indrelid, + pg_get_expr(indpred, indrelid) AS where + FROM pg_class, pg_index + WHERE oid IN ( + SELECT indexrelid + FROM pg_index si, pg_class sc, pg_namespace sn + WHERE ' . $this->getTableWhereClause($table, 'sc', 'sn') . ' + AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid + ) AND pg_index.indexrelid = oid'; + } + + /** + * @param string $table + * @param string $classAlias + * @param string $namespaceAlias + */ + private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n'): string + { + $whereClause = $namespaceAlias . ".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND "; + if (strpos($table, '.') !== false) { + [$schema, $table] = explode('.', $table); + $schema = $this->quoteStringLiteral($schema); + } else { + $schema = 'ANY(current_schemas(false))'; + } + + $table = new Identifier($table); + $table = $this->quoteStringLiteral($table->getName()); + + return $whereClause . sprintf( + '%s.relname = %s AND %s.nspname = %s', + $classAlias, + $table, + $namespaceAlias, + $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) + { + return "SELECT + 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, pg_class c, pg_type t, pg_namespace n + WHERE " . $this->getTableWhereClause($table, 'c', 'n') . ' + AND a.attnum > 0 + AND a.attrelid = c.oid + AND a.atttypid = t.oid + AND n.oid = c.relnamespace + ORDER BY a.attnum'; + } + + /** + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = ''; + + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) { + $query .= ' DEFERRABLE'; + } else { + $query .= ' NOT DEFERRABLE'; + } + + if ( + ($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false) + || ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) + ) { + $query .= ' INITIALLY DEFERRED'; + } else { + $query .= ' INITIALLY IMMEDIATE'; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = []; + $commentsSQL = []; + $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( + $addedColumn->getQuotedName($this), + $addedColumn->toArray(), + ); + + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; + + $comment = $this->getColumnComment($addedColumn); + + if ($comment === null || $comment === '') { + continue; + } + + $commentsSQL[] = $this->getCommentOnColumnSQL( + $tableNameSQL, + $addedColumn->getQuotedName($this), + $comment, + ); + } + + foreach ($diff->getDroppedColumns() as $droppedColumn) { + if ($this->onSchemaAlterTableRemoveColumn($droppedColumn, $diff, $columnSql)) { + continue; + } + + $query = 'DROP ' . $droppedColumn->getQuotedName($this); + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; + } + + foreach ($diff->getModifiedColumns() as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + if ($this->isUnchangedBinaryColumn($columnDiff)) { + continue; + } + + $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); + $newColumn = $columnDiff->getNewColumn(); + + $oldColumnName = $oldColumn->getQuotedName($this); + + if ( + $columnDiff->hasTypeChanged() + || $columnDiff->hasPrecisionChanged() + || $columnDiff->hasScaleChanged() + || $columnDiff->hasFixedChanged() + ) { + $type = $newColumn->getType(); + + // SERIAL/BIGSERIAL are not "real" types and we can't alter a column to that type + $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 ' . $tableNameSQL . ' ' . $query; + } + + if ($columnDiff->hasDefaultChanged()) { + $defaultClause = $newColumn->getDefault() === null + ? ' DROP DEFAULT' + : ' SET' . $this->getDefaultValueDeclarationSQL($newColumn->toArray()); + + $query = 'ALTER ' . $oldColumnName . $defaultClause; + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; + } + + if ($columnDiff->hasNotNullChanged()) { + $query = 'ALTER ' . $oldColumnName . ' ' . ($newColumn->getNotnull() ? 'SET' : 'DROP') . ' NOT NULL'; + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; + } + + if ($columnDiff->hasAutoIncrementChanged()) { + if ($newColumn->getAutoincrement()) { + // add autoincrement + $seqName = $this->getIdentitySequenceName( + $table->getName(), + $oldColumnName, + ); + + $sql[] = 'CREATE SEQUENCE ' . $seqName; + $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ') FROM ' + . $tableNameSQL . '))'; + $query = 'ALTER ' . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; + } 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 ' . $tableNameSQL . ' ' . $query; + } + + $oldComment = $this->getOldColumnComment($columnDiff); + $newComment = $this->getColumnComment($newColumn); + + if ( + $columnDiff->hasCommentChanged() + || ($columnDiff->getOldColumn() !== null && $oldComment !== $newComment) + ) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $tableNameSQL, + $newColumn->getQuotedName($this), + $newComment, + ); + } + + if (! $columnDiff->hasLengthChanged()) { + continue; + } + + $query = 'ALTER ' . $oldColumnName . ' TYPE ' + . $newColumn->getType()->getSQLDeclaration($newColumn->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; + } + + foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) + . ' TO ' . $column->getQuotedName($this); + } + + $tableSql = []; + + if (! $this->onSchemaAlterTable($diff, $tableSql)) { + $sql = array_merge($sql, $commentsSQL); + + $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', + $tableNameSQL, + $newName->getQuotedName($this), + ); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff), + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Checks whether a given column diff is a logically unchanged binary type column. + * + * Used to determine whether a column alteration for a binary type column can be skipped. + * Doctrine's {@see BinaryType} and {@see BlobType} are mapped to the same database column type on this platform + * as this platform does not have a native VARBINARY/BINARY column type. Therefore the comparator + * might detect differences for binary type columns which do not have to be propagated + * to database as there actually is no difference at database level. + */ + private function isUnchangedBinaryColumn(ColumnDiff $columnDiff): bool + { + $newColumnType = $columnDiff->getNewColumn()->getType(); + + if (! $newColumnType instanceof BinaryType && ! $newColumnType instanceof BlobType) { + return false; + } + + $oldColumn = $columnDiff->getOldColumn() instanceof Column ? $columnDiff->getOldColumn() : null; + + if ($oldColumn !== null) { + $oldColumnType = $oldColumn->getType(); + + if (! $oldColumnType instanceof BinaryType && ! $oldColumnType instanceof BlobType) { + return false; + } + + return count(array_diff($columnDiff->changedProperties, ['type', 'length', 'fixed'])) === 0; + } + + if ($columnDiff->hasTypeChanged()) { + return false; + } + + return count(array_diff($columnDiff->changedProperties, ['length', 'fixed'])) === 0; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + if (strpos($tableName, '.') !== false) { + [$schema] = explode('.', $tableName); + $oldIndexName = $schema . '.' . $oldIndexName; + } + + return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)]; + } + + /** + * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); + $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); + + return sprintf( + 'COMMENT ON COLUMN %s.%s IS %s', + $tableName->getQuotedName($this), + $columnName->getQuotedName($this), + $comment, + ); + } + + /** + * {@inheritDoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' START ' . $sequence->getInitialValue() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * Cache definition for sequences + */ + private function getSequenceCacheSQL(Sequence $sequence): string + { + if ($sequence->getCache() > 1) { + return ' CACHE ' . $sequence->getCache(); + } + + return ''; + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + return parent::getDropSequenceSQL($sequence) . ' CASCADE'; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + return $this->getDropConstraintSQL($foreignKey, $table); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($name, array $columns, array $options = []) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; + + $sql = [$query]; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index) { + $sql[] = $this->getCreateIndexSQL($index, $name); + } + } + + if (isset($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $uniqueConstraint) { + $sql[] = $this->getCreateConstraintSQL($uniqueConstraint, $name); + } + } + + if (isset($options['foreignKeys'])) { + foreach ($options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $name); + } + } + + return $sql; + } + + /** + * Converts a single boolean value. + * + * First converts the value to its native PHP boolean type + * and passes it to the given callback function to be reconverted + * into any custom representation. + * + * @param mixed $value The value to convert. + * @param callable $callback The callback function to use for converting the real boolean value. + * + * @return mixed + * + * @throws UnexpectedValueException + */ + private function convertSingleBooleanValue($value, $callback) + { + if ($value === null) { + return $callback(null); + } + + if (is_bool($value) || is_numeric($value)) { + return $callback((bool) $value); + } + + if (! is_string($value)) { + return $callback(true); + } + + /** + * Better safe than sorry: http://php.net/in_array#106319 + */ + if (in_array(strtolower(trim($value)), $this->booleanLiterals['false'], true)) { + return $callback(false); + } + + if (in_array(strtolower(trim($value)), $this->booleanLiterals['true'], true)) { + return $callback(true); + } + + throw new UnexpectedValueException(sprintf("Unrecognized boolean literal '%s'", $value)); + } + + /** + * Converts one or multiple boolean values. + * + * First converts the value(s) to their native PHP boolean type + * and passes them to the given callback function to be reconverted + * into any custom representation. + * + * @param mixed $item The value(s) to convert. + * @param callable $callback The callback function to use for converting the real boolean value(s). + * + * @return mixed + */ + private function doConvertBooleans($item, $callback) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + $item[$key] = $this->convertSingleBooleanValue($value, $callback); + } + + return $item; + } + + return $this->convertSingleBooleanValue($item, $callback); + } + + /** + * {@inheritDoc} + * + * Postgres wants boolean values converted to the strings 'true'/'false'. + */ + public function convertBooleans($item) + { + if (! $this->useBooleanTrueFalseStrings) { + return parent::convertBooleans($item); + } + + return $this->doConvertBooleans( + $item, + /** @param mixed $value */ + static function ($value) { + if ($value === null) { + return 'NULL'; + } + + return $value === true ? 'true' : 'false'; + }, + ); + } + + /** + * {@inheritDoc} + */ + public function convertBooleansToDatabaseValue($item) + { + if (! $this->useBooleanTrueFalseStrings) { + return parent::convertBooleansToDatabaseValue($item); + } + + return $this->doConvertBooleans( + $item, + /** @param mixed $value */ + static function ($value): ?int { + return $value === null ? null : (int) $value; + }, + ); + } + + /** + * {@inheritDoc} + */ + public function convertFromBoolean($item) + { + if ($item !== null && in_array(strtolower($item), $this->booleanLiterals['false'], true)) { + return false; + } + + return parent::convertFromBoolean($item); + } + + /** + * {@inheritDoc} + */ + public function getSequenceNextValSQL($sequence) + { + return "SELECT NEXTVAL('" . $sequence . "')"; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' + . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $column) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $column) + { + if (! empty($column['autoincrement'])) { + return 'SERIAL'; + } + + return 'INT'; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $column) + { + if (! empty($column['autoincrement'])) { + return 'BIGSERIAL'; + } + + return 'BIGINT'; + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $column) + { + if (! empty($column['autoincrement'])) { + return 'SMALLSERIAL'; + } + + return 'SMALLINT'; + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $column) + { + return 'UUID'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $column) + { + return 'TIMESTAMP(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $column) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $column) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $column) + { + return 'TIME(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $column) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'BYTEA'; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $column) + { + return 'TEXT'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4749', + 'PostgreSQLPlatform::getName() is deprecated. Identify platforms by their class.', + ); + + return 'postgresql'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sO'; + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableIdentifier = new Identifier($tableName); + $sql = 'TRUNCATE ' . $tableIdentifier->getQuotedName($this); + + if ($cascade) { + $sql .= ' CASCADE'; + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'FOR SHARE'; + } + + /** + * {@inheritDoc} + */ + 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', + ]; + } + + /** + * {@inheritDoc} + * + * @deprecated + */ + public function getVarcharMaxLength() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'PostgreSQLPlatform::getVarcharMaxLength() is deprecated.', + ); + + return 65535; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'PostgreSQLPlatform::getBinaryMaxLength() is deprecated.', + ); + + return 0; + } + + /** + * {@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} + * + * @deprecated + */ + public function hasNativeJsonType() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + + return true; + } + + /** + * {@inheritDoc} + * + * @deprecated Implement {@see createReservedKeywordsList()} instead. + */ + protected function getReservedKeywordsClass() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'PostgreSQLPlatform::getReservedKeywordsClass() is deprecated,' + . ' use PostgreSQLPlatform::createReservedKeywordsList() instead.', + ); + + return Keywords\PostgreSQL94Keywords::class; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $column) + { + return 'BYTEA'; + } + + /** + * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function getDefaultValueDeclarationSQL($column) + { + if (isset($column['autoincrement']) && $column['autoincrement'] === true) { + return ''; + } + + return parent::getDefaultValueDeclarationSQL($column); + } + + /** + * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getJsonTypeDeclarationSQL(array $column) + { + if (! empty($column['jsonb'])) { + return 'JSONB'; + } + + return 'JSON'; + } + + private function getOldColumnComment(ColumnDiff $columnDiff): ?string + { + $oldColumn = $columnDiff->getOldColumn(); + + if ($oldColumn !== null) { + return $this->getColumnComment($oldColumn); + } + + 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) { + $table = $schema . '.' . $table; + } + + return sprintf( + <<<'SQL' +SELECT obj_description(%s::regclass) AS table_comment; +SQL + , + $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 new file mode 100644 index 000000000..e231e8083 --- /dev/null +++ b/doctrine/dbal/src/Platforms/SQLServer/Comparator.php @@ -0,0 +1,53 @@ +databaseCollation = $databaseCollation; + } + + /** + * {@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); + } + + private function normalizeColumns(Table $table): void + { + foreach ($table->getColumns() as $column) { + $options = $column->getPlatformOptions(); + + if (! isset($options['collation']) || $options['collation'] !== $this->databaseCollation) { + continue; + } + + unset($options['collation']); + $column->setPlatformOptions($options); + } + } +} diff --git a/doctrine/dbal/src/Platforms/SQLServer2012Platform.php b/doctrine/dbal/src/Platforms/SQLServer2012Platform.php index 4db8f055c..a8ba2fa0b 100644 --- a/doctrine/dbal/src/Platforms/SQLServer2012Platform.php +++ b/doctrine/dbal/src/Platforms/SQLServer2012Platform.php @@ -2,1729 +2,12 @@ namespace Doctrine\DBAL\Platforms; -use Doctrine\DBAL\Exception\InvalidLockMode; -use Doctrine\DBAL\LockMode; -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\Sequence; -use Doctrine\DBAL\Schema\Table; -use Doctrine\DBAL\Schema\TableDiff; -use Doctrine\Deprecations\Deprecation; -use InvalidArgumentException; - -use function array_merge; -use function array_unique; -use function array_values; -use function count; -use function crc32; -use function dechex; -use function explode; -use function func_get_args; -use function implode; -use function is_array; -use function is_bool; -use function is_numeric; -use function is_string; -use function preg_match; -use function preg_match_all; -use function sprintf; -use function str_replace; -use function strpos; -use function strtoupper; -use function substr_count; - -use const PREG_OFFSET_CAPTURE; - /** - * Provides the behavior, features and SQL dialect of the Microsoft SQL Server 2012 database platform. + * Provides the behavior, features and SQL dialect of the Microsoft SQL Server database platform + * of the oldest supported version. + * + * @deprecated Use {@see SQLServerPlatform} instead. */ -class SQLServer2012Platform extends AbstractPlatform +class SQLServer2012Platform extends SQLServerPlatform { - /** - * {@inheritdoc} - */ - public function getCurrentDateSQL() - { - return $this->getConvertExpression('date', 'GETDATE()'); - } - - /** - * {@inheritdoc} - */ - public function getCurrentTimeSQL() - { - return $this->getConvertExpression('time', 'GETDATE()'); - } - - /** - * Returns an expression that converts an expression of one data type to another. - * - * @param string $dataType The target native data type. Alias data types cannot be used. - * @param string $expression The SQL expression to convert. - * - * @return string - */ - private function getConvertExpression($dataType, $expression) - { - return sprintf('CONVERT(%s, %s)', $dataType, $expression); - } - - /** - * {@inheritdoc} - */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) - { - $factorClause = ''; - - if ($operator === '-') { - $factorClause = '-1 * '; - } - - return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; - } - - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) - { - return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; - } - - /** - * {@inheritDoc} - * - * Microsoft SQL Server prefers "autoincrement" identity columns - * since sequences can only be emulated with a table. - */ - public function prefersIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - * - * Microsoft SQL Server supports this through AUTO_INCREMENT columns. - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsReleaseSavepoints() - { - return false; - } - - /** - * {@inheritdoc} - */ - public function supportsSchemas() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function getDefaultSchemaName() - { - return 'dbo'; - } - - /** - * {@inheritDoc} - */ - public function supportsColumnCollation() - { - return true; - } - - public function supportsSequences(): bool - { - return true; - } - - public function getAlterSequenceSQL(Sequence $sequence): string - { - return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . - ' INCREMENT BY ' . $sequence->getAllocationSize(); - } - - public function getCreateSequenceSQL(Sequence $sequence): string - { - return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . - ' START WITH ' . $sequence->getInitialValue() . - ' INCREMENT BY ' . $sequence->getAllocationSize() . - ' MINVALUE ' . $sequence->getInitialValue(); - } - - /** - * {@inheritdoc} - */ - public function getDropSequenceSQL($sequence): string - { - if ($sequence instanceof Sequence) { - $sequence = $sequence->getQuotedName($this); - } - - return 'DROP SEQUENCE ' . $sequence; - } - - /** - * {@inheritdoc} - */ - public function getListSequencesSQL($database) - { - return 'SELECT seq.name, - CAST( - seq.increment AS VARCHAR(MAX) - ) AS increment, -- CAST avoids driver error for sql_variant type - CAST( - seq.start_value AS VARCHAR(MAX) - ) AS start_value -- CAST avoids driver error for sql_variant type - FROM sys.sequences AS seq'; - } - - /** - * {@inheritdoc} - */ - public function getSequenceNextValSQL($sequence) - { - return 'SELECT NEXT VALUE FOR ' . $sequence; - } - - /** - * {@inheritDoc} - */ - public function hasNativeGuidType() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getCreateDatabaseSQL($name) - { - return 'CREATE DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getDropDatabaseSQL($name) - { - return 'DROP DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - public function supportsCreateDropDatabase() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getCreateSchemaSQL($schemaName) - { - return 'CREATE SCHEMA ' . $schemaName; - } - - /** - * {@inheritDoc} - */ - public function getDropForeignKeySQL($foreignKey, $table) - { - if (! $foreignKey instanceof ForeignKeyConstraint) { - $foreignKey = new Identifier($foreignKey); - } - - if (! $table instanceof Table) { - $table = new Identifier($table); - } - - $foreignKey = $foreignKey->getQuotedName($this); - $table = $table->getQuotedName($this); - - return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; - } - - /** - * {@inheritDoc} - */ - public function getDropIndexSQL($index, $table = null) - { - if ($index instanceof Index) { - $index = $index->getQuotedName($this); - } elseif (! is_string($index)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.' - ); - } - - if (! isset($table)) { - return 'DROP INDEX ' . $index; - } - - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } - - return sprintf( - <<getCommentOnTableSQL($name, $tableComment); - } - - // @todo does other code breaks because of this? - // force primary keys to be not null - foreach ($columns as &$column) { - if (! empty($column['primary'])) { - $column['notnull'] = true; - } - - // Build default constraints SQL statements. - if (isset($column['default'])) { - $defaultConstraintsSql[] = 'ALTER TABLE ' . $name . - ' ADD' . $this->getDefaultConstraintDeclarationSQL($name, $column); - } - - if (empty($column['comment']) && ! is_numeric($column['comment'])) { - continue; - } - - $commentsSql[] = $this->getCreateColumnCommentSQL($name, $column['name'], $column['comment']); - } - - $columnListSql = $this->getColumnDeclarationListSQL($columns); - - if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $constraintName => $definition) { - $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); - } - } - - if (isset($options['primary']) && ! empty($options['primary'])) { - $flags = ''; - if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) { - $flags = ' NONCLUSTERED'; - } - - $columnListSql .= ', PRIMARY KEY' . $flags - . ' (' . implode(', ', array_unique(array_values($options['primary']))) . ')'; - } - - $query = 'CREATE TABLE ' . $name . ' (' . $columnListSql; - - $check = $this->getCheckDeclarationSQL($columns); - if (! empty($check)) { - $query .= ', ' . $check; - } - - $query .= ')'; - - $sql = [$query]; - - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $index) { - $sql[] = $this->getCreateIndexSQL($index, $name); - } - } - - if (isset($options['foreignKeys'])) { - foreach ((array) $options['foreignKeys'] as $definition) { - $sql[] = $this->getCreateForeignKeySQL($definition, $name); - } - } - - return array_merge($sql, $commentsSql, $defaultConstraintsSql); - } - - /** - * {@inheritDoc} - */ - public function getCreatePrimaryKeySQL(Index $index, $table) - { - if ($table instanceof Table) { - $identifier = $table->getQuotedName($this); - } else { - $identifier = $table; - } - - $sql = 'ALTER TABLE ' . $identifier . ' ADD PRIMARY KEY'; - - if ($index->hasFlag('nonclustered')) { - $sql .= ' NONCLUSTERED'; - } - - return $sql . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; - } - - /** - * Returns the SQL statement for creating a column comment. - * - * SQL Server does not support native column comments, - * therefore the extended properties functionality is used - * as a workaround to store them. - * The property name used to store column comments is "MS_Description" - * which provides compatibility with SQL Server Management Studio, - * as column comments are stored in the same property there when - * specifying a column's "Description" attribute. - * - * @param string $tableName The quoted table name to which the column belongs. - * @param string $columnName The quoted column name to create the comment for. - * @param string|null $comment The column's comment. - * - * @return string - */ - protected function getCreateColumnCommentSQL($tableName, $columnName, $comment) - { - if (strpos($tableName, '.') !== false) { - [$schemaSQL, $tableSQL] = explode('.', $tableName); - $schemaSQL = $this->quoteStringLiteral($schemaSQL); - $tableSQL = $this->quoteStringLiteral($tableSQL); - } else { - $schemaSQL = "'dbo'"; - $tableSQL = $this->quoteStringLiteral($tableName); - } - - return $this->getAddExtendedPropertySQL( - 'MS_Description', - $comment, - 'SCHEMA', - $schemaSQL, - 'TABLE', - $tableSQL, - 'COLUMN', - $columnName - ); - } - - /** - * Returns the SQL snippet for declaring a default constraint. - * - * @param string $table Name of the table to return the default constraint declaration for. - * @param mixed[] $column Column definition. - * - * @return string - * - * @throws InvalidArgumentException - */ - public function getDefaultConstraintDeclarationSQL($table, array $column) - { - if (! isset($column['default'])) { - throw new InvalidArgumentException("Incomplete column definition. 'default' required."); - } - - $columnName = new Identifier($column['name']); - - return ' CONSTRAINT ' . - $this->generateDefaultConstraintName($table, $column['name']) . - $this->getDefaultValueDeclarationSQL($column) . - ' FOR ' . $columnName->getQuotedName($this); - } - - /** - * {@inheritDoc} - */ - public function getCreateIndexSQL(Index $index, $table) - { - $constraint = parent::getCreateIndexSQL($index, $table); - - if ($index->isUnique() && ! $index->isPrimary()) { - $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); - } - - return $constraint; - } - - /** - * {@inheritDoc} - */ - protected function getCreateIndexSQLFlags(Index $index) - { - $type = ''; - if ($index->isUnique()) { - $type .= 'UNIQUE '; - } - - if ($index->hasFlag('clustered')) { - $type .= 'CLUSTERED '; - } elseif ($index->hasFlag('nonclustered')) { - $type .= 'NONCLUSTERED '; - } - - return $type; - } - - /** - * Extend unique key constraint with required filters - * - * @param string $sql - * - * @return string - */ - private function _appendUniqueConstraintDefinition($sql, Index $index) - { - $fields = []; - - foreach ($index->getQuotedColumns($this) as $field) { - $fields[] = $field . ' IS NOT NULL'; - } - - return $sql . ' WHERE ' . implode(' AND ', $fields); - } - - /** - * {@inheritDoc} - */ - public function getAlterTableSQL(TableDiff $diff) - { - $queryParts = []; - $sql = []; - $columnSql = []; - $commentsSql = []; - - foreach ($diff->addedColumns 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); - } - - $queryParts[] = $addColumnSql; - - $comment = $this->getColumnComment($column); - - if (empty($comment) && ! is_numeric($comment)) { - continue; - } - - $commentsSql[] = $this->getCreateColumnCommentSQL( - $diff->name, - $column->getQuotedName($this), - $comment - ); - } - - foreach ($diff->removedColumns as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - - $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); - } - - foreach ($diff->changedColumns as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - - $column = $columnDiff->column; - $comment = $this->getColumnComment($column); - $hasComment = ! empty($comment) || is_numeric($comment); - - if ($columnDiff->fromColumn instanceof Column) { - $fromComment = $this->getColumnComment($columnDiff->fromColumn); - $hasFromComment = ! empty($fromComment) || is_numeric($fromComment); - - if ($hasFromComment && $hasComment && $fromComment !== $comment) { - $commentsSql[] = $this->getAlterColumnCommentSQL( - $diff->name, - $column->getQuotedName($this), - $comment - ); - } elseif ($hasFromComment && ! $hasComment) { - $commentsSql[] = $this->getDropColumnCommentSQL($diff->name, $column->getQuotedName($this)); - } elseif (! $hasFromComment && $hasComment) { - $commentsSql[] = $this->getCreateColumnCommentSQL( - $diff->name, - $column->getQuotedName($this), - $comment - ); - } - } - - // Do not add query part if only comment has changed. - if ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1) { - continue; - } - - $requireDropDefaultConstraint = $this->alterColumnRequiresDropDefaultConstraint($columnDiff); - - if ($requireDropDefaultConstraint) { - $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( - $diff->name, - $columnDiff->oldColumnName - ); - } - - $columnDef = $column->toArray(); - - $queryParts[] = 'ALTER COLUMN ' . - $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); - - if ( - ! isset($columnDef['default']) - || (! $requireDropDefaultConstraint && ! $columnDiff->hasChanged('default')) - ) { - continue; - } - - $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); - } - - foreach ($diff->renamedColumns as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - - $oldColumnName = new Identifier($oldColumnName); - - $sql[] = "sp_rename '" . - $diff->getName($this)->getQuotedName($this) . '.' . $oldColumnName->getQuotedName($this) . - "', '" . $column->getQuotedName($this) . "', 'COLUMN'"; - - // Recreate default constraint with new column name if necessary (for future reference). - if ($column->getDefault() === null) { - continue; - } - - $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( - $diff->name, - $oldColumnName->getQuotedName($this) - ); - $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); - } - - $tableSql = []; - - if ($this->onSchemaAlterTable($diff, $tableSql)) { - return array_merge($tableSql, $columnSql); - } - - foreach ($queryParts as $query) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - } - - $sql = array_merge($sql, $commentsSql); - - $newName = $diff->getNewName(); - - if ($newName !== false) { - $sql[] = "sp_rename '" . $diff->getName($this)->getQuotedName($this) . "', '" . $newName->getName() . "'"; - - /** - * 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( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) - ); - - return array_merge($sql, $tableSql, $columnSql); - } - - /** - * Returns the SQL clause for adding a default constraint in an ALTER TABLE statement. - * - * @param string $tableName The name of the table to generate the clause for. - * @param Column $column The column to generate the clause for. - * - * @return string - */ - private function getAlterTableAddDefaultConstraintClause($tableName, Column $column) - { - $columnDef = $column->toArray(); - $columnDef['name'] = $column->getQuotedName($this); - - return 'ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $columnDef); - } - - /** - * Returns the SQL clause for dropping an existing default constraint in an ALTER TABLE statement. - * - * @param string $tableName The name of the table to generate the clause for. - * @param string $columnName The name of the column to generate the clause for. - * - * @return string - */ - private function getAlterTableDropDefaultConstraintClause($tableName, $columnName) - { - return 'DROP CONSTRAINT ' . $this->generateDefaultConstraintName($tableName, $columnName); - } - - /** - * Checks whether a column alteration requires dropping its default constraint first. - * - * Different to other database vendors SQL Server implements column default values - * as constraints and therefore changes in a column's default value as well as changes - * in a column's type require dropping the default constraint first before being to - * alter the particular column to the new definition. - * - * @param ColumnDiff $columnDiff The column diff to evaluate. - * - * @return bool True if the column alteration requires dropping its default constraint first, false otherwise. - */ - private function alterColumnRequiresDropDefaultConstraint(ColumnDiff $columnDiff) - { - // We can only decide whether to drop an existing default constraint - // if we know the original default value. - if (! $columnDiff->fromColumn 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) { - 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')) { - 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'); - } - - /** - * Returns the SQL statement for altering a column comment. - * - * SQL Server does not support native column comments, - * therefore the extended properties functionality is used - * as a workaround to store them. - * The property name used to store column comments is "MS_Description" - * which provides compatibility with SQL Server Management Studio, - * as column comments are stored in the same property there when - * specifying a column's "Description" attribute. - * - * @param string $tableName The quoted table name to which the column belongs. - * @param string $columnName The quoted column name to alter the comment for. - * @param string|null $comment The column's comment. - * - * @return string - */ - protected function getAlterColumnCommentSQL($tableName, $columnName, $comment) - { - if (strpos($tableName, '.') !== false) { - [$schemaSQL, $tableSQL] = explode('.', $tableName); - $schemaSQL = $this->quoteStringLiteral($schemaSQL); - $tableSQL = $this->quoteStringLiteral($tableSQL); - } else { - $schemaSQL = "'dbo'"; - $tableSQL = $this->quoteStringLiteral($tableName); - } - - return $this->getUpdateExtendedPropertySQL( - 'MS_Description', - $comment, - 'SCHEMA', - $schemaSQL, - 'TABLE', - $tableSQL, - 'COLUMN', - $columnName - ); - } - - /** - * Returns the SQL statement for dropping a column comment. - * - * SQL Server does not support native column comments, - * therefore the extended properties functionality is used - * as a workaround to store them. - * The property name used to store column comments is "MS_Description" - * which provides compatibility with SQL Server Management Studio, - * as column comments are stored in the same property there when - * specifying a column's "Description" attribute. - * - * @param string $tableName The quoted table name to which the column belongs. - * @param string $columnName The quoted column name to drop the comment for. - * - * @return string - */ - protected function getDropColumnCommentSQL($tableName, $columnName) - { - if (strpos($tableName, '.') !== false) { - [$schemaSQL, $tableSQL] = explode('.', $tableName); - $schemaSQL = $this->quoteStringLiteral($schemaSQL); - $tableSQL = $this->quoteStringLiteral($tableSQL); - } else { - $schemaSQL = "'dbo'"; - $tableSQL = $this->quoteStringLiteral($tableName); - } - - return $this->getDropExtendedPropertySQL( - 'MS_Description', - 'SCHEMA', - $schemaSQL, - 'TABLE', - $tableSQL, - 'COLUMN', - $columnName - ); - } - - /** - * {@inheritdoc} - */ - protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) - { - return [sprintf( - "EXEC sp_rename N'%s.%s', N'%s', N'INDEX'", - $tableName, - $oldIndexName, - $index->getQuotedName($this) - ), - ]; - } - - /** - * Returns the SQL statement for adding an extended property to a database object. - * - * @link http://msdn.microsoft.com/en-us/library/ms180047%28v=sql.90%29.aspx - * - * @param string $name The name of the property to add. - * @param string|null $value The value of the property to add. - * @param string|null $level0Type The type of the object at level 0 the property belongs to. - * @param string|null $level0Name The name of the object at level 0 the property belongs to. - * @param string|null $level1Type The type of the object at level 1 the property belongs to. - * @param string|null $level1Name The name of the object at level 1 the property belongs to. - * @param string|null $level2Type The type of the object at level 2 the property belongs to. - * @param string|null $level2Name The name of the object at level 2 the property belongs to. - * - * @return string - */ - public function getAddExtendedPropertySQL( - $name, - $value = null, - $level0Type = null, - $level0Name = null, - $level1Type = null, - $level1Name = null, - $level2Type = null, - $level2Name = null - ) { - return 'EXEC sp_addextendedproperty ' . - 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . - 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; - } - - /** - * Returns the SQL statement for dropping an extended property from a database object. - * - * @link http://technet.microsoft.com/en-gb/library/ms178595%28v=sql.90%29.aspx - * - * @param string $name The name of the property to drop. - * @param string|null $level0Type The type of the object at level 0 the property belongs to. - * @param string|null $level0Name The name of the object at level 0 the property belongs to. - * @param string|null $level1Type The type of the object at level 1 the property belongs to. - * @param string|null $level1Name The name of the object at level 1 the property belongs to. - * @param string|null $level2Type The type of the object at level 2 the property belongs to. - * @param string|null $level2Name The name of the object at level 2 the property belongs to. - * - * @return string - */ - public function getDropExtendedPropertySQL( - $name, - $level0Type = null, - $level0Name = null, - $level1Type = null, - $level1Name = null, - $level2Type = null, - $level2Name = null - ) { - return 'EXEC sp_dropextendedproperty ' . - 'N' . $this->quoteStringLiteral($name) . ', ' . - 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; - } - - /** - * Returns the SQL statement for updating an extended property of a database object. - * - * @link http://msdn.microsoft.com/en-us/library/ms186885%28v=sql.90%29.aspx - * - * @param string $name The name of the property to update. - * @param string|null $value The value of the property to update. - * @param string|null $level0Type The type of the object at level 0 the property belongs to. - * @param string|null $level0Name The name of the object at level 0 the property belongs to. - * @param string|null $level1Type The type of the object at level 1 the property belongs to. - * @param string|null $level1Name The name of the object at level 1 the property belongs to. - * @param string|null $level2Type The type of the object at level 2 the property belongs to. - * @param string|null $level2Name The name of the object at level 2 the property belongs to. - * - * @return string - */ - public function getUpdateExtendedPropertySQL( - $name, - $value = null, - $level0Type = null, - $level0Name = null, - $level1Type = null, - $level1Name = null, - $level2Type = null, - $level2Name = null - ) { - return 'EXEC sp_updateextendedproperty ' . - 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . - 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; - } - - /** - * {@inheritDoc} - */ - public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) - { - return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; - } - - /** - * {@inheritDoc} - */ - public function getListTablesSQL() - { - // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams - // Category 2 must be ignored as it is "MS SQL Server 'pseudo-system' object[s]" for replication - return 'SELECT name, SCHEMA_NAME (uid) AS schema_name FROM sysobjects' - . " WHERE type = 'U' AND name != 'sysdiagrams' AND category != 2 ORDER BY name"; - } - - /** - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - return "SELECT 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(prop.value AS NVARCHAR(MAX)) AS comment -- CAST avoids driver error for sql_variant type - 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' - WHERE obj.type = 'U' - AND " . $this->getTableWhereClause($table, 'scm.name', 'obj.name'); - } - - /** - * @param string $table - * @param string|null $database - * - * @return string - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - return 'SELECT 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 - WHERE ' . - $this->getTableWhereClause($table, 'SCHEMA_NAME (f.schema_id)', 'OBJECT_NAME (f.parent_object_id)') . - ' ORDER BY fc.constraint_column_id'; - } - - /** - * {@inheritDoc} - */ - public function getListTableIndexesSQL($table, $database = null) - { - return "SELECT 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 - WHERE " . $this->getTableWhereClause($table, 'scm.name', 'tbl.name') . ' - ORDER BY idx.index_id ASC, idxcol.key_ordinal ASC'; - } - - /** - * {@inheritDoc} - */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; - } - - /** - * {@inheritDoc} - */ - public function getListViewsSQL($database) - { - return "SELECT name FROM sysobjects WHERE type = 'V' ORDER BY name"; - } - - /** - * 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. - * - * @return string - */ - private function getTableWhereClause($table, $schemaColumn, $tableColumn) - { - if (strpos($table, '.') !== false) { - [$schema, $table] = explode('.', $table); - $schema = $this->quoteStringLiteral($schema); - $table = $this->quoteStringLiteral($table); - } else { - $schema = 'SCHEMA_NAME()'; - $table = $this->quoteStringLiteral($table); - } - - return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); - } - - /** - * {@inheritDoc} - */ - public function getDropViewSQL($name) - { - return 'DROP VIEW ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) - { - if ($startPos === false) { - return 'CHARINDEX(' . $substr . ', ' . $str . ')'; - } - - return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')'; - } - - /** - * {@inheritDoc} - */ - public function getModExpression($expression1, $expression2) - { - return $expression1 . ' % ' . $expression2; - } - - /** - * {@inheritDoc} - */ - public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false) - { - if ($char === false) { - switch ($mode) { - case TrimMode::LEADING: - $trimFn = 'LTRIM'; - break; - - case TrimMode::TRAILING: - $trimFn = 'RTRIM'; - break; - - default: - return 'LTRIM(RTRIM(' . $str . '))'; - } - - return $trimFn . '(' . $str . ')'; - } - - $pattern = "'%[^' + " . $char . " + ']%'"; - - if ($mode === TrimMode::LEADING) { - return 'stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)'; - } - - if ($mode === TrimMode::TRAILING) { - return 'reverse(stuff(reverse(' . $str . '), 1, ' - . 'patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))'; - } - - return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)), 1, ' - . 'patindex(' . $pattern . ', reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str - . ') - 1, null))) - 1, null))'; - } - - /** - * {@inheritDoc} - */ - public function getConcatExpression() - { - return sprintf('CONCAT(%s)', implode(', ', func_get_args())); - } - - /** - * {@inheritDoc} - */ - public function getListDatabasesSQL() - { - return 'SELECT * FROM sys.databases'; - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link SQLServerSchemaManager::listSchemaNames()} instead. - */ - public function getListNamespacesSQL() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4503', - 'SQLServer2012Platform::getListNamespacesSQL() is deprecated,' - . ' use SQLServerSchemaManager::listSchemaNames() instead.' - ); - - return "SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')"; - } - - /** - * {@inheritDoc} - */ - public function getSubstringExpression($string, $start, $length = null) - { - if ($length !== null) { - return 'SUBSTRING(' . $string . ', ' . $start . ', ' . $length . ')'; - } - - return 'SUBSTRING(' . $string . ', ' . $start . ', LEN(' . $string . ') - ' . $start . ' + 1)'; - } - - /** - * {@inheritDoc} - */ - public function getLengthExpression($column) - { - return 'LEN(' . $column . ')'; - } - - public function getCurrentDatabaseExpression(): string - { - return 'DB_NAME()'; - } - - /** - * {@inheritDoc} - */ - public function getSetTransactionIsolationSQL($level) - { - return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); - } - - /** - * {@inheritDoc} - */ - public function getIntegerTypeDeclarationSQL(array $column) - { - return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getBigIntTypeDeclarationSQL(array $column) - { - return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getSmallIntTypeDeclarationSQL(array $column) - { - return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getGuidTypeDeclarationSQL(array $column) - { - return 'UNIQUEIDENTIFIER'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTzTypeDeclarationSQL(array $column) - { - return 'DATETIMEOFFSET(6)'; - } - - /** - * {@inheritDoc} - */ - public function getAsciiStringTypeDeclarationSQL(array $column): string - { - $length = $column['length'] ?? null; - - if (! isset($column['fixed'])) { - return sprintf('VARCHAR(%d)', $length ?? 255); - } - - return sprintf('CHAR(%d)', $length ?? 255); - } - - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed - ? ($length > 0 ? 'NCHAR(' . $length . ')' : 'CHAR(255)') - : ($length > 0 ? 'NVARCHAR(' . $length . ')' : 'NVARCHAR(255)'); - } - - /** - * {@inheritdoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed - ? 'BINARY(' . ($length > 0 ? $length : 255) . ')' - : 'VARBINARY(' . ($length > 0 ? $length : 255) . ')'; - } - - /** - * {@inheritdoc} - */ - public function getBinaryMaxLength() - { - return 8000; - } - - /** - * {@inheritDoc} - */ - public function getClobTypeDeclarationSQL(array $column) - { - return 'VARCHAR(MAX)'; - } - - /** - * {@inheritDoc} - */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) - { - return ! empty($column['autoincrement']) ? ' IDENTITY' : ''; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTypeDeclarationSQL(array $column) - { - // 3 - microseconds precision length - // http://msdn.microsoft.com/en-us/library/ms187819.aspx - return 'DATETIME2(6)'; - } - - /** - * {@inheritDoc} - */ - public function getDateTypeDeclarationSQL(array $column) - { - return 'DATE'; - } - - /** - * {@inheritDoc} - */ - public function getTimeTypeDeclarationSQL(array $column) - { - return 'TIME(0)'; - } - - /** - * {@inheritDoc} - */ - public function getBooleanTypeDeclarationSQL(array $column) - { - return 'BIT'; - } - - /** - * {@inheritDoc} - */ - protected function doModifyLimitQuery($query, $limit, $offset) - { - if ($limit === null && $offset <= 0) { - return $query; - } - - if ($this->shouldAddOrderBy($query)) { - if (preg_match('/^SELECT\s+DISTINCT/im', $query) > 0) { - // SQL Server won't let us order by a non-selected column in a DISTINCT query, - // so we have to do this madness. This says, order by the first column in the - // result. SQL Server's docs say that a nonordered query's result order is non- - // deterministic anyway, so this won't do anything that a bunch of update and - // deletes to the table wouldn't do anyway. - $query .= ' ORDER BY 1'; - } else { - // In another DBMS, we could do ORDER BY 0, but SQL Server gets angry if you - // use constant expressions in the order by list. - $query .= ' ORDER BY (SELECT 0)'; - } - } - - // This looks somewhat like MYSQL, but limit/offset are in inverse positions - // Supposedly SQL:2008 core standard. - // Per TSQL spec, FETCH NEXT n ROWS ONLY is not valid without OFFSET n ROWS. - $query .= sprintf(' OFFSET %d ROWS', $offset); - - if ($limit !== null) { - $query .= sprintf(' FETCH NEXT %d ROWS ONLY', $limit); - } - - return $query; - } - - /** - * {@inheritDoc} - */ - public function supportsLimitOffset() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function convertBooleans($item) - { - if (is_array($item)) { - foreach ($item as $key => $value) { - if (! is_bool($value) && ! is_numeric($value)) { - continue; - } - - $item[$key] = (int) (bool) $value; - } - } elseif (is_bool($item) || is_numeric($item)) { - $item = (int) (bool) $item; - } - - return $item; - } - - /** - * {@inheritDoc} - */ - public function getCreateTemporaryTableSnippetSQL() - { - return 'CREATE TABLE'; - } - - /** - * {@inheritDoc} - */ - public function getTemporaryTableName($tableName) - { - return '#' . $tableName; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeFormatString() - { - return 'Y-m-d H:i:s.u'; - } - - /** - * {@inheritDoc} - */ - public function getDateFormatString() - { - return 'Y-m-d'; - } - - /** - * {@inheritDoc} - */ - public function getTimeFormatString() - { - return 'H:i:s'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTzFormatString() - { - return 'Y-m-d H:i:s.u P'; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return 'mssql'; - } - - /** - * {@inheritDoc} - */ - 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', - ]; - } - - /** - * {@inheritDoc} - */ - public function createSavePoint($savepoint) - { - return 'SAVE TRANSACTION ' . $savepoint; - } - - /** - * {@inheritDoc} - */ - public function releaseSavePoint($savepoint) - { - return ''; - } - - /** - * {@inheritDoc} - */ - public function rollbackSavePoint($savepoint) - { - return 'ROLLBACK TRANSACTION ' . $savepoint; - } - - /** - * {@inheritdoc} - */ - public function getForeignKeyReferentialActionSQL($action) - { - // RESTRICT is not supported, therefore falling back to NO ACTION. - if (strtoupper($action) === 'RESTRICT') { - return 'NO ACTION'; - } - - return parent::getForeignKeyReferentialActionSQL($action); - } - - public function appendLockHint(string $fromClause, int $lockMode): string - { - switch ($lockMode) { - case LockMode::NONE: - case LockMode::OPTIMISTIC: - return $fromClause; - - case LockMode::PESSIMISTIC_READ: - return $fromClause . ' WITH (HOLDLOCK, ROWLOCK)'; - - case LockMode::PESSIMISTIC_WRITE: - return $fromClause . ' WITH (UPDLOCK, ROWLOCK)'; - - default: - throw InvalidLockMode::fromLockMode($lockMode); - } - } - - /** - * {@inheritDoc} - */ - public function getForUpdateSQL() - { - return ' '; - } - - /** - * {@inheritDoc} - * - * @deprecated Implement {@link createReservedKeywordsList()} instead. - */ - protected function getReservedKeywordsClass() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'QLServer2012Platform::getReservedKeywordsClass() is deprecated,' - . ' use QLServer2012Platform::createReservedKeywordsList() instead.' - ); - - return Keywords\SQLServer2012Keywords::class; - } - - /** - * {@inheritDoc} - */ - public function quoteSingleIdentifier($str) - { - return '[' . str_replace(']', ']]', $str) . ']'; - } - - /** - * {@inheritDoc} - */ - public function getTruncateTableSQL($tableName, $cascade = false) - { - $tableIdentifier = new Identifier($tableName); - - return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); - } - - /** - * {@inheritDoc} - */ - public function getBlobTypeDeclarationSQL(array $column) - { - return 'VARBINARY(MAX)'; - } - - /** - * {@inheritdoc} - * - * Modifies column declaration order as it differs in Microsoft SQL Server. - */ - public function getColumnDeclarationSQL($name, array $column) - { - if (isset($column['columnDefinition'])) { - $columnDef = $this->getCustomTypeDeclarationSQL($column); - } else { - $collation = ! empty($column['collation']) ? - ' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : ''; - - $notnull = ! empty($column['notnull']) ? ' NOT NULL' : ''; - - $unique = ! empty($column['unique']) ? - ' ' . $this->getUniqueFieldDeclarationSQL() : ''; - - $check = ! empty($column['check']) ? - ' ' . $column['check'] : ''; - - $typeDecl = $column['type']->getSQLDeclaration($column, $this); - $columnDef = $typeDecl . $collation . $notnull . $unique . $check; - } - - return $name . ' ' . $columnDef; - } - - protected function getLikeWildcardCharacters(): string - { - return parent::getLikeWildcardCharacters() . '[]^'; - } - - /** - * Returns a unique default constraint name for a table and column. - * - * @param string $table Name of the table to generate the unique default constraint name for. - * @param string $column Name of the column in the table to generate the unique default constraint name for. - * - * @return string - */ - private function generateDefaultConstraintName($table, $column) - { - return 'DF_' . $this->generateIdentifierName($table) . '_' . $this->generateIdentifierName($column); - } - - /** - * Returns a hash value for a given identifier. - * - * @param string $identifier Identifier to generate a hash value for. - * - * @return string - */ - private function generateIdentifierName($identifier) - { - // Always generate name for unquoted identifiers to ensure consistency. - $identifier = new Identifier($identifier); - - return strtoupper(dechex(crc32($identifier->getName()))); - } - - protected function getCommentOnTableSQL(string $tableName, ?string $comment): string - { - return sprintf( - <<<'SQL' - EXEC sys.sp_addextendedproperty @name=N'MS_Description', - @value=N%s, @level0type=N'SCHEMA', @level0name=N'dbo', - @level1type=N'TABLE', @level1name=N%s - SQL - , - $this->quoteStringLiteral((string) $comment), - $this->quoteStringLiteral($tableName) - ); - } - - public function getListTableMetadataSQL(string $table): string - { - return sprintf( - <<<'SQL' - SELECT - 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 - WHERE - (tbl.name=N%s and SCHEMA_NAME(tbl.schema_id)=N'dbo' and p.name=N'MS_Description') - SQL - , - $this->quoteStringLiteral($table) - ); - } - - /** - * @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 - // but can be in a newline - $matches = []; - $matchesCount = preg_match_all('/[\\s]+order\\s+by\\s/im', $query, $matches, PREG_OFFSET_CAPTURE); - if ($matchesCount === 0) { - return true; - } - - // ORDER BY instance may be in a subquery after ORDER BY - // e.g. SELECT col1 FROM test ORDER BY (SELECT col2 from test ORDER BY col2) - // if in the searched query ORDER BY clause was found where - // number of open parentheses after the occurrence of the clause is equal to - // number of closed brackets after the occurrence of the clause, - // it means that ORDER BY is included in the query being checked - while ($matchesCount > 0) { - $orderByPos = $matches[0][--$matchesCount][1]; - $openBracketsCount = substr_count($query, '(', $orderByPos); - $closedBracketsCount = substr_count($query, ')', $orderByPos); - if ($openBracketsCount === $closedBracketsCount) { - return false; - } - } - - return true; - } } diff --git a/doctrine/dbal/src/Platforms/SQLServerPlatform.php b/doctrine/dbal/src/Platforms/SQLServerPlatform.php new file mode 100644 index 000000000..027f7239c --- /dev/null +++ b/doctrine/dbal/src/Platforms/SQLServerPlatform.php @@ -0,0 +1,1821 @@ +getConvertExpression('date', 'GETDATE()'); + } + + /** + * {@inheritdoc} + */ + public function getCurrentTimeSQL() + { + return $this->getConvertExpression('time', 'GETDATE()'); + } + + /** + * Returns an expression that converts an expression of one data type to another. + * + * @param string $dataType The target native data type. Alias data types cannot be used. + * @param string $expression The SQL expression to convert. + */ + private function getConvertExpression($dataType, $expression): string + { + return sprintf('CONVERT(%s, %s)', $dataType, $expression); + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $factorClause = ''; + + if ($operator === '-') { + $factorClause = '-1 * '; + } + + return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server prefers "autoincrement" identity columns + * since sequences can only be emulated with a table. + * + * @deprecated + */ + public function prefersIdentityColumns() + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/1519', + 'SQLServerPlatform::prefersIdentityColumns() is deprecated.', + ); + + return true; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@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() + { + return true; + } + + public function supportsSequences(): bool + { + return true; + } + + public function getAlterSequenceSQL(Sequence $sequence): string + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + public function getCreateSequenceSQL(Sequence $sequence): string + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' START WITH ' . $sequence->getInitialValue() . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue(); + } + + /** + * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. + */ + public function getListSequencesSQL($database) + { + return 'SELECT seq.name, + CAST( + seq.increment AS VARCHAR(MAX) + ) AS increment, -- CAST avoids driver error for sql_variant type + CAST( + seq.start_value AS VARCHAR(MAX) + ) AS start_value -- CAST avoids driver error for sql_variant type + FROM sys.sequences AS seq'; + } + + /** + * {@inheritdoc} + */ + public function getSequenceNextValSQL($sequence) + { + return 'SELECT NEXT VALUE FOR ' . $sequence; + } + + /** + * {@inheritDoc} + * + * @deprecated + */ + public function hasNativeGuidType() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5509', + '%s is deprecated.', + __METHOD__, + ); + + return true; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if (! $foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = new Identifier($foreignKey); + } + + if (! $table instanceof Table) { + $table = new Identifier($table); + } + + $foreignKey = $foreignKey->getQuotedName($this); + $table = $table->getQuotedName($this); + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + /** + * {@inheritDoc} + */ + 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 . '.', + ); + } + + 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 . '.', + ); + } + + return 'DROP INDEX ' . $index . ' ON ' . $table; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($name, array $columns, array $options = []) + { + $defaultConstraintsSql = []; + $commentsSql = []; + + $tableComment = $options['comment'] ?? null; + if ($tableComment !== null) { + $commentsSql[] = $this->getCommentOnTableSQL($name, $tableComment); + } + + // @todo does other code breaks because of this? + // force primary keys to be not null + foreach ($columns as &$column) { + if (! empty($column['primary'])) { + $column['notnull'] = true; + } + + // Build default constraints SQL statements. + if (isset($column['default'])) { + $defaultConstraintsSql[] = 'ALTER TABLE ' . $name . + ' ADD' . $this->getDefaultConstraintDeclarationSQL($name, $column); + } + + if (empty($column['comment']) && ! is_numeric($column['comment'])) { + continue; + } + + $commentsSql[] = $this->getCreateColumnCommentSQL($name, $column['name'], $column['comment']); + } + + $columnListSql = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $constraintName => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); + } + } + + if (isset($options['primary']) && ! empty($options['primary'])) { + $flags = ''; + if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) { + $flags = ' NONCLUSTERED'; + } + + $columnListSql .= ', PRIMARY KEY' . $flags + . ' (' . implode(', ', array_unique(array_values($options['primary']))) . ')'; + } + + $query = 'CREATE TABLE ' . $name . ' (' . $columnListSql; + + $check = $this->getCheckDeclarationSQL($columns); + if (! empty($check)) { + $query .= ', ' . $check; + } + + $query .= ')'; + + $sql = [$query]; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index) { + $sql[] = $this->getCreateIndexSQL($index, $name); + } + } + + if (isset($options['foreignKeys'])) { + foreach ($options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $name); + } + } + + return array_merge($sql, $commentsSql, $defaultConstraintsSql); + } + + /** + * {@inheritDoc} + */ + 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; + } + + $sql = 'ALTER TABLE ' . $identifier . ' ADD PRIMARY KEY'; + + if ($index->hasFlag('nonclustered')) { + $sql .= ' NONCLUSTERED'; + } + + return $sql . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; + } + + /** + * Returns the SQL statement for creating a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to create the comment for. + * @param string|null $comment The column's comment. + * + * @return string + */ + protected function getCreateColumnCommentSQL($tableName, $columnName, $comment) + { + if (strpos($tableName, '.') !== false) { + [$schemaSQL, $tableSQL] = explode('.', $tableName); + $schemaSQL = $this->quoteStringLiteral($schemaSQL); + $tableSQL = $this->quoteStringLiteral($tableSQL); + } else { + $schemaSQL = "'dbo'"; + $tableSQL = $this->quoteStringLiteral($tableName); + } + + return $this->getAddExtendedPropertySQL( + 'MS_Description', + $comment, + 'SCHEMA', + $schemaSQL, + 'TABLE', + $tableSQL, + 'COLUMN', + $columnName, + ); + } + + /** + * Returns the SQL snippet for declaring a default constraint. + * + * @internal The method should be only used from within the SQLServerPlatform class hierarchy. + * + * @param string $table Name of the table to return the default constraint declaration for. + * @param mixed[] $column Column definition. + * + * @return string + * + * @throws InvalidArgumentException + */ + public function getDefaultConstraintDeclarationSQL($table, array $column) + { + if (! isset($column['default'])) { + throw new InvalidArgumentException("Incomplete column definition. 'default' required."); + } + + $columnName = new Identifier($column['name']); + + return ' CONSTRAINT ' . + $this->generateDefaultConstraintName($table, $column['name']) . + $this->getDefaultValueDeclarationSQL($column) . + ' FOR ' . $columnName->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function getCreateIndexSQL(Index $index, $table) + { + $constraint = parent::getCreateIndexSQL($index, $table); + + if ($index->isUnique() && ! $index->isPrimary()) { + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + } + + return $constraint; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } + + if ($index->hasFlag('clustered')) { + $type .= 'CLUSTERED '; + } elseif ($index->hasFlag('nonclustered')) { + $type .= 'NONCLUSTERED '; + } + + return $type; + } + + /** + * Extend unique key constraint with required filters + * + * @param string $sql + */ + private function _appendUniqueConstraintDefinition($sql, Index $index): string + { + $fields = []; + + foreach ($index->getQuotedColumns($this) as $field) { + $fields[] = $field . ' IS NOT NULL'; + } + + return $sql . ' WHERE ' . implode(' AND ', $fields); + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $queryParts = []; + $sql = []; + $columnSql = []; + $commentsSql = []; + + $table = $diff->getOldTable() ?? $diff->getName($this); + + $tableName = $table->getName(); + + foreach ($diff->getAddedColumns() as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $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; + + $comment = $this->getColumnComment($column); + + if (empty($comment) && ! is_numeric($comment)) { + continue; + } + + $commentsSql[] = $this->getCreateColumnCommentSQL( + $tableName, + $column->getQuotedName($this), + $comment, + ); + } + + foreach ($diff->getDroppedColumns() as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); + } + + foreach ($diff->getModifiedColumns() as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $newColumn = $columnDiff->getNewColumn(); + $newComment = $this->getColumnComment($newColumn); + $hasNewComment = ! empty($newComment) || is_numeric($newComment); + + $oldColumn = $columnDiff->getOldColumn(); + + if ($oldColumn instanceof Column) { + $oldComment = $this->getColumnComment($oldColumn); + $hasOldComment = ! empty($oldComment) || is_numeric($oldComment); + + if ($hasOldComment && $hasNewComment && $oldComment !== $newComment) { + $commentsSql[] = $this->getAlterColumnCommentSQL( + $tableName, + $newColumn->getQuotedName($this), + $newComment, + ); + } elseif ($hasOldComment && ! $hasNewComment) { + $commentsSql[] = $this->getDropColumnCommentSQL( + $tableName, + $newColumn->getQuotedName($this), + ); + } elseif (! $hasOldComment && $hasNewComment) { + $commentsSql[] = $this->getCreateColumnCommentSQL( + $tableName, + $newColumn->getQuotedName($this), + $newComment, + ); + } + } + + // Do not add query part if only comment has changed. + if ($columnDiff->hasCommentChanged() && count($columnDiff->changedProperties) === 1) { + continue; + } + + $requireDropDefaultConstraint = $this->alterColumnRequiresDropDefaultConstraint($columnDiff); + + if ($requireDropDefaultConstraint) { + $oldColumn = $columnDiff->getOldColumn(); + + if ($oldColumn !== null) { + $oldColumnName = $oldColumn->getName(); + } else { + $oldColumnName = $columnDiff->oldColumnName; + } + + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause($tableName, $oldColumnName); + } + + $columnProperties = $newColumn->toArray(); + + $queryParts[] = 'ALTER COLUMN ' . + $this->getColumnDeclarationSQL($newColumn->getQuotedName($this), $columnProperties); + + if ( + ! isset($columnProperties['default']) + || (! $requireDropDefaultConstraint && ! $columnDiff->hasDefaultChanged()) + ) { + continue; + } + + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($tableName, $newColumn); + } + + $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 '" . $tableNameSQL . '.' . $oldColumnName->getQuotedName($this) . + "', '" . $newColumn->getQuotedName($this) . "', 'COLUMN'"; + + // Recreate default constraint with new column name if necessary (for future reference). + if ($newColumn->getDefault() === null) { + continue; + } + + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( + $tableName, + $oldColumnName->getQuotedName($this), + ); + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($tableName, $newColumn); + } + + $tableSql = []; + + if ($this->onSchemaAlterTable($diff, $tableSql)) { + return array_merge($tableSql, $columnSql); + } + + foreach ($queryParts as $query) { + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; + } + + $sql = array_merge($sql, $commentsSql); + + $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 = array_merge($sql, $this->getRenameTableSQL($tableName, $newName->getName())); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $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. + * + * @param string $tableName The name of the table to generate the clause for. + * @param Column $column The column to generate the clause for. + */ + private function getAlterTableAddDefaultConstraintClause($tableName, Column $column): string + { + $columnDef = $column->toArray(); + $columnDef['name'] = $column->getQuotedName($this); + + return 'ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $columnDef); + } + + /** + * Returns the SQL clause for dropping an existing default constraint in an ALTER TABLE statement. + * + * @param string $tableName The name of the table to generate the clause for. + * @param string $columnName The name of the column to generate the clause for. + */ + private function getAlterTableDropDefaultConstraintClause($tableName, $columnName): string + { + return 'DROP CONSTRAINT ' . $this->generateDefaultConstraintName($tableName, $columnName); + } + + /** + * Checks whether a column alteration requires dropping its default constraint first. + * + * Different to other database vendors SQL Server implements column default values + * as constraints and therefore changes in a column's default value as well as changes + * in a column's type require dropping the default constraint first before being to + * alter the particular column to the new definition. + */ + 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 (! $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 ($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->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->hasTypeChanged() || $columnDiff->hasFixedChanged(); + } + + /** + * Returns the SQL statement for altering a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to alter the comment for. + * @param string|null $comment The column's comment. + * + * @return string + */ + protected function getAlterColumnCommentSQL($tableName, $columnName, $comment) + { + if (strpos($tableName, '.') !== false) { + [$schemaSQL, $tableSQL] = explode('.', $tableName); + $schemaSQL = $this->quoteStringLiteral($schemaSQL); + $tableSQL = $this->quoteStringLiteral($tableSQL); + } else { + $schemaSQL = "'dbo'"; + $tableSQL = $this->quoteStringLiteral($tableName); + } + + return $this->getUpdateExtendedPropertySQL( + 'MS_Description', + $comment, + 'SCHEMA', + $schemaSQL, + 'TABLE', + $tableSQL, + 'COLUMN', + $columnName, + ); + } + + /** + * Returns the SQL statement for dropping a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to drop the comment for. + * + * @return string + */ + protected function getDropColumnCommentSQL($tableName, $columnName) + { + if (strpos($tableName, '.') !== false) { + [$schemaSQL, $tableSQL] = explode('.', $tableName); + $schemaSQL = $this->quoteStringLiteral($schemaSQL); + $tableSQL = $this->quoteStringLiteral($tableSQL); + } else { + $schemaSQL = "'dbo'"; + $tableSQL = $this->quoteStringLiteral($tableName); + } + + return $this->getDropExtendedPropertySQL( + 'MS_Description', + 'SCHEMA', + $schemaSQL, + 'TABLE', + $tableSQL, + 'COLUMN', + $columnName, + ); + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return [sprintf( + "EXEC sp_rename N'%s.%s', N'%s', N'INDEX'", + $tableName, + $oldIndexName, + $index->getQuotedName($this), + ), + ]; + } + + /** + * Returns the SQL statement for adding an extended property to a database object. + * + * @internal The method should be only used from within the SQLServerPlatform class hierarchy. + * + * @link http://msdn.microsoft.com/en-us/library/ms180047%28v=sql.90%29.aspx + * + * @param string $name The name of the property to add. + * @param string|null $value The value of the property to add. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + */ + public function getAddExtendedPropertySQL( + $name, + $value = null, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return 'EXEC sp_addextendedproperty ' . + 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . + 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; + } + + /** + * Returns the SQL statement for dropping an extended property from a database object. + * + * @internal The method should be only used from within the SQLServerPlatform class hierarchy. + * + * @link http://technet.microsoft.com/en-gb/library/ms178595%28v=sql.90%29.aspx + * + * @param string $name The name of the property to drop. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + */ + public function getDropExtendedPropertySQL( + $name, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return 'EXEC sp_dropextendedproperty ' . + 'N' . $this->quoteStringLiteral($name) . ', ' . + 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; + } + + /** + * Returns the SQL statement for updating an extended property of a database object. + * + * @internal The method should be only used from within the SQLServerPlatform class hierarchy. + * + * @link http://msdn.microsoft.com/en-us/library/ms186885%28v=sql.90%29.aspx + * + * @param string $name The name of the property to update. + * @param string|null $value The value of the property to update. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + */ + public function getUpdateExtendedPropertySQL( + $name, + $value = null, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return 'EXEC sp_updateextendedproperty ' . + 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . + 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; + } + + /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * + * {@inheritDoc} + */ + public function getListTablesSQL() + { + // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams + // Category 2 must be ignored as it is "MS SQL Server 'pseudo-system' object[s]" for replication + return 'SELECT name, SCHEMA_NAME (uid) AS schema_name FROM sysobjects' + . " WHERE type = 'U' AND name != 'sysdiagrams' AND category != 2 ORDER BY name"; + } + + /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT 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(prop.value AS NVARCHAR(MAX)) AS comment -- CAST avoids driver error for sql_variant type + 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' + WHERE obj.type = 'U' + AND " . $this->getTableWhereClause($table, 'scm.name', 'obj.name'); + } + + /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * + * @param string $table + * @param string|null $database + * + * @return string + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + return 'SELECT 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 + WHERE ' . + $this->getTableWhereClause($table, 'SCHEMA_NAME (f.schema_id)', 'OBJECT_NAME (f.parent_object_id)') . + ' ORDER BY fc.constraint_column_id'; + } + + /** + * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $database = null) + { + return "SELECT 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 + WHERE " . $this->getTableWhereClause($table, 'scm.name', 'tbl.name') . ' + ORDER BY idx.index_id ASC, idxcol.key_ordinal ASC'; + } + + /** + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. + */ + public function getListViewsSQL($database) + { + return "SELECT name, definition FROM sysobjects + INNER JOIN sys.sql_modules ON sysobjects.id = sys.sql_modules.object_id + WHERE type = 'V' ORDER BY name"; + } + + /** + * 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->quoteStringLiteral($schema); + $table = $this->quoteStringLiteral($table); + } else { + $schema = 'SCHEMA_NAME()'; + $table = $this->quoteStringLiteral($table); + } + + return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos === false) { + return 'CHARINDEX(' . $substr . ', ' . $str . ')'; + } + + return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')'; + } + + /** + * {@inheritDoc} + */ + public function getModExpression($expression1, $expression2) + { + return $expression1 . ' % ' . $expression2; + } + + /** + * {@inheritDoc} + */ + public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false) + { + if ($char === false) { + switch ($mode) { + case TrimMode::LEADING: + $trimFn = 'LTRIM'; + break; + + case TrimMode::TRAILING: + $trimFn = 'RTRIM'; + break; + + default: + return 'LTRIM(RTRIM(' . $str . '))'; + } + + return $trimFn . '(' . $str . ')'; + } + + $pattern = "'%[^' + " . $char . " + ']%'"; + + if ($mode === TrimMode::LEADING) { + return 'stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)'; + } + + if ($mode === TrimMode::TRAILING) { + return 'reverse(stuff(reverse(' . $str . '), 1, ' + . 'patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))'; + } + + return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)), 1, ' + . 'patindex(' . $pattern . ', reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str + . ') - 1, null))) - 1, null))'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + return sprintf('CONCAT(%s)', implode(', ', func_get_args())); + } + + /** + * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. + */ + public function getListDatabasesSQL() + { + return 'SELECT * FROM sys.databases'; + } + + /** + * {@inheritDoc} + * + * @deprecated Use {@see SQLServerSchemaManager::listSchemaNames()} instead. + */ + public function getListNamespacesSQL() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'SQLServerPlatform::getListNamespacesSQL() is deprecated,' + . ' use SQLServerSchemaManager::listSchemaNames() instead.', + ); + + return "SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')"; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($string, $start, $length = null) + { + if ($length !== null) { + return 'SUBSTRING(' . $string . ', ' . $start . ', ' . $length . ')'; + } + + return 'SUBSTRING(' . $string . ', ' . $start . ', LEN(' . $string . ') - ' . $start . ' + 1)'; + } + + /** + * {@inheritDoc} + */ + public function getLengthExpression($column) + { + return 'LEN(' . $column . ')'; + } + + public function getCurrentDatabaseExpression(): string + { + return 'DB_NAME()'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $column) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $column) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $column) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $column) + { + return 'UNIQUEIDENTIFIER'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $column) + { + return 'DATETIMEOFFSET(6)'; + } + + /** + * {@inheritDoc} + */ + public function getAsciiStringTypeDeclarationSQL(array $column): string + { + $length = $column['length'] ?? null; + + if (! isset($column['fixed'])) { + return sprintf('VARCHAR(%d)', $length ?? 255); + } + + return sprintf('CHAR(%d)', $length ?? 255); + } + + /** + * {@inheritDoc} + */ + 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 + ? 'NCHAR(' . ($length > 0 ? $length : 255) . ')' + : 'NVARCHAR(' . ($length > 0 ? $length : 255) . ')'; + } + + /** + * {@inheritdoc} + */ + 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} + * + * @deprecated + */ + public function getBinaryMaxLength() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'SQLServerPlatform::getBinaryMaxLength() is deprecated.', + ); + + return 8000; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $column) + { + return 'VARCHAR(MAX)'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $column) + { + return ! empty($column['autoincrement']) ? ' IDENTITY' : ''; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $column) + { + // 3 - microseconds precision length + // http://msdn.microsoft.com/en-us/library/ms187819.aspx + return 'DATETIME2(6)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $column) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $column) + { + return 'TIME(0)'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $column) + { + return 'BIT'; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ($limit === null && $offset <= 0) { + return $query; + } + + if ($this->shouldAddOrderBy($query)) { + if (preg_match('/^SELECT\s+DISTINCT/im', $query) > 0) { + // SQL Server won't let us order by a non-selected column in a DISTINCT query, + // so we have to do this madness. This says, order by the first column in the + // result. SQL Server's docs say that a nonordered query's result order is non- + // deterministic anyway, so this won't do anything that a bunch of update and + // deletes to the table wouldn't do anyway. + $query .= ' ORDER BY 1'; + } else { + // In another DBMS, we could do ORDER BY 0, but SQL Server gets angry if you + // use constant expressions in the order by list. + $query .= ' ORDER BY (SELECT 0)'; + } + } + + // This looks somewhat like MYSQL, but limit/offset are in inverse positions + // Supposedly SQL:2008 core standard. + // Per TSQL spec, FETCH NEXT n ROWS ONLY is not valid without OFFSET n ROWS. + $query .= sprintf(' OFFSET %d ROWS', $offset); + + if ($limit !== null) { + $query .= sprintf(' FETCH NEXT %d ROWS ONLY', $limit); + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + if (! is_bool($value) && ! is_numeric($value)) { + continue; + } + + $item[$key] = (int) (bool) $value; + } + } elseif (is_bool($item) || is_numeric($item)) { + $item = (int) (bool) $item; + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return 'CREATE TABLE'; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + return '#' . $tableName; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.u'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return 'H:i:s'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s.u P'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'mssql'; + } + + /** + * {@inheritDoc} + */ + 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', + ]; + } + + /** + * {@inheritDoc} + */ + public function createSavePoint($savepoint) + { + return 'SAVE TRANSACTION ' . $savepoint; + } + + /** + * {@inheritDoc} + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + /** + * {@inheritDoc} + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TRANSACTION ' . $savepoint; + } + + /** + * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function getForeignKeyReferentialActionSQL($action) + { + // RESTRICT is not supported, therefore falling back to NO ACTION. + if (strtoupper($action) === 'RESTRICT') { + return 'NO ACTION'; + } + + return parent::getForeignKeyReferentialActionSQL($action); + } + + public function appendLockHint(string $fromClause, int $lockMode): string + { + switch ($lockMode) { + case LockMode::NONE: + case LockMode::OPTIMISTIC: + return $fromClause; + + case LockMode::PESSIMISTIC_READ: + return $fromClause . ' WITH (HOLDLOCK, ROWLOCK)'; + + case LockMode::PESSIMISTIC_WRITE: + return $fromClause . ' WITH (UPDLOCK, ROWLOCK)'; + + default: + throw InvalidLockMode::fromLockMode($lockMode); + } + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSQL() + { + return ' '; + } + + /** + * {@inheritDoc} + * + * @deprecated Implement {@see createReservedKeywordsList()} instead. + */ + protected function getReservedKeywordsClass() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'SQLServerPlatform::getReservedKeywordsClass() is deprecated,' + . ' use SQLServerPlatform::createReservedKeywordsList() instead.', + ); + + return Keywords\SQLServer2012Keywords::class; + } + + /** + * {@inheritDoc} + */ + public function quoteSingleIdentifier($str) + { + return '[' . str_replace(']', ']]', $str) . ']'; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableIdentifier = new Identifier($tableName); + + return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $column) + { + return 'VARBINARY(MAX)'; + } + + /** + * {@inheritdoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + */ + public function getColumnDeclarationSQL($name, array $column) + { + if (isset($column['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($column); + } else { + $collation = ! empty($column['collation']) ? + ' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : ''; + + $notnull = ! empty($column['notnull']) ? ' NOT NULL' : ''; + + 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 = ' ' . $column['check']; + } else { + $check = ''; + } + + $typeDecl = $column['type']->getSQLDeclaration($column, $this); + $columnDef = $typeDecl . $collation . $notnull . $unique . $check; + } + + 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)) { + return false; + } + + return $this->getDefaultValueDeclarationSQL($column1->toArray()) + === $this->getDefaultValueDeclarationSQL($column2->toArray()); + } + + protected function getLikeWildcardCharacters(): string + { + return parent::getLikeWildcardCharacters() . '[]^'; + } + + /** + * Returns a unique default constraint name for a table and column. + * + * @param string $table Name of the table to generate the unique default constraint name for. + * @param string $column Name of the column in the table to generate the unique default constraint name for. + */ + private function generateDefaultConstraintName($table, $column): string + { + return 'DF_' . $this->generateIdentifierName($table) . '_' . $this->generateIdentifierName($column); + } + + /** + * Returns a hash value for a given identifier. + * + * @param string $identifier Identifier to generate a hash value for. + */ + private function generateIdentifierName($identifier): string + { + // Always generate name for unquoted identifiers to ensure consistency. + $identifier = new Identifier($identifier); + + return strtoupper(dechex(crc32($identifier->getName()))); + } + + protected function getCommentOnTableSQL(string $tableName, ?string $comment): string + { + return sprintf( + <<<'SQL' + EXEC sys.sp_addextendedproperty @name=N'MS_Description', + @value=N%s, @level0type=N'SCHEMA', @level0name=N'dbo', + @level1type=N'TABLE', @level1name=N%s + SQL + , + $this->quoteStringLiteral((string) $comment), + $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( + <<<'SQL' + SELECT + 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 + WHERE + (tbl.name=N%s and SCHEMA_NAME(tbl.schema_id)=N'dbo' and p.name=N'MS_Description') + SQL + , + $this->quoteStringLiteral($table), + ); + } + + /** @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 + // but can be in a newline + $matches = []; + $matchesCount = preg_match_all('/[\\s]+order\\s+by\\s/im', $query, $matches, PREG_OFFSET_CAPTURE); + if ($matchesCount === 0) { + return true; + } + + // ORDER BY instance may be in a subquery after ORDER BY + // e.g. SELECT col1 FROM test ORDER BY (SELECT col2 from test ORDER BY col2) + // if in the searched query ORDER BY clause was found where + // number of open parentheses after the occurrence of the clause is equal to + // number of closed brackets after the occurrence of the clause, + // it means that ORDER BY is included in the query being checked + while ($matchesCount > 0) { + $orderByPos = $matches[0][--$matchesCount][1]; + $openBracketsCount = substr_count($query, '(', $orderByPos); + $closedBracketsCount = substr_count($query, ')', $orderByPos); + if ($openBracketsCount === $closedBracketsCount) { + return false; + } + } + + 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 new file mode 100644 index 000000000..263f3561e --- /dev/null +++ b/doctrine/dbal/src/Platforms/SQLite/Comparator.php @@ -0,0 +1,51 @@ +normalizeColumns($fromTable); + $this->normalizeColumns($toTable); + + return parent::diffTable($fromTable, $toTable); + } + + private function normalizeColumns(Table $table): void + { + foreach ($table->getColumns() as $column) { + $options = $column->getPlatformOptions(); + + if (! isset($options['collation']) || strcasecmp($options['collation'], 'binary') !== 0) { + continue; + } + + unset($options['collation']); + $column->setPlatformOptions($options); + } + } +} diff --git a/doctrine/dbal/src/Platforms/SqlitePlatform.php b/doctrine/dbal/src/Platforms/SqlitePlatform.php index 168182610..71c29f38a 100644 --- a/doctrine/dbal/src/Platforms/SqlitePlatform.php +++ b/doctrine/dbal/src/Platforms/SqlitePlatform.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Platforms; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\API\SQLite\UserDefinedFunctions; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\Constraint; @@ -9,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; @@ -21,13 +25,13 @@ 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; use function sqrt; use function str_replace; use function strlen; -use function strpos; use function strtolower; use function trim; @@ -39,6 +43,8 @@ */ class SqlitePlatform extends AbstractPlatform { + private bool $schemaEmulationEnabled = true; + /** * {@inheritDoc} */ @@ -48,12 +54,20 @@ public function getRegexpExpression() } /** + * @deprecated Generate dates within the application. + * * @param string $type * * @return string */ 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.', + ); + switch ($type) { case 'time': return 'time(\'now\')'; @@ -67,6 +81,14 @@ public function getNowExpression($type = 'timestamp') } } + /** + * {@inheritDoc} + */ + public function getModExpression($expression1, $expression2) + { + return $expression1 . ' % ' . $expression2; + } + /** * {@inheritDoc} */ @@ -109,11 +131,13 @@ 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'; } /** @@ -158,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'"; } /** @@ -197,9 +222,17 @@ public function getSetTransactionIsolationSQL($level) /** * {@inheritDoc} + * + * @deprecated */ public function prefersIdentityColumns() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/1519', + 'SqlitePlatform::prefersIdentityColumns() is deprecated.', + ); + return true; } @@ -233,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); @@ -261,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); @@ -312,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(), )); } @@ -331,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'])) { @@ -418,17 +491,33 @@ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) /** * {@inheritdoc} + * + * @deprecated */ public function getBinaryMaxLength() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3263', + 'SqlitePlatform::getBinaryMaxLength() is deprecated.', + ); + return 0; } /** * {@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; } @@ -441,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() @@ -489,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) { @@ -497,22 +596,8 @@ public function getListViewsSQL($database) /** * {@inheritDoc} - */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; - } - - /** - * {@inheritDoc} - */ - public function getDropViewSQL($name) - { - return 'DROP VIEW ' . $name; - } - - /** - * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { @@ -534,6 +619,23 @@ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey return $query; } + /** + * {@inheritDoc} + * + * @deprecated + */ + public function supportsCreateDropDatabase() + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5513', + '%s is deprecated.', + __METHOD__, + ); + + return false; + } + /** * {@inheritDoc} */ @@ -544,6 +646,8 @@ public function supportsIdentityColumns() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsColumnCollation() { @@ -552,6 +656,8 @@ public function supportsColumnCollation() /** * {@inheritDoc} + * + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsInlineColumnComments() { @@ -563,6 +669,12 @@ public function supportsInlineColumnComments() */ public function getName() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4749', + 'SqlitePlatform::getName() is deprecated. Identify platforms by their class.', + ); + return 'sqlite'; } @@ -572,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; } @@ -580,6 +692,8 @@ public function getTruncateTableSQL($tableName, $cascade = false) /** * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction(). * + * @deprecated The driver will use {@see sqrt()} in the next major release. + * * @param int|float $value * * @return float @@ -592,6 +706,8 @@ public static function udfSqrt($value) /** * User-defined function for Sqlite that implements MOD(a, b). * + * @deprecated The driver will use {@see UserDefinedFunctions::mod()} in the next major release. + * * @param int $a * @param int $b * @@ -599,10 +715,12 @@ public static function udfSqrt($value) */ public static function udfMod($a, $b) { - return $a % $b; + return UserDefinedFunctions::mod($a, $b); } /** + * @deprecated The driver will use {@see UserDefinedFunctions::locate()} in the next major release. + * * @param string $str * @param string $substr * @param int $offset @@ -611,19 +729,7 @@ public static function udfMod($a, $b) */ public static function udfLocate($str, $substr, $offset = 0) { - // 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) { - $offset -= 1; - } - - $pos = strpos($str, $substr, $offset); - - if ($pos !== false) { - return $pos + 1; - } - - return 0; + return UserDefinedFunctions::locate($str, $substr, $offset); } /** @@ -636,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) { @@ -691,7 +799,7 @@ protected function initializeDoctrineTypeMappings() /** * {@inheritDoc} * - * @deprecated Implement {@link createReservedKeywordsList()} instead. + * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { @@ -699,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; @@ -710,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 []; } /** @@ -733,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', ); } @@ -748,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; } @@ -765,7 +858,7 @@ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) protected function doModifyLimitQuery($query, $limit, $offset) { if ($limit === null && $offset > 0) { - return $query . ' LIMIT -1 OFFSET ' . $offset; + return sprintf('%s LIMIT -1 OFFSET %d', $query, $offset); } return parent::doModifyLimitQuery($query, $limit, $offset); @@ -784,7 +877,7 @@ public function getBlobTypeDeclarationSQL(array $column) */ public function getTemporaryTableName($tableName) { - $tableName = str_replace('.', '__', $tableName); + $tableName = $this->emulateSchemaNamespacing($tableName); return $tableName; } @@ -792,6 +885,8 @@ public function getTemporaryTableName($tableName) /** * {@inheritDoc} * + * @deprecated + * * Sqlite Platform emulates schema by underscoring each dot and generating tables * into the default database. * @@ -800,15 +895,41 @@ public function getTemporaryTableName($tableName) */ public function canEmulateSchemas() { - return true; + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4805', + 'SqlitePlatform::canEmulateSchemas() is deprecated.', + ); + + 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; } /** @@ -837,6 +958,8 @@ public function getDropForeignKeySQL($foreignKey, $table) /** * {@inheritDoc} + * + * @deprecated */ public function getCreateConstraintSQL(Constraint $constraint, $table) { @@ -847,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) { @@ -856,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 * @@ -863,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)); } @@ -878,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 = []; @@ -898,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; } @@ -911,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; @@ -930,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 = []; @@ -961,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); @@ -974,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( @@ -984,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), ); } @@ -1040,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; @@ -1065,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; } @@ -1107,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 ' @@ -1117,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 ' @@ -1127,10 +1287,8 @@ private function getSimpleAlterTableSQL(TableDiff $diff) return array_merge($sql, $tableSql, $columnSql); } - /** - * @return string[] - */ - private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable) + /** @return string[] */ + private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable): array { $columns = []; @@ -1138,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; } @@ -1147,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; } @@ -1167,16 +1328,14 @@ private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable) return $columns; } - /** - * @return Index[] - */ - private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable) + /** @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; } @@ -1210,11 +1369,11 @@ private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable) $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; @@ -1223,7 +1382,13 @@ private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable) 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; @@ -1235,10 +1400,8 @@ private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable) return $indexes; } - /** - * @return ForeignKeyConstraint[] - */ - private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable) + /** @return ForeignKeyConstraint[] */ + private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable): array { $foreignKeys = $fromTable->getForeignKeys(); $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable); @@ -1270,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); } @@ -1287,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; @@ -1299,10 +1462,8 @@ private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable) return $foreignKeys; } - /** - * @return Index[] - */ - private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $fromTable) + /** @return Index[] */ + private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $fromTable): array { $primaryIndex = []; @@ -1316,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 a56397924..fc1bdd9ad 100644 --- a/doctrine/dbal/src/Portability/Connection.php +++ b/doctrine/dbal/src/Portability/Connection.php @@ -3,14 +3,14 @@ namespace Doctrine\DBAL\Portability; use Doctrine\DBAL\Driver\Connection as ConnectionInterface; +use Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware; use Doctrine\DBAL\Driver\Result as DriverResult; use Doctrine\DBAL\Driver\Statement as DriverStatement; -use Doctrine\DBAL\ParameterType; /** * Portability wrapper for a Connection. */ -final class Connection implements ConnectionInterface +final class Connection extends AbstractConnectionMiddleware { public const PORTABILITY_ALL = 255; public const PORTABILITY_NONE = 0; @@ -18,76 +18,28 @@ final class Connection implements ConnectionInterface public const PORTABILITY_EMPTY_TO_NULL = 4; public const PORTABILITY_FIX_CASE = 8; - /** @var ConnectionInterface */ - private $connection; - - /** @var Converter */ - private $converter; + private Converter $converter; public function __construct(ConnectionInterface $connection, Converter $converter) { - $this->connection = $connection; - $this->converter = $converter; + parent::__construct($connection); + + $this->converter = $converter; } public function prepare(string $sql): DriverStatement { return new Statement( - $this->connection->prepare($sql), - $this->converter + parent::prepare($sql), + $this->converter, ); } public function query(string $sql): DriverResult { return new Result( - $this->connection->query($sql), - $this->converter + parent::query($sql), + $this->converter, ); } - - /** - * {@inheritDoc} - */ - public function quote($value, $type = ParameterType::STRING) - { - return $this->connection->quote($value, $type); - } - - public function exec(string $sql): int - { - return $this->connection->exec($sql); - } - - /** - * {@inheritDoc} - */ - public function lastInsertId($name = null) - { - return $this->connection->lastInsertId($name); - } - - /** - * {@inheritDoc} - */ - public function beginTransaction() - { - return $this->connection->beginTransaction(); - } - - /** - * {@inheritDoc} - */ - public function commit() - { - return $this->connection->commit(); - } - - /** - * {@inheritDoc} - */ - public function rollBack() - { - return $this->connection->rollBack(); - } } diff --git a/doctrine/dbal/src/Portability/Converter.php b/doctrine/dbal/src/Portability/Converter.php index 5763c2603..eb76eb782 100644 --- a/doctrine/dbal/src/Portability/Converter.php +++ b/doctrine/dbal/src/Portability/Converter.php @@ -34,7 +34,7 @@ final class Converter * @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 {@link CASE_LOWER} and {@link CASE_UPPER}) + * (one of {@see CASE_LOWER} and {@see CASE_UPPER}) */ public function __construct(bool $convertEmptyStringToNull, bool $rightTrimString, ?int $case) { diff --git a/doctrine/dbal/src/Portability/Driver.php b/doctrine/dbal/src/Portability/Driver.php index 5ae410cd0..2425dde03 100644 --- a/doctrine/dbal/src/Portability/Driver.php +++ b/doctrine/dbal/src/Portability/Driver.php @@ -3,31 +3,28 @@ namespace Doctrine\DBAL\Portability; use Doctrine\DBAL\ColumnCase; -use Doctrine\DBAL\Connection as DBALConnection; use Doctrine\DBAL\Driver as DriverInterface; -use Doctrine\DBAL\Driver\API\ExceptionConverter; -use Doctrine\DBAL\Driver\PDO; -use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; +use LogicException; +use PDO; + +use function method_exists; use const CASE_LOWER; use const CASE_UPPER; -final class Driver implements DriverInterface +final class Driver extends AbstractDriverMiddleware { - /** @var DriverInterface */ - private $driver; - - /** @var int */ - private $mode; + private int $mode; - /** @var int */ - private $case; + private int $case; public function __construct(DriverInterface $driver, int $mode, int $case) { - $this->driver = $driver; - $this->mode = $mode; - $this->case = $case; + parent::__construct($driver); + + $this->mode = $mode; + $this->case = $case; } /** @@ -35,20 +32,27 @@ public function __construct(DriverInterface $driver, int $mode, int $case) */ public function connect(array $params) { - $connection = $this->driver->connect($params); + $connection = parent::connect($params); $portability = (new OptimizeFlags())( $this->getDatabasePlatform(), - $this->mode + $this->mode, ); - $case = 0; + $case = null; if ($this->case !== 0 && ($portability & Connection::PORTABILITY_FIX_CASE) !== 0) { - if ($connection instanceof PDO\Connection) { - // make use of c-level support for case handling + $nativeConnection = null; + if (method_exists($connection, 'getNativeConnection')) { + try { + $nativeConnection = $connection->getNativeConnection(); + } catch (LogicException $e) { + } + } + + if ($nativeConnection instanceof PDO) { $portability &= ~Connection::PORTABILITY_FIX_CASE; - $connection->getWrappedConnection()->setAttribute(\PDO::ATTR_CASE, $this->case); + $nativeConnection->setAttribute(PDO::ATTR_CASE, $this->case); } else { $case = $this->case === ColumnCase::LOWER ? CASE_LOWER : CASE_UPPER; } @@ -57,34 +61,13 @@ public function connect(array $params) $convertEmptyStringToNull = ($portability & Connection::PORTABILITY_EMPTY_TO_NULL) !== 0; $rightTrimString = ($portability & Connection::PORTABILITY_RTRIM) !== 0; - if (! $convertEmptyStringToNull && ! $rightTrimString && $case === 0) { + if (! $convertEmptyStringToNull && ! $rightTrimString && $case === null) { return $connection; } return new Connection( $connection, - new Converter($convertEmptyStringToNull, $rightTrimString, $case) + new Converter($convertEmptyStringToNull, $rightTrimString, $case), ); } - - /** - * {@inheritDoc} - */ - public function getDatabasePlatform() - { - return $this->driver->getDatabasePlatform(); - } - - /** - * {@inheritDoc} - */ - public function getSchemaManager(DBALConnection $conn, AbstractPlatform $platform) - { - return $this->driver->getSchemaManager($conn, $platform); - } - - public function getExceptionConverter(): ExceptionConverter - { - return $this->driver->getExceptionConverter(); - } } diff --git a/doctrine/dbal/src/Portability/Middleware.php b/doctrine/dbal/src/Portability/Middleware.php index b00147062..6e4bea2f2 100644 --- a/doctrine/dbal/src/Portability/Middleware.php +++ b/doctrine/dbal/src/Portability/Middleware.php @@ -9,11 +9,9 @@ final class Middleware implements MiddlewareInterface { - /** @var int */ - private $mode; + private int $mode; - /** @var int */ - private $case; + private int $case; public function __construct(int $mode, int $case) { diff --git a/doctrine/dbal/src/Portability/OptimizeFlags.php b/doctrine/dbal/src/Portability/OptimizeFlags.php index 742eb93fb..09f528310 100644 --- a/doctrine/dbal/src/Portability/OptimizeFlags.php +++ b/doctrine/dbal/src/Portability/OptimizeFlags.php @@ -7,9 +7,9 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\PostgreSQL94Platform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; -use Doctrine\DBAL\Platforms\SQLServer2012Platform; +use Doctrine\DBAL\Platforms\SQLServerPlatform; final class OptimizeFlags { @@ -19,12 +19,12 @@ final class OptimizeFlags * * @var array */ - private static $platforms = [ - DB2Platform::class => 0, - OraclePlatform::class => Connection::PORTABILITY_EMPTY_TO_NULL, - PostgreSQL94Platform::class => 0, - SqlitePlatform::class => 0, - SQLServer2012Platform::class => 0, + private static array $platforms = [ + DB2Platform::class => 0, + OraclePlatform::class => Connection::PORTABILITY_EMPTY_TO_NULL, + PostgreSQLPlatform::class => 0, + SqlitePlatform::class => 0, + SQLServerPlatform::class => 0, ]; public function __invoke(AbstractPlatform $platform, int $flags): int diff --git a/doctrine/dbal/src/Portability/Result.php b/doctrine/dbal/src/Portability/Result.php index 1fa91ab48..da1eca985 100644 --- a/doctrine/dbal/src/Portability/Result.php +++ b/doctrine/dbal/src/Portability/Result.php @@ -4,22 +4,18 @@ namespace Doctrine\DBAL\Portability; +use Doctrine\DBAL\Driver\Middleware\AbstractResultMiddleware; use Doctrine\DBAL\Driver\Result as ResultInterface; -final class Result implements ResultInterface +final class Result extends AbstractResultMiddleware { - /** @var ResultInterface */ - private $result; + private Converter $converter; - /** @var Converter */ - private $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) { - $this->result = $result; + parent::__construct($result); + $this->converter = $converter; } @@ -29,7 +25,7 @@ public function __construct(ResultInterface $result, Converter $converter) public function fetchNumeric() { return $this->converter->convertNumeric( - $this->result->fetchNumeric() + parent::fetchNumeric(), ); } @@ -39,7 +35,7 @@ public function fetchNumeric() public function fetchAssociative() { return $this->converter->convertAssociative( - $this->result->fetchAssociative() + parent::fetchAssociative(), ); } @@ -49,7 +45,7 @@ public function fetchAssociative() public function fetchOne() { return $this->converter->convertOne( - $this->result->fetchOne() + parent::fetchOne(), ); } @@ -59,7 +55,7 @@ public function fetchOne() public function fetchAllNumeric(): array { return $this->converter->convertAllNumeric( - $this->result->fetchAllNumeric() + parent::fetchAllNumeric(), ); } @@ -69,7 +65,7 @@ public function fetchAllNumeric(): array public function fetchAllAssociative(): array { return $this->converter->convertAllAssociative( - $this->result->fetchAllAssociative() + parent::fetchAllAssociative(), ); } @@ -79,22 +75,7 @@ public function fetchAllAssociative(): array public function fetchFirstColumn(): array { return $this->converter->convertFirstColumn( - $this->result->fetchFirstColumn() + parent::fetchFirstColumn(), ); } - - public function rowCount(): int - { - return $this->result->rowCount(); - } - - public function columnCount(): int - { - return $this->result->columnCount(); - } - - public function free(): void - { - $this->result->free(); - } } diff --git a/doctrine/dbal/src/Portability/Statement.php b/doctrine/dbal/src/Portability/Statement.php index f3f94e413..9b3e74f58 100644 --- a/doctrine/dbal/src/Portability/Statement.php +++ b/doctrine/dbal/src/Portability/Statement.php @@ -2,44 +2,25 @@ namespace Doctrine\DBAL\Portability; +use Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware; use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as DriverStatement; -use Doctrine\DBAL\ParameterType; /** * Portability wrapper for a Statement. */ -final class Statement implements DriverStatement +final class Statement extends AbstractStatementMiddleware { - /** @var DriverStatement */ - private $stmt; - - /** @var Converter */ - private $converter; + private Converter $converter; /** * Wraps Statement and applies portability measures. */ public function __construct(DriverStatement $stmt, Converter $converter) { - $this->stmt = $stmt; - $this->converter = $converter; - } - - /** - * {@inheritdoc} - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) - { - return $this->stmt->bindParam($param, $variable, $type, $length); - } + parent::__construct($stmt); - /** - * {@inheritdoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING) - { - return $this->stmt->bindValue($param, $value, $type); + $this->converter = $converter; } /** @@ -48,8 +29,8 @@ public function bindValue($param, $value, $type = ParameterType::STRING) public function execute($params = null): ResultInterface { return new Result( - $this->stmt->execute($params), - $this->converter + parent::execute($params), + $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 8cb531503..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()); @@ -308,7 +306,10 @@ public function notIn($x, $y) } /** - * Quotes a given input parameter. + * Builds an SQL literal from a given input parameter. + * + * The usage of this method is discouraged. Use prepared statements + * or {@see AbstractPlatform::quoteStringLiteral()} instead. * * @param mixed $input The parameter to be quoted. * @param int|null $type The type of the parameter. diff --git a/doctrine/dbal/src/Query/QueryBuilder.php b/doctrine/dbal/src/Query/QueryBuilder.php index 6d74d0105..5fa722d6d 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,37 @@ class QueryBuilder * * @var array|array */ - private $paramTypes = []; + private array $paramTypes = []; /** * The type of query this is. Can be select, update or delete. - * - * @var int */ - private $type = self::SELECT; + private int $type = self::SELECT; /** * The state of the query object. Can be dirty or clean. - * - * @var int */ - 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 +167,56 @@ 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. */ 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 +230,7 @@ public function getState() */ public function fetchAssociative() { - return $this->connection->fetchAssociative($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAssociative(); } /** @@ -221,7 +243,7 @@ public function fetchAssociative() */ public function fetchNumeric() { - return $this->connection->fetchNumeric($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchNumeric(); } /** @@ -234,7 +256,7 @@ public function fetchNumeric() */ public function fetchOne() { - return $this->connection->fetchOne($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchOne(); } /** @@ -246,7 +268,7 @@ public function fetchOne() */ public function fetchAllNumeric(): array { - return $this->connection->fetchAllNumeric($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAllNumeric(); } /** @@ -258,7 +280,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 +293,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 +307,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 +319,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 +329,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, + ); } /** @@ -327,9 +354,9 @@ public function executeStatement(): int /** * Executes this query using the bound parameters and their types. * - * @deprecated Use {@link executeQuery()} or {@link executeStatement()} instead. + * @deprecated Use {@see executeQuery()} or {@see executeStatement()} instead. * - * @return Result|int + * @return Result|int|string * * @throws Exception */ @@ -339,16 +366,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); @@ -414,10 +441,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 +524,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 +663,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 +722,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 +983,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 +1103,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 +1141,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 +1321,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,12 +1346,8 @@ public function resetQueryPart($queryPartName) return $this; } - /** - * @return string - * - * @throws QueryException - */ - private function getSQLForSelect() + /** @throws QueryException */ + private function getSQLForSelect(): string { $query = 'SELECT ' . ($this->sqlParts['distinct'] ? 'DISTINCT ' : '') . implode(', ', $this->sqlParts['select']); @@ -1334,7 +1362,7 @@ private function getSQLForSelect() return $this->connection->getDatabasePlatform()->modifyLimitQuery( $query, $this->maxResults, - $this->firstResult + $this->firstResult, ); } @@ -1346,7 +1374,7 @@ private function getSQLForSelect() * * @throws QueryException */ - private function getFromClauses() + private function getFromClauses(): array { $fromClauses = []; $knownAliases = []; @@ -1385,20 +1413,15 @@ private function verifyAllAliasesAreKnown(array $knownAliases): void } } - /** - * @return bool - */ - private function isLimitQuery() + private function isLimitQuery(): bool { return $this->maxResults !== null || $this->firstResult !== 0; } /** * Converts this instance into an INSERT string in SQL. - * - * @return string */ - private function getSQLForInsert() + private function getSQLForInsert(): string { return 'INSERT INTO ' . $this->sqlParts['from']['table'] . ' (' . implode(', ', array_keys($this->sqlParts['values'])) . ')' . @@ -1407,10 +1430,8 @@ private function getSQLForInsert() /** * Converts this instance into an UPDATE string in SQL. - * - * @return string */ - private function getSQLForUpdate() + private function getSQLForUpdate(): string { $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); @@ -1422,10 +1443,8 @@ private function getSQLForUpdate() /** * Converts this instance into a DELETE string in SQL. - * - * @return string */ - private function getSQLForDelete() + private function getSQLForDelete(): string { $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); @@ -1448,18 +1467,18 @@ public function __toString() /** * Creates a new named parameter and bind the value $value to it. * - * This method provides a shortcut for {@link Statement::bindValue()} + * This method provides a shortcut for {@see Statement::bindValue()} * when using prepared statements. * * The parameter $value specifies the value that you want to bind. If - * $placeholder is not provided bindValue() will automatically create a - * placeholder for you. An automatic placeholder will be of the name - * ':dcValue1', ':dcValue2' etc. + * $placeholder is not provided createNamedParameter() will automatically + * create a placeholder for you. An automatic placeholder will be of the + * name ':dcValue1', ':dcValue2' etc. * * Example: * * $value = 2; - * $q->eq( 'id', $q->bindValue( $value ) ); + * $q->eq( 'id', $q->createNamedParameter( $value ) ); * $stmt = $q->executeQuery(); // executed with 'id = 2' * * @@ -1517,11 +1536,9 @@ public function createPositionalParameter($value, $type = ParameterType::STRING) * @param string $fromAlias * @param array $knownAliases * - * @return string - * * @throws QueryException */ - private function getSQLForJoins($fromAlias, array &$knownAliases) + private function getSQLForJoins($fromAlias, array &$knownAliases): string { $sql = ''; @@ -1577,4 +1594,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 59c1065a0..0426fd48f 100644 --- a/doctrine/dbal/src/Result.php +++ b/doctrine/dbal/src/Result.php @@ -15,15 +15,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 {@link Connection} or {@link Statement}. - */ + /** @internal The result can be only instantiated by {@see Connection} or {@see Statement}. */ public function __construct(DriverResult $result, Connection $connection) { $this->result = $result; @@ -170,12 +165,8 @@ public function fetchFirstColumn(): array */ public function iterateNumeric(): Traversable { - try { - while (($row = $this->result->fetchNumeric()) !== false) { - yield $row; - } - } catch (DriverException $e) { - throw $this->connection->convertException($e); + while (($row = $this->fetchNumeric()) !== false) { + yield $row; } } @@ -186,12 +177,8 @@ public function iterateNumeric(): Traversable */ public function iterateAssociative(): Traversable { - try { - while (($row = $this->result->fetchAssociative()) !== false) { - yield $row; - } - } catch (DriverException $e) { - throw $this->connection->convertException($e); + while (($row = $this->fetchAssociative()) !== false) { + yield $row; } } @@ -231,18 +218,12 @@ public function iterateAssociativeIndexed(): Traversable */ public function iterateColumn(): Traversable { - try { - while (($value = $this->result->fetchOne()) !== false) { - yield $value; - } - } catch (DriverException $e) { - throw $this->connection->convertException($e); + while (($value = $this->fetchOne()) !== false) { + yield $value; } } - /** - * @throws Exception - */ + /** @throws Exception */ public function rowCount(): int { try { @@ -252,9 +233,7 @@ public function rowCount(): int } } - /** - * @throws Exception - */ + /** @throws Exception */ public function columnCount(): int { try { @@ -269,9 +248,7 @@ public function free(): void $this->result->free(); } - /** - * @throws Exception - */ + /** @throws Exception */ private function ensureHasKeyValue(): void { $columnCount = $this->columnCount(); @@ -287,11 +264,13 @@ private function ensureHasKeyValue(): void * @deprecated This API is deprecated and will be removed after 2022 * * @return mixed + * + * @throws Exception */ public function fetch(int $mode = FetchMode::ASSOCIATIVE) { if (func_num_args() > 1) { - throw new LogicException('Only invocations with one argument are still supported by this legecy API.'); + throw new LogicException('Only invocations with one argument are still supported by this legacy API.'); } if ($mode === FetchMode::ASSOCIATIVE) { @@ -315,11 +294,13 @@ public function fetch(int $mode = FetchMode::ASSOCIATIVE) * @deprecated This API is deprecated and will be removed after 2022 * * @return list + * + * @throws Exception */ public function fetchAll(int $mode = FetchMode::ASSOCIATIVE): array { if (func_num_args() > 1) { - throw new LogicException('Only invocations with one argument are still supported by this legecy API.'); + throw new LogicException('Only invocations with one argument are still supported by this legacy API.'); } if ($mode === FetchMode::ASSOCIATIVE) { 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 f1ad6617f..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) { @@ -70,7 +69,7 @@ public function __construct(bool $mySQLStringEscaping) self::OTHER, ]); - $this->sqlPattern = sprintf('(%s)+', implode('|', $patterns)); + $this->sqlPattern = sprintf('(%s)', implode('|', $patterns)); } /** diff --git a/doctrine/dbal/src/Schema/AbstractAsset.php b/doctrine/dbal/src/Schema/AbstractAsset.php index e6b383a65..ab8fdec03 100644 --- a/doctrine/dbal/src/Schema/AbstractAsset.php +++ b/doctrine/dbal/src/Schema/AbstractAsset.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; use function array_map; use function crc32; @@ -110,12 +111,21 @@ public function getShortestName($defaultNamespaceName) * Every non-namespaced element is prefixed with the default namespace * name which is passed as argument to this method. * + * @deprecated Use {@see getNamespaceName()} and {@see getName()} instead. + * * @param string $defaultNamespaceName * * @return string */ public function getFullQualifiedName($defaultNamespaceName) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4814', + 'AbstractAsset::getFullQualifiedName() is deprecated.' + . ' Use AbstractAsset::getNamespaceName() and ::getName() instead.', + ); + $name = $this->getName(); if ($this->_namespace === null) { $name = $defaultNamespaceName . '.' . $name; diff --git a/doctrine/dbal/src/Schema/AbstractSchemaManager.php b/doctrine/dbal/src/Schema/AbstractSchemaManager.php index 50b9facee..445c9ff2f 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; @@ -28,6 +30,8 @@ /** * Base class for schema managers. Schema managers are used to inspect and/or * modify the database schema/structure. + * + * @template T of AbstractPlatform */ abstract class AbstractSchemaManager { @@ -41,10 +45,11 @@ abstract class AbstractSchemaManager /** * Holds instance of the database platform used for this schema manager. * - * @var AbstractPlatform + * @var T */ protected $_platform; + /** @param T $platform */ public function __construct(Connection $connection, AbstractPlatform $platform) { $this->_conn = $connection; @@ -54,10 +59,19 @@ public function __construct(Connection $connection, AbstractPlatform $platform) /** * Returns the associated platform. * - * @return AbstractPlatform + * @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; } @@ -71,10 +85,18 @@ public function getDatabasePlatform() * $result = $sm->tryMethod('dropView', 'view_name'); * * + * @deprecated + * * @return mixed */ public function tryMethod() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4897', + 'AbstractSchemaManager::tryMethod() is deprecated.', + ); + $args = func_get_args(); $method = $args[0]; unset($args[0]); @@ -109,7 +131,7 @@ public function listDatabases() /** * Returns a list of all namespaces in the current database. * - * @deprecated Use {@link listSchemaNames()} instead. + * @deprecated Use {@see listSchemaNames()} instead. * * @return string[] * @@ -121,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(); @@ -155,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); @@ -185,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::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5284', + 'Passing $database to AbstractSchemaManager::listTableColumns() is deprecated.', + ); } $sql = $this->_platform->getListTableColumnsSQL($table, $database); @@ -195,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::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/5284', + 'Passing $database to AbstractSchemaManager::listTableColumns() 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. * @@ -215,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. * @@ -233,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.', ); } @@ -259,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. @@ -280,7 +380,7 @@ protected function filterAssetNames($assetNames) /** * Lists the tables for this connection. * - * @return Table[] + * @return list
* * @throws Exception */ @@ -290,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 @@ -305,6 +442,13 @@ public function listTables() */ public function listTableDetails($name) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5595', + '%s is deprecated. Use introspectTable() instead.', + __METHOD__, + ); + $columns = $this->listTableColumns($name); $foreignKeys = []; @@ -317,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. * @@ -346,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); @@ -355,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 */ /** @@ -370,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), + ); } /** @@ -380,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), + ); } /** @@ -394,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), + ); } /** @@ -410,15 +753,37 @@ 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), + ); } /** * Drops the constraint from the given table. * + * @deprecated Use {@see dropIndex()}, {@see dropForeignKey()} or {@see dropUniqueConstraint()} instead. + * * @param Table|string $table The name of the table. * * @return void @@ -427,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, + )); } /** @@ -442,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), + ); } /** @@ -456,7 +860,21 @@ public function dropForeignKey($foreignKey, $table) */ public function dropSequence($name) { - $this->_execSql($this->_platform->getDropSequenceSQL($name)); + $this->_conn->executeStatement( + $this->_platform->getDropSequenceSQL($name), + ); + } + + /** + * Drops the unique constraint from the given table. + * + * @throws Exception + */ + public function dropUniqueConstraint(string $name, string $tableName): void + { + $this->_conn->executeStatement( + $this->_platform->getDropUniqueConstraintSQL($name, $tableName), + ); } /** @@ -470,11 +888,19 @@ public function dropSequence($name) */ 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. * @@ -486,7 +912,9 @@ public function dropView($name) */ public function createDatabase($database) { - $this->_execSql($this->_platform->getCreateDatabaseSQL($database)); + $this->_conn->executeStatement( + $this->_platform->getCreateDatabaseSQL($database), + ); } /** @@ -513,12 +941,16 @@ public function createTable(Table $table) */ public function createSequence($sequence) { - $this->_execSql($this->_platform->getCreateSequenceSQL($sequence)); + $this->_conn->executeStatement( + $this->_platform->getCreateSequenceSQL($sequence), + ); } /** * Creates a constraint on a table. * + * @deprecated Use {@see createIndex()}, {@see createForeignKey()} or {@see createUniqueConstraint()} instead. + * * @param Table|string $table * * @return void @@ -527,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), + ); } /** @@ -541,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), + ); } /** @@ -556,7 +992,21 @@ 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), + ); + } + + /** + * Creates a unique constraint on a table. + * + * @throws Exception + */ + public function createUniqueConstraint(UniqueConstraint $uniqueConstraint, string $tableName): void + { + $this->_conn->executeStatement( + $this->_platform->getCreateUniqueConstraintSQL($uniqueConstraint, $tableName), + ); } /** @@ -568,14 +1018,29 @@ public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) */ 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. * + * @deprecated Use {@see dropIndex()} and {@see createIndex()}, + * {@see dropForeignKey()} and {@see createForeignKey()} + * or {@see dropUniqueConstraint()} and {@see createUniqueConstraint()} instead. + * * @see dropConstraint() * @see createConstraint() * @@ -587,6 +1052,16 @@ public function createView(View $view) */ public function dropAndCreateConstraint(Constraint $constraint, $table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4897', + 'AbstractSchemaManager::dropAndCreateConstraint() is deprecated.' + . ' Use AbstractSchemaManager::dropIndex() and AbstractSchemaManager::createIndex(),' + . ' AbstractSchemaManager::dropForeignKey() and AbstractSchemaManager::createForeignKey()' + . ' or AbstractSchemaManager::dropUniqueConstraint()' + . ' and AbstractSchemaManager::createUniqueConstraint() instead.', + ); + $this->tryMethod('dropConstraint', $constraint, $table); $this->createConstraint($constraint, $table); } @@ -594,6 +1069,8 @@ public function dropAndCreateConstraint(Constraint $constraint, $table) /** * Drops and creates a new index on a table. * + * @deprecated Use {@see dropIndex()} and {@see createIndex()} instead. + * * @param Table|string $table The name of the table on which the index is to be created. * * @return void @@ -602,6 +1079,13 @@ public function dropAndCreateConstraint(Constraint $constraint, $table) */ public function dropAndCreateIndex(Index $index, $table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4897', + 'AbstractSchemaManager::dropAndCreateIndex() is deprecated.' + . ' Use AbstractSchemaManager::dropIndex() and AbstractSchemaManager::createIndex() instead.', + ); + $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table); $this->createIndex($index, $table); } @@ -609,6 +1093,8 @@ public function dropAndCreateIndex(Index $index, $table) /** * Drops and creates a new foreign key. * + * @deprecated Use {@see dropForeignKey()} and {@see createForeignKey()} instead. + * * @param ForeignKeyConstraint $foreignKey An associative array that defines properties * of the foreign key to be created. * @param Table|string $table The name of the table on which the foreign key is to be created. @@ -619,6 +1105,13 @@ public function dropAndCreateIndex(Index $index, $table) */ public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4897', + 'AbstractSchemaManager::dropAndCreateForeignKey() is deprecated.' + . ' Use AbstractSchemaManager::dropForeignKey() and AbstractSchemaManager::createForeignKey() instead.', + ); + $this->tryMethod('dropForeignKey', $foreignKey, $table); $this->createForeignKey($foreignKey, $table); } @@ -626,12 +1119,21 @@ public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table /** * Drops and create a new sequence. * + * @deprecated Use {@see dropSequence()} and {@see createSequence()} instead. + * * @return void * * @throws Exception */ public function dropAndCreateSequence(Sequence $sequence) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4897', + 'AbstractSchemaManager::dropAndCreateSequence() is deprecated.' + . ' Use AbstractSchemaManager::dropSequence() and AbstractSchemaManager::createSequence() instead.', + ); + $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform)); $this->createSequence($sequence); } @@ -639,12 +1141,21 @@ public function dropAndCreateSequence(Sequence $sequence) /** * Drops and creates a new table. * + * @deprecated Use {@see dropTable()} and {@see createTable()} instead. + * * @return void * * @throws Exception */ public function dropAndCreateTable(Table $table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4897', + 'AbstractSchemaManager::dropAndCreateTable() is deprecated.' + . ' Use AbstractSchemaManager::dropTable() and AbstractSchemaManager::createTable() instead.', + ); + $this->tryMethod('dropTable', $table->getQuotedName($this->_platform)); $this->createTable($table); } @@ -652,6 +1163,8 @@ public function dropAndCreateTable(Table $table) /** * Drops and creates a new database. * + * @deprecated Use {@see dropDatabase()} and {@see createDatabase()} instead. + * * @param string $database The name of the database to create. * * @return void @@ -660,6 +1173,13 @@ public function dropAndCreateTable(Table $table) */ public function dropAndCreateDatabase($database) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4897', + 'AbstractSchemaManager::dropAndCreateDatabase() is deprecated.' + . ' Use AbstractSchemaManager::dropDatabase() and AbstractSchemaManager::createDatabase() instead.', + ); + $this->tryMethod('dropDatabase', $database); $this->createDatabase($database); } @@ -667,16 +1187,48 @@ public function dropAndCreateDatabase($database) /** * Drops and creates a new view. * + * @deprecated Use {@see dropView()} and {@see createView()} instead. + * * @return void * * @throws Exception */ public function dropAndCreateView(View $view) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4897', + 'AbstractSchemaManager::dropAndCreateView() is deprecated.' + . ' Use AbstractSchemaManager::dropView() and AbstractSchemaManager::createView() instead.', + ); + $this->tryMethod('dropView', $view->getQuotedName($this->_platform)); $this->createView($view); } + /** + * Alters an existing schema. + * + * @throws Exception + */ + public function alterSchema(SchemaDiff $schemaDiff): void + { + $this->_execSql($this->_platform->getAlterSchemaSQL($schemaDiff)); + } + + /** + * Migrates an existing schema to a new schema. + * + * @throws Exception + */ + public function migrateSchema(Schema $toSchema): void + { + $schemaDiff = $this->createComparator() + ->compareSchemas($this->introspectSchema(), $toSchema); + + $this->alterSchema($schemaDiff); + } + /* alterTable() Methods */ /** @@ -688,9 +1240,7 @@ public function dropAndCreateView(View $view) */ public function alterTable(TableDiff $tableDiff) { - foreach ($this->_platform->getAlterTableSQL($tableDiff) as $ddlQuery) { - $this->_execSql($ddlQuery); - } + $this->_execSql($this->_platform->getAlterTableSQL($tableDiff)); } /** @@ -705,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)); } /** @@ -733,7 +1281,7 @@ protected function _getPortableDatabasesList($databases) /** * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition. * - * @deprecated Use {@link listSchemaNames()} instead. + * @deprecated Use {@see listSchemaNames()} instead. * * @param array> $namespaces The list of namespace names * in the native DBMS data definition. @@ -746,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 = []; @@ -771,7 +1319,7 @@ protected function _getPortableDatabaseDefinition($database) /** * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition. * - * @deprecated Use {@link listSchemaNames()} instead. + * @deprecated Use {@see listSchemaNames()} instead. * * @param array $namespace The native DBMS namespace definition. * @@ -783,43 +1331,12 @@ 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; } - /** - * @param mixed[][] $triggers - * - * @return mixed[][] - */ - protected function _getPortableTriggersList($triggers) - { - $list = []; - foreach ($triggers as $value) { - $value = $this->_getPortableTriggerDefinition($value); - - if (! $value) { - continue; - } - - $list[] = $value; - } - - return $list; - } - - /** - * @param mixed[] $trigger - * - * @return mixed - */ - protected function _getPortableTriggerDefinition($trigger) - { - return $trigger; - } - /** * @param mixed[][] $sequences * @@ -873,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); @@ -958,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); @@ -972,7 +1503,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null $data['unique'], $data['primary'], $data['flags'], - $data['options'] + $data['options'], ); } @@ -1011,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 * @@ -1088,6 +1594,8 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) * @param mixed $tableForeignKey * * @return ForeignKeyConstraint + * + * @abstract */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) { @@ -1095,6 +1603,8 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) } /** + * @internal + * * @param string[]|string $sql * * @return void @@ -1111,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()) { @@ -1134,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. * @@ -1175,12 +1704,20 @@ public function createSchemaConfig() * For databases that don't support subschema/namespaces this method * returns the name of the currently connected database. * + * @deprecated + * * @return string[] * * @throws Exception */ public function getSchemaSearchPaths() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4821', + 'AbstractSchemaManager::getSchemaSearchPaths() is deprecated.', + ); + $database = $this->_conn->getDatabase(); if ($database !== null) { @@ -1194,6 +1731,8 @@ public function getSchemaSearchPaths() * Given a table comment this method tries to extract a typehint for Doctrine Type, or returns * the type given as default. * + * @internal This method should be only used from within the AbstractSchemaManager class hierarchy. + * * @param string|null $comment * @param string $currentType * @@ -1209,6 +1748,8 @@ public function extractDoctrineTypeFromComment($comment, $currentType) } /** + * @internal This method should be only used from within the AbstractSchemaManager class hierarchy. + * * @param string|null $comment * @param string|null $type * @@ -1222,4 +1763,38 @@ 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->_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..8a5703c0a 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::trigger( + '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 c9c2a52bc..bd1b0eee0 100644 --- a/doctrine/dbal/src/Schema/ColumnDiff.php +++ b/doctrine/dbal/src/Schema/ColumnDiff.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Schema; +use Doctrine\Deprecations\Deprecation; + use function in_array; /** @@ -9,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 */ @@ -31,13 +53,86 @@ public function __construct( array $changedProperties = [], ?Column $fromColumn = null ) { + if ($fromColumn === null) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4785', + 'Not passing the $fromColumn to %s is deprecated.', + __METHOD__, + ); + } + $this->oldColumnName = $oldColumnName; $this->column = $column; $this->changedProperties = $changedProperties; $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 @@ -48,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 7ddf5238d..01c0abcdd 100644 --- a/doctrine/dbal/src/Schema/Comparator.php +++ b/doctrine/dbal/src/Schema/Comparator.php @@ -2,7 +2,11 @@ namespace Doctrine\DBAL\Schema; +use BadMethodCallException; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types; +use Doctrine\Deprecations\Deprecation; use function array_intersect_key; use function array_key_exists; @@ -13,40 +17,87 @@ use function assert; use function count; use function get_class; +use function sprintf; use function strtolower; /** * Compares two Schemas and return an instance of SchemaDiff. + * + * @method SchemaDiff compareSchemas(Schema $fromSchema, Schema $toSchema) */ class Comparator { - /** - * @return SchemaDiff - * - * @throws SchemaException - */ - public static function compareSchemas(Schema $fromSchema, Schema $toSchema) + private ?AbstractPlatform $platform; + + /** @internal The comparator can be only instantiated by a schema manager. */ + public function __construct(?AbstractPlatform $platform = null) + { + if ($platform === null) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4746', + 'Not passing a $platform to %s is deprecated.' + . ' Use AbstractSchemaManager::createComparator() to instantiate the comparator.', + __METHOD__, + ); + } + + $this->platform = $platform; + } + + /** @param list $args */ + public function __call(string $method, array $args): SchemaDiff { - $c = new self(); + if ($method !== 'compareSchemas') { + throw new BadMethodCallException(sprintf('Unknown method "%s"', $method)); + } - return $c->compare($fromSchema, $toSchema); + return $this->doCompareSchemas(...$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); } /** * Returns a SchemaDiff object containing the differences between the schemas $fromSchema and $toSchema. * - * The returned differences are returned in such a way that they contain the - * operations to change the schema stored in $fromSchema to the schema that is - * stored in $toSchema. + * This method should be called non-statically since it will be declared as non-static in the next major release. * * @return SchemaDiff * * @throws SchemaException */ - public function compare(Schema $fromSchema, Schema $toSchema) - { - $diff = new SchemaDiff(); - $diff->fromSchema = $fromSchema; + private function doCompareSchemas( + Schema $fromSchema, + Schema $toSchema + ) { + $createdSchemas = []; + $droppedSchemas = []; + $createdTables = []; + $alteredTables = []; + $droppedTables = []; + $createdSequences = []; + $alteredSequences = []; + $droppedSequences = []; + + $orphanedForeignKeys = []; $foreignKeysToTable = []; @@ -55,7 +106,7 @@ public function compare(Schema $fromSchema, Schema $toSchema) continue; } - $diff->newNamespaces[$namespace] = $namespace; + $createdSchemas[$namespace] = $namespace; } foreach ($fromSchema->getNamespaces() as $namespace) { @@ -63,21 +114,21 @@ public function compare(Schema $fromSchema, Schema $toSchema) 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; } } } @@ -88,7 +139,7 @@ public function compare(Schema $fromSchema, Schema $toSchema) $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 @@ -102,31 +153,37 @@ public function compare(Schema $fromSchema, Schema $toSchema) } } - 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); } } } @@ -135,11 +192,11 @@ public function compare(Schema $fromSchema, Schema $toSchema) $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); } } } @@ -155,19 +212,49 @@ public function compare(Schema $fromSchema, Schema $toSchema) continue; } - $diff->removedSequences[] = $sequence; + $droppedSequences[] = $sequence; } + $diff = new SchemaDiff( + $createdTables, + $alteredTables, + $droppedTables, + $fromSchema, + $createdSchemas, + $droppedSchemas, + $createdSequences, + $alteredSequences, + $droppedSequences, + ); + + $diff->orphanedForeignKeys = $orphanedForeignKeys; + return $diff; } + /** + * @deprecated Use non-static call to {@see compareSchemas()} instead. + * + * @return SchemaDiff + * + * @throws SchemaException + */ + 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.', + ); + + return $this->compareSchemas($fromSchema, $toSchema); + } + /** * @param Schema $schema * @param Sequence $sequence - * - * @return bool */ - private function isAutoIncrementSequenceInSchema($schema, $sequence) + private function isAutoIncrementSequenceInSchema($schema, $sequence): bool { foreach ($schema->getTables() as $table) { if ($sequence->isAutoIncrementsFor($table)) { @@ -178,9 +265,7 @@ private function isAutoIncrementSequenceInSchema($schema, $sequence) return false; } - /** - * @return bool - */ + /** @return bool */ public function diffSequence(Sequence $sequence1, Sequence $sequence2) { if ($sequence1->getAllocationSize() !== $sequence2->getAllocationSize()) { @@ -195,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 SchemaException + * @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(); @@ -214,57 +330,62 @@ 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; } + $toColumn = $toTable->getColumn($columnName); + // See if column has changed properties in "to" table. - $changedProperties = $this->diffColumn($column, $toTable->getColumn($columnName)); + $changedProperties = $this->diffColumn($column, $toColumn); - if (count($changedProperties) === 0) { + if ($this->platform !== null) { + if ($this->columnsEqual($column, $toColumn)) { + continue; + } + } elseif (count($changedProperties) === 0) { continue; } - $columnDiff = new ColumnDiff($column->getName(), $toTable->getColumn($columnName), $changedProperties); - - $columnDiff->fromColumn = $column; - $tableDifferences->changedColumns[$column->getName()] = $columnDiff; - $changes++; + $modifiedColumns[$column->getName()] = new ColumnDiff( + $column->getName(), + $toColumn, + $changedProperties, + $column, + ); } - $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; } @@ -276,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(); @@ -291,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]); } } @@ -300,106 +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++; + $addedForeignKeys[] = $toConstraint; } - return $changes > 0 ? $tableDifferences : false; + 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. * - * @return void + * @param array $addedColumns + * @param array $removedColumns + * + * @return array + * + * @throws Exception */ - private function detectColumnRenamings(TableDiff $tableDifferences) + private function detectRenamedColumns(array &$addedColumns, array &$removedColumns): array { - $renameCandidates = []; - foreach ($tableDifferences->addedColumns as $addedColumnName => $addedColumn) { - foreach ($tableDifferences->removedColumns as $removedColumn) { - if (count($this->diffColumn($addedColumn, $removedColumn)) !== 0) { + $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]; - $removedColumnName = strtolower($removedColumn->getName()); + [$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[$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. * - * @return void + * @param array $addedIndexes + * @param array $removedIndexes + * + * @return array */ - private function detectIndexRenamings(TableDiff $tableDifferences) + 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) @@ -429,16 +580,41 @@ public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint return $key1->onDelete() !== $key2->onDelete(); } + /** + * 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 + { + if ($this->platform === null) { + return $this->diffColumn($column1, $column2) === []; + } + + return $this->platform->columnsEqual($column1, $column2); + } + /** * Returns the difference between the columns * - * If there are differences this method returns $field2, otherwise the - * boolean false. + * 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(); @@ -529,10 +705,12 @@ public function diffColumn(Column $column1, Column $column2) * Compares $index1 with $index2 and returns $index2 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 65e239ec1..f47ee1fd1 100644 --- a/doctrine/dbal/src/Schema/Constraint.php +++ b/doctrine/dbal/src/Schema/Constraint.php @@ -6,17 +6,15 @@ /** * Marker interface for constraints. + * + * @deprecated Use {@see ForeignKeyConstraint}, {@see Index} or {@see UniqueConstraint} instead. */ 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 82c287e00..0308d69b5 100644 --- a/doctrine/dbal/src/Schema/DB2SchemaManager.php +++ b/doctrine/dbal/src/Schema/DB2SchemaManager.php @@ -4,37 +4,84 @@ 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 assert; +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; /** * IBM Db2 Schema Manager. + * + * @extends AbstractSchemaManager */ 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::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5595', + '%s is deprecated. Use introspectTable() instead.', + __METHOD__, + ); - $tables = $this->_conn->fetchAllAssociative($sql); + return $this->doListTableDetails($name); + } - return $this->filterAssetNames($this->_getPortableTablesList($tables)); + /** + * {@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); } /** @@ -125,15 +172,11 @@ protected function _getPortableTableColumnDefinition($tableColumn) /** * {@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']; } /** @@ -159,7 +202,7 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) $tableForeignKey['foreign_table'], $tableForeignKey['foreign_columns'], $tableForeignKey['name'], - $tableForeignKey['options'] + $tableForeignKey['options'], ); } @@ -228,23 +271,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); + } - $platform = $this->_platform; - assert($platform instanceof DB2Platform); - $sql = $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/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 @@ +_options[$event])) { $onEvent = strtoupper($this->_options[$event]); diff --git a/doctrine/dbal/src/Schema/Index.php b/doctrine/dbal/src/Schema/Index.php index 508aa4031..6c2d7cd5a 100644 --- a/doctrine/dbal/src/Schema/Index.php +++ b/doctrine/dbal/src/Schema/Index.php @@ -43,7 +43,7 @@ class Index extends AbstractAsset implements Constraint * @todo $_flags should eventually be refactored into options * @var mixed[] */ - private $options; + private array $options; /** * @param string $name @@ -77,9 +77,7 @@ public function __construct( } } - /** - * @throws InvalidArgumentException - */ + /** @throws InvalidArgumentException */ protected function _addColumn(string $column): void { $this->_columns[$column] = new Identifier($column); @@ -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; @@ -336,10 +336,8 @@ public function getOptions() /** * Return whether the two indexes have the same partial index - * - * @return bool */ - private function samePartialIndex(Index $other) + private function samePartialIndex(Index $other): bool { if ( $this->hasOption('where') diff --git a/doctrine/dbal/src/Schema/MySQLSchemaManager.php b/doctrine/dbal/src/Schema/MySQLSchemaManager.php index 4d54f7159..f2305098a 100644 --- a/doctrine/dbal/src/Schema/MySQLSchemaManager.php +++ b/doctrine/dbal/src/Schema/MySQLSchemaManager.php @@ -2,15 +2,20 @@ namespace Doctrine\DBAL\Schema; +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\MariaDb1027Platform; -use Doctrine\DBAL\Platforms\MySQLPlatform; +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 array_values; use function assert; use function explode; +use function implode; use function is_string; use function preg_match; use function strpos; @@ -22,12 +27,12 @@ /** * Schema manager for the MySQL RDBMS. + * + * @extends AbstractSchemaManager */ 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", "\\'" => "'", @@ -46,30 +51,76 @@ 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} + */ + public function listTables() + { + return $this->doListTables(); + } + + /** + * {@inheritDoc} + * + * @deprecated Use {@see introspectTable()} instead. + */ + public function listTableDetails($name) + { + Deprecation::trigger( + '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} */ - protected function _getPortableTableDefinition($table) + protected function _getPortableViewDefinition($view) { - return array_shift($table); + return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); } /** * {@inheritdoc} */ - protected function _getPortableUserDefinition($user) + protected function _getPortableTableDefinition($table) { - return [ - 'user' => $user['User'], - 'password' => $user['Password'], - ]; + return array_shift($table); } /** @@ -155,7 +206,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) preg_match( '([A-Za-z]+\(([0-9]+),([0-9]+)\))', $tableColumn['type'], - $match + $match, ) === 1 ) { $precision = $match[1]; @@ -166,27 +217,27 @@ protected function _getPortableTableColumnDefinition($tableColumn) break; case 'tinytext': - $length = MySQLPlatform::LENGTH_LIMIT_TINYTEXT; + $length = AbstractMySQLPlatform::LENGTH_LIMIT_TINYTEXT; break; case 'text': - $length = MySQLPlatform::LENGTH_LIMIT_TEXT; + $length = AbstractMySQLPlatform::LENGTH_LIMIT_TEXT; break; case 'mediumtext': - $length = MySQLPlatform::LENGTH_LIMIT_MEDIUMTEXT; + $length = AbstractMySQLPlatform::LENGTH_LIMIT_MEDIUMTEXT; break; case 'tinyblob': - $length = MySQLPlatform::LENGTH_LIMIT_TINYBLOB; + $length = AbstractMySQLPlatform::LENGTH_LIMIT_TINYBLOB; break; case 'blob': - $length = MySQLPlatform::LENGTH_LIMIT_BLOB; + $length = AbstractMySQLPlatform::LENGTH_LIMIT_BLOB; break; case 'mediumblob': - $length = MySQLPlatform::LENGTH_LIMIT_MEDIUMBLOB; + $length = AbstractMySQLPlatform::LENGTH_LIMIT_MEDIUMBLOB; break; case 'tinyint': @@ -309,59 +360,214 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name']; } - $result = []; - foreach ($list as $constraint) { - $result[] = new ForeignKeyConstraint( - array_values($constraint['local']), - $constraint['foreignTable'], - array_values($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 + { + return new ForeignKeyConstraint( + $tableForeignKey['local'], + $tableForeignKey['foreignTable'], + $tableForeignKey['foreign'], + $tableForeignKey['name'], + [ + 'onDelete' => $tableForeignKey['onDelete'], + 'onUpdate' => $tableForeignKey['onUpdate'], + ], + ); + } + + public function createComparator(): Comparator + { + return new MySQL\Comparator( + $this->_platform, + new CachingCollationMetadataProvider( + new ConnectionCollationMetadataProvider($this->_conn), + ), + ); + } + + protected function selectTableNames(string $databaseName): Result { - $table = parent::listTableDetails($name); + $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]); + } + + protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' c.TABLE_NAME,'; + } + + $sql .= <<<'SQL' + c.COLUMN_NAME AS field, + c.COLUMN_TYPE AS type, + c.IS_NULLABLE AS `null`, + c.COLUMN_KEY AS `key`, + c.COLUMN_DEFAULT AS `default`, + c.EXTRA, + c.COLUMN_COMMENT AS comment, + c.CHARACTER_SET_NAME AS characterset, + c.COLLATION_NAME AS collation +FROM information_schema.COLUMNS c + INNER JOIN information_schema.TABLES t + ON t.TABLE_NAME = c.TABLE_NAME +SQL; + + // The schema name is passed multiple times as a literal 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 + $conditions = ['c.TABLE_SCHEMA = ?', 't.TABLE_SCHEMA = ?', "t.TABLE_TYPE = 'BASE TABLE'"]; + $params = [$databaseName, $databaseName]; + + if ($tableName !== null) { + $conditions[] = 't.TABLE_NAME = ?'; + $params[] = $tableName; + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY ORDINAL_POSITION'; + + return $this->_conn->executeQuery($sql, $params); + } - $platform = $this->_platform; - assert($platform instanceof MySQLPlatform); - $sql = $platform->getListTableMetadataSQL($name); + protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; - $tableOptions = $this->_conn->fetchAssociative($sql); + if ($tableName === null) { + $sql .= ' TABLE_NAME,'; + } - if ($tableOptions === false) { - return $table; + $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; } - $table->addOption('engine', $tableOptions['ENGINE']); + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY SEQ_IN_INDEX'; + + return $this->_conn->executeQuery($sql, $params); + } + + protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT DISTINCT'; - if ($tableOptions['TABLE_COLLATION'] !== null) { - $table->addOption('collation', $tableOptions['TABLE_COLLATION']); + if ($tableName === null) { + $sql .= ' k.TABLE_NAME,'; } - if ($tableOptions['AUTO_INCREMENT'] !== null) { - $table->addOption('autoincrement', $tableOptions['AUTO_INCREMENT']); + $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; } - $table->addOption('comment', $tableOptions['TABLE_COMMENT']); - $table->addOption('create_options', $this->parseCreateOptions($tableOptions['CREATE_OPTIONS'])); + $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 $table; + 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 13c885243..8b0d8e3cc 100644 --- a/doctrine/dbal/src/Schema/OracleSchemaManager.php +++ b/doctrine/dbal/src/Schema/OracleSchemaManager.php @@ -4,45 +4,95 @@ 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 assert; +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; /** * Oracle Schema Manager. + * + * @extends AbstractSchemaManager */ 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} + * + * @deprecated Use {@see introspectTable()} instead. + */ + public function listTableDetails($name) + { + Deprecation::trigger( + '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} */ - protected function _getPortableUserDefinition($user) + protected function _getPortableViewDefinition($view) { - $user = array_change_key_case($user, CASE_LOWER); + $view = array_change_key_case($view, CASE_LOWER); - return [ - 'user' => $user['username'], - ]; + return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']); } /** @@ -214,18 +264,21 @@ 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']], + ); } /** @@ -238,7 +291,7 @@ protected function _getPortableSequenceDefinition($sequence) return new Sequence( $this->getQuotedIdentifierName($sequence['sequence_name']), (int) $sequence['increment_by'], - (int) $sequence['min_value'] + (int) $sequence['min_value'], ); } @@ -257,7 +310,7 @@ protected function _getPortableDatabaseDefinition($database) */ public function createDatabase($database) { - $statement = 'CREATE USER ' . $database; + $statement = $this->_platform->getCreateDatabaseSQL($database); $params = $this->_conn->getParams(); @@ -282,8 +335,6 @@ public function createDatabase($database) */ public function dropAutoincrement($table) { - assert($this->_platform instanceof OraclePlatform); - $sql = $this->_platform->getDropAutoincrementSql($table); foreach ($sql as $query) { $this->_conn->executeStatement($query); @@ -309,10 +360,8 @@ public function dropTable($name) * and thus make references to the particular identifier work. * * @param string $identifier The identifier to quote. - * - * @return string The quoted identifier. */ - private function getQuotedIdentifierName($identifier) + private function getQuotedIdentifierName($identifier): string { if (preg_match('/[a-z]/', $identifier) === 1) { return $this->_platform->quoteIdentifier($identifier); @@ -321,23 +370,170 @@ private function getQuotedIdentifierName($identifier) 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; + } - $platform = $this->_platform; - assert($platform instanceof OraclePlatform); - $sql = $platform->getListTableCommentsSQL($name); + $sql .= ' FROM ALL_TAB_COMMENTS WHERE ' . implode(' AND ', $conditions); - $tableOptions = $this->_conn->fetchAssociative($sql); + /** @var array> $metadata */ + $metadata = $this->_conn->executeQuery($sql, $params) + ->fetchAllAssociativeIndexed(); - if ($tableOptions !== false) { - $table->addOption('comment', $tableOptions['COMMENTS']); + $tableOptions = []; + foreach ($metadata as $table => $data) { + $data = array_change_key_case($data, CASE_LOWER); + + $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 abc06af47..7583c4d02 100644 --- a/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php +++ b/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php @@ -3,18 +3,21 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Exception; -use Doctrine\DBAL\Platforms\PostgreSQL94Platform; +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; @@ -29,16 +32,75 @@ /** * PostgreSQL Schema Manager. + * + * @extends AbstractSchemaManager */ 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::trigger( + '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. * - * @deprecated Use {@link listSchemaNames()} instead. + * @deprecated Use {@see listSchemaNames()} instead. * * @return string[] * @@ -50,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(); @@ -67,15 +129,23 @@ public function listSchemaNames(): array FROM information_schema.schemata WHERE schema_name NOT LIKE 'pg\_%' AND schema_name != 'information_schema' -SQL +SQL, ); } /** * {@inheritDoc} + * + * @deprecated */ public function getSchemaSearchPaths() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4821', + 'PostgreSQLSchemaManager::getSchemaSearchPaths() is deprecated.', + ); + $params = $this->_conn->getParams(); $searchPaths = $this->_conn->fetchOne('SHOW search_path'); @@ -98,6 +168,8 @@ public function getSchemaSearchPaths() * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. * * @return string[] + * + * @throws Exception */ public function getExistingSchemaSearchPaths() { @@ -110,6 +182,20 @@ public function getExistingSchemaSearchPaths() return $this->existingSchemaPaths; } + /** + * Returns the name of the current schema. + * + * @return string|null + * + * @throws Exception + */ + protected function getCurrentSchema() + { + $schemas = $this->getExistingSchemaSearchPaths(); + + return array_shift($schemas); + } + /** * Sets or resets the order of the existing schemas in the current search path of the user. * @@ -118,6 +204,8 @@ public function getExistingSchemaSearchPaths() * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. * * @return void + * + * @throws Exception */ public function determineExistingSchemaSearchPaths() { @@ -141,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]; @@ -151,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]; @@ -171,18 +259,10 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) $foreignTable, $foreignColumns, $tableForeignKey['conname'], - ['onUpdate' => $onUpdate, 'onDelete' => $onDelete] + ['onUpdate' => $onUpdate, 'onDelete' => $onDelete], ); } - /** - * {@inheritdoc} - */ - protected function _getPortableTriggerDefinition($trigger) - { - return $trigger['trigger_name']; - } - /** * {@inheritdoc} */ @@ -191,26 +271,14 @@ protected function _getPortableViewDefinition($view) return new View($view['schemaname'] . '.' . $view['viewname'], $view['definition']); } - /** - * {@inheritdoc} - */ - protected function _getPortableUserDefinition($user) - { - return [ - 'user' => $user['usename'], - 'password' => $user['passwd'], - ]; - } - /** * {@inheritdoc} */ protected function _getPortableTableDefinition($table) { - $schemas = $this->getExistingSchemaSearchPaths(); - $firstSchema = array_shift($schemas); + $currentSchema = $this->getCurrentSchema(); - if ($table['schema_name'] === $firstSchema) { + if ($table['schema_name'] === $currentSchema) { return $table['table_name']; } @@ -230,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); @@ -264,36 +332,10 @@ protected function _getPortableDatabaseDefinition($database) return $database['datname']; } - /** - * {@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} * - * @deprecated Use {@link listSchemaNames()} instead. + * @deprecated Use {@see listSchemaNames()} instead. */ protected function getPortableNamespaceDefinition(array $namespace) { @@ -301,7 +343,7 @@ 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']; @@ -318,15 +360,6 @@ 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']); } @@ -458,9 +491,9 @@ protected function _getPortableTableColumnDefinition($tableColumn) if ( preg_match( - '([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', + '([A-Za-z]+\(([0-9]+),([0-9]+)\))', $tableColumn['complete_type'], - $match + $match, ) === 1 ) { $precision = $match[1]; @@ -484,7 +517,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) $tableColumn['default'] !== null && preg_match( "('([^']+)'::)", $tableColumn['default'], - $match + $match, ) === 1 ) { $tableColumn['default'] = $match[1]; @@ -511,6 +544,20 @@ protected function _getPortableTableColumnDefinition($tableColumn) } 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); } @@ -545,23 +592,182 @@ 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' +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, + obj_description(c.oid, 'pg_class') AS comment +FROM pg_class c + INNER JOIN pg_namespace n + ON n.oid = c.relnamespace +SQL; + + $conditions = array_merge(["c.relkind = 'r'"], $this->buildQueryConditions($tableName)); + + $sql .= ' WHERE ' . implode(' AND ', $conditions); + + return $this->_conn->fetchAllAssociativeIndexed($sql); + } - $platform = $this->_platform; - assert($platform instanceof PostgreSQL94Platform); - $sql = $platform->getListTableMetadataSQL($name); + /** + * @param string|null $tableName + * + * @return list + */ + private function buildQueryConditions($tableName): array + { + $conditions = []; - $tableOptions = $this->_conn->fetchAssociative($sql); + 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))'; + } - if ($tableOptions !== false) { - $table->addOption('comment', $tableOptions['table_comment']); + $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 f1836fc52..657e3651a 100644 --- a/doctrine/dbal/src/Schema/SQLServerSchemaManager.php +++ b/doctrine/dbal/src/Schema/SQLServerSchemaManager.php @@ -3,12 +3,17 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Exception; -use Doctrine\DBAL\Platforms\SQLServer2012Platform; +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; @@ -16,11 +21,74 @@ use function strpos; use function strtok; +use const CASE_LOWER; + /** * SQL Server Schema Manager. + * + * @extends AbstractSchemaManager */ class SQLServerSchemaManager extends AbstractSchemaManager { + 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::trigger( + '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} */ @@ -31,7 +99,7 @@ public function listSchemaNames(): array SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys') -SQL +SQL, ); } @@ -194,7 +262,7 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) $tableForeignKey['foreign_table'], $tableForeignKey['foreign_columns'], $tableForeignKey['name'], - $tableForeignKey['options'] + $tableForeignKey['options'], ); } @@ -203,11 +271,11 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) */ 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']; } /** @@ -221,7 +289,7 @@ protected function _getPortableDatabaseDefinition($database) /** * {@inheritdoc} * - * @deprecated Use {@link listSchemaNames()} instead. + * @deprecated Use {@see listSchemaNames()} instead. */ protected function getPortableNamespaceDefinition(array $namespace) { @@ -229,7 +297,7 @@ 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']; @@ -241,44 +309,27 @@ protected function getPortableNamespaceDefinition(array $namespace) protected function _getPortableViewDefinition($view) { // @todo - return new View($view['name'], ''); + return new View($view['name'], $view['definition']); } /** * {@inheritdoc} */ - public function listTableIndexes($table) + public function alterTable(TableDiff $tableDiff) { - $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); - - try { - $tableIndexes = $this->_conn->fetchAllAssociative($sql); - } catch (Exception $e) { - if (strpos($e->getMessage(), 'SQLSTATE [01000, 15472]') === 0) { - return []; - } - - throw $e; - } + $droppedColumns = $tableDiff->getDroppedColumns(); - return $this->_getPortableTableIndexesList($tableIndexes, $table); - } + if (count($droppedColumns) > 0) { + $tableName = ($tableDiff->getOldTable() ?? $tableDiff->getName($this->_platform))->getName(); - /** - * {@inheritdoc} - */ - public function alterTable(TableDiff $tableDiff) - { - if (count($tableDiff->removedColumns) > 0) { - foreach ($tableDiff->removedColumns as $col) { - $columnConstraintSql = $this->getColumnConstraintSQL($tableDiff->name, $col->getName()); - foreach ($this->_conn->fetchAllAssociative($columnConstraintSql) 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['Name'] - ) + $tableName, + $constraint, + ), ); } } @@ -288,43 +339,265 @@ public function alterTable(TableDiff $tableDiff) } /** - * Returns the SQL to retrieve the constraints for a given column. + * Returns the names of the constraints for a given column. * - * @param string $table - * @param string $column + * @return iterable * - * @return string + * @throws Exception */ - private function getColumnConstraintSQL($table, $column) + private function getColumnConstraints(string $table, string $column): iterable { - return "SELECT sysobjects.[Name] - FROM sysobjects INNER JOIN (SELECT [Name],[ID] FROM sysobjects WHERE XType = 'U') AS Tab - ON Tab.[ID] = sysobjects.[Parent_Obj] - INNER JOIN sys.default_constraints DefCons ON DefCons.[object_id] = sysobjects.[ID] - INNER JOIN syscolumns Col ON Col.[ColID] = DefCons.[parent_column_id] AND Col.[ID] = Tab.[ID] - WHERE Col.[Name] = " . $this->_conn->quote($column) . ' AND Tab.[Name] = ' . $this->_conn->quote($table) . ' - ORDER BY Col.[Name]'; + return $this->_conn->iterateColumn( + <<<'SQL' +SELECT o.name +FROM sys.objects o + INNER JOIN sys.objects t + ON t.object_id = o.parent_object_id + AND t.type = 'U' + INNER JOIN sys.default_constraints dc + ON dc.object_id = o.object_id + INNER JOIN sys.columns c + ON c.column_id = dc.parent_column_id + AND c.object_id = t.object_id +WHERE t.name = ? + AND c.name = ? +SQL + , + [$table, $column], + ); + } + + /** @throws Exception */ + public function createComparator(): Comparator + { + return new SQLServer\Comparator($this->_platform, $this->getDatabaseCollation()); + } + + /** @throws Exception */ + private function getDatabaseCollation(): string + { + if ($this->databaseCollation === null) { + $databaseCollation = $this->_conn->fetchOne( + 'SELECT collation_name FROM sys.databases WHERE name = ' + . $this->_platform->getCurrentDatabaseExpression(), + ); + + // a database is always selected, even if omitted in the connection parameters + assert(is_string($databaseCollation)); + + $this->databaseCollation = $databaseCollation; + } + + 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, [$databaseName]); + } + + 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); } /** - * @param string $name - * - * @throws Exception + * {@inheritDoc} */ - public function listTableDetails($name): Table + protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { - $table = parent::listTableDetails($name); + $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(); - $platform = $this->_platform; - assert($platform instanceof SQLServer2012Platform); - $sql = $platform->getListTableMetadataSQL($name); + $tableOptions = []; + foreach ($metadata as $table => $data) { + $data = array_change_key_case($data, CASE_LOWER); - $tableOptions = $this->_conn->fetchAssociative($sql); + $tableOptions[$table] = [ + 'comment' => $data['table_comment'], + ]; + } + + return $tableOptions; + } - if ($tableOptions !== false) { - $table->addOption('comment', $tableOptions['table_comment']); + /** + * 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 $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 08eb26833..703c83c59 100644 --- a/doctrine/dbal/src/Schema/Schema.php +++ b/doctrine/dbal/src/Schema/Schema.php @@ -2,11 +2,13 @@ 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; use function strpos; @@ -43,7 +45,7 @@ class Schema extends AbstractAsset * * @var string[] */ - private $namespaces = []; + private array $namespaces = []; /** @var Table[] */ protected $_tables = []; @@ -67,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'); @@ -88,10 +88,18 @@ public function __construct( } /** + * @deprecated + * * @return bool */ public function hasExplicitForeignKeyIndexes() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4822', + 'Schema::hasExplicitForeignKeyIndexes() is deprecated.', + ); + return $this->_schemaConfig->hasExplicitForeignKeyIndexes(); } @@ -103,7 +111,7 @@ public function hasExplicitForeignKeyIndexes() protected function _addTable(Table $table) { $namespaceName = $table->getNamespaceName(); - $tableName = $table->getFullQualifiedName($this->getName()); + $tableName = $this->normalizeName($table); if (isset($this->_tables[$tableName])) { throw SchemaException::tableAlreadyExists($tableName); @@ -129,7 +137,7 @@ protected function _addTable(Table $table) protected function _addSequence(Sequence $sequence) { $namespaceName = $sequence->getNamespaceName(); - $seqName = $sequence->getFullQualifiedName($this->getName()); + $seqName = $this->normalizeName($sequence); if (isset($this->_sequences[$seqName])) { throw SchemaException::sequenceAlreadyExists($seqName); @@ -183,12 +191,8 @@ public function getTable($name) return $this->_tables[$name]; } - /** - * @param string $name - * - * @return string - */ - private function getFullQualifiedAssetName($name) + /** @param string $name */ + private function getFullQualifiedAssetName($name): string { $name = $this->getUnquotedAssetName($name); @@ -199,14 +203,17 @@ private function getFullQualifiedAssetName($name) return strtolower($name); } + private function normalizeName(AbstractAsset $asset): string + { + return $asset->getFullQualifiedName($this->getName()); + } + /** * Returns the unquoted representation of a given asset name. * * @param string $assetName Quoted or unquoted representation of an asset name. - * - * @return string */ - private function getUnquotedAssetName($assetName) + private function getUnquotedAssetName($assetName): string { if ($this->isIdentifierQuoted($assetName)) { return $this->trimQuotes($assetName); @@ -246,10 +253,20 @@ public function hasTable($name) /** * Gets all table names, prefixed with a schema name, even the default one if present. * + * @deprecated Use {@see getTables()} and {@see Table::getName()} instead. + * * @return string[] */ public function getTableNames() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4800', + 'Schema::getTableNames() is deprecated.' + . ' Use Schema::getTables() and Table::getName() instead.', + __METHOD__, + ); + return array_keys($this->_tables); } @@ -282,9 +299,7 @@ public function getSequence($name) return $this->_sequences[$name]; } - /** - * @return Sequence[] - */ + /** @return Sequence[] */ public function getSequences() { return $this->_sequences; @@ -407,60 +422,72 @@ 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); } /** + * @deprecated + * * @return string[] * * @throws SchemaException */ public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform) { - $comparator = new Comparator(); - $schemaDiff = $comparator->compare($this, $toSchema); + $schemaDiff = (new Comparator())->compareSchemas($this, $toSchema); return $schemaDiff->toSql($platform); } /** + * @deprecated + * * @return string[] * * @throws SchemaException */ public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform) { - $comparator = new Comparator(); - $schemaDiff = $comparator->compare($fromSchema, $this); + $schemaDiff = (new Comparator())->compareSchemas($fromSchema, $this); return $schemaDiff->toSql($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 56d49c4a7..5e394514b 100644 --- a/doctrine/dbal/src/Schema/SchemaConfig.php +++ b/doctrine/dbal/src/Schema/SchemaConfig.php @@ -2,12 +2,18 @@ namespace Doctrine\DBAL\Schema; +use Doctrine\Deprecations\Deprecation; + /** * Configuration for a Schema. */ class SchemaConfig { - /** @var bool */ + /** + * @deprecated + * + * @var bool + */ protected $hasExplicitForeignKeyIndexes = false; /** @var int */ @@ -20,20 +26,36 @@ class SchemaConfig protected $defaultTableOptions = []; /** + * @deprecated + * * @return bool */ public function hasExplicitForeignKeyIndexes() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4822', + 'SchemaConfig::hasExplicitForeignKeyIndexes() is deprecated.', + ); + return $this->hasExplicitForeignKeyIndexes; } /** + * @deprecated + * * @param bool $flag * * @return void */ public function setExplicitForeignKeyIndexes($flag) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4822', + 'SchemaConfig::setExplicitForeignKeyIndexes() is deprecated.', + ); + $this->hasExplicitForeignKeyIndexes = (bool) $flag; } @@ -47,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 67fb9bb9d..a6a866579 100644 --- a/doctrine/dbal/src/Schema/SchemaDiff.php +++ b/doctrine/dbal/src/Schema/SchemaDiff.php @@ -3,20 +3,32 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; +use function array_filter; use function array_merge; +use function count; /** - * Schema Diff. + * Differences between two schemas. + * + * The object contains the operations to change the schema stored in $fromSchema + * to a target schema. */ class SchemaDiff { - /** @var Schema|null */ + /** + * @deprecated + * + * @var Schema|null + */ public $fromSchema; /** * All added namespaces. * + * @internal Use {@link getCreatedSchemas()} instead. + * * @var string[] */ public $newNamespaces = []; @@ -24,6 +36,8 @@ class SchemaDiff /** * All removed namespaces. * + * @internal Use {@link getDroppedSchemas()} instead. + * * @var string[] */ public $removedNamespaces = []; @@ -31,6 +45,8 @@ class SchemaDiff /** * All added tables. * + * @internal Use {@link getCreatedTables()} instead. + * * @var Table[] */ public $newTables = []; @@ -38,6 +54,8 @@ class SchemaDiff /** * All changed tables. * + * @internal Use {@link getAlteredTables()} instead. + * * @var TableDiff[] */ public $changedTables = []; @@ -45,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; } /** @@ -85,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); } } @@ -122,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/Sequence.php b/doctrine/dbal/src/Schema/Sequence.php index a634e84fa..527c63c35 100644 --- a/doctrine/dbal/src/Schema/Sequence.php +++ b/doctrine/dbal/src/Schema/Sequence.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\Deprecations\Deprecation; use function count; use function sprintf; @@ -35,25 +36,19 @@ public function __construct($name, $allocationSize = 1, $initialValue = 1, $cach $this->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 140ebb3c0..c240ce212 100644 --- a/doctrine/dbal/src/Schema/SqliteSchemaManager.php +++ b/doctrine/dbal/src/Schema/SqliteSchemaManager.php @@ -4,24 +4,28 @@ use Doctrine\DBAL\DriverManager; 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; +use Doctrine\Deprecations\Deprecation; use function array_change_key_case; use function array_map; use function array_merge; -use function array_reverse; -use function array_values; +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 sprintf; use function str_replace; +use function strcasecmp; use function strpos; use function strtolower; use function trim; @@ -32,14 +36,89 @@ /** * Sqlite SchemaManager. + * + * @extends AbstractSchemaManager */ class SqliteSchemaManager extends AbstractSchemaManager { + /** + * {@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. */ 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.', + ); + if (! file_exists($database)) { return; } @@ -49,9 +128,18 @@ public function dropDatabase($database) /** * {@inheritdoc} + * + * @deprecated The engine will create the database file automatically. */ public function createDatabase($database) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4963', + 'SqliteSchemaManager::createDatabase() is deprecated.' + . ' The engine will create the database file automatically.', + ); + $params = $this->_conn->getParams(); $params['path'] = $database; @@ -62,37 +150,37 @@ public function createDatabase($database) $conn->close(); } - /** - * {@inheritdoc} - */ - public function renameTable($name, $newName) - { - $tableDiff = new TableDiff($name); - $tableDiff->fromTable = $this->listTableDetails($name); - $tableDiff->newName = $newName; - $this->alterTable($tableDiff); - } - /** * {@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} + * + * @deprecated Use {@see dropForeignKey()} and {@see createForeignKey()} instead. */ public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) { - $tableDiff = $this->getTableDiffForAlterForeignKey($table); - $tableDiff->changedForeignKeys[] = $foreignKey; + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4897', + 'SqliteSchemaManager::dropAndCreateForeignKey() is deprecated.' + . ' Use SqliteSchemaManager::dropForeignKey() and SqliteSchemaManager::createForeignKey() instead.', + ); - $this->alterTable($tableDiff); + if (! $table instanceof Table) { + $table = $this->listTableDetails($table); + } + + $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [], [$foreignKey])); } /** @@ -100,10 +188,11 @@ public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table */ 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])); } /** @@ -111,50 +200,16 @@ public function dropForeignKey($foreignKey, $table) */ 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); } /** @@ -162,7 +217,7 @@ public function listTableForeignKeys($table, $database = null) */ protected function _getPortableTableDefinition($table) { - return $table['name']; + return $table['table_name']; } /** @@ -175,10 +230,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null $indexBuffer = []; // fetch primary - $indexArray = $this->_conn->fetchAllAssociative(sprintf( - 'PRAGMA TABLE_INFO (%s)', - $this->_conn->quote($tableName) - )); + $indexArray = $this->_conn->fetchAllAssociative('SELECT * FROM PRAGMA_TABLE_INFO (?)', [$tableName]); usort( $indexArray, @@ -192,7 +244,7 @@ static function (array $a, array $b): int { } return $a['pk'] - $b['pk']; - } + }, ); foreach ($indexArray as $indexColumnRow) { @@ -221,10 +273,7 @@ static function (array $a, array $b): int { $idx['primary'] = false; $idx['non_unique'] = ! $tableIndex['unique']; - $indexArray = $this->_conn->fetchAllAssociative(sprintf( - 'PRAGMA INDEX_INFO (%s)', - $this->_conn->quote($keyName) - )); + $indexArray = $this->_conn->fetchAllAssociative('SELECT * FROM PRAGMA_INDEX_INFO (?)', [$keyName]); foreach ($indexArray as $indexColumnRow) { $idx['column_name'] = $indexColumnRow['name']; @@ -278,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', ); } @@ -397,8 +446,8 @@ 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; } @@ -407,8 +456,8 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) $value['on_update'] = null; } - $list[$name] = [ - 'name' => $name, + $list[$id] = [ + 'name' => $value['constraint_name'], 'local' => [], 'foreign' => [], 'foreignTable' => $value['table'], @@ -419,59 +468,35 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) ]; } - $list[$name]['local'][] = $value['from']; + $list[$id]['local'][] = $value['from']; if ($value['to'] === null) { continue; } - $list[$name]['foreign'][] = $value['to']; + $list[$id]['foreign'][] = $value['to']; } - $result = []; - foreach ($list as $constraint) { - $result[] = new ForeignKeyConstraint( - array_values($constraint['local']), - $constraint['foreignTable'], - array_values($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 - * - * @return TableDiff - * - * @throws Exception + * {@inheritDoc} */ - private function getTableDiffForAlterForeignKey($table) + protected function _getPortableTableForeignKeyDefinition($tableForeignKey): ForeignKeyConstraint { - if (! $table instanceof Table) { - $tableDetails = $this->tryMethod('listTableDetails', $table); - - if ($tableDetails === false) { - throw new Exception( - sprintf('Sqlite schema manager requires to modify foreign keys table definition "%s".', $table) - ); - } - - $table = $tableDetails; - } - - $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 @@ -520,9 +545,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( @@ -539,7 +562,7 @@ private function getCreateTableSQL(string $table): string AND name = ? SQL , - [$table] + [$table], ); if ($sql !== false) { @@ -550,31 +573,209 @@ 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->_platform); } /** * {@inheritDoc} + * + * @deprecated */ public function getSchemaSearchPaths() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4821', + '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 d330d03f4..7fbd6091a 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)); } @@ -314,8 +305,6 @@ public function columnsAreIndexed(array $columnNames) * @param string[] $flags * @param mixed[] $options * - * @return Index - * * @throws SchemaException */ private function _createIndex( @@ -325,7 +314,7 @@ private function _createIndex( $isPrimary, array $flags = [], array $options = [] - ) { + ): Index { if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { throw SchemaException::indexNameInvalid($indexName); } @@ -360,6 +349,8 @@ public function addColumn($name, $typeName, array $options = []) /** * Change Column Details. * + * @deprecated Use {@link modifyColumn()} instead. + * * @param string $name * @param mixed[] $options * @@ -368,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); @@ -413,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) { @@ -440,7 +449,7 @@ public function addForeignKeyConstraint( $foreignTable, $foreignColumnNames, $name, - $options + $options, ); return $this->_addForeignKeyConstraint($constraint); @@ -490,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; } @@ -517,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()); @@ -539,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; } } @@ -549,9 +556,7 @@ protected function _addUniqueConstraint(UniqueConstraint $constraint): Table return $this; } - /** - * @return self - */ + /** @return self */ protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) { $constraint->setLocalTable($this); @@ -562,7 +567,7 @@ protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) $name = $this->_generateIdentifierName( array_merge([$this->getName()], $constraint->getLocalColumns()), 'fk', - $this->_getMaxIdentifierLength() + $this->_getMaxIdentifierLength(), ); } @@ -580,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; } } @@ -700,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); @@ -713,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::trigger( + '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) { @@ -791,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::trigger( + '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) { @@ -809,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); } @@ -849,9 +881,7 @@ public function getIndex($name) return $this->_indexes[$name]; } - /** - * @return Index[] - */ + /** @return Index[] */ public function getIndexes() { return $this->_indexes; @@ -897,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..58128d75b 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 $fromColumn 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..85502e714 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 @@ -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..3d2ad27c0 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 { diff --git a/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php b/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php index c08fb6fec..e7908c3ee 100644 --- a/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php +++ b/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php @@ -6,28 +6,35 @@ 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; } @@ -71,9 +78,7 @@ public function acceptSequence(Sequence $sequence) $this->createSequenceQueries[] = $this->platform->getCreateSequenceSQL($sequence); } - /** - * @return void - */ + /** @return void */ public function resetQueries() { $this->createNamespaceQueries = []; @@ -91,9 +96,9 @@ public function getQueries() { return array_merge( $this->createNamespaceQueries, - $this->createTableQueries, $this->createSequenceQueries, - $this->createFkConstraintQueries + $this->createTableQueries, + $this->createFkConstraintQueries, ); } } diff --git a/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php b/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php index 1b77268b4..0c5730258 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,23 +15,24 @@ /** * 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(); } @@ -63,17 +65,13 @@ 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 299ff0e7f..e3c61411e 100644 --- a/doctrine/dbal/src/Schema/Visitor/Graphviz.php +++ b/doctrine/dbal/src/Schema/Visitor/Graphviz.php @@ -13,11 +13,12 @@ /** * Create a Graphviz output of a Schema. + * + * @deprecated */ class Graphviz extends AbstractVisitor { - /** @var string */ - private $output = ''; + private string $output = ''; /** * {@inheritdoc} @@ -31,7 +32,7 @@ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkCons 'dir' => 'back', 'arrowtail' => 'dot', 'arrowhead' => 'normal', - ] + ], ); } @@ -58,14 +59,11 @@ public function acceptTable(Table $table) [ 'label' => $this->createTableLabel($table), 'shape' => 'plaintext', - ] + ], ); } - /** - * @return string - */ - private function createTableLabel(Table $table) + private function createTableLabel(Table $table): string { // Start the table $label = '<
'; @@ -107,10 +105,8 @@ private function createTableLabel(Table $table) /** * @param string $name * @param string[] $options - * - * @return string */ - private function createNode($name, $options) + private function createNode($name, $options): string { $node = $name . ' ['; foreach ($options as $key => $value) { @@ -126,10 +122,8 @@ private function createNode($name, $options) * @param string $node1 * @param string $node2 * @param string[] $options - * - * @return string */ - private function createNodeRelation($node1, $node2, $options) + private function createNodeRelation($node1, $node2, $options): string { $relation = $node1 . ' -> ' . $node2 . ' ['; foreach ($options as $key => $value) { 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..c67bf99e8 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,12 +17,23 @@ * 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} 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 @@ -Statement for the given SQL and Connection. * - * @internal The statement can be only instantiated by {@link Connection}. + * @internal The statement can be only instantiated by {@see Connection}. * - * @param string $sql The SQL of the statement. - * @param Connection $conn The connection on which the statement should be executed. + * @param Connection $conn The connection for handling statement errors. + * @param Driver\Statement $statement The underlying driver-level statement. + * @param string $sql The SQL of the statement. * * @throws Exception */ - public function __construct($sql, Connection $conn) + public function __construct(Connection $conn, Driver\Statement $statement, string $sql) { - $driverConnection = $conn->getWrappedConnection(); - - try { - $stmt = $driverConnection->prepare($sql); - } catch (Exception $ex) { - throw $conn->convertExceptionDuringQuery($ex, $sql); - } - - $this->sql = $sql; - $this->stmt = $stmt; $this->conn = $conn; + $this->stmt = $statement; + $this->sql = $sql; $this->platform = $conn->getDatabasePlatform(); } @@ -122,7 +113,7 @@ public function bindValue($param, $value, $type = ParameterType::STRING) try { return $this->stmt->bindValue($param, $value, $bindingType); - } catch (Exception $e) { + } catch (Driver\Exception $e) { throw $this->conn->convertException($e); } } @@ -132,6 +123,8 @@ public function bindValue($param, $value, $type = ParameterType::STRING) * * Binding a parameter by reference does not support DBAL mapping types. * + * @deprecated Use {@see bindValue()} instead. + * * @param string|int $param The name or position of the parameter. * @param mixed $variable The reference to the variable to bind. * @param int $type The binding type. @@ -144,6 +137,13 @@ public function bindValue($param, $value, $type = ParameterType::STRING) */ 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__, + ); + $this->params[$param] = $variable; $this->types[$param] = $type; @@ -153,7 +153,7 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le } return $this->stmt->bindParam($param, $variable, $type); - } catch (Exception $e) { + } catch (Driver\Exception $e) { throw $this->conn->convertException($e); } } @@ -172,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) { @@ -187,9 +188,9 @@ public function execute($params = null): Result try { return new Result( $this->stmt->execute($params), - $this->conn + $this->conn, ); - } catch (Exception $ex) { + } catch (Driver\Exception $ex) { throw $this->conn->convertExceptionDuringQuery($ex, $this->sql, $this->params, $this->types); } finally { if ($logger !== null) { @@ -207,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. } @@ -223,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. } @@ -233,7 +252,7 @@ public function executeStatement(array $params = []): int /** * Gets the wrapped driver statement. * - * @return DriverStatement + * @return Driver\Statement */ public function getWrappedStatement() { diff --git a/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php b/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php index 86a2842cb..eb854a594 100644 --- a/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php +++ b/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php @@ -31,16 +31,22 @@ use function is_array; use function is_string; +/** @deprecated Use database documentation instead. */ class ReservedWordsCommand extends Command { /** @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; @@ -80,7 +86,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 +104,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,8 +132,7 @@ protected function configure() * pgsql100 * sqlite * sqlserver -EOT - ); +EOT); } /** @@ -139,6 +144,12 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $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 +169,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 +178,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 +190,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 875f2a20b..5bd72ea6a 100644 --- a/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php +++ b/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php @@ -5,19 +5,19 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Tools\Console\ConnectionProvider; -use Doctrine\DBAL\Tools\Dumper; -use LogicException; use RuntimeException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use function array_keys; use function assert; use function is_bool; -use function is_numeric; use function is_string; +use function sprintf; use function stripos; /** @@ -26,8 +26,7 @@ */ class RunSqlCommand extends Command { - /** @var ConnectionProvider */ - private $connectionProvider; + private ConnectionProvider $connectionProvider; public function __construct(ConnectionProvider $connectionProvider) { @@ -44,16 +43,15 @@ protected function configure() ->setDefinition([ new InputOption('connection', null, InputOption::VALUE_REQUIRED, 'The named database connection'), new InputArgument('sql', InputArgument::REQUIRED, 'The SQL statement to execute.'), - new InputOption('depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of result set.', '7'), + 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); } /** @@ -66,6 +64,7 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $conn = $this->getConnection($input); + $io = new SymfonyStyle($input, $output); $sql = $input->getArgument('sql'); @@ -75,23 +74,19 @@ protected function execute(InputInterface $input, OutputInterface $output) assert(is_string($sql)); - $depth = $input->getOption('depth'); - - if (! is_numeric($depth)) { - throw new LogicException("Option 'depth' must contains an integer value"); + if ($input->getOption('depth') !== null) { + $io->warning('Parameter "depth" is deprecated and has no effect anymore.'); } $forceFetch = $input->getOption('force-fetch'); assert(is_bool($forceFetch)); if (stripos($sql, 'select') === 0 || $forceFetch) { - $resultSet = $conn->fetchAllAssociative($sql); + $this->runQuery($io, $conn, $sql); } else { - $resultSet = $conn->executeStatement($sql); + $this->runStatement($io, $conn, $sql); } - $output->write(Dumper::dump($resultSet, (int) $depth)); - return 0; } @@ -106,4 +101,23 @@ private function getConnection(InputInterface $input): Connection return $this->connectionProvider->getDefaultConnection(); } + + /** @throws Exception */ + private function runQuery(SymfonyStyle $io, Connection $conn, string $sql): void + { + $resultSet = $conn->fetchAllAssociative($sql); + if ($resultSet === []) { + $io->success('The query yielded an empty result set.'); + + return; + } + + $io->table(array_keys($resultSet[0]), $resultSet); + } + + /** @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 1cc315dee..e8fa3c60b 100644 --- a/doctrine/dbal/src/Tools/Console/ConsoleRunner.php +++ b/doctrine/dbal/src/Tools/Console/ConsoleRunner.php @@ -2,15 +2,19 @@ namespace Doctrine\DBAL\Tools\Console; +use Composer\InstalledVersions; use Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand; use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand; use Exception; -use PackageVersions\Versions; use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; +use function assert; + /** * Handles running the Console Tools inside Symfony Console context. + * + * @deprecated Use Symfony Console documentation to bootstrap a command-line application. */ class ConsoleRunner { @@ -25,7 +29,10 @@ class ConsoleRunner */ public static function run(ConnectionProvider $connectionProvider, $commands = []) { - $cli = new Application('Doctrine Command Line Interface', Versions::getVersion('doctrine/dbal')); + $version = InstalledVersions::getVersion('doctrine/dbal'); + assert($version !== null); + + $cli = new Application('Doctrine Command Line Interface', $version); $cli->setCatchExceptions(true); self::addCommands($cli, $connectionProvider); @@ -33,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([ @@ -47,6 +52,8 @@ public static function addCommands(Application $cli, ConnectionProvider $connect /** * Prints the instructions to create a configuration file * + * @deprecated This method will be removed without replacement. + * * @return void */ public static function printCliConfigTemplate() diff --git a/doctrine/dbal/src/Tools/Dumper.php b/doctrine/dbal/src/Tools/Dumper.php deleted file mode 100644 index 23dc7a059..000000000 --- a/doctrine/dbal/src/Tools/Dumper.php +++ /dev/null @@ -1,181 +0,0 @@ -toArray(); - } - - if ($maxDepth === 0) { - return is_object($var) ? get_class($var) - : (is_array($var) ? 'Array(' . count($var) . ')' : $var); - } - - if (is_array($var)) { - $return = []; - - foreach ($var as $k => $v) { - $return[$k] = self::export($v, $maxDepth - 1); - } - - return $return; - } - - if (! $isObj) { - return $var; - } - - $return = new stdClass(); - if ($var instanceof DateTimeInterface) { - $return->__CLASS__ = get_class($var); - $return->date = $var->format('c'); - $return->timezone = $var->getTimezone()->getName(); - - return $return; - } - - $return->__CLASS__ = self::getClass($var); - - if ($var instanceof Proxy) { - $return->__IS_PROXY__ = true; - $return->__PROXY_INITIALIZED__ = $var->__isInitialized(); - } - - if ($var instanceof ArrayObject || $var instanceof ArrayIterator) { - $return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1); - } - - return self::fillReturnWithClassAttributes($var, $return, $maxDepth); - } - - /** - * Fill the $return variable with class attributes - * Based on obj2array function from {@see https://secure.php.net/manual/en/function.get-object-vars.php#47075} - * - * @param object $var - * - * @return mixed - */ - private static function fillReturnWithClassAttributes($var, stdClass $return, int $maxDepth) - { - $clone = (array) $var; - - foreach (array_keys($clone) as $key) { - $aux = explode("\0", $key); - $name = end($aux); - if ($aux[0] === '') { - $name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private'); - } - - $return->$name = self::export($clone[$key], $maxDepth - 1); - } - - return $return; - } - - /** - * @param object $object - */ - private static function getClass($object): string - { - $class = get_class($object); - - if (! class_exists(Proxy::class)) { - return $class; - } - - $pos = strrpos($class, '\\' . Proxy::MARKER . '\\'); - - if ($pos === false) { - return $class; - } - - return substr($class, $pos + strlen(Proxy::MARKER) + 2); - } -} 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 3137e03f3..fb202406f 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; @@ -11,8 +12,13 @@ use function stream_get_contents; use function unserialize; +use const E_DEPRECATED; +use const E_USER_DEPRECATED; + /** * Type that maps a PHP array to a clob SQL type. + * + * @deprecated Use {@link JsonType} instead. */ class ArrayType extends Type { @@ -45,6 +51,10 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $value = is_resource($value) ? stream_get_contents($value) : $value; set_error_handler(function (int $code, string $message): bool { + if ($code === E_DEPRECATED || $code === E_USER_DEPRECATED) { + return false; + } + throw ConversionException::conversionFailedUnserialization($this->getName(), $message); }); @@ -65,9 +75,18 @@ public function getName() /** * {@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 e79757486..ab1e0e061 100644 --- a/doctrine/dbal/src/Types/AsciiStringType.php +++ b/doctrine/dbal/src/Types/AsciiStringType.php @@ -12,15 +12,12 @@ final class AsciiStringType extends StringType /** * {@inheritdoc} */ - public function getSQLDeclaration(array $column, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { return $platform->getAsciiStringTypeDeclarationSQL($column); } - /** - * {@inheritdoc} - */ - public function getBindingType() + public function getBindingType(): int { return ParameterType::ASCII; } diff --git a/doctrine/dbal/src/Types/BooleanType.php b/doctrine/dbal/src/Types/BooleanType.php index f6e4df3b2..92f530558 100644 --- a/doctrine/dbal/src/Types/BooleanType.php +++ b/doctrine/dbal/src/Types/BooleanType.php @@ -4,6 +4,8 @@ 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. @@ -49,4 +51,23 @@ public function getBindingType() { return ParameterType::BOOLEAN; } + + /** + * @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 f1a18742e..154b06d3e 100644 --- a/doctrine/dbal/src/Types/ConversionException.php +++ b/doctrine/dbal/src/Types/ConversionException.php @@ -5,6 +5,8 @@ use Doctrine\DBAL\Exception; use Throwable; +use function func_get_arg; +use function func_num_args; use function get_class; use function gettype; use function implode; @@ -25,7 +27,7 @@ class ConversionException extends Exception /** * Thrown when a Database to Doctrine Type Conversion fails. * - * @param string $value + * @param mixed $value * @param string $toType * * @return ConversionException @@ -41,7 +43,7 @@ public static function conversionFailed($value, $toType, ?Throwable $previous = * Thrown when a Database to Doctrine Type Conversion fails and we can make a statement * about the expected format. * - * @param string $value + * @param mixed $value * @param string $toType * @param string $expectedFormat * @@ -55,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, ); } @@ -79,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); } @@ -87,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); } @@ -98,7 +100,7 @@ public static function conversionFailedInvalidType( * * @return ConversionException */ - public static function conversionFailedSerialization($value, $format, $error) + public static function conversionFailedSerialization($value, $format, $error /*, ?Throwable $previous = null */) { $actualType = is_object($value) ? get_class($value) : gettype($value); @@ -106,8 +108,8 @@ 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); } public static function conversionFailedUnserialization(string $format, string $error): self @@ -115,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..98fa6dc61 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}. @@ -34,7 +35,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', DateTimeImmutable::class] + ['null', DateTimeImmutable::class], ); } @@ -53,7 +54,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedFormat( $value, $this->getName(), - $platform->getDateFormatString() + $platform->getDateFormatString(), ); } @@ -62,9 +63,18 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@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..72156f17d 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; @@ -30,7 +31,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { $column['length'] = 255; - return $platform->getVarcharTypeDeclarationSQL($column); + return $platform->getStringTypeDeclarationSQL($column); } /** @@ -80,9 +81,18 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@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..6f7896202 100644 --- a/doctrine/dbal/src/Types/DateTimeImmutableType.php +++ b/doctrine/dbal/src/Types/DateTimeImmutableType.php @@ -4,6 +4,7 @@ use DateTimeImmutable; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; use function date_create_immutable; @@ -36,7 +37,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', DateTimeImmutable::class] + ['null', DateTimeImmutable::class], ); } @@ -59,7 +60,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedFormat( $value, $this->getName(), - $platform->getDateTimeFormatString() + $platform->getDateTimeFormatString(), ); } @@ -68,9 +69,18 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@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..738c6bfb5 100644 --- a/doctrine/dbal/src/Types/DateTimeType.php +++ b/doctrine/dbal/src/Types/DateTimeType.php @@ -64,7 +64,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedFormat( $value, $this->getName(), - $platform->getDateTimeFormatString() + $platform->getDateTimeFormatString(), ); } diff --git a/doctrine/dbal/src/Types/DateTimeTzImmutableType.php b/doctrine/dbal/src/Types/DateTimeTzImmutableType.php index 6e707e065..222a9c3b0 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}. @@ -34,7 +35,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', DateTimeImmutable::class] + ['null', DateTimeImmutable::class], ); } @@ -53,7 +54,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedFormat( $value, $this->getName(), - $platform->getDateTimeTzFormatString() + $platform->getDateTimeTzFormatString(), ); } @@ -62,9 +63,18 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@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..ee08f9d1e 100644 --- a/doctrine/dbal/src/Types/DateTimeTzType.php +++ b/doctrine/dbal/src/Types/DateTimeTzType.php @@ -56,7 +56,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', 'DateTime'] + ['null', 'DateTime'], ); } @@ -74,7 +74,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedFormat( $value, $this->getName(), - $platform->getDateTimeTzFormatString() + $platform->getDateTimeTzFormatString(), ); } diff --git a/doctrine/dbal/src/Types/DateType.php b/doctrine/dbal/src/Types/DateType.php index 6f86f5436..533667dd5 100644 --- a/doctrine/dbal/src/Types/DateType.php +++ b/doctrine/dbal/src/Types/DateType.php @@ -57,7 +57,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedFormat( $value, $this->getName(), - $platform->getDateFormatString() + $platform->getDateFormatString(), ); } diff --git a/doctrine/dbal/src/Types/DecimalType.php b/doctrine/dbal/src/Types/DecimalType.php index f75d3db2c..144e97a04 100644 --- a/doctrine/dbal/src/Types/DecimalType.php +++ b/doctrine/dbal/src/Types/DecimalType.php @@ -3,8 +3,10 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\SqlitePlatform; use function is_float; +use function is_int; use const PHP_VERSION_ID; @@ -34,9 +36,9 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) */ public function convertToPHPValue($value, AbstractPlatform $platform) { - // Some drivers starting from PHP 8.1 can represent decimals as float + // 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)) { + if ((PHP_VERSION_ID >= 80100 || $platform instanceof SqlitePlatform) && (is_float($value) || is_int($value))) { return (string) $value; } diff --git a/doctrine/dbal/src/Types/GuidType.php b/doctrine/dbal/src/Types/GuidType.php index a4974f9d0..6ce903e2b 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. @@ -27,9 +28,18 @@ public function getName() /** * {@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/JsonType.php b/doctrine/dbal/src/Types/JsonType.php index d17b7f51e..20ee79deb 100644 --- a/doctrine/dbal/src/Types/JsonType.php +++ b/doctrine/dbal/src/Types/JsonType.php @@ -3,15 +3,16 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; +use JsonException; use function is_resource; use function json_decode; use function json_encode; -use function json_last_error; -use function json_last_error_msg; use function stream_get_contents; -use const JSON_ERROR_NONE; +use const JSON_PRESERVE_ZERO_FRACTION; +use const JSON_THROW_ON_ERROR; /** * Type generating json objects values @@ -35,13 +36,11 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return null; } - $encoded = json_encode($value); - - if (json_last_error() !== JSON_ERROR_NONE) { - throw ConversionException::conversionFailedSerialization($value, 'json', json_last_error_msg()); + try { + return json_encode($value, JSON_THROW_ON_ERROR | JSON_PRESERVE_ZERO_FRACTION); + } catch (JsonException $e) { + throw ConversionException::conversionFailedSerialization($value, 'json', $e->getMessage(), $e); } - - return $encoded; } /** @@ -57,13 +56,11 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $value = stream_get_contents($value); } - $val = json_decode($value, true); - - if (json_last_error() !== JSON_ERROR_NONE) { - throw ConversionException::conversionFailed($value, $this->getName()); + try { + return json_decode($value, true, 512, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + throw ConversionException::conversionFailed($value, $this->getName(), $e); } - - return $val; } /** @@ -76,9 +73,18 @@ public function getName() /** * {@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..f8bcc9437 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,6 +14,8 @@ /** * Type that maps a PHP object to a clob SQL type. + * + * @deprecated Use {@link JsonType} instead. */ class ObjectType extends Type { @@ -64,9 +67,18 @@ public function getName() /** * {@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..3ec695cb7 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; @@ -62,9 +63,18 @@ public function getName() /** * {@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/StringType.php b/doctrine/dbal/src/Types/StringType.php index 4e7bd55d0..e409c2a8f 100644 --- a/doctrine/dbal/src/Types/StringType.php +++ b/doctrine/dbal/src/Types/StringType.php @@ -14,7 +14,7 @@ class StringType extends Type */ public function getSQLDeclaration(array $column, AbstractPlatform $platform) { - return $platform->getVarcharTypeDeclarationSQL($column); + return $platform->getStringTypeDeclarationSQL($column); } /** diff --git a/doctrine/dbal/src/Types/TimeImmutableType.php b/doctrine/dbal/src/Types/TimeImmutableType.php index 8d2c1517c..eef8ba91f 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}. @@ -34,7 +35,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', DateTimeImmutable::class] + ['null', DateTimeImmutable::class], ); } @@ -53,7 +54,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedFormat( $value, $this->getName(), - $platform->getTimeFormatString() + $platform->getTimeFormatString(), ); } @@ -62,9 +63,18 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@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..9c8f03e98 100644 --- a/doctrine/dbal/src/Types/TimeType.php +++ b/doctrine/dbal/src/Types/TimeType.php @@ -57,7 +57,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedFormat( $value, $this->getName(), - $platform->getTimeFormatString() + $platform->getTimeFormatString(), ); } diff --git a/doctrine/dbal/src/Types/Type.php b/doctrine/dbal/src/Types/Type.php index 03e5b8248..536cf36f2 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; @@ -12,7 +13,7 @@ /** * The base class for so-called Doctrine mapping types. * - * A Type object is obtained by calling the static {@link getType()} method. + * A Type object is obtained by calling the static {@see getType()} method. */ abstract class Type { @@ -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,15 @@ 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. * - * @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 @@ -189,7 +183,7 @@ public static function overrideType($name, $className) * Gets the (preferred) binding type for values of this type that * can be used when binding parameters to prepared statements. * - * This method should return one of the {@link ParameterType} constants. + * This method should return one of the {@see ParameterType} constants. * * @return int */ @@ -202,7 +196,7 @@ public function getBindingType() * Gets the types array map which holds all registered types and the corresponding * type class * - * @return string[] + * @return array */ public static function getTypesMap() { @@ -210,7 +204,7 @@ public static function getTypesMap() static function (Type $type): string { return get_class($type); }, - self::getTypeRegistry()->getMap() + self::getTypeRegistry()->getMap(), ); } @@ -218,10 +212,13 @@ static function (Type $type): string { * Does working with this column require SQL conversion functions? * * This is a metadata function that is required for example in the ORM. - * Usage of {@link convertToDatabaseValueSQL} and - * {@link convertToPHPValueSQL} works for any type and mostly + * Usage of {@see convertToDatabaseValueSQL} and + * {@see convertToPHPValueSQL} works for any type and mostly * does nothing. This method can additionally be used for optimization purposes. * + * @deprecated Consumers should call {@see convertToDatabaseValueSQL} and {@see convertToPHPValueSQL} + * regardless of the type. + * * @return bool */ public function canRequireSQLConversion() @@ -270,10 +267,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..b5e800ad1 100644 --- a/doctrine/dbal/src/Types/TypeRegistry.php +++ b/doctrine/dbal/src/Types/TypeRegistry.php @@ -16,11 +16,9 @@ final class TypeRegistry { /** @var array Map of type names and their corresponding flyweight objects. */ - private $instances; + private array $instances; - /** - * @param array $instances - */ + /** @param array $instances */ public function __construct(array $instances = []) { $this->instances = $instances; 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..24bddc443 100644 --- a/doctrine/dbal/src/Types/VarDateTimeImmutableType.php +++ b/doctrine/dbal/src/Types/VarDateTimeImmutableType.php @@ -4,6 +4,7 @@ use DateTimeImmutable; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; use function date_create_immutable; @@ -36,7 +37,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), - ['null', DateTimeImmutable::class] + ['null', DateTimeImmutable::class], ); } @@ -60,9 +61,18 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@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/VersionAwarePlatformDriver.php b/doctrine/dbal/src/VersionAwarePlatformDriver.php index b3ec8b843..ffcfcd636 100644 --- a/doctrine/dbal/src/VersionAwarePlatformDriver.php +++ b/doctrine/dbal/src/VersionAwarePlatformDriver.php @@ -11,6 +11,8 @@ * support the correct features and SQL syntax of each version. * This interface should be implemented by drivers that are capable to do this * distinction. + * + * @deprecated All drivers will have to be aware of the server version in the next major release. */ interface VersionAwarePlatformDriver extends Driver { diff --git a/doctrine/deprecations/.gitignore b/doctrine/deprecations/.gitignore deleted file mode 100644 index 2ee7dedc1..000000000 --- a/doctrine/deprecations/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor -.phpcs-cache -composer.lock diff --git a/composer/package-versions-deprecated/LICENSE b/doctrine/deprecations/LICENSE similarity index 96% rename from composer/package-versions-deprecated/LICENSE rename to doctrine/deprecations/LICENSE index a90b0792c..156905cdd 100644 --- a/composer/package-versions-deprecated/LICENSE +++ b/doctrine/deprecations/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016 Marco Pivetta +Copyright (c) 2020-2021 Doctrine Project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/doctrine/deprecations/README.md b/doctrine/deprecations/README.md index d66822214..22f0cced3 100644 --- a/doctrine/deprecations/README.md +++ b/doctrine/deprecations/README.md @@ -137,6 +137,13 @@ class MyTest extends TestCase triggerTheCodeWithDeprecation(); } + + public function testSomethingDeprecationFixed() + { + $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234'); + + triggerTheCodeWithoutDeprecation(); + } } ``` diff --git a/doctrine/deprecations/composer.json b/doctrine/deprecations/composer.json index 5cc7ac13d..c79e38cdc 100644 --- a/doctrine/deprecations/composer.json +++ b/doctrine/deprecations/composer.json @@ -8,9 +8,9 @@ "php": "^7.1|^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.0|^8.0|^9.0", - "psr/log": "^1.0", - "doctrine/coding-standard": "^6.0|^7.0|^8.0" + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3", + "doctrine/coding-standard": "^9" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" @@ -23,5 +23,10 @@ "DeprecationTests\\": "test_fixtures/src", "Doctrine\\Foo\\": "test_fixtures/vendor/doctrine/foo" } + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } } } diff --git a/doctrine/deprecations/phpcs.xml b/doctrine/deprecations/phpcs.xml index 4e0cc21fd..f115e43dd 100644 --- a/doctrine/deprecations/phpcs.xml +++ b/doctrine/deprecations/phpcs.xml @@ -9,6 +9,8 @@ + + lib tests diff --git a/doctrine/deprecations/phpunit.xml.dist b/doctrine/deprecations/phpunit.xml.dist deleted file mode 100644 index 4740c0604..000000000 --- a/doctrine/deprecations/phpunit.xml.dist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - tests - - - diff --git a/doctrine/deprecations/test_fixtures/src/Foo.php b/doctrine/deprecations/test_fixtures/src/Foo.php deleted file mode 100644 index c4b8ebece..000000000 --- a/doctrine/deprecations/test_fixtures/src/Foo.php +++ /dev/null @@ -1,22 +0,0 @@ -oldFunc(); - } - - public static function triggerDependencyWithDeprecationFromInside(): void - { - $bar = new Bar(); - $bar->newFunc(); - } -} diff --git a/doctrine/deprecations/test_fixtures/src/RootDeprecation.php b/doctrine/deprecations/test_fixtures/src/RootDeprecation.php deleted file mode 100644 index feccd4865..000000000 --- a/doctrine/deprecations/test_fixtures/src/RootDeprecation.php +++ /dev/null @@ -1,20 +0,0 @@ - + + + + + + + + diff --git a/doctrine/event-manager/lib/Doctrine/Common/EventArgs.php b/doctrine/event-manager/src/EventArgs.php similarity index 97% rename from doctrine/event-manager/lib/Doctrine/Common/EventArgs.php rename to doctrine/event-manager/src/EventArgs.php index 9b9cc0565..4795ba6be 100644 --- a/doctrine/event-manager/lib/Doctrine/Common/EventArgs.php +++ b/doctrine/event-manager/src/EventArgs.php @@ -16,7 +16,7 @@ class EventArgs /** * Single instance of EventArgs. * - * @var EventArgs + * @var EventArgs|null */ private static $_emptyEventArgsInstance; diff --git a/doctrine/event-manager/lib/Doctrine/Common/EventManager.php b/doctrine/event-manager/src/EventManager.php similarity index 72% rename from doctrine/event-manager/lib/Doctrine/Common/EventManager.php rename to doctrine/event-manager/src/EventManager.php index a0860e37e..6d841be27 100644 --- a/doctrine/event-manager/lib/Doctrine/Common/EventManager.php +++ b/doctrine/event-manager/src/EventManager.php @@ -2,6 +2,8 @@ namespace Doctrine\Common; +use Doctrine\Deprecations\Deprecation; + use function spl_object_hash; /** @@ -15,9 +17,9 @@ class EventManager * Map of registered listeners. * => * - * @var object[][] + * @var array */ - private $_listeners = []; + private $listeners = []; /** * Dispatches an event to all registered listeners. @@ -31,27 +33,49 @@ class EventManager */ public function dispatchEvent($eventName, ?EventArgs $eventArgs = null) { - if (! isset($this->_listeners[$eventName])) { + if (! isset($this->listeners[$eventName])) { return; } $eventArgs = $eventArgs ?? EventArgs::getEmptyInstance(); - foreach ($this->_listeners[$eventName] as $listener) { + foreach ($this->listeners[$eventName] as $listener) { $listener->$eventName($eventArgs); } } /** - * Gets the listeners of a specific event or all listeners. + * Gets the listeners of a specific event. * * @param string|null $event The name of the event. * - * @return object[]|object[][] The event listeners for the specified event, or all event listeners. + * @return object[]|array The event listeners for the specified event, or all event listeners. + * @psalm-return ($event is null ? array : object[]) */ public function getListeners($event = null) { - return $event ? $this->_listeners[$event] : $this->_listeners; + if ($event === null) { + Deprecation::trigger( + 'doctrine/event-manager', + 'https://github.com/doctrine/event-manager/pull/50', + 'Calling %s without an event name is deprecated. Call getAllListeners() instead.', + __METHOD__ + ); + + return $this->getAllListeners(); + } + + return $this->listeners[$event] ?? []; + } + + /** + * Gets all listeners keyed by event name. + * + * @return array The event listeners for the specified event, or all event listeners. + */ + public function getAllListeners(): array + { + return $this->listeners; } /** @@ -63,7 +87,7 @@ public function getListeners($event = null) */ public function hasListeners($event) { - return ! empty($this->_listeners[$event]); + return ! empty($this->listeners[$event]); } /** @@ -82,7 +106,7 @@ public function addEventListener($events, $listener) foreach ((array) $events as $event) { // Overrides listener if a previous one was associated already // Prevents duplicate listeners on same event (same instance only) - $this->_listeners[$event][$hash] = $listener; + $this->listeners[$event][$hash] = $listener; } } @@ -100,7 +124,7 @@ public function removeEventListener($events, $listener) $hash = spl_object_hash($listener); foreach ((array) $events as $event) { - unset($this->_listeners[$event][$hash]); + unset($this->listeners[$event][$hash]); } } diff --git a/doctrine/event-manager/lib/Doctrine/Common/EventSubscriber.php b/doctrine/event-manager/src/EventSubscriber.php similarity index 86% rename from doctrine/event-manager/lib/Doctrine/Common/EventSubscriber.php rename to doctrine/event-manager/src/EventSubscriber.php index 7d5e2ea3f..89cef558a 100644 --- a/doctrine/event-manager/lib/Doctrine/Common/EventSubscriber.php +++ b/doctrine/event-manager/src/EventSubscriber.php @@ -5,7 +5,7 @@ namespace Doctrine\Common; /** - * An EventSubscriber knows himself what events he is interested in. + * An EventSubscriber knows what events it is interested in. * If an EventSubscriber is added to an EventManager, the manager invokes * {@link getSubscribedEvents} and registers the subscriber as a listener for all * returned events. diff --git a/psr/cache/LICENSE.txt b/psr/cache/LICENSE.txt new file mode 100644 index 000000000..b1c2c97b9 --- /dev/null +++ b/psr/cache/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2015 PHP Framework Interoperability Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/psr/cache/README.md b/psr/cache/README.md new file mode 100644 index 000000000..c8706ceea --- /dev/null +++ b/psr/cache/README.md @@ -0,0 +1,9 @@ +PSR Cache +========= + +This repository holds all interfaces defined by +[PSR-6](http://www.php-fig.org/psr/psr-6/). + +Note that this is not a Cache implementation of its own. It is merely an +interface that describes a Cache implementation. See the specification for more +details. diff --git a/psr/cache/composer.json b/psr/cache/composer.json new file mode 100644 index 000000000..e828fec94 --- /dev/null +++ b/psr/cache/composer.json @@ -0,0 +1,25 @@ +{ + "name": "psr/cache", + "description": "Common interface for caching libraries", + "keywords": ["psr", "psr-6", "cache"], + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/psr/cache/src/CacheException.php b/psr/cache/src/CacheException.php new file mode 100644 index 000000000..e27f22f8d --- /dev/null +++ b/psr/cache/src/CacheException.php @@ -0,0 +1,10 @@ +