diff options
author | dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> | 2022-06-18 05:50:25 +0300 |
---|---|---|
committer | nextcloud-command <nextcloud-command@users.noreply.github.com> | 2022-06-24 16:48:35 +0300 |
commit | 58cd2d57fb06b93e15c8d8c406c98f7ac6345227 (patch) | |
tree | 3d243fbbe0e9726847bec8f8b4d25a26c110993b | |
parent | b7964c17e8a2198313ee31f6117c1eb692f6e2c9 (diff) |
Bump doctrine/dbal from 3.1.4 to 3.3.7dependabot/composer/stable24/doctrine/dbal-3.3.7
Bumps [doctrine/dbal](https://github.com/doctrine/dbal) from 3.1.4 to 3.3.7.
- [Release notes](https://github.com/doctrine/dbal/releases)
- [Commits](https://github.com/doctrine/dbal/compare/3.1.4...3.3.7)
---
updated-dependencies:
- dependency-name: doctrine/dbal
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
188 files changed, 8848 insertions, 10394 deletions
diff --git a/composer.json b/composer.json index eeae51e5..b236fcd6 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.3.7", "egulias/email-validator": "3.1.1", "giggsey/libphonenumber-for-php": "^8.12.37", "guzzlehttp/guzzle": "^7.4.0", diff --git a/composer.lock b/composer.lock index a03c64d6..1a450db2 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": "7461dcd296fb237e529761da75c52926", + "content-hash": "072f60e4e10d9a0f3f299c806a2a5eee", "packages": [ { "name": "aws/aws-sdk-php", @@ -290,79 +290,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", "source": { @@ -523,16 +450,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": { @@ -542,18 +469,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": { @@ -602,7 +523,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": [ { @@ -618,40 +539,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.3.7", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "821b4f01a36ce63ed36c090ea74767b72db367e9" + "reference": "9f79d4650430b582f4598fe0954ef4d52fbc0a8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/821b4f01a36ce63ed36c090ea74767b72db367e9", - "reference": "821b4f01a36ce63ed36c090ea74767b72db367e9", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/9f79d4650430b582f4598fe0954ef4d52fbc0a8a", + "reference": "9f79d4650430b582f4598fe0954ef4d52fbc0a8a", "shasum": "" }, "require": { - "composer/package-versions-deprecated": "^1.11.99", - "doctrine/cache": "^1.0|^2.0", - "doctrine/deprecations": "^0.5.3", + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", "doctrine/event-manager": "^1.0", - "php": "^7.3 || ^8.0" + "php": "^7.3 || ^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", + "jetbrains/phpstorm-stubs": "2022.1", + "phpstan/phpstan": "1.7.13", + "phpstan/phpstan-strict-rules": "^1.2", + "phpunit/phpunit": "9.5.20", "psalm/plugin-phpunit": "0.16.1", - "squizlabs/php_codesniffer": "3.6.1", + "squizlabs/php_codesniffer": "3.7.0", "symfony/cache": "^5.2|^6.0", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", - "vimeo/psalm": "4.12.0" + "symfony/console": "^2.7|^3.0|^4.0|^5.0|^6.0", + "vimeo/psalm": "4.23.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -711,7 +634,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.3.7" }, "funding": [ { @@ -727,29 +650,29 @@ "type": "tidelift" } ], - "time": "2021-11-15T16:44:33+00:00" + "time": "2022-06-13T21:43:03+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" @@ -768,9 +691,9 @@ "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", @@ -2961,6 +2884,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", "source": { diff --git a/composer/InstalledVersions.php b/composer/InstalledVersions.php index 41bc143c..c6b54af7 100644 --- a/composer/InstalledVersions.php +++ b/composer/InstalledVersions.php @@ -28,7 +28,7 @@ class InstalledVersions { /** * @var mixed[]|null - * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null */ private static $installed; @@ -39,7 +39,7 @@ class InstalledVersions /** * @var array[] - * @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}> + * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}> */ private static $installedByVendor = array(); @@ -243,7 +243,7 @@ class InstalledVersions /** * @return array - * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string} + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} */ public static function getRootPackage() { @@ -257,7 +257,7 @@ class InstalledVersions * * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. * @return array[] - * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} */ public static function getRawData() { @@ -280,7 +280,7 @@ class InstalledVersions * Returns the raw data of all installed.php which are currently loaded for custom implementations * * @return array[] - * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}> + * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}> */ public static function getAllRawData() { @@ -303,7 +303,7 @@ class InstalledVersions * @param array[] $data A vendor/composer/installed.php data set * @return void * - * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data */ public static function reload($data) { @@ -313,7 +313,7 @@ class InstalledVersions /** * @return array[] - * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}> + * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}> */ private static function getInstalled() { diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php index 7092f706..7f3568a7 100644 --- a/composer/autoload_classmap.php +++ b/composer/autoload_classmap.php @@ -924,7 +924,6 @@ return array( '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', @@ -940,6 +939,7 @@ return array( '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', @@ -961,11 +961,16 @@ return array( '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', @@ -1026,11 +1031,16 @@ return array( '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\\DeadlockException' => $vendorDir . '/doctrine/dbal/src/Exception/DeadlockException.php', @@ -1045,6 +1055,7 @@ return array( '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', @@ -1055,15 +1066,21 @@ return array( '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', @@ -1071,17 +1088,25 @@ return array( '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\\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', @@ -1142,7 +1167,6 @@ return array( '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', @@ -1813,9 +1837,6 @@ return array( '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', @@ -2066,6 +2087,10 @@ return array( '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 518c268b..9bc21fdc 100644 --- a/composer/autoload_psr4.php +++ b/composer/autoload_psr4.php @@ -52,8 +52,8 @@ return array( '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'), diff --git a/composer/autoload_static.php b/composer/autoload_static.php index 94aa4916..c579fbe2 100644 --- a/composer/autoload_static.php +++ b/composer/autoload_static.php @@ -202,8 +202,8 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Psr\\Http\\Client\\' => 16, 'Psr\\EventDispatcher\\' => 20, 'Psr\\Container\\' => 14, + 'Psr\\Cache\\' => 10, 'PhpParser\\' => 10, - 'PackageVersions\\' => 16, ), 'O' => array ( @@ -469,13 +469,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 ( @@ -1555,7 +1555,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 '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', @@ -1571,6 +1570,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', @@ -1592,11 +1592,16 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 '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', @@ -1657,11 +1662,16 @@ 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\\DeadlockException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/DeadlockException.php', @@ -1676,6 +1686,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', @@ -1686,15 +1697,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', @@ -1702,17 +1719,25 @@ 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\\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', @@ -1773,7 +1798,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', @@ -2444,9 +2468,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', @@ -2697,6 +2718,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 ca3f50b8..f10ef5ae 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -299,82 +299,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", "version_normalized": "1.7.1.0", @@ -544,17 +468,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": { @@ -564,20 +488,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": { @@ -626,7 +544,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": [ { @@ -646,42 +564,44 @@ }, { "name": "doctrine/dbal", - "version": "3.1.4", - "version_normalized": "3.1.4.0", + "version": "3.3.7", + "version_normalized": "3.3.7.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "821b4f01a36ce63ed36c090ea74767b72db367e9" + "reference": "9f79d4650430b582f4598fe0954ef4d52fbc0a8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/821b4f01a36ce63ed36c090ea74767b72db367e9", - "reference": "821b4f01a36ce63ed36c090ea74767b72db367e9", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/9f79d4650430b582f4598fe0954ef4d52fbc0a8a", + "reference": "9f79d4650430b582f4598fe0954ef4d52fbc0a8a", "shasum": "" }, "require": { - "composer/package-versions-deprecated": "^1.11.99", - "doctrine/cache": "^1.0|^2.0", - "doctrine/deprecations": "^0.5.3", + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", "doctrine/event-manager": "^1.0", - "php": "^7.3 || ^8.0" + "php": "^7.3 || ^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", + "jetbrains/phpstorm-stubs": "2022.1", + "phpstan/phpstan": "1.7.13", + "phpstan/phpstan-strict-rules": "^1.2", + "phpunit/phpunit": "9.5.20", "psalm/plugin-phpunit": "0.16.1", - "squizlabs/php_codesniffer": "3.6.1", + "squizlabs/php_codesniffer": "3.7.0", "symfony/cache": "^5.2|^6.0", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", - "vimeo/psalm": "4.12.0" + "symfony/console": "^2.7|^3.0|^4.0|^5.0|^6.0", + "vimeo/psalm": "4.23.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-06-13T21:43:03+00:00", "bin": [ "bin/doctrine-dbal" ], @@ -738,7 +658,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.3.7" }, "funding": [ { @@ -758,31 +678,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": { @@ -798,7 +718,7 @@ "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" }, @@ -3087,6 +3007,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", "version_normalized": "1.1.1.0", @@ -6372,6 +6344,6 @@ "install-path": "../web-auth/webauthn-lib" } ], - "dev": true, + "dev": false, "dev-package-names": [] } diff --git a/composer/installed.php b/composer/installed.php index 8e52ef36..2c894977 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -1,370 +1,355 @@ <?php return array( 'root' => array( + 'name' => 'nextcloud/3rdparty', 'pretty_version' => 'dev-master', 'version' => 'dev-master', + 'reference' => 'cf36c9bd42b49cf5e7aa78a9dec8961518ca4f1c', 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), - 'reference' => '6d82f0c103262a2f7c26514b5debe55557cfe7b6', - 'name' => 'nextcloud/3rdparty', - 'dev' => true, + 'dev' => false, ), 'versions' => array( 'aws/aws-sdk-php' => array( 'pretty_version' => '3.184.6', 'version' => '3.184.6.0', + 'reference' => '0b7187c96ced465d400ad9427157e05ddee68edc', 'type' => 'library', 'install_path' => __DIR__ . '/../aws/aws-sdk-php', 'aliases' => array(), - 'reference' => '0b7187c96ced465d400ad9427157e05ddee68edc', 'dev_requirement' => false, ), 'bantu/ini-get-wrapper' => array( 'pretty_version' => 'v1.0.1', 'version' => '1.0.1.0', + 'reference' => '4770c7feab370c62e23db4f31c112b7c6d90aee2', 'type' => 'library', 'install_path' => __DIR__ . '/../bantu/ini-get-wrapper', 'aliases' => array(), - 'reference' => '4770c7feab370c62e23db4f31c112b7c6d90aee2', 'dev_requirement' => false, ), 'beberlei/assert' => array( 'pretty_version' => 'v3.3.1', 'version' => '3.3.1.0', + 'reference' => '5e721d7e937ca3ba2cdec1e1adf195f9e5188372', 'type' => 'library', 'install_path' => __DIR__ . '/../beberlei/assert', 'aliases' => array(), - 'reference' => '5e721d7e937ca3ba2cdec1e1adf195f9e5188372', 'dev_requirement' => false, ), 'brick/math' => array( 'pretty_version' => '0.9.2', 'version' => '0.9.2.0', + 'reference' => 'dff976c2f3487d42c1db75a3b180e2b9f0e72ce0', 'type' => 'library', 'install_path' => __DIR__ . '/../brick/math', 'aliases' => array(), - 'reference' => 'dff976c2f3487d42c1db75a3b180e2b9f0e72ce0', 'dev_requirement' => false, ), 'christophwurst/id3parser' => array( 'pretty_version' => 'v0.1.4', 'version' => '0.1.4.0', + 'reference' => '050c9d81ea89b0cf53e23a27efc4e1840f9ab260', 'type' => 'library', 'install_path' => __DIR__ . '/../christophwurst/id3parser', 'aliases' => array(), - 'reference' => '050c9d81ea89b0cf53e23a27efc4e1840f9ab260', - 'dev_requirement' => false, - ), - 'composer/package-versions-deprecated' => array( - 'pretty_version' => '1.11.99.4', - 'version' => '1.11.99.4', - 'type' => 'composer-plugin', - 'install_path' => __DIR__ . '/./package-versions-deprecated', - 'aliases' => array(), - 'reference' => 'b174585d1fe49ceed21928a945138948cb394600', 'dev_requirement' => false, ), 'cweagans/composer-patches' => array( 'pretty_version' => '1.7.1', 'version' => '1.7.1.0', + 'reference' => '9888dcc74993c030b75f3dd548bb5e20cdbd740c', 'type' => 'composer-plugin', 'install_path' => __DIR__ . '/../cweagans/composer-patches', 'aliases' => array(), - 'reference' => '9888dcc74993c030b75f3dd548bb5e20cdbd740c', 'dev_requirement' => false, ), 'deepdiver/zipstreamer' => array( 'pretty_version' => '2.0.0', 'version' => '2.0.0.0', + 'reference' => 'b8c59647ff34fb97e8937aefb2a65de2bc4b4755', 'type' => 'library', 'install_path' => __DIR__ . '/../deepdiver/zipstreamer', 'aliases' => array(), - 'reference' => 'b8c59647ff34fb97e8937aefb2a65de2bc4b4755', 'dev_requirement' => false, ), 'deepdiver1975/tarstreamer' => array( 'pretty_version' => '2.0.0', 'version' => '2.0.0.0', + 'reference' => 'ad48505d1ab54a8e94e6b1cc5297bbed72e956de', 'type' => 'library', 'install_path' => __DIR__ . '/../deepdiver1975/tarstreamer', 'aliases' => array(), - 'reference' => 'ad48505d1ab54a8e94e6b1cc5297bbed72e956de', 'dev_requirement' => false, ), 'doctrine/cache' => array( - 'pretty_version' => '2.1.1', - 'version' => '2.1.1.0', + 'pretty_version' => '2.2.0', + 'version' => '2.2.0.0', + 'reference' => '1ca8f21980e770095a31456042471a57bc4c68fb', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/cache', 'aliases' => array(), - 'reference' => '331b4d5dbaeab3827976273e9356b3b453c300ce', 'dev_requirement' => false, ), 'doctrine/dbal' => array( - 'pretty_version' => '3.1.4', - 'version' => '3.1.4.0', + 'pretty_version' => '3.3.7', + 'version' => '3.3.7.0', + 'reference' => '9f79d4650430b582f4598fe0954ef4d52fbc0a8a', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/dbal', 'aliases' => array(), - 'reference' => '821b4f01a36ce63ed36c090ea74767b72db367e9', 'dev_requirement' => false, ), 'doctrine/deprecations' => array( - 'pretty_version' => 'v0.5.3', - 'version' => '0.5.3.0', + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'reference' => '0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/deprecations', 'aliases' => array(), - 'reference' => '9504165960a1f83cc1480e2be1dd0a0478561314', 'dev_requirement' => false, ), 'doctrine/event-manager' => array( 'pretty_version' => '1.1.1', 'version' => '1.1.1.0', + 'reference' => '41370af6a30faa9dc0368c4a6814d596e81aba7f', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/event-manager', 'aliases' => array(), - 'reference' => '41370af6a30faa9dc0368c4a6814d596e81aba7f', 'dev_requirement' => false, ), 'doctrine/lexer' => array( 'pretty_version' => '1.2.1', 'version' => '1.2.1.0', + 'reference' => 'e864bbf5904cb8f5bb334f99209b48018522f042', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/lexer', 'aliases' => array(), - 'reference' => 'e864bbf5904cb8f5bb334f99209b48018522f042', 'dev_requirement' => false, ), 'egulias/email-validator' => array( 'pretty_version' => '3.1.1', 'version' => '3.1.1.0', + 'reference' => 'c81f18a3efb941d8c4d2e025f6183b5c6d697307', 'type' => 'library', 'install_path' => __DIR__ . '/../egulias/email-validator', 'aliases' => array(), - 'reference' => 'c81f18a3efb941d8c4d2e025f6183b5c6d697307', 'dev_requirement' => false, ), 'fgrosse/phpasn1' => array( 'pretty_version' => 'v2.3.0', 'version' => '2.3.0.0', + 'reference' => '20299033c35f4300eb656e7e8e88cf52d1d6694e', 'type' => 'library', 'install_path' => __DIR__ . '/../fgrosse/phpasn1', 'aliases' => array(), - 'reference' => '20299033c35f4300eb656e7e8e88cf52d1d6694e', 'dev_requirement' => false, ), 'giggsey/libphonenumber-for-php' => array( 'pretty_version' => '8.12.38', 'version' => '8.12.38.0', + 'reference' => '0a6293c57de9256f4bd0d673280fbfbfd1e47533', 'type' => 'library', 'install_path' => __DIR__ . '/../giggsey/libphonenumber-for-php', 'aliases' => array(), - 'reference' => '0a6293c57de9256f4bd0d673280fbfbfd1e47533', 'dev_requirement' => false, ), 'giggsey/locale' => array( 'pretty_version' => '2.1', 'version' => '2.1.0.0', + 'reference' => '8d324583b5899e6280a875c43bf1fc9658bc6962', 'type' => 'library', 'install_path' => __DIR__ . '/../giggsey/locale', 'aliases' => array(), - 'reference' => '8d324583b5899e6280a875c43bf1fc9658bc6962', 'dev_requirement' => false, ), 'guzzlehttp/guzzle' => array( 'pretty_version' => '7.4.0', 'version' => '7.4.0.0', + 'reference' => '868b3571a039f0ebc11ac8f344f4080babe2cb94', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/guzzle', 'aliases' => array(), - 'reference' => '868b3571a039f0ebc11ac8f344f4080babe2cb94', 'dev_requirement' => false, ), 'guzzlehttp/promises' => array( 'pretty_version' => '1.5.1', 'version' => '1.5.1.0', + 'reference' => 'fe752aedc9fd8fcca3fe7ad05d419d32998a06da', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/promises', 'aliases' => array(), - 'reference' => 'fe752aedc9fd8fcca3fe7ad05d419d32998a06da', 'dev_requirement' => false, ), 'guzzlehttp/psr7' => array( 'pretty_version' => '1.8.5', 'version' => '1.8.5.0', + 'reference' => '337e3ad8e5716c15f9657bd214d16cc5e69df268', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'aliases' => array(), - 'reference' => '337e3ad8e5716c15f9657bd214d16cc5e69df268', 'dev_requirement' => false, ), 'guzzlehttp/uri-template' => array( 'pretty_version' => 'v0.2.0', 'version' => '0.2.0.0', + 'reference' => 'db46525d6d8fee71033b73cc07160f3e5271a8ce', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/uri-template', 'aliases' => array(), - 'reference' => 'db46525d6d8fee71033b73cc07160f3e5271a8ce', 'dev_requirement' => false, ), 'icewind/searchdav' => array( 'pretty_version' => 'v3.0.1', 'version' => '3.0.1.0', + 'reference' => '52c8cfc21bd69271f224671c4924d260b0a8d4fb', 'type' => 'library', 'install_path' => __DIR__ . '/../icewind/searchdav', 'aliases' => array(), - 'reference' => '52c8cfc21bd69271f224671c4924d260b0a8d4fb', 'dev_requirement' => false, ), 'icewind/streams' => array( 'pretty_version' => 'v0.7.5', 'version' => '0.7.5.0', + 'reference' => '0c6aae16ebdadb257f0bd089c1e1e4cf5e20ddc2', 'type' => 'library', 'install_path' => __DIR__ . '/../icewind/streams', 'aliases' => array(), - 'reference' => '0c6aae16ebdadb257f0bd089c1e1e4cf5e20ddc2', 'dev_requirement' => false, ), 'justinrainbow/json-schema' => array( 'pretty_version' => '5.2.10', 'version' => '5.2.10.0', + 'reference' => '2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b', 'type' => 'library', 'install_path' => __DIR__ . '/../justinrainbow/json-schema', 'aliases' => array(), - 'reference' => '2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b', 'dev_requirement' => false, ), 'league/uri' => array( 'pretty_version' => '6.4.0', 'version' => '6.4.0.0', + 'reference' => '09da64118eaf4c5d52f9923a1e6a5be1da52fd9a', 'type' => 'library', 'install_path' => __DIR__ . '/../league/uri', 'aliases' => array(), - 'reference' => '09da64118eaf4c5d52f9923a1e6a5be1da52fd9a', 'dev_requirement' => false, ), 'league/uri-interfaces' => array( 'pretty_version' => '2.2.0', 'version' => '2.2.0.0', + 'reference' => '667f150e589d65d79c89ffe662e426704f84224f', 'type' => 'library', 'install_path' => __DIR__ . '/../league/uri-interfaces', 'aliases' => array(), - 'reference' => '667f150e589d65d79c89ffe662e426704f84224f', 'dev_requirement' => false, ), 'microsoft/azure-storage-blob' => array( 'pretty_version' => '1.5.2', 'version' => '1.5.2.0', + 'reference' => '2475330963372d519387cb8135d6a9cfd42272da', 'type' => 'library', 'install_path' => __DIR__ . '/../microsoft/azure-storage-blob', 'aliases' => array(), - 'reference' => '2475330963372d519387cb8135d6a9cfd42272da', 'dev_requirement' => false, ), 'microsoft/azure-storage-common' => array( 'pretty_version' => '1.5.1', 'version' => '1.5.1.0', + 'reference' => 'e5738035891546075bd369954e8af121d65ebd6d', 'type' => 'library', 'install_path' => __DIR__ . '/../microsoft/azure-storage-common', 'aliases' => array(), - 'reference' => 'e5738035891546075bd369954e8af121d65ebd6d', 'dev_requirement' => false, ), 'mtdowling/jmespath.php' => array( 'pretty_version' => '2.6.1', 'version' => '2.6.1.0', + 'reference' => '9b87907a81b87bc76d19a7fb2d61e61486ee9edb', 'type' => 'library', 'install_path' => __DIR__ . '/../mtdowling/jmespath.php', 'aliases' => array(), - 'reference' => '9b87907a81b87bc76d19a7fb2d61e61486ee9edb', 'dev_requirement' => false, ), 'nextcloud/3rdparty' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', + 'reference' => 'cf36c9bd42b49cf5e7aa78a9dec8961518ca4f1c', 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), - 'reference' => '6d82f0c103262a2f7c26514b5debe55557cfe7b6', 'dev_requirement' => false, ), 'nextcloud/lognormalizer' => array( 'pretty_version' => 'v1.0.0', 'version' => '1.0.0.0', + 'reference' => '87445d69225c247aaff64643b1fc83c6d6df741f', 'type' => 'library', 'install_path' => __DIR__ . '/../nextcloud/lognormalizer', 'aliases' => array(), - 'reference' => '87445d69225c247aaff64643b1fc83c6d6df741f', 'dev_requirement' => false, ), 'nikic/php-parser' => array( 'pretty_version' => 'v4.10.5', 'version' => '4.10.5.0', + 'reference' => '4432ba399e47c66624bc73c8c0f811e5c109576f', 'type' => 'library', 'install_path' => __DIR__ . '/../nikic/php-parser', 'aliases' => array(), - 'reference' => '4432ba399e47c66624bc73c8c0f811e5c109576f', - 'dev_requirement' => false, - ), - 'ocramius/package-versions' => array( 'dev_requirement' => false, - 'replaced' => array( - 0 => '1.11.99', - ), ), 'opis/closure' => array( 'pretty_version' => '3.6.2', 'version' => '3.6.2.0', + 'reference' => '06e2ebd25f2869e54a306dda991f7db58066f7f6', 'type' => 'library', 'install_path' => __DIR__ . '/../opis/closure', 'aliases' => array(), - 'reference' => '06e2ebd25f2869e54a306dda991f7db58066f7f6', 'dev_requirement' => false, ), 'pear/archive_tar' => array( 'pretty_version' => '1.4.14', 'version' => '1.4.14.0', + 'reference' => '4d761c5334c790e45ef3245f0864b8955c562caa', 'type' => 'library', 'install_path' => __DIR__ . '/../pear/archive_tar', 'aliases' => array(), - 'reference' => '4d761c5334c790e45ef3245f0864b8955c562caa', 'dev_requirement' => false, ), 'pear/console_getopt' => array( 'pretty_version' => 'v1.4.3', 'version' => '1.4.3.0', + 'reference' => 'a41f8d3e668987609178c7c4a9fe48fecac53fa0', 'type' => 'library', 'install_path' => __DIR__ . '/../pear/console_getopt', 'aliases' => array(), - 'reference' => 'a41f8d3e668987609178c7c4a9fe48fecac53fa0', 'dev_requirement' => false, ), 'pear/pear-core-minimal' => array( 'pretty_version' => 'v1.10.10', 'version' => '1.10.10.0', + 'reference' => '625a3c429d9b2c1546438679074cac1b089116a7', 'type' => 'library', 'install_path' => __DIR__ . '/../pear/pear-core-minimal', 'aliases' => array(), - 'reference' => '625a3c429d9b2c1546438679074cac1b089116a7', 'dev_requirement' => false, ), 'pear/pear_exception' => array( 'pretty_version' => 'v1.0.2', 'version' => '1.0.2.0', + 'reference' => 'b14fbe2ddb0b9f94f5b24cf08783d599f776fff0', 'type' => 'class', 'install_path' => __DIR__ . '/../pear/pear_exception', 'aliases' => array(), - 'reference' => 'b14fbe2ddb0b9f94f5b24cf08783d599f776fff0', 'dev_requirement' => false, ), 'php-ds/php-ds' => array( 'pretty_version' => 'v1.3.0', 'version' => '1.3.0.0', + 'reference' => 'b98396862fb8a13cbdbbaf4d18be28ee5c01ed3c', 'type' => 'library', 'install_path' => __DIR__ . '/../php-ds/php-ds', 'aliases' => array(), - 'reference' => 'b98396862fb8a13cbdbbaf4d18be28ee5c01ed3c', 'dev_requirement' => false, ), 'php-http/async-client-implementation' => array( @@ -382,73 +367,82 @@ 'php-http/guzzle7-adapter' => array( 'pretty_version' => '1.0.0', 'version' => '1.0.0.0', + 'reference' => 'fb075a71dbfa4847cf0c2938c4e5a9c478ef8b01', 'type' => 'library', 'install_path' => __DIR__ . '/../php-http/guzzle7-adapter', 'aliases' => array(), - 'reference' => 'fb075a71dbfa4847cf0c2938c4e5a9c478ef8b01', 'dev_requirement' => false, ), 'php-http/httplug' => array( 'pretty_version' => '2.2.0', 'version' => '2.2.0.0', + 'reference' => '191a0a1b41ed026b717421931f8d3bd2514ffbf9', 'type' => 'library', 'install_path' => __DIR__ . '/../php-http/httplug', 'aliases' => array(), - 'reference' => '191a0a1b41ed026b717421931f8d3bd2514ffbf9', 'dev_requirement' => false, ), 'php-http/promise' => array( 'pretty_version' => '1.1.0', 'version' => '1.1.0.0', + 'reference' => '4c4c1f9b7289a2ec57cde7f1e9762a5789506f88', 'type' => 'library', 'install_path' => __DIR__ . '/../php-http/promise', 'aliases' => array(), - 'reference' => '4c4c1f9b7289a2ec57cde7f1e9762a5789506f88', 'dev_requirement' => false, ), 'php-opencloud/openstack' => array( 'pretty_version' => 'v3.1.0', 'version' => '3.1.0.0', + 'reference' => '7b0eeb63defe533fb802514af3c70855c45eaf1e', 'type' => 'library', 'install_path' => __DIR__ . '/../php-opencloud/openstack', 'aliases' => array(), - 'reference' => '7b0eeb63defe533fb802514af3c70855c45eaf1e', 'dev_requirement' => false, ), 'phpseclib/phpseclib' => array( 'pretty_version' => '2.0.32', 'version' => '2.0.32.0', + 'reference' => 'f5c4c19880d45d0be3e7d24ae8ac434844a898cd', 'type' => 'library', 'install_path' => __DIR__ . '/../phpseclib/phpseclib', 'aliases' => array(), - 'reference' => 'f5c4c19880d45d0be3e7d24ae8ac434844a898cd', 'dev_requirement' => false, ), 'pimple/pimple' => array( 'pretty_version' => 'v3.5.0', 'version' => '3.5.0.0', + 'reference' => 'a94b3a4db7fb774b3d78dad2315ddc07629e1bed', 'type' => 'library', 'install_path' => __DIR__ . '/../pimple/pimple', 'aliases' => array(), - 'reference' => 'a94b3a4db7fb774b3d78dad2315ddc07629e1bed', + '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', + 'reference' => '8622567409010282b7aeebe4bb841fe98b58dcaf', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/container', 'aliases' => array(), - 'reference' => '8622567409010282b7aeebe4bb841fe98b58dcaf', 'dev_requirement' => false, ), 'psr/event-dispatcher' => array( 'pretty_version' => '1.0.0', 'version' => '1.0.0.0', + 'reference' => 'dbefd12671e8a14ec7f180cab83036ed26714bb0', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/event-dispatcher', 'aliases' => array(), - 'reference' => 'dbefd12671e8a14ec7f180cab83036ed26714bb0', 'dev_requirement' => false, ), 'psr/event-dispatcher-implementation' => array( @@ -460,10 +454,10 @@ 'psr/http-client' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', + 'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-client', 'aliases' => array(), - 'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621', 'dev_requirement' => false, ), 'psr/http-client-implementation' => array( @@ -475,19 +469,19 @@ 'psr/http-factory' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', + 'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-factory', 'aliases' => array(), - 'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be', 'dev_requirement' => false, ), 'psr/http-message' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', + 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-message', 'aliases' => array(), - 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', 'dev_requirement' => false, ), 'psr/http-message-implementation' => array( @@ -499,10 +493,10 @@ 'psr/log' => array( 'pretty_version' => '1.1.4', 'version' => '1.1.4.0', + 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/log', 'aliases' => array(), - 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11', 'dev_requirement' => false, ), 'psr/log-implementation' => array( @@ -526,37 +520,37 @@ 'punic/punic' => array( 'pretty_version' => '1.6.5', 'version' => '1.6.5.0', + 'reference' => '7bc85ce1137cf52db4d2a6298256a4c4a24da99a', 'type' => 'library', 'install_path' => __DIR__ . '/../punic/punic', 'aliases' => array(), - 'reference' => '7bc85ce1137cf52db4d2a6298256a4c4a24da99a', 'dev_requirement' => false, ), 'ralouphie/getallheaders' => array( 'pretty_version' => '3.0.3', 'version' => '3.0.3.0', + 'reference' => '120b605dfeb996808c31b6477290a714d356e822', 'type' => 'library', 'install_path' => __DIR__ . '/../ralouphie/getallheaders', 'aliases' => array(), - 'reference' => '120b605dfeb996808c31b6477290a714d356e822', 'dev_requirement' => false, ), 'ramsey/collection' => array( 'pretty_version' => '1.1.3', 'version' => '1.1.3.0', + 'reference' => '28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1', 'type' => 'library', 'install_path' => __DIR__ . '/../ramsey/collection', 'aliases' => array(), - 'reference' => '28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1', 'dev_requirement' => false, ), 'ramsey/uuid' => array( 'pretty_version' => '4.1.1', 'version' => '4.1.1.0', + 'reference' => 'cd4032040a750077205918c86049aa0f43d22947', 'type' => 'library', 'install_path' => __DIR__ . '/../ramsey/uuid', 'aliases' => array(), - 'reference' => 'cd4032040a750077205918c86049aa0f43d22947', 'dev_requirement' => false, ), 'rhumsaa/uuid' => array( @@ -574,136 +568,136 @@ 'sabre/dav' => array( 'pretty_version' => '4.2.1', 'version' => '4.2.1.0', + 'reference' => '5e651f9ddc2b05745f5b28cd697c8e8c6a0b6146', 'type' => 'library', 'install_path' => __DIR__ . '/../sabre/dav', 'aliases' => array(), - 'reference' => '5e651f9ddc2b05745f5b28cd697c8e8c6a0b6146', 'dev_requirement' => false, ), 'sabre/event' => array( 'pretty_version' => '5.1.4', 'version' => '5.1.4.0', + 'reference' => 'd7da22897125d34d7eddf7977758191c06a74497', 'type' => 'library', 'install_path' => __DIR__ . '/../sabre/event', 'aliases' => array(), - 'reference' => 'd7da22897125d34d7eddf7977758191c06a74497', 'dev_requirement' => false, ), 'sabre/http' => array( 'pretty_version' => '5.1.3', 'version' => '5.1.3.0', + 'reference' => '315f592adfcba8aeb73c2fd64285205747acbbd7', 'type' => 'library', 'install_path' => __DIR__ . '/../sabre/http', 'aliases' => array(), - 'reference' => '315f592adfcba8aeb73c2fd64285205747acbbd7', 'dev_requirement' => false, ), 'sabre/uri' => array( 'pretty_version' => '2.2.2', 'version' => '2.2.2.0', + 'reference' => '7cb0f489578afad5006e85cd60f18ff33f2d440d', 'type' => 'library', 'install_path' => __DIR__ . '/../sabre/uri', 'aliases' => array(), - 'reference' => '7cb0f489578afad5006e85cd60f18ff33f2d440d', 'dev_requirement' => false, ), 'sabre/vobject' => array( 'pretty_version' => '4.4.1', 'version' => '4.4.1.0', + 'reference' => '06feff370141fd3118609f808e86d9315864bf14', 'type' => 'library', 'install_path' => __DIR__ . '/../sabre/vobject', 'aliases' => array(), - 'reference' => '06feff370141fd3118609f808e86d9315864bf14', 'dev_requirement' => false, ), 'sabre/xml' => array( 'pretty_version' => '2.2.5', 'version' => '2.2.5.0', + 'reference' => 'a6af111850e7536d200d9637c34885cd3c77a86c', 'type' => 'library', 'install_path' => __DIR__ . '/../sabre/xml', 'aliases' => array(), - 'reference' => 'a6af111850e7536d200d9637c34885cd3c77a86c', 'dev_requirement' => false, ), 'scssphp/scssphp' => array( 'pretty_version' => 'v1.8.1', 'version' => '1.8.1.0', + 'reference' => '5e37759a63caf54392a4b709358a39ac7425a69f', 'type' => 'library', 'install_path' => __DIR__ . '/../scssphp/scssphp', 'aliases' => array(), - 'reference' => '5e37759a63caf54392a4b709358a39ac7425a69f', 'dev_requirement' => false, ), 'spomky-labs/base64url' => array( 'pretty_version' => 'v2.0.4', 'version' => '2.0.4.0', + 'reference' => '7752ce931ec285da4ed1f4c5aa27e45e097be61d', 'type' => 'library', 'install_path' => __DIR__ . '/../spomky-labs/base64url', 'aliases' => array(), - 'reference' => '7752ce931ec285da4ed1f4c5aa27e45e097be61d', 'dev_requirement' => false, ), 'spomky-labs/cbor-php' => array( 'pretty_version' => 'v2.0.1', 'version' => '2.0.1.0', + 'reference' => '9776578000be884cd7864eeb7c37a4ac92d8c995', 'type' => 'library', 'install_path' => __DIR__ . '/../spomky-labs/cbor-php', 'aliases' => array(), - 'reference' => '9776578000be884cd7864eeb7c37a4ac92d8c995', 'dev_requirement' => false, ), 'stecman/symfony-console-completion' => array( 'pretty_version' => '0.11.0', 'version' => '0.11.0.0', + 'reference' => 'a9502dab59405e275a9f264536c4e1cb61fc3518', 'type' => 'library', 'install_path' => __DIR__ . '/../stecman/symfony-console-completion', 'aliases' => array(), - 'reference' => 'a9502dab59405e275a9f264536c4e1cb61fc3518', 'dev_requirement' => false, ), 'swiftmailer/swiftmailer' => array( 'pretty_version' => 'v6.2.7', 'version' => '6.2.7.0', + 'reference' => '15f7faf8508e04471f666633addacf54c0ab5933', 'type' => 'library', 'install_path' => __DIR__ . '/../swiftmailer/swiftmailer', 'aliases' => array(), - 'reference' => '15f7faf8508e04471f666633addacf54c0ab5933', 'dev_requirement' => false, ), 'symfony/console' => array( 'pretty_version' => 'v4.4.30', 'version' => '4.4.30.0', + 'reference' => 'a3f7189a0665ee33b50e9e228c46f50f5acbed22', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/console', 'aliases' => array(), - 'reference' => 'a3f7189a0665ee33b50e9e228c46f50f5acbed22', 'dev_requirement' => false, ), 'symfony/deprecation-contracts' => array( 'pretty_version' => 'v2.5.0', 'version' => '2.5.0.0', + 'reference' => '6f981ee24cf69ee7ce9736146d1c57c2780598a8', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', 'aliases' => array(), - 'reference' => '6f981ee24cf69ee7ce9736146d1c57c2780598a8', 'dev_requirement' => false, ), 'symfony/event-dispatcher' => array( 'pretty_version' => 'v4.4.30', 'version' => '4.4.30.0', + 'reference' => '2fe81680070043c4c80e7cedceb797e34f377bac', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/event-dispatcher', 'aliases' => array(), - 'reference' => '2fe81680070043c4c80e7cedceb797e34f377bac', 'dev_requirement' => false, ), 'symfony/event-dispatcher-contracts' => array( 'pretty_version' => 'v1.1.9', 'version' => '1.1.9.0', + 'reference' => '84e23fdcd2517bf37aecbd16967e83f0caee25a7', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/event-dispatcher-contracts', 'aliases' => array(), - 'reference' => '84e23fdcd2517bf37aecbd16967e83f0caee25a7', 'dev_requirement' => false, ), 'symfony/event-dispatcher-implementation' => array( @@ -715,127 +709,127 @@ 'symfony/polyfill-ctype' => array( 'pretty_version' => 'v1.23.0', 'version' => '1.23.0.0', + 'reference' => '46cd95797e9df938fdd2b03693b5fca5e64b01ce', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-ctype', 'aliases' => array(), - 'reference' => '46cd95797e9df938fdd2b03693b5fca5e64b01ce', 'dev_requirement' => false, ), 'symfony/polyfill-iconv' => array( 'pretty_version' => 'v1.23.0', 'version' => '1.23.0.0', + 'reference' => '63b5bb7db83e5673936d6e3b8b3e022ff6474933', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-iconv', 'aliases' => array(), - 'reference' => '63b5bb7db83e5673936d6e3b8b3e022ff6474933', 'dev_requirement' => false, ), 'symfony/polyfill-intl-grapheme' => array( 'pretty_version' => 'v1.23.1', 'version' => '1.23.1.0', + 'reference' => '16880ba9c5ebe3642d1995ab866db29270b36535', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme', 'aliases' => array(), - 'reference' => '16880ba9c5ebe3642d1995ab866db29270b36535', 'dev_requirement' => false, ), 'symfony/polyfill-intl-idn' => array( 'pretty_version' => 'v1.23.0', 'version' => '1.23.0.0', + 'reference' => '65bd267525e82759e7d8c4e8ceea44f398838e65', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn', 'aliases' => array(), - 'reference' => '65bd267525e82759e7d8c4e8ceea44f398838e65', 'dev_requirement' => false, ), 'symfony/polyfill-intl-normalizer' => array( 'pretty_version' => 'v1.23.0', 'version' => '1.23.0.0', + 'reference' => '8590a5f561694770bdcd3f9b5c69dde6945028e8', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer', 'aliases' => array(), - 'reference' => '8590a5f561694770bdcd3f9b5c69dde6945028e8', 'dev_requirement' => false, ), 'symfony/polyfill-mbstring' => array( 'pretty_version' => 'v1.23.1', 'version' => '1.23.1.0', + 'reference' => '9174a3d80210dca8daa7f31fec659150bbeabfc6', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', 'aliases' => array(), - 'reference' => '9174a3d80210dca8daa7f31fec659150bbeabfc6', 'dev_requirement' => false, ), 'symfony/polyfill-php72' => array( 'pretty_version' => 'v1.23.0', 'version' => '1.23.0.0', + 'reference' => '9a142215a36a3888e30d0a9eeea9766764e96976', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php72', 'aliases' => array(), - 'reference' => '9a142215a36a3888e30d0a9eeea9766764e96976', 'dev_requirement' => false, ), 'symfony/polyfill-php73' => array( 'pretty_version' => 'v1.23.0', 'version' => '1.23.0.0', + 'reference' => 'fba8933c384d6476ab14fb7b8526e5287ca7e010', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php73', 'aliases' => array(), - 'reference' => 'fba8933c384d6476ab14fb7b8526e5287ca7e010', 'dev_requirement' => false, ), 'symfony/polyfill-php80' => array( 'pretty_version' => 'v1.23.1', 'version' => '1.23.1.0', + 'reference' => '1100343ed1a92e3a38f9ae122fc0eb21602547be', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php80', 'aliases' => array(), - 'reference' => '1100343ed1a92e3a38f9ae122fc0eb21602547be', 'dev_requirement' => false, ), 'symfony/process' => array( 'pretty_version' => 'v4.4.30', 'version' => '4.4.30.0', + 'reference' => '13d3161ef63a8ec21eeccaaf9a4d7f784a87a97d', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/process', 'aliases' => array(), - 'reference' => '13d3161ef63a8ec21eeccaaf9a4d7f784a87a97d', 'dev_requirement' => false, ), 'symfony/routing' => array( 'pretty_version' => 'v4.4.30', 'version' => '4.4.30.0', + 'reference' => '9ddf033927ad9f30ba2bfd167a7b342cafa13e8e', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/routing', 'aliases' => array(), - 'reference' => '9ddf033927ad9f30ba2bfd167a7b342cafa13e8e', 'dev_requirement' => false, ), 'symfony/service-contracts' => array( 'pretty_version' => 'v2.4.0', 'version' => '2.4.0.0', + 'reference' => 'f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/service-contracts', 'aliases' => array(), - 'reference' => 'f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb', 'dev_requirement' => false, ), 'symfony/translation' => array( 'pretty_version' => 'v4.4.41', 'version' => '4.4.41.0', + 'reference' => 'dcb67eae126e74507e0b4f0b9ac6ef35b37c3331', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/translation', 'aliases' => array(), - 'reference' => 'dcb67eae126e74507e0b4f0b9ac6ef35b37c3331', 'dev_requirement' => false, ), 'symfony/translation-contracts' => array( 'pretty_version' => 'v2.4.0', 'version' => '2.4.0.0', + 'reference' => '95c812666f3e91db75385749fe219c5e494c7f95', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/translation-contracts', 'aliases' => array(), - 'reference' => '95c812666f3e91db75385749fe219c5e494c7f95', 'dev_requirement' => false, ), 'symfony/translation-implementation' => array( @@ -847,37 +841,37 @@ 'thecodingmachine/safe' => array( 'pretty_version' => 'v1.3.3', 'version' => '1.3.3.0', + 'reference' => 'a8ab0876305a4cdaef31b2350fcb9811b5608dbc', 'type' => 'library', 'install_path' => __DIR__ . '/../thecodingmachine/safe', 'aliases' => array(), - 'reference' => 'a8ab0876305a4cdaef31b2350fcb9811b5608dbc', 'dev_requirement' => false, ), 'web-auth/cose-lib' => array( 'pretty_version' => 'v3.3.9', 'version' => '3.3.9.0', + 'reference' => 'ed172d2dc1a6b87b5c644c07c118cd30c1b3819b', 'type' => 'library', 'install_path' => __DIR__ . '/../web-auth/cose-lib', 'aliases' => array(), - 'reference' => 'ed172d2dc1a6b87b5c644c07c118cd30c1b3819b', 'dev_requirement' => false, ), 'web-auth/metadata-service' => array( 'pretty_version' => 'v3.3.9', 'version' => '3.3.9.0', + 'reference' => '8488d3a832a38cc81c670fce05de1e515c6e64b1', 'type' => 'library', 'install_path' => __DIR__ . '/../web-auth/metadata-service', 'aliases' => array(), - 'reference' => '8488d3a832a38cc81c670fce05de1e515c6e64b1', 'dev_requirement' => false, ), 'web-auth/webauthn-lib' => array( 'pretty_version' => 'v3.3.9', 'version' => '3.3.9.0', + 'reference' => '04b98ee3d39cb79dad68a7c15c297c085bf66bfe', 'type' => 'library', 'install_path' => __DIR__ . '/../web-auth/webauthn-lib', 'aliases' => array(), - 'reference' => '04b98ee3d39cb79dad68a7c15c297c085bf66bfe', 'dev_requirement' => false, ), ), diff --git a/composer/package-versions-deprecated/CHANGELOG.md b/composer/package-versions-deprecated/CHANGELOG.md deleted file mode 100644 index a838c56a..00000000 --- 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 <package-name>`. - -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 71806175..00000000 --- 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 7fe2097d..00000000 --- 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 da9c516d..00000000 --- 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 d5a40daa..00000000 --- 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 b711f6b1..00000000 --- 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 18e5fe64..00000000 --- a/composer/package-versions-deprecated/src/PackageVersions/FallbackVersions.php +++ /dev/null @@ -1,128 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace PackageVersions; - -use Generator; -use OutOfBoundsException; -use UnexpectedValueException; -use function array_key_exists; -use function array_merge; -use function basename; -use function file_exists; -use function file_get_contents; -use function getcwd; -use function iterator_to_array; -use function json_decode; -use function json_encode; -use function sprintf; - -/** - * @internal - * - * This is a fallback for {@see \PackageVersions\Versions::getVersion()} - * Do not use this class directly: it is intended to be only used when - * {@see \PackageVersions\Versions} fails to be generated, which typically - * happens when running composer with `--no-scripts` flag) - */ -final class FallbackVersions -{ - const ROOT_PACKAGE_NAME = 'unknown/root-package@UNKNOWN'; - - private function __construct() - { - } - - /** - * @throws OutOfBoundsException If a version cannot be located. - * @throws UnexpectedValueException If the composer.lock file could not be located. - */ - public static function getVersion(string $packageName): string - { - $versions = iterator_to_array(self::getVersions(self::getPackageData())); - - if (! array_key_exists($packageName, $versions)) { - throw new OutOfBoundsException( - 'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files' - ); - } - - return $versions[$packageName]; - } - - /** - * @return mixed[] - * - * @throws UnexpectedValueException - */ - private static function getPackageData(): array - { - $checkedPaths = [ - // The top-level project's ./vendor/composer/installed.json - getcwd() . '/vendor/composer/installed.json', - __DIR__ . '/../../../../composer/installed.json', - // The top-level project's ./composer.lock - getcwd() . '/composer.lock', - __DIR__ . '/../../../../../composer.lock', - // This package's composer.lock - __DIR__ . '/../../composer.lock', - ]; - - $packageData = []; - foreach ($checkedPaths as $path) { - if (! file_exists($path)) { - continue; - } - - $data = json_decode(file_get_contents($path), true); - switch (basename($path)) { - case 'installed.json': - // composer 2.x installed.json format - if (isset($data['packages'])) { - $packageData[] = $data['packages']; - } else { - // composer 1.x installed.json format - $packageData[] = $data; - } - - break; - case 'composer.lock': - $packageData[] = $data['packages'] + ($data['packages-dev'] ?? []); - break; - default: - // intentionally left blank - } - } - - if ($packageData !== []) { - return array_merge(...$packageData); - } - - throw new UnexpectedValueException(sprintf( - 'PackageVersions could not locate the `vendor/composer/installed.json` or your `composer.lock` ' - . 'location. This is assumed to be in %s. If you customized your composer vendor directory and ran composer ' - . 'installation with --no-scripts, or if you deployed without the required composer files, PackageVersions ' - . 'can\'t detect installed versions.', - json_encode($checkedPaths) - )); - } - - /** - * @param mixed[] $packageData - * - * @return Generator&string[] - * - * @psalm-return Generator<string, string> - */ - 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 05bdac9a..00000000 --- a/composer/package-versions-deprecated/src/PackageVersions/Installer.php +++ /dev/null @@ -1,290 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace PackageVersions; - -use Composer\Composer; -use Composer\Config; -use Composer\EventDispatcher\EventSubscriberInterface; -use Composer\IO\IOInterface; -use Composer\Package\AliasPackage; -use Composer\Package\Locker; -use Composer\Package\PackageInterface; -use Composer\Package\RootPackageInterface; -use Composer\Plugin\PluginInterface; -use Composer\Script\Event; -use Composer\Script\ScriptEvents; -use Generator; -use RuntimeException; - -use function array_key_exists; -use function array_merge; -use function chmod; -use function dirname; -use function file_exists; -use function file_put_contents; -use function is_writable; -use function iterator_to_array; -use function rename; -use function sprintf; -use function uniqid; -use function var_export; - -final class Installer implements PluginInterface, EventSubscriberInterface -{ - private static $generatedClassTemplate = <<<'PHP' -<?php - -declare(strict_types=1); - -namespace PackageVersions; - -use Composer\InstalledVersions; -use OutOfBoundsException; - -class_exists(InstalledVersions::class); - -/** - * This class is generated by composer/package-versions-deprecated, specifically by - * @see \PackageVersions\Installer - * - * This file is overwritten at every run of `composer install` or `composer update`. - * - * @deprecated in favor of the Composer\InstalledVersions class provided by Composer 2. Require composer-runtime-api:^2 to ensure it is present. - */ -%s -{ - /** - * @deprecated please use {@see self::rootPackageName()} instead. - * This constant will be removed in version 2.0.0. - */ - const ROOT_PACKAGE_NAME = '%s'; - - /** - * Array of all available composer packages. - * Dont read this array from your calling code, but use the \PackageVersions\Versions::getVersion() method instead. - * - * @var array<string, string> - * @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<self::VERSIONS> $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('<info>composer/package-versions-deprecated:</info> Package not found (probably scheduled for removal); generation of version class skipped.'); - - return; - } - - if (! is_writable($installDir)) { - $io->write( - sprintf( - '<info>composer/package-versions-deprecated:</info> %s is not writable; generation of version class skipped.', - $installDir - ) - ); - - return; - } - - $io->write('<info>composer/package-versions-deprecated:</info> Generating version class...'); - - $installPathTmp = $installPath . '_' . uniqid('tmp', true); - file_put_contents($installPathTmp, $versionClassSource); - chmod($installPathTmp, 0664); - rename($installPathTmp, $installPath); - - $io->write('<info>composer/package-versions-deprecated:</info> ...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<string, string> - */ - 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 9cbf1877..00000000 --- a/composer/package-versions-deprecated/src/PackageVersions/Versions.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace PackageVersions; - -use Composer\InstalledVersions; -use OutOfBoundsException; -use UnexpectedValueException; - -class_exists(InstalledVersions::class); - -/** - * This is a stub class: it is in place only for scenarios where PackageVersions - * is installed with a `--no-scripts` flag, in which scenarios the Versions class - * is not being replaced. - * - * If you are reading this docBlock inside your `vendor/` dir, then this means - * that PackageVersions didn't correctly install, and is in "fallback" mode. - */ -final class Versions -{ - /** - * @deprecated please use {@see self::rootPackageName()} instead. - * This constant will be removed in version 2.0.0. - */ - const ROOT_PACKAGE_NAME = 'unknown/root-package@UNKNOWN'; - - /** @internal */ - const VERSIONS = []; - - 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 (!class_exists(InstalledVersions::class, false) || !InstalledVersions::getRawData()) { - return self::ROOT_PACKAGE_NAME; - } - - return InstalledVersions::getRootPackage()['name']; - } - - /** - * @throws OutOfBoundsException if a version cannot be located. - * @throws UnexpectedValueException if the composer.lock file could not be located. - */ - public static function getVersion(string $packageName): string - { - if (!self::composer2ApiUsable()) { - return FallbackVersions::getVersion($packageName); - } - - /** @psalm-suppress DeprecatedConstant */ - if ($packageName === self::ROOT_PACKAGE_NAME) { - $rootPackage = InstalledVersions::getRootPackage(); - - return $rootPackage['pretty_version'] . '@' . $rootPackage['reference']; - } - - return InstalledVersions::getPrettyVersion($packageName) - . '@' . InstalledVersions::getReference($packageName); - } - - 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; - } -} diff --git a/doctrine/cache/UPGRADE-1.11.md b/doctrine/cache/UPGRADE-1.11.md index a33be230..6c5ddb55 100644 --- a/doctrine/cache/UPGRADE-1.11.md +++ b/doctrine/cache/UPGRADE-1.11.md @@ -13,3 +13,15 @@ There are two new classes to use in the `Doctrine\Common\Cache\Psr6` namespace: This implementation is designed for libraries that leak the cache and want to switch to allowing PSR-6 implementations. This class is design to be used during the transition phase of sunsetting doctrine/cache support. + +A full example to setup a filesystem based PSR-6 cache with symfony/cache +using the `DoctrineProvider` to convert back to Doctrine's `Cache` interface: + +```php +use Doctrine\Common\Cache\Psr6\DoctrineProvider; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; + +$cachePool = new FilesystemAdapter(); +$cache = DoctrineProvider::wrap($cachePool); +// $cache instanceof \Doctrine\Common\Cache\Cache +``` diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php index c15497e5..3b0f416c 100644 --- a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php +++ b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php @@ -15,6 +15,7 @@ use Doctrine\Common\Cache\Cache; use Doctrine\Common\Cache\CacheProvider; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\DoctrineAdapter as SymfonyDoctrineAdapter; +use Symfony\Contracts\Service\ResetInterface; use function rawurlencode; @@ -57,6 +58,15 @@ final class DoctrineProvider extends CacheProvider return $this->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 8a175788..21888b6f 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.3][3.3] | +|:-----------------------------------------------:|:---------------------------------------------------:| +| [![GitHub Actions][GA 4.0 image]][GA 4.0] | [![GitHub Actions][GA 3.3 image]][GA 3.3] | +| [![AppVeyor][AppVeyor 4.0 image]][AppVeyor 4.0] | [![AppVeyor][AppVeyor 3.3 image]][AppVeyor 3.3] | +| [![Code Coverage][Coverage image]][CodeCov 4.0] | [![Code Coverage][Coverage 3.3 image]][CodeCov 3.3] | +| N/A | [![Code Coverage][TypeCov 3.3 image]][TypeCov 3.3] | 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.3 image]: https://codecov.io/gh/doctrine/dbal/branch/3.3.x/graph/badge.svg + [3.3]: https://github.com/doctrine/dbal/tree/3.3.x + [CodeCov 3.3]: https://codecov.io/gh/doctrine/dbal/branch/3.3.x + [AppVeyor 3.3]: https://ci.appveyor.com/project/doctrine/dbal/branch/3.3.x + [AppVeyor 3.3 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/3.3.x?svg=true + [GA 3.3]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A3.3.x + [GA 3.3 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=3.3.x + [TypeCov 3.3]: https://shepherd.dev/github/doctrine/dbal + [TypeCov 3.3 image]: https://shepherd.dev/github/doctrine/dbal/coverage.svg diff --git a/doctrine/dbal/composer.json b/doctrine/dbal/composer.json index 2374d79c..b0805793 100644 --- a/doctrine/dbal/composer.json +++ b/doctrine/dbal/composer.json @@ -32,29 +32,35 @@ ], "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" + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "doctrine/event-manager": "^1.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", + "jetbrains/phpstorm-stubs": "2022.1", + "phpstan/phpstan": "1.7.13", + "phpstan/phpstan-strict-rules": "^1.2", + "phpunit/phpunit": "9.5.20", "psalm/plugin-phpunit": "0.16.1", - "squizlabs/php_codesniffer": "3.6.1", + "squizlabs/php_codesniffer": "3.7.0", "symfony/cache": "^5.2|^6.0", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", - "vimeo/psalm": "4.12.0" + "symfony/console": "^2.7|^3.0|^4.0|^5.0|^6.0", + "vimeo/psalm": "4.23.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/Cache/ArrayResult.php b/doctrine/dbal/src/Cache/ArrayResult.php index e2dc6e11..3b70d0c9 100644 --- a/doctrine/dbal/src/Cache/ArrayResult.php +++ b/doctrine/dbal/src/Cache/ArrayResult.php @@ -14,7 +14,7 @@ use function reset; */ final class ArrayResult implements Result { - /** @var mixed[] */ + /** @var list<array<string, mixed>> */ private $data; /** @var int */ @@ -24,7 +24,7 @@ final class ArrayResult implements Result private $num = 0; /** - * @param mixed[] $data + * @param list<array<string, mixed>> $data */ public function __construct(array $data) { @@ -112,7 +112,7 @@ final class ArrayResult implements Result } /** - * @return mixed|false + * @return array<string, mixed>|false */ private function fetch() { diff --git a/doctrine/dbal/src/Cache/CachingResult.php b/doctrine/dbal/src/Cache/CachingResult.php deleted file mode 100644 index c060673c..00000000 --- a/doctrine/dbal/src/Cache/CachingResult.php +++ /dev/null @@ -1,184 +0,0 @@ -<?php - -namespace Doctrine\DBAL\Cache; - -use Doctrine\Common\Cache\Cache; -use Doctrine\DBAL\Driver\Exception; -use Doctrine\DBAL\Driver\FetchUtils; -use Doctrine\DBAL\Driver\Result as DriverResult; -use Doctrine\DBAL\Result; - -use function array_map; -use function array_values; - -/** - * A result is saved in multiple cache keys, there is the originally specified - * cache key which is just pointing to result rows by key. The following things - * have to be ensured: - * - * 1. lifetime of the original key has to be longer than that of all the individual rows keys - * 2. if any one row key is missing the query has to be re-executed. - * - * Also you have to realize that the cache will load the whole result into memory at once to ensure 2. - * This means that the memory usage for cached results might increase by using this feature. - * - * @internal The class is internal to the caching layer implementation. - */ -class CachingResult implements DriverResult -{ - /** @var Cache */ - private $cache; - - /** @var string */ - private $cacheKey; - - /** @var string */ - private $realKey; - - /** @var int */ - private $lifetime; - - /** @var Result */ - private $result; - - /** @var array<int,array<string,mixed>>|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<string,mixed>|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<int,array<string,mixed>> $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 21bd144d..7115cc72 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,8 @@ use function sha1; */ class QueryCacheProfile { - /** @var Cache|null */ - private $resultCacheDriver; + /** @var CacheItemPoolInterface|null */ + private $resultCache; /** @var int */ private $lifetime; @@ -26,22 +33,57 @@ 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; + 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 { - $this->lifetime = $lifetime; - $this->cacheKey = $cacheKey; - $this->resultCacheDriver = $resultCache; + 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; } /** @@ -97,12 +139,26 @@ class QueryCacheProfile 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 +168,7 @@ class QueryCacheProfile */ public function setCacheKey($cacheKey) { - return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCacheDriver); + return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCache); } /** @@ -122,6 +178,6 @@ class QueryCacheProfile */ 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 109289f5..2d27d733 100644 --- a/doctrine/dbal/src/Configuration.php +++ b/doctrine/dbal/src/Configuration.php @@ -3,8 +3,12 @@ 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; /** * Configuration container for the Doctrine DBAL. @@ -24,6 +28,15 @@ class Configuration /** * The cache driver implementation that is used for query result caching. * + * @var CacheItemPoolInterface|null + */ + private $resultCache; + + /** + * The cache driver implementation that is used for query result caching. + * + * @deprecated Use {@see $resultCache} instead. + * * @var Cache|null */ protected $resultCacheImpl; @@ -61,17 +74,53 @@ class Configuration /** * 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); } /** diff --git a/doctrine/dbal/src/Connection.php b/doctrine/dbal/src/Connection.php index 7b3686c6..9e4b1d23 100644 --- a/doctrine/dbal/src/Connection.php +++ b/doctrine/dbal/src/Connection.php @@ -6,12 +6,14 @@ use Closure; 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,26 @@ use Doctrine\DBAL\Schema\AbstractSchemaManager; 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 { @@ -52,6 +58,11 @@ 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. */ public const ARRAY_PARAM_OFFSET = 100; @@ -70,7 +81,7 @@ class Connection protected $_eventManager; /** - * @deprecated Use {@link createExpressionBuilder()} instead. + * @deprecated Use {@see createExpressionBuilder()} instead. * * @var ExpressionBuilder */ @@ -108,7 +119,6 @@ class Connection * The parameters used during creation of the Connection instance. * * @var array<string,mixed> - * @phpstan-var array<string,mixed> * @psalm-var Params */ private $params; @@ -129,7 +139,7 @@ class Connection /** * The schema manager. * - * @deprecated Use {@link createSchemaManager()} instead. + * @deprecated Use {@see createSchemaManager()} instead. * * @var AbstractSchemaManager|null */ @@ -172,14 +182,6 @@ class Connection $this->_driver = $driver; $this->params = $params; - 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(); @@ -192,6 +194,15 @@ class Connection $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']; + $this->platform->setEventManager($this->_eventManager); + } + $this->_expr = $this->createExpressionBuilder(); $this->autoCommit = $config->getAutoCommit(); @@ -204,7 +215,6 @@ class Connection * * @return array<string,mixed> * @psalm-return Params - * @phpstan-return array<string,mixed> */ public function getParams() { @@ -289,7 +299,7 @@ class Connection /** * Gets the ExpressionBuilder for the connection. * - * @deprecated Use {@link createExpressionBuilder()} instead. + * @deprecated Use {@see createExpressionBuilder()} instead. * * @return ExpressionBuilder */ @@ -308,6 +318,8 @@ class Connection /** * 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 +327,12 @@ class Connection */ 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; } @@ -438,6 +456,13 @@ class Connection } } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pulls/4750', + 'Not implementing the ServerInfoAwareConnection interface in %s is deprecated', + get_class($connection) + ); + // Unable to detect platform version. return null; } @@ -503,11 +528,7 @@ class Connection */ 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 +539,13 @@ class Connection * @param list<mixed>|array<string, mixed> $params Query parameters * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types * - * @return list< mixed>|false False is returned if no rows are found. + * @return list<mixed>|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 +562,7 @@ class Connection */ 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 +624,7 @@ class Connection * @param array<string, mixed> $criteria Deletion criteria * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types * - * @return int The number of affected rows. + * @return int|string The number of affected rows. * * @throws Exception */ @@ -648,7 +661,7 @@ class Connection * * @param int $level The level to set. * - * @return int + * @return int|string * * @throws Exception */ @@ -685,7 +698,7 @@ class Connection * @param array<string, mixed> $criteria Update criteria * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types * - * @return int The number of affected rows. + * @return int|string The number of affected rows. * * @throws Exception */ @@ -720,7 +733,7 @@ class Connection * @param array<string, mixed> $data Column-value pairs * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types * - * @return int The number of affected rows. + * @return int|string The number of affected rows. * * @throws Exception */ @@ -756,7 +769,7 @@ class Connection * * @return array<int, int|string|Type|null>|array<string, int|string|Type|null> */ - private function extractTypeValues(array $columnList, array $types) + private function extractTypeValues(array $columnList, array $types): array { $typeValues = []; @@ -787,6 +800,9 @@ class Connection } /** + * 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 +830,7 @@ class Connection */ 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 +846,7 @@ class Connection */ 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 +897,7 @@ class Connection */ 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 +913,7 @@ class Connection */ 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 +930,7 @@ class Connection */ 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 +981,7 @@ class Connection */ 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 +993,15 @@ class Connection */ 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); } /** @@ -1084,7 +1072,7 @@ class Connection */ 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 +1083,31 @@ class Connection [$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 +1126,7 @@ class Connection * @param list<mixed>|array<string, mixed> $params Statement parameters * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types * - * @return int The number of affected rows. + * @return int|string The number of affected rows. * * @throws Exception */ @@ -1204,6 +1194,14 @@ class Connection */ 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) { @@ -1273,10 +1271,9 @@ class Connection } /** - * 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() { @@ -1317,6 +1314,8 @@ class Connection } } + $this->getEventManager()->dispatchEvent(Events::onTransactionBegin, new TransactionBeginEventArgs($this)); + return true; } @@ -1364,6 +1363,8 @@ class Connection --$this->transactionNestingLevel; + $this->getEventManager()->dispatchEvent(Events::onTransactionCommit, new TransactionCommitEventArgs($this)); + if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) { return $result; } @@ -1440,6 +1441,8 @@ class Connection --$this->transactionNestingLevel; } + $this->getEventManager()->dispatchEvent(Events::onTransactionRollBack, new TransactionRollBackEventArgs($this)); + return true; } @@ -1510,12 +1513,21 @@ class Connection /** * 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); @@ -1524,6 +1536,24 @@ class Connection } /** + * @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. * @@ -1541,7 +1571,7 @@ class Connection * 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 * @@ -1761,7 +1791,11 @@ class Connection } 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; } } diff --git a/doctrine/dbal/src/Driver/API/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/ExceptionConverter.php index b848b0e2..a7bf2713 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 20831e0f..dbde0b10 100644 --- a/doctrine/dbal/src/Driver/API/IBMDB2/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/IBMDB2/ExceptionConverter.php @@ -6,16 +6,60 @@ namespace Doctrine\DBAL\Driver\API\IBMDB2; 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 9abc0c16..c851949b 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\API\ExceptionConverter as ExceptionConverterInterface; 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; @@ -33,6 +34,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 +99,7 @@ final class ExceptionConverter implements ExceptionConverterInterface 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 f25e58d6..727a605d 100644 --- a/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php @@ -7,6 +7,8 @@ namespace Doctrine\DBAL\Driver\API\OCI; 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; @@ -57,6 +59,14 @@ final class ExceptionConverter implements ExceptionConverterInterface 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 b9530d0b..df34802c 100644 --- a/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php @@ -7,12 +7,14 @@ namespace Doctrine\DBAL\Driver\API\PostgreSQL; 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; @@ -54,6 +56,12 @@ final class ExceptionConverter implements ExceptionConverterInterface 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); diff --git a/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php index 3fc3ff08..d0e8e9f4 100644 --- a/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php @@ -6,16 +6,64 @@ namespace Doctrine\DBAL\Driver\API\SQLSrv; 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/UserDefinedFunctions.php b/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php new file mode 100644 index 00000000..29e73d1f --- /dev/null +++ b/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php @@ -0,0 +1,48 @@ +<?php + +namespace Doctrine\DBAL\Driver\API\SQLite; + +use function strpos; + +/** + * User-defined SQLite functions. + * + * @internal + */ +final class UserDefinedFunctions +{ + /** + * 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 + { + // 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 d8c707bb..38a46086 100644 --- a/doctrine/dbal/src/Driver/AbstractDB2Driver.php +++ b/doctrine/dbal/src/Driver/AbstractDB2Driver.php @@ -10,8 +10,10 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Schema\DB2SchemaManager; +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 { @@ -28,6 +30,8 @@ abstract class AbstractDB2Driver implements Driver */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + 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 d3866935..f14501b2 100644 --- a/doctrine/dbal/src/Driver/AbstractException.php +++ b/doctrine/dbal/src/Driver/AbstractException.php @@ -8,7 +8,7 @@ use Exception as BaseException; use Throwable; /** - * Base implementation of the {@link Exception} interface. + * Base implementation of the {@see Exception} interface. * * @internal * diff --git a/doctrine/dbal/src/Driver/AbstractMySQLDriver.php b/doctrine/dbal/src/Driver/AbstractMySQLDriver.php index 31949b9c..7f74e42d 100644 --- a/doctrine/dbal/src/Driver/AbstractMySQLDriver.php +++ b/doctrine/dbal/src/Driver/AbstractMySQLDriver.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Connection; 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\MySQL80Platform; 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 @@ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver } } + 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(); } @@ -112,7 +122,7 @@ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver /** * {@inheritdoc} * - * @return MySQLPlatform + * @return AbstractMySQLPlatform */ public function getDatabasePlatform() { @@ -126,6 +136,8 @@ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + 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 80e4a02c..c62c87d9 100644 --- a/doctrine/dbal/src/Driver/AbstractOracleDriver.php +++ b/doctrine/dbal/src/Driver/AbstractOracleDriver.php @@ -11,8 +11,10 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Schema\OracleSchemaManager; +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 { @@ -29,6 +31,8 @@ abstract class AbstractOracleDriver implements Driver */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + assert($platform instanceof OraclePlatform); + return new OracleSchemaManager($conn, $platform); } diff --git a/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php b/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php index 6b11d617..adc0be09 100644 --- a/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php +++ b/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php @@ -9,14 +9,17 @@ use Doctrine\DBAL\Exception; 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 { @@ -41,6 +44,13 @@ abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver 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(); } @@ -57,6 +67,8 @@ abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + 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 b3d0928b..adf31f55 100644 --- a/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php +++ b/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php @@ -10,8 +10,10 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SQLServer2012Platform; use Doctrine\DBAL\Schema\SQLServerSchemaManager; +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 { @@ -28,6 +30,8 @@ abstract class AbstractSQLServerDriver implements Driver */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + assert($platform instanceof SQLServer2012Platform); + return new SQLServerSchemaManager($conn, $platform); } diff --git a/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php b/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php index 12b4cfa6..b6479267 100644 --- a/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php +++ b/doctrine/dbal/src/Driver/AbstractSQLiteDriver.php @@ -10,8 +10,10 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\SqliteSchemaManager; +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 { @@ -28,6 +30,8 @@ abstract class AbstractSQLiteDriver implements Driver */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { + assert($platform instanceof SqlitePlatform); + return new SqliteSchemaManager($conn, $platform); } diff --git a/doctrine/dbal/src/Driver/Connection.php b/doctrine/dbal/src/Driver/Connection.php index 9febe72a..2f460fd1 100644 --- a/doctrine/dbal/src/Driver/Connection.php +++ b/doctrine/dbal/src/Driver/Connection.php @@ -7,6 +7,8 @@ use Doctrine\DBAL\ParameterType; /** * Connection interface. * Driver connections must implement this interface. + * + * @method resource|object getNativeConnection() */ interface Connection { @@ -27,6 +29,9 @@ interface Connection /** * 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/IBMDB2/Connection.php b/doctrine/dbal/src/Driver/IBMDB2/Connection.php index c8e36da3..aca2a02c 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 @@ use const DB2_AUTOCOMMIT_ON; final class Connection implements ServerInfoAwareConnection { /** @var resource */ - private $conn; + private $connection; /** * @internal The connection can be only instantiated by its driver. * - * @param array<string,mixed> $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 @@ final class Connection implements ServerInfoAwareConnection */ 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 @@ final class Connection implements ServerInfoAwareConnection 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 @@ final class Connection implements ServerInfoAwareConnection 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,54 @@ final class Connection implements ServerInfoAwareConnection */ 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/Driver.php b/doctrine/dbal/src/Driver/IBMDB2/Driver.php index c0816552..142b369b 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/ConnectionError.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionError.php index 97bff611..a27a99a1 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionError.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionError.php @@ -21,6 +21,11 @@ final class ConnectionError extends AbstractException */ public static function new($connection): self { - return new self(db2_conn_errormsg($connection), db2_conn_error($connection)); + $message = db2_conn_errormsg($connection); + $sqlState = db2_conn_error($connection); + + return Factory::create($message, static function (int $code) use ($message, $sqlState): self { + return new self($message, $sqlState, $code); + }); } } diff --git a/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionFailed.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionFailed.php index fe79c227..9dd04434 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionFailed.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionFailed.php @@ -18,6 +18,11 @@ final class ConnectionFailed extends AbstractException { public static function new(): self { - return new self(db2_conn_errormsg(), db2_conn_error()); + $message = db2_conn_errormsg(); + $sqlState = db2_conn_error(); + + return Factory::create($message, static function (int $code) use ($message, $sqlState): self { + return new self($message, $sqlState, $code); + }); } } diff --git a/doctrine/dbal/src/Driver/IBMDB2/Exception/Factory.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/Factory.php new file mode 100644 index 00000000..91b9b439 --- /dev/null +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/Factory.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\DBAL\Driver\IBMDB2\Exception; + +use Doctrine\DBAL\Driver\AbstractException; + +use function preg_match; + +/** + * @internal + * + * @psalm-immutable + */ +final class Factory +{ + /** + * @param callable(int): T $constructor + * + * @return T + * + * @template T of AbstractException + */ + public static function create(string $message, callable $constructor): AbstractException + { + $code = 0; + + if (preg_match('/ SQL(\d+)N /', $message, $matches) === 1) { + $code = -(int) $matches[1]; + } + + return $constructor($code); + } +} diff --git a/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php index c3c5f87a..5c30d1ca 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php @@ -17,10 +17,20 @@ use function db2_stmt_errormsg; final class StatementError extends AbstractException { /** - * @param resource $statement + * @param resource|null $statement */ - public static function new($statement): self + public static function new($statement = null): self { - return new self(db2_stmt_errormsg($statement), db2_stmt_error($statement)); + if ($statement !== null) { + $message = db2_stmt_errormsg($statement); + $sqlState = db2_stmt_error($statement); + } else { + $message = db2_stmt_errormsg(); + $sqlState = db2_stmt_error(); + } + + return Factory::create($message, static function (int $code) use ($message, $sqlState): self { + return new self($message, $sqlState, $code); + }); } } diff --git a/doctrine/dbal/src/Driver/IBMDB2/Statement.php b/doctrine/dbal/src/Driver/IBMDB2/Statement.php index c655ea74..39cd6a47 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Statement.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Statement.php @@ -59,7 +59,7 @@ final class Statement implements StatementInterface /** * {@inheritdoc} */ - public function bindValue($param, $value, $type = ParameterType::STRING) + public function bindValue($param, $value, $type = ParameterType::STRING): bool { assert(is_int($param)); @@ -69,7 +69,7 @@ final class Statement implements StatementInterface /** * {@inheritdoc} */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { assert(is_int($param)); @@ -140,7 +140,7 @@ final class Statement implements StatementInterface $this->writeStringToStream($source, $target); } - $result = db2_execute($this->stmt, $params); + $result = @db2_execute($this->stmt, $params); foreach ($this->lobs as [, $handle]) { fclose($handle); diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php new file mode 100644 index 00000000..a0e69a5e --- /dev/null +++ b/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php @@ -0,0 +1,116 @@ +<?php + +namespace Doctrine\DBAL\Driver\Middleware; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\Result; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; +use LogicException; + +use function get_class; +use function method_exists; +use function sprintf; + +abstract class AbstractConnectionMiddleware implements ServerInfoAwareConnection +{ + /** @var Connection */ + private $wrappedConnection; + + public function __construct(Connection $wrappedConnection) + { + $this->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 00000000..ab1f508f --- /dev/null +++ b/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php @@ -0,0 +1,61 @@ +<?php + +namespace Doctrine\DBAL\Driver\Middleware; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\API\ExceptionConverter; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +abstract class AbstractDriverMiddleware implements VersionAwarePlatformDriver +{ + /** @var Driver */ + private $wrappedDriver; + + public function __construct(Driver $wrappedDriver) + { + $this->wrappedDriver = $wrappedDriver; + } + + /** + * {@inheritdoc} + */ + public function connect(array $params) + { + return $this->wrappedDriver->connect($params); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return $this->wrappedDriver->getDatabasePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(Connection $conn, AbstractPlatform $platform) + { + 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 00000000..ebc63c57 --- /dev/null +++ b/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php @@ -0,0 +1,79 @@ +<?php + +namespace Doctrine\DBAL\Driver\Middleware; + +use Doctrine\DBAL\Driver\Result; + +abstract class AbstractResultMiddleware implements Result +{ + /** @var Result */ + private $wrappedResult; + + public function __construct(Result $result) + { + $this->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 00000000..a646cd30 --- /dev/null +++ b/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php @@ -0,0 +1,42 @@ +<?php + +namespace Doctrine\DBAL\Driver\Middleware; + +use Doctrine\DBAL\Driver\Result; +use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\ParameterType; + +abstract class AbstractStatementMiddleware implements Statement +{ + /** @var Statement */ + private $wrappedStatement; + + public function __construct(Statement $wrappedStatement) + { + $this->wrappedStatement = $wrappedStatement; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = ParameterType::STRING) + { + return $this->wrappedStatement->bindValue($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + { + 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 73634536..8453f740 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 { /** @@ -25,49 +19,14 @@ final class Connection implements ServerInfoAwareConnection public const OPTION_FLAGS = 'flags'; /** @var mysqli */ - private $conn; + private $connection; /** * @internal The connection can be only instantiated by its driver. - * - * @param iterable<Initializer> $preInitializers - * @param iterable<Initializer> $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); - } - - if (! $success) { - throw ConnectionFailed::new($connection); - } - - foreach ($postInitializers as $initializer) { - $initializer->initialize($connection); - } - - $this->conn = $connection; + public function __construct(mysqli $connection) + { + $this->connection = $connection; } /** @@ -75,38 +34,38 @@ final class Connection implements ServerInfoAwareConnection * * 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 +78,22 @@ final class Connection implements ServerInfoAwareConnection */ 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 +101,44 @@ final class Connection implements ServerInfoAwareConnection */ 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 d459863d..41a66a73 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 @@ final class Driver extends AbstractMySQLDriver $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); } /** diff --git a/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php b/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php index faceeb08..44a8cab9 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; 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/Result.php b/doctrine/dbal/src/Driver/Mysqli/Result.php index 23047152..ac36df03 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Result.php +++ b/doctrine/dbal/src/Driver/Mysqli/Result.php @@ -10,11 +10,10 @@ use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError; 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 @@ -58,11 +57,7 @@ final class Result implements ResultInterface $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 +79,8 @@ final class Result implements ResultInterface // 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 249cb2c8..6e493c9e 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Statement.php +++ b/doctrine/dbal/src/Driver/Mysqli/Statement.php @@ -4,14 +4,12 @@ namespace Doctrine\DBAL\Driver\Mysqli; 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 mysqli_sql_exception; use mysqli_stmt; @@ -28,7 +26,7 @@ use function str_repeat; final class Statement implements StatementInterface { /** @var string[] */ - protected static $_paramTypeMap = [ + private static $paramTypeMap = [ ParameterType::ASCII => 's', ParameterType::STRING => 's', ParameterType::BINARY => 's', @@ -38,66 +36,47 @@ final class Statement implements StatementInterface ParameterType::LARGE_OBJECT => 'b', ]; - /** @var mysqli */ - protected $_conn; - /** @var mysqli_stmt */ - protected $_stmt; + private $stmt; /** @var mixed[] */ - protected $_bindedValues; + private $boundValues; /** @var string */ - protected $types; + private $types; /** * Contains ref values for bindValue(). * * @var mixed[] */ - protected $_values = []; + private $values = []; /** * @internal The statement can be only instantiated by its driver connection. - * - * @param string $prepareString - * - * @throws Exception */ - public function __construct(mysqli $conn, $prepareString) + public function __construct(mysqli_stmt $stmt) { - $this->_conn = $conn; - - try { - $stmt = $conn->prepare($prepareString); - } catch (mysqli_sql_exception $e) { - throw ConnectionError::upcast($e); - } + $this->stmt = $stmt; - if ($stmt === false) { - throw ConnectionError::new($this->_conn); - } - - $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} */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { assert(is_int($param)); - if (! isset(self::$_paramTypeMap[$type])) { + 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 +84,17 @@ final class Statement implements StatementInterface /** * {@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 (! 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; } @@ -127,23 +106,23 @@ final class Statement implements StatementInterface { 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 +135,14 @@ final class Statement implements StatementInterface $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 +153,14 @@ final class Statement implements StatementInterface 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 +183,8 @@ final class Statement implements StatementInterface 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 +194,9 @@ final class Statement implements StatementInterface * 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 b05892f9..46ff4186 100644 --- a/doctrine/dbal/src/Driver/OCI8/Connection.php +++ b/doctrine/dbal/src/Driver/OCI8/Connection.php @@ -3,79 +3,56 @@ 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 Parser */ + private $parser; /** @var ExecutionMode */ private $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 +61,25 @@ final class Connection implements ServerInfoAwareConnection 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 +99,10 @@ final class Connection implements ServerInfoAwareConnection 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 +114,8 @@ final class Connection implements ServerInfoAwareConnection * @param string|null $name * * @return int|false + * + * @throws Parser\Exception */ public function lastInsertId($name = null) { @@ -126,6 +123,12 @@ final class Connection implements ServerInfoAwareConnection 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 +138,17 @@ final class Connection implements ServerInfoAwareConnection 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 +156,22 @@ final class Connection implements ServerInfoAwareConnection 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/Driver.php b/doctrine/dbal/src/Driver/OCI8/Driver.php index 351ffb87..48ef0bbe 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/Statement.php b/doctrine/dbal/src/Driver/OCI8/Statement.php index 56602a36..8caa9f28 100644 --- a/doctrine/dbal/src/Driver/OCI8/Statement.php +++ b/doctrine/dbal/src/Driver/OCI8/Statement.php @@ -2,21 +2,16 @@ 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 function assert; 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,84 +24,62 @@ use const SQLT_CHR; final class Statement implements StatementInterface { /** @var resource */ - protected $_dbh; + private $connection; /** @var resource */ - protected $_sth; + private $statement; + + /** @var array<int,string> */ + private $parameterMap; /** @var ExecutionMode */ private $executionMode; - /** @var string[] */ - protected $_paramMap = []; - - /** - * 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<int,string> $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); + return $this->bindParam($param, $value, $type); } /** * {@inheritdoc} */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { 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 = oci_new_descriptor($this->connection, OCI_D_LOB); $lob->writeTemporary($variable, OCI_TEMP_BLOB); $variable =& $lob; } - $this->boundValues[$param] =& $variable; - return oci_bind_by_name( - $this->_sth, + $this->statement, $param, $variable, $length ?? -1, @@ -152,11 +125,11 @@ final class Statement implements StatementInterface $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 16901693..505acba5 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; @@ -20,22 +20,12 @@ final class Connection implements ServerInfoAwareConnection /** * @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) + 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 +60,7 @@ final class Connection implements ServerInfoAwareConnection $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 +96,50 @@ final class Connection implements ServerInfoAwareConnection 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 49f55951..7036a0e4 100644 --- a/doctrine/dbal/src/Driver/PDO/Exception.php +++ b/doctrine/dbal/src/Driver/PDO/Exception.php @@ -18,6 +18,10 @@ final class Exception extends AbstractException { if ($exception->errorInfo !== null) { [$sqlState, $code] = $exception->errorInfo; + + if ($code === null) { + $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 50c05a40..8aeb2696 100644 --- a/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php @@ -4,7 +4,9 @@ namespace Doctrine\DBAL\Driver\PDO\MySQL; 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 @@ final class Driver extends AbstractMySQLDriver $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 a2c1f465..705db611 100644 --- a/doctrine/dbal/src/Driver/PDO/OCI/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/OCI/Driver.php @@ -4,7 +4,9 @@ namespace Doctrine\DBAL\Driver\PDO\OCI; 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 @@ final class Driver extends AbstractOracleDriver $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 e9620bde..b90e4726 100644 --- a/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php @@ -4,9 +4,9 @@ namespace Doctrine\DBAL\Driver\PDO\PgSQL; use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver; use Doctrine\DBAL\Driver\PDO\Connection; +use Doctrine\DBAL\Driver\PDO\Exception; use PDO; - -use function defined; +use PDOException; final class Driver extends AbstractPostgreSQLDriver { @@ -23,24 +23,27 @@ final class Driver extends AbstractPostgreSQLDriver $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 +57,8 @@ final class Driver extends AbstractPostgreSQLDriver * 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:'; diff --git a/doctrine/dbal/src/Driver/PDO/Result.php b/doctrine/dbal/src/Driver/PDO/Result.php index 888716d8..b0efdf2e 100644 --- a/doctrine/dbal/src/Driver/PDO/Result.php +++ b/doctrine/dbal/src/Driver/PDO/Result.php @@ -9,9 +9,6 @@ use PDO; use PDOException; use PDOStatement; -use function assert; -use function is_array; - final class Result implements ResultInterface { /** @var PDOStatement */ @@ -118,13 +115,9 @@ final class Result implements ResultInterface 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 a64f4245..0c34d4b9 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php +++ b/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php @@ -2,20 +2,21 @@ 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; public function __construct(PDOConnection $connection) { + parent::__construct($connection); + $this->connection = $connection; } @@ -26,72 +27,43 @@ final class Connection implements ServerInfoAwareConnection ); } - 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); } + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4687', + 'The usage of Connection::lastInsertId() with a sequence name is deprecated.' + ); + return $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') ->execute([$name]) ->fetchOne(); } - /** - * {@inheritDoc} - */ - public function beginTransaction() - { - return $this->connection->beginTransaction(); - } - - /** - * {@inheritDoc} - */ - public function commit() - { - return $this->connection->commit(); - } - - /** - * {@inheritDoc} - */ - public function rollBack() + public function getNativeConnection(): PDO { - return $this->connection->rollBack(); + return $this->connection->getNativeConnection(); } /** - * {@inheritDoc} + * @deprecated Call {@see getNativeConnection()} instead. */ - public function getServerVersion() - { - return $this->connection->getServerVersion(); - } - 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 c4842504..b664950f 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; 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 @@ final class Driver extends AbstractSQLServerDriver } 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 @@ final class Driver extends AbstractSQLServerDriver * @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 2f48874e..43b07a3a 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php +++ b/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php @@ -2,16 +2,15 @@ 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; @@ -21,6 +20,8 @@ final class Statement implements StatementInterface */ public function __construct(PDOStatement $statement) { + parent::__construct($statement); + $this->statement = $statement; } @@ -33,8 +34,13 @@ final class Statement implements StatementInterface * @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 { if (func_num_args() > 4) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', @@ -65,16 +71,8 @@ final class Statement implements StatementInterface /** * {@inheritdoc} */ - public function bindValue($param, $value, $type = ParameterType::STRING) + public function bindValue($param, $value, $type = ParameterType::STRING): bool { return $this->bindParam($param, $value, $type); } - - /** - * {@inheritdoc} - */ - public function execute($params = null): Result - { - return $this->statement->execute($params); - } } diff --git a/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php b/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php index d85ba28f..5e72de08 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php @@ -4,14 +4,17 @@ namespace Doctrine\DBAL\Driver\PDO\SQLite; use Doctrine\DBAL\Driver\AbstractSQLiteDriver; use Doctrine\DBAL\Driver\PDO\Connection; +use Doctrine\DBAL\Driver\PDO\Exception; use Doctrine\DBAL\Platforms\SqlitePlatform; +use PDO; +use PDOException; use function array_merge; final class Driver extends AbstractSQLiteDriver { /** @var mixed[] */ - protected $_userDefinedFunctions = [ + private $userDefinedFunctions = [ 'sqrt' => ['callback' => [SqlitePlatform::class, 'udfSqrt'], 'numArgs' => 1], 'mod' => ['callback' => [SqlitePlatform::class, 'udfMod'], 'numArgs' => 2], 'locate' => ['callback' => [SqlitePlatform::class, 'udfLocate'], 'numArgs' => -1], @@ -27,37 +30,37 @@ final class Driver extends AbstractSQLiteDriver $driverOptions = $params['driverOptions'] ?? []; if (isset($driverOptions['userDefinedFunctions'])) { - $this->_userDefinedFunctions = array_merge( - $this->_userDefinedFunctions, + $this->userDefinedFunctions = array_merge( + $this->userDefinedFunctions, $driverOptions['userDefinedFunctions'] ); unset($driverOptions['userDefinedFunctions']); } - $connection = new Connection( - $this->_constructPdoDsn($params), - $params['user'] ?? '', - $params['password'] ?? '', - $driverOptions - ); - - $pdo = $connection->getWrappedConnection(); + try { + $pdo = new PDO( + $this->constructPdoDsn($params), + $params['user'] ?? '', + $params['password'] ?? '', + $driverOptions + ); + } catch (PDOException $exception) { + throw Exception::new($exception); + } - foreach ($this->_userDefinedFunctions as $fn => $data) { + foreach ($this->userDefinedFunctions as $fn => $data) { $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); } - return $connection; + 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 1461239e..bb137708 100644 --- a/doctrine/dbal/src/Driver/PDO/Statement.php +++ b/doctrine/dbal/src/Driver/PDO/Statement.php @@ -61,11 +61,14 @@ final class Statement implements StatementInterface * @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 { if (func_num_args() > 4) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', @@ -77,7 +80,13 @@ final class Statement implements StatementInterface $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); } diff --git a/doctrine/dbal/src/Driver/SQLSrv/Connection.php b/doctrine/dbal/src/Driver/SQLSrv/Connection.php index e37ea900..0295db7e 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 @@ use function str_replace; 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 @@ final class Connection implements ServerInfoAwareConnection */ 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 @@ final class Connection implements ServerInfoAwareConnection 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 @@ final class Connection implements ServerInfoAwareConnection 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,38 @@ final class Connection implements ServerInfoAwareConnection 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 efa9ccfe..085b2241 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Driver.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Driver.php @@ -4,6 +4,10 @@ namespace Doctrine\DBAL\Driver\SQLSrv; 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 @@ final class Driver extends AbstractSQLServerDriver $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/Statement.php b/doctrine/dbal/src/Driver/SQLSrv/Statement.php index 8267bc0d..035567a3 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Statement.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Statement.php @@ -47,14 +47,14 @@ final class Statement implements StatementInterface /** * References to the variables bound as statement parameters. * - * @var mixed + * @var array<int, mixed> */ private $variables = []; /** * Bound parameter types. * - * @var int[] + * @var array<int, int> */ private $types = []; @@ -84,7 +84,7 @@ final class Statement implements StatementInterface /** * {@inheritdoc} */ - public function bindValue($param, $value, $type = ParameterType::STRING) + public function bindValue($param, $value, $type = ParameterType::STRING): bool { assert(is_int($param)); @@ -97,7 +97,7 @@ final class Statement implements StatementInterface /** * {@inheritdoc} */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { assert(is_int($param)); diff --git a/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php b/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php index 622f98c6..5687ab0b 100644 --- a/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php +++ b/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php @@ -4,11 +4,14 @@ namespace Doctrine\DBAL\Driver; /** * 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 334634a8..50fac842 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 @@ interface Statement /** * 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. * @@ -47,7 +47,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 $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 +62,7 @@ interface Statement * 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 698e0438..a2ed2361 100644 --- a/doctrine/dbal/src/DriverManager.php +++ b/doctrine/dbal/src/DriverManager.php @@ -24,7 +24,7 @@ use function strpos; use function substr; /** - * Factory for creating {@link Connection} instances. + * Factory for creating {@see Connection} instances. * * @psalm-type OverrideParams = array{ * charset?: string, @@ -40,10 +40,12 @@ use function substr; * platform?: Platforms\AbstractPlatform, * port?: int, * user?: string, + * unix_socket?: string, * } * @psalm-type Params = array{ * charset?: string, * dbname?: string, + * defaultTableOptions?: array<string, mixed>, * default_dbname?: string, * driver?: key-of<self::DRIVER_MAP>, * driverClass?: class-string<Driver>, @@ -60,10 +62,12 @@ use function substr; * port?: int, * primary?: OverrideParams, * replica?: array<OverrideParams>, + * serverVersion?: string, * sharding?: array<string,mixed>, * slaves?: array<OverrideParams>, * user?: string, * wrapperClass?: class-string<Connection>, + * unix_socket?: string, * } */ final class DriverManager @@ -71,7 +75,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, @@ -118,7 +122,7 @@ final class DriverManager * * $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 +145,8 @@ final class DriverManager * <b>driverClass</b>: * The driver class to use. * - * @param array<string,mixed> $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 +171,6 @@ final class DriverManager * user?: string, * wrapperClass?: class-string<T>, * } $params - * @phpstan-param array<string,mixed> $params * * @psalm-return ($params is array{wrapperClass:mixed} ? T : Connection) * @@ -340,7 +342,7 @@ final class DriverManager * 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 +396,7 @@ final class DriverManager /** * 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 +415,7 @@ final class DriverManager /** * 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 * diff --git a/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php index e61a48d0..5a1160a9 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php @@ -12,7 +12,7 @@ use function func_get_args; 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}. */ class SchemaAlterTableAddColumnEventArgs extends SchemaEventArgs { diff --git a/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php index e9ebb66c..e3fe778d 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php @@ -11,7 +11,7 @@ use function func_get_args; 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}. */ class SchemaAlterTableChangeColumnEventArgs extends SchemaEventArgs { diff --git a/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php index 9c2b9b31..d51b4a20 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php @@ -10,7 +10,7 @@ use function func_get_args; 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}. */ class SchemaAlterTableEventArgs extends SchemaEventArgs { diff --git a/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php index d32af597..47ed242b 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php @@ -11,7 +11,7 @@ use function func_get_args; 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}. */ class SchemaAlterTableRemoveColumnEventArgs extends SchemaEventArgs { diff --git a/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php index 6d52b6c0..2b2cb220 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php @@ -11,7 +11,7 @@ use function func_get_args; 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}. */ class SchemaAlterTableRenameColumnEventArgs extends SchemaEventArgs { diff --git a/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php b/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php index 58efa7b5..997a90d1 100644 --- a/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php @@ -6,7 +6,7 @@ use Doctrine\DBAL\Connection; 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}. */ class SchemaColumnDefinitionEventArgs extends SchemaEventArgs { diff --git a/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php index 246d2de0..6ba1bc70 100644 --- a/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php @@ -11,7 +11,7 @@ use function func_get_args; 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}. */ class SchemaCreateTableColumnEventArgs extends SchemaEventArgs { diff --git a/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php b/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php index e9829a64..d83f7a26 100644 --- a/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php @@ -10,7 +10,7 @@ use function func_get_args; 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}. */ class SchemaCreateTableEventArgs extends SchemaEventArgs { diff --git a/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php b/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php index 072e1efb..3d5dd3c0 100644 --- a/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php @@ -7,7 +7,7 @@ use Doctrine\DBAL\Schema\Table; 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}. */ class SchemaDropTableEventArgs extends SchemaEventArgs { diff --git a/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php b/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php index 82c17a26..7e9f0db6 100644 --- a/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php @@ -6,7 +6,7 @@ use Doctrine\DBAL\Connection; 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}. */ class SchemaIndexDefinitionEventArgs extends SchemaEventArgs { diff --git a/doctrine/dbal/src/Event/TransactionBeginEventArgs.php b/doctrine/dbal/src/Event/TransactionBeginEventArgs.php new file mode 100644 index 00000000..946e8f2e --- /dev/null +++ b/doctrine/dbal/src/Event/TransactionBeginEventArgs.php @@ -0,0 +1,9 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\DBAL\Event; + +class TransactionBeginEventArgs extends TransactionEventArgs +{ +} diff --git a/doctrine/dbal/src/Event/TransactionCommitEventArgs.php b/doctrine/dbal/src/Event/TransactionCommitEventArgs.php new file mode 100644 index 00000000..c87d0575 --- /dev/null +++ b/doctrine/dbal/src/Event/TransactionCommitEventArgs.php @@ -0,0 +1,9 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\DBAL\Event; + +class TransactionCommitEventArgs extends TransactionEventArgs +{ +} diff --git a/doctrine/dbal/src/Event/TransactionEventArgs.php b/doctrine/dbal/src/Event/TransactionEventArgs.php new file mode 100644 index 00000000..e56e214c --- /dev/null +++ b/doctrine/dbal/src/Event/TransactionEventArgs.php @@ -0,0 +1,24 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\DBAL\Connection; + +abstract class TransactionEventArgs extends EventArgs +{ + /** @var Connection */ + private $connection; + + public function __construct(Connection $connection) + { + $this->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 00000000..607a5f94 --- /dev/null +++ b/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php @@ -0,0 +1,9 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\DBAL\Event; + +class TransactionRollBackEventArgs extends TransactionEventArgs +{ +} diff --git a/doctrine/dbal/src/Events.php b/doctrine/dbal/src/Events.php index de30815c..1c0b9559 100644 --- a/doctrine/dbal/src/Events.php +++ b/doctrine/dbal/src/Events.php @@ -30,4 +30,7 @@ final class Events public const onSchemaAlterTableRenameColumn = 'onSchemaAlterTableRenameColumn'; public const onSchemaColumnDefinition = 'onSchemaColumnDefinition'; public const onSchemaIndexDefinition = 'onSchemaIndexDefinition'; + public const onTransactionBegin = 'onTransactionBegin'; + public const onTransactionCommit = 'onTransactionCommit'; + public const onTransactionRollBack = 'onTransactionRollBack'; } diff --git a/doctrine/dbal/src/Exception.php b/doctrine/dbal/src/Exception.php index 56c4cc21..b8d7804d 100644 --- a/doctrine/dbal/src/Exception.php +++ b/doctrine/dbal/src/Exception.php @@ -110,11 +110,6 @@ class Exception extends \Exception return new self('No columns specified for table ' . $tableName); } - public static function limitOffsetInvalid(): self - { - return new self('Invalid Offset in Limit Query, it has to be larger than or equal to 0.'); - } - public static function typeExists(string $name): self { return new self('Type ' . $name . ' already exists.'); diff --git a/doctrine/dbal/src/Exception/DatabaseDoesNotExist.php b/doctrine/dbal/src/Exception/DatabaseDoesNotExist.php new file mode 100644 index 00000000..87eb1381 --- /dev/null +++ b/doctrine/dbal/src/Exception/DatabaseDoesNotExist.php @@ -0,0 +1,10 @@ +<?php + +namespace Doctrine\DBAL\Exception; + +/** + * @psalm-immutable + */ +class DatabaseDoesNotExist extends DatabaseObjectNotFoundException +{ +} diff --git a/doctrine/dbal/src/Exception/SchemaDoesNotExist.php b/doctrine/dbal/src/Exception/SchemaDoesNotExist.php new file mode 100644 index 00000000..cbb0658b --- /dev/null +++ b/doctrine/dbal/src/Exception/SchemaDoesNotExist.php @@ -0,0 +1,10 @@ +<?php + +namespace Doctrine\DBAL\Exception; + +/** + * @psalm-immutable + */ +class SchemaDoesNotExist extends DatabaseObjectNotFoundException +{ +} diff --git a/doctrine/dbal/src/ExpandArrayParameters.php b/doctrine/dbal/src/ExpandArrayParameters.php index 063fd3f9..6c4e532a 100644 --- a/doctrine/dbal/src/ExpandArrayParameters.php +++ b/doctrine/dbal/src/ExpandArrayParameters.php @@ -100,7 +100,11 @@ final class ExpandArrayParameters implements Visitor $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; diff --git a/doctrine/dbal/src/Id/TableGenerator.php b/doctrine/dbal/src/Id/TableGenerator.php index 4058bcb6..d4f97fce 100644 --- a/doctrine/dbal/src/Id/TableGenerator.php +++ b/doctrine/dbal/src/Id/TableGenerator.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\Driver; 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,6 +52,8 @@ use const CASE_LOWER; * * 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 { @@ -70,6 +73,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.'); } diff --git a/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php b/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php index 3ec22f37..f47c9a91 100644 --- a/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php +++ b/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php @@ -9,7 +9,11 @@ use Doctrine\DBAL\Schema\Schema; 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 */ @@ -20,6 +24,12 @@ class TableGeneratorSchemaVisitor implements Visitor */ 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 00000000..2175dc46 --- /dev/null +++ b/doctrine/dbal/src/Logging/Connection.php @@ -0,0 +1,85 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\DBAL\Logging; + +use Doctrine\DBAL\Driver\Connection as ConnectionInterface; +use Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware; +use Doctrine\DBAL\Driver\Result; +use Doctrine\DBAL\Driver\Statement as DriverStatement; +use Psr\Log\LoggerInterface; + +final class Connection extends AbstractConnectionMiddleware +{ + /** @var LoggerInterface */ + private $logger; + + /** + * @internal This connection can be only instantiated by its driver. + */ + public function __construct(ConnectionInterface $connection, LoggerInterface $logger) + { + parent::__construct($connection); + + $this->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 6a9fab5a..24a2d68c 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 00000000..533dcc05 --- /dev/null +++ b/doctrine/dbal/src/Logging/Driver.php @@ -0,0 +1,56 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\DBAL\Logging; + +use Doctrine\DBAL\Driver as DriverInterface; +use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; +use Psr\Log\LoggerInterface; + +final class Driver extends AbstractDriverMiddleware +{ + /** @var LoggerInterface */ + private $logger; + + /** + * @internal This driver can be only instantiated by its middleware. + */ + public function __construct(DriverInterface $driver, LoggerInterface $logger) + { + parent::__construct($driver); + + $this->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<string,mixed> $params Connection parameters + * + * @return array<string,mixed> + */ + private function maskPassword(array $params): array + { + if (isset($params['password'])) { + $params['password'] = '<redacted>'; + } + + if (isset($params['url'])) { + $params['url'] = '<redacted>'; + } + + return $params; + } +} diff --git a/doctrine/dbal/src/Logging/LoggerChain.php b/doctrine/dbal/src/Logging/LoggerChain.php index 9b44dc0e..c256dd72 100644 --- a/doctrine/dbal/src/Logging/LoggerChain.php +++ b/doctrine/dbal/src/Logging/LoggerChain.php @@ -2,8 +2,12 @@ namespace Doctrine\DBAL\Logging; +use Doctrine\Deprecations\Deprecation; + /** * Chains multiple SQLLogger. + * + * @deprecated */ class LoggerChain implements SQLLogger { @@ -15,6 +19,12 @@ class LoggerChain implements SQLLogger */ 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 00000000..4d5c6b06 --- /dev/null +++ b/doctrine/dbal/src/Logging/Middleware.php @@ -0,0 +1,25 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\DBAL\Logging; + +use Doctrine\DBAL\Driver as DriverInterface; +use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface; +use Psr\Log\LoggerInterface; + +final class Middleware implements MiddlewareInterface +{ + /** @var LoggerInterface */ + private $logger; + + public function __construct(LoggerInterface $logger) + { + $this->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 a0bdf1bf..dab4a3a7 100644 --- a/doctrine/dbal/src/Logging/SQLLogger.php +++ b/doctrine/dbal/src/Logging/SQLLogger.php @@ -6,6 +6,9 @@ use Doctrine\DBAL\Types\Type; /** * 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 00000000..e993767a --- /dev/null +++ b/doctrine/dbal/src/Logging/Statement.php @@ -0,0 +1,76 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\DBAL\Logging; + +use Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware; +use Doctrine\DBAL\Driver\Result as ResultInterface; +use Doctrine\DBAL\Driver\Statement as StatementInterface; +use Doctrine\DBAL\ParameterType; +use Psr\Log\LoggerInterface; + +use function array_slice; +use function func_get_args; + +final class Statement extends AbstractStatementMiddleware +{ + /** @var LoggerInterface */ + private $logger; + + /** @var string */ + private $sql; + + /** @var array<int,mixed>|array<string,mixed> */ + private $params = []; + + /** @var array<int,int>|array<string,int> */ + private $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} + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + { + $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) + { + $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 00000000..b8029149 --- /dev/null +++ b/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php @@ -0,0 +1,1188 @@ +<?php + +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; + +/** + * Provides the base implementation for the lowest versions of supported MySQL-like database platforms. + */ +abstract class AbstractMySQLPlatform extends AbstractPlatform +{ + 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 .= sprintf(' LIMIT %d', $limit); + + if ($offset > 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} + */ + 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 getLengthExpression($column) + { + return 'CHAR_LENGTH(' . $column . ')'; + } + + /** + * {@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) { + 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} + */ + public function getListViewsSQL($database) + { + return 'SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = ' . $this->quoteStringLiteral($database); + } + + /** + * @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) + { + 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} + * + * @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/pulls/1519', + 'AbstractMySQLPlatform::prefersIdentityColumns() is deprecated.' + ); + + 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) + { + 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'; + } + + 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} + */ + 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 + */ + 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'])) { + $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'] + : ''; + } + + /** + * {@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): array + { + $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): array + { + $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): array + { + 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 + */ + 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} + */ + 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'; + } + + /** + * 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} + */ + public function getVarcharMaxLength() + { + return 65535; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + 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) { + $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; + } + + private function getDatabaseNameSQL(?string $databaseName): string + { + if ($databaseName !== null) { + return $this->quoteStringLiteral($databaseName); + } + + return $this->getCurrentDatabaseExpression(); + } +} diff --git a/doctrine/dbal/src/Platforms/AbstractPlatform.php b/doctrine/dbal/src/Platforms/AbstractPlatform.php index f22fab5b..e143c6dc 100644 --- a/doctrine/dbal/src/Platforms/AbstractPlatform.php +++ b/doctrine/dbal/src/Platforms/AbstractPlatform.php @@ -80,6 +80,8 @@ 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; @@ -94,10 +96,6 @@ abstract class AbstractPlatform */ protected $_keywords; - public function __construct() - { - } - /** * Sets the EventManager used by the Platform. * @@ -173,10 +171,8 @@ abstract class AbstractPlatform /** * 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(); @@ -341,6 +337,8 @@ abstract class AbstractPlatform /** * Gets the name of the platform. * + * @deprecated Identify platforms by their class. + * * @return string */ abstract public function getName(); @@ -424,10 +422,19 @@ abstract class AbstractPlatform /** * 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 +451,24 @@ abstract class AbstractPlatform /** * 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 +480,13 @@ abstract class AbstractPlatform */ 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(); } @@ -494,7 +515,7 @@ abstract class AbstractPlatform { $comment = $column->getComment(); - if ($this->isCommentedDoctrineType($column->getType())) { + if ($column->getType()->requiresSQLCommentHint($this)) { $comment .= $this->getDoctrineTypeComment($column->getType()); } @@ -514,78 +535,143 @@ abstract class AbstractPlatform /** * Gets the string portion that starts an SQL comment. * + * @deprecated + * * @return string */ public function getSqlCommentStartString() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pulls/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/pulls/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/pulls/4724', + 'AbstractPlatform::getWildcards() is deprecated.' + . ' Use AbstractPlatform::getLikeWildcardCharacters() instead.' + ); + return ['%', '_']; } @@ -604,12 +690,20 @@ abstract class AbstractPlatform /** * 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/pulls/4724', + 'AbstractPlatform::getAvgExpression() is deprecated. Use AVG() in SQL instead.' + ); + return 'AVG(' . $column . ')'; } @@ -618,48 +712,80 @@ abstract class AbstractPlatform * * 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/pulls/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/pulls/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/pulls/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/pulls/4724', + 'AbstractPlatform::getSumExpression() is deprecated. Use SUM() in SQL instead.' + ); + return 'SUM(' . $column . ')'; } @@ -670,17 +796,25 @@ abstract class AbstractPlatform * * 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/pulls/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 +828,41 @@ abstract class AbstractPlatform /** * 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/pulls/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/pulls/4724', + 'AbstractPlatform::getRoundExpression() is deprecated. Use ROUND() in SQL instead.' + ); + return 'ROUND(' . $column . ', ' . $decimals . ')'; } @@ -770,24 +920,40 @@ abstract class AbstractPlatform /** * 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/pulls/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/pulls/4724', + 'AbstractPlatform::getLtrimExpression() is deprecated. Use LTRIM() in SQL instead.' + ); + return 'LTRIM(' . $str . ')'; } @@ -795,12 +961,20 @@ abstract class AbstractPlatform * 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/pulls/4724', + 'AbstractPlatform::getUpperExpression() is deprecated. Use UPPER() in SQL instead.' + ); + return 'UPPER(' . $str . ')'; } @@ -808,21 +982,29 @@ abstract class AbstractPlatform * 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/pulls/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 +1018,18 @@ abstract class AbstractPlatform /** * 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 +1040,9 @@ abstract class AbstractPlatform * * 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 +1078,60 @@ abstract class AbstractPlatform * ->where($e->eq('id', $e->not('null')); * </code> * + * @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/pulls/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/pulls/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/pulls/4724', + 'AbstractPlatform::getIsNotNullExpression() is deprecated. Use IS NOT NULL in SQL instead.' + ); + return $expression . ' IS NOT NULL'; } @@ -930,6 +1144,8 @@ abstract class AbstractPlatform * 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 +1154,90 @@ abstract class AbstractPlatform */ public function getBetweenExpression($expression, $value1, $value2) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pulls/4724', + 'AbstractPlatform::getBetweenExpression() is deprecated. Use BETWEEN in SQL instead.' + ); + 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/pulls/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/pulls/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/pulls/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/pulls/4724', + 'AbstractPlatform::getCosExpression() is deprecated. Use COS() in SQL instead.' + ); + return 'COS(' . $value . ')'; } @@ -1309,6 +1563,7 @@ abstract class AbstractPlatform * * @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 { @@ -1350,26 +1605,6 @@ abstract class AbstractPlatform } /** - * 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. * * @param Table|string $table @@ -1425,8 +1660,8 @@ abstract class AbstractPlatform /** * 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 * @@ -1448,6 +1683,8 @@ abstract class AbstractPlatform /** * 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 * @@ -1494,6 +1731,14 @@ abstract class AbstractPlatform } /** + * 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. * @@ -1566,23 +1811,13 @@ abstract class AbstractPlatform } } - $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)) { @@ -1747,8 +1982,33 @@ abstract class AbstractPlatform } /** + * 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) { + $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 @@ -1873,7 +2133,33 @@ abstract class AbstractPlatform */ 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; } /** @@ -2312,7 +2598,7 @@ abstract class AbstractPlatform } if ($type instanceof Types\BooleanType) { - return " DEFAULT '" . $this->convertBooleans($default) . "'"; + return ' DEFAULT ' . $this->convertBooleans($default); } return ' DEFAULT ' . $this->quoteStringLiteral($default); @@ -2452,11 +2738,19 @@ abstract class AbstractPlatform * 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/pulls/4724', + 'AbstractPlatform::getTemporaryTableSQL() is deprecated.' + ); + return 'TEMPORARY'; } @@ -2573,11 +2867,19 @@ abstract class AbstractPlatform * 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/pulls/4724', + 'AbstractPlatform::getUniqueFieldDeclarationSQL() is deprecated. Use UNIQUE in SQL instead.' + ); + return 'UNIQUE'; } @@ -2613,10 +2915,18 @@ abstract class AbstractPlatform * 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/pulls/1519', + 'AbstractPlatform::prefersIdentityColumns() is deprecated.' + ); + return false; } @@ -2752,7 +3062,7 @@ abstract class AbstractPlatform /** * 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 * @@ -2818,12 +3128,20 @@ abstract class AbstractPlatform } /** + * @deprecated + * * @return string * * @throws Exception If not supported on this platform. */ public function getListUsersSQL() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pulls/4724', + 'AbstractPlatform::getListUsersSQL() is deprecated.' + ); + throw Exception::notSupported(__METHOD__); } @@ -2880,64 +3198,66 @@ abstract class AbstractPlatform * @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; } /** @@ -3092,10 +3412,18 @@ abstract class AbstractPlatform /** * Whether the platform supports indexes. * + * @deprecated + * * @return bool */ public function supportsIndexes() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pulls/4724', + 'AbstractPlatform::supportsIndexes() is deprecated.' + ); + return true; } @@ -3120,20 +3448,36 @@ abstract class AbstractPlatform /** * 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/pulls/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/pulls/4724', + 'AbstractPlatform::supportsTransactions() is deprecated.' + ); + return true; } @@ -3160,10 +3504,18 @@ abstract class AbstractPlatform /** * Whether the platform supports primary key constraints. * + * @deprecated + * * @return bool */ public function supportsPrimaryConstraints() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pulls/4724', + 'AbstractPlatform::supportsPrimaryConstraints() is deprecated.' + ); + return true; } @@ -3190,13 +3542,21 @@ abstract class AbstractPlatform /** * 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::createSchema()}. * * @return bool */ public function canEmulateSchemas() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4805', + 'AbstractPlatform::canEmulateSchemas() is deprecated.' + ); + return false; } @@ -3227,10 +3587,18 @@ abstract class AbstractPlatform /** * 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/pulls/4724', + 'AbstractPlatform::supportsGettingAffectedRows() is deprecated.' + ); + return true; } @@ -3277,10 +3645,18 @@ abstract class AbstractPlatform /** * 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/pulls/4724', + 'AbstractPlatform::supportsViews() is deprecated. All platforms must implement support for views.' + ); + return true; } @@ -3345,11 +3721,9 @@ abstract class AbstractPlatform * @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( @@ -3384,11 +3758,11 @@ abstract class AbstractPlatform 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 +3771,19 @@ abstract class AbstractPlatform /** * 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/pulls/4724', + 'AbstractPlatform::supportsViews() is deprecated.' + . ' All platforms must implement support for offsets in modify limit clauses.' + ); + return true; } @@ -3496,11 +3879,9 @@ abstract class AbstractPlatform /** * 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) { @@ -3532,7 +3913,7 @@ abstract class AbstractPlatform /** * 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<KeywordList> @@ -3596,6 +3977,27 @@ abstract class AbstractPlatform } /** + * @return array<string,mixed> 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); + + $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'] = $this->getVarcharDefaultLength(); + } + + return $columnData; + } + + /** * @internal */ public function createSQLParser(): Parser @@ -3607,4 +4009,37 @@ abstract class AbstractPlatform { 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(); + } } diff --git a/doctrine/dbal/src/Platforms/DB2Platform.php b/doctrine/dbal/src/Platforms/DB2Platform.php index 03631054..979bb2c8 100644 --- a/doctrine/dbal/src/Platforms/DB2Platform.php +++ b/doctrine/dbal/src/Platforms/DB2Platform.php @@ -69,7 +69,7 @@ class DB2Platform extends AbstractPlatform /** * {@inheritDoc} */ - public function initializeDoctrineTypeMappings() + protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ 'bigint' => 'bigint', @@ -95,6 +95,13 @@ class DB2Platform extends AbstractPlatform */ 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. @@ -135,6 +142,12 @@ class DB2Platform extends AbstractPlatform */ 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'; } @@ -399,52 +412,12 @@ class DB2Platform extends AbstractPlatform /** * {@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} - */ public function supportsCreateDropDatabase() { return false; } /** - * {@inheritDoc} - */ - public function supportsReleaseSavepoints() - { - return false; - } - - /** * {@inheritdoc} */ public function supportsCommentOnStatement() @@ -659,11 +632,9 @@ class DB2Platform extends AbstractPlatform /** * 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(); @@ -845,6 +816,14 @@ class DB2Platform extends AbstractPlatform return 'SUBSTR(' . $string . ', ' . $start . ', ' . $length . ')'; } + /** + * {@inheritDoc} + */ + public function getLengthExpression($column) + { + return 'LENGTH(' . $column . ', CODEUNITS32)'; + } + public function getCurrentDatabaseExpression(): string { return 'CURRENT_USER'; @@ -860,9 +839,17 @@ class DB2Platform extends AbstractPlatform /** * {@inheritDoc} + * + * @deprecated */ public function prefersIdentityColumns() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pulls/1519', + 'DB2Platform::prefersIdentityColumns() is deprecated.' + ); + return true; } @@ -899,7 +886,7 @@ class DB2Platform extends AbstractPlatform /** * {@inheritDoc} * - * @deprecated Implement {@link createReservedKeywordsList()} instead. + * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { diff --git a/doctrine/dbal/src/Platforms/Keywords/KeywordList.php b/doctrine/dbal/src/Platforms/Keywords/KeywordList.php index 852a58ab..34b703f3 100644 --- a/doctrine/dbal/src/Platforms/Keywords/KeywordList.php +++ b/doctrine/dbal/src/Platforms/Keywords/KeywordList.php @@ -8,6 +8,8 @@ use function strtoupper; /** * Abstract interface for a SQL reserved keyword dictionary. + * + * @psalm-consistent-constructor */ abstract class KeywordList { diff --git a/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php b/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php new file mode 100644 index 00000000..93c595f2 --- /dev/null +++ b/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php @@ -0,0 +1,267 @@ +<?php + +namespace Doctrine\DBAL\Platforms\Keywords; + +class MariaDBKeywords extends MySQLKeywords +{ + public function getName(): string + { + return 'MariaDB'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords(): array + { + return [ + 'ACCESSIBLE', + 'ADD', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ASENSITIVE', + 'BEFORE', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOTH', + 'BY', + 'CALL', + 'CASCADE', + 'CASE', + 'CHANGE', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONDITION', + 'CONSTRAINT', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DATABASES', + 'DAY_HOUR', + 'DAY_MICROSECOND', + 'DAY_MINUTE', + 'DAY_SECOND', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELAYED', + 'DELETE', + 'DESC', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISTINCT', + 'DISTINCTROW', + 'DIV', + 'DOUBLE', + 'DROP', + 'DUAL', + 'EACH', + 'ELSE', + 'ELSEIF', + 'ENCLOSED', + 'ESCAPED', + 'EXCEPT', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'FALSE', + 'FETCH', + 'FLOAT', + 'FLOAT4', + 'FLOAT8', + 'FOR', + 'FORCE', + 'FOREIGN', + 'FROM', + 'FULLTEXT', + 'GENERATED', + 'GET', + 'GENERAL', + 'GRANT', + 'GROUP', + 'HAVING', + 'HIGH_PRIORITY', + 'HOUR_MICROSECOND', + 'HOUR_MINUTE', + 'HOUR_SECOND', + 'IF', + 'IGNORE', + 'IGNORE_SERVER_IDS', + 'IN', + 'INDEX', + 'INFILE', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INT1', + 'INT2', + 'INT3', + 'INT4', + 'INT8', + 'INTEGER', + 'INTERSECT', + 'INTERVAL', + 'INTO', + 'IO_AFTER_GTIDS', + 'IO_BEFORE_GTIDS', + 'IS', + 'ITERATE', + 'JOIN', + 'KEY', + 'KEYS', + 'KILL', + 'LEADING', + 'LEAVE', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LINEAR', + 'LINES', + 'LOAD', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOCK', + 'LONG', + 'LONGBLOB', + 'LONGTEXT', + 'LOOP', + 'LOW_PRIORITY', + 'MASTER_BIND', + 'MASTER_HEARTBEAT_PERIOD', + 'MASTER_SSL_VERIFY_SERVER_CERT', + 'MATCH', + 'MAXVALUE', + 'MEDIUMBLOB', + 'MEDIUMINT', + 'MEDIUMTEXT', + 'MIDDLEINT', + 'MINUTE_MICROSECOND', + 'MINUTE_SECOND', + 'MOD', + 'MODIFIES', + 'NATURAL', + 'NO_WRITE_TO_BINLOG', + 'NOT', + 'NULL', + 'NUMERIC', + 'OFFSET', + 'ON', + 'OPTIMIZE', + 'OPTIMIZER_COSTS', + 'OPTION', + 'OPTIONALLY', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OUTFILE', + 'OVER', + 'PARTITION', + 'PRECISION', + 'PRIMARY', + 'PROCEDURE', + 'PURGE', + 'RANGE', + 'READ', + 'READ_WRITE', + 'READS', + 'REAL', + 'RECURSIVE', + 'REFERENCES', + 'REGEXP', + 'RELEASE', + 'RENAME', + 'REPEAT', + 'REPLACE', + 'REQUIRE', + 'RESIGNAL', + 'RESTRICT', + 'RETURN', + 'RETURNING', + 'REVOKE', + 'RIGHT', + 'RLIKE', + 'ROWS', + 'SCHEMA', + 'SCHEMAS', + 'SECOND_MICROSECOND', + 'SELECT', + 'SENSITIVE', + 'SEPARATOR', + 'SET', + 'SHOW', + 'SIGNAL', + 'SLOW', + 'SMALLINT', + 'SPATIAL', + 'SPECIFIC', + 'SQL', + 'SQL_BIG_RESULT', + 'SQL_CALC_FOUND_ROWS', + 'SQL_SMALL_RESULT', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SSL', + 'STARTING', + 'STORED', + 'STRAIGHT_JOIN', + 'TABLE', + 'TERMINATED', + 'THEN', + 'TINYBLOB', + 'TINYINT', + 'TINYTEXT', + 'TO', + 'TRAILING', + 'TRIGGER', + 'TRUE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNLOCK', + 'UNSIGNED', + 'UPDATE', + 'USAGE', + 'USE', + 'USING', + 'UTC_DATE', + 'UTC_TIME', + 'UTC_TIMESTAMP', + 'VALUES', + 'VARBINARY', + 'VARCHAR', + 'VARCHARACTER', + 'VARYING', + 'VIRTUAL', + 'WHEN', + 'WHERE', + 'WHILE', + 'WINDOW', + 'WITH', + 'WRITE', + 'XOR', + 'YEAR_MONTH', + 'ZEROFILL', + ]; + } +} diff --git a/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php b/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php index 8cb2befe..aaa746b8 100644 --- a/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php @@ -5,267 +5,14 @@ namespace Doctrine\DBAL\Platforms\Keywords; /** * MariaDb reserved keywords list. * + * @deprecated Use {@link MariaDBKeywords} instead. + * * @link https://mariadb.com/kb/en/the-mariadb-library/reserved-words/ */ -final class MariaDb102Keywords extends MySQLKeywords +final class MariaDb102Keywords extends MariaDBKeywords { public function getName(): string { return 'MariaDb102'; } - - /** - * {@inheritdoc} - */ - protected function getKeywords(): array - { - return [ - 'ACCESSIBLE', - 'ADD', - 'ALL', - 'ALTER', - 'ANALYZE', - 'AND', - 'AS', - 'ASC', - 'ASENSITIVE', - 'BEFORE', - 'BETWEEN', - 'BIGINT', - 'BINARY', - 'BLOB', - 'BOTH', - 'BY', - 'CALL', - 'CASCADE', - 'CASE', - 'CHANGE', - 'CHAR', - 'CHARACTER', - 'CHECK', - 'COLLATE', - 'COLUMN', - 'CONDITION', - 'CONSTRAINT', - 'CONTINUE', - 'CONVERT', - 'CREATE', - 'CROSS', - 'CURRENT_DATE', - 'CURRENT_TIME', - 'CURRENT_TIMESTAMP', - 'CURRENT_USER', - 'CURSOR', - 'DATABASE', - 'DATABASES', - 'DAY_HOUR', - 'DAY_MICROSECOND', - 'DAY_MINUTE', - 'DAY_SECOND', - 'DEC', - 'DECIMAL', - 'DECLARE', - 'DEFAULT', - 'DELAYED', - 'DELETE', - 'DESC', - 'DESCRIBE', - 'DETERMINISTIC', - 'DISTINCT', - 'DISTINCTROW', - 'DIV', - 'DOUBLE', - 'DROP', - 'DUAL', - 'EACH', - 'ELSE', - 'ELSEIF', - 'ENCLOSED', - 'ESCAPED', - 'EXCEPT', - 'EXISTS', - 'EXIT', - 'EXPLAIN', - 'FALSE', - 'FETCH', - 'FLOAT', - 'FLOAT4', - 'FLOAT8', - 'FOR', - 'FORCE', - 'FOREIGN', - 'FROM', - 'FULLTEXT', - 'GENERATED', - 'GET', - 'GENERAL', - 'GRANT', - 'GROUP', - 'HAVING', - 'HIGH_PRIORITY', - 'HOUR_MICROSECOND', - 'HOUR_MINUTE', - 'HOUR_SECOND', - 'IF', - 'IGNORE', - 'IGNORE_SERVER_IDS', - 'IN', - 'INDEX', - 'INFILE', - 'INNER', - 'INOUT', - 'INSENSITIVE', - 'INSERT', - 'INT', - 'INT1', - 'INT2', - 'INT3', - 'INT4', - 'INT8', - 'INTEGER', - 'INTERSECT', - 'INTERVAL', - 'INTO', - 'IO_AFTER_GTIDS', - 'IO_BEFORE_GTIDS', - 'IS', - 'ITERATE', - 'JOIN', - 'KEY', - 'KEYS', - 'KILL', - 'LEADING', - 'LEAVE', - 'LEFT', - 'LIKE', - 'LIMIT', - 'LINEAR', - 'LINES', - 'LOAD', - 'LOCALTIME', - 'LOCALTIMESTAMP', - 'LOCK', - 'LONG', - 'LONGBLOB', - 'LONGTEXT', - 'LOOP', - 'LOW_PRIORITY', - 'MASTER_BIND', - 'MASTER_HEARTBEAT_PERIOD', - 'MASTER_SSL_VERIFY_SERVER_CERT', - 'MATCH', - 'MAXVALUE', - 'MEDIUMBLOB', - 'MEDIUMINT', - 'MEDIUMTEXT', - 'MIDDLEINT', - 'MINUTE_MICROSECOND', - 'MINUTE_SECOND', - 'MOD', - 'MODIFIES', - 'NATURAL', - 'NO_WRITE_TO_BINLOG', - 'NOT', - 'NULL', - 'NUMERIC', - 'ON', - 'OPTIMIZE', - 'OPTIMIZER_COSTS', - 'OPTION', - 'OPTIONALLY', - 'OR', - 'ORDER', - 'OUT', - 'OUTER', - 'OUTFILE', - 'OVER', - 'PARTITION', - 'PRECISION', - 'PRIMARY', - 'PROCEDURE', - 'PURGE', - 'RANGE', - 'READ', - 'READ_WRITE', - 'READS', - 'REAL', - 'RECURSIVE', - 'REFERENCES', - 'REGEXP', - 'RELEASE', - 'RENAME', - 'REPEAT', - 'REPLACE', - 'REQUIRE', - 'RESIGNAL', - 'RESTRICT', - 'RETURN', - 'RETURNING', - 'REVOKE', - 'RIGHT', - 'RLIKE', - 'ROWS', - 'SCHEMA', - 'SCHEMAS', - 'SECOND_MICROSECOND', - 'SELECT', - 'SENSITIVE', - 'SEPARATOR', - 'SET', - 'SHOW', - 'SIGNAL', - 'SLOW', - 'SMALLINT', - 'SPATIAL', - 'SPECIFIC', - 'SQL', - 'SQL_BIG_RESULT', - 'SQL_CALC_FOUND_ROWS', - 'SQL_SMALL_RESULT', - 'SQLEXCEPTION', - 'SQLSTATE', - 'SQLWARNING', - 'SSL', - 'STARTING', - 'STORED', - 'STRAIGHT_JOIN', - 'TABLE', - 'TERMINATED', - 'THEN', - 'TINYBLOB', - 'TINYINT', - 'TINYTEXT', - 'TO', - 'TRAILING', - 'TRIGGER', - 'TRUE', - 'UNDO', - 'UNION', - 'UNIQUE', - 'UNLOCK', - 'UNSIGNED', - 'UPDATE', - 'USAGE', - 'USE', - 'USING', - 'UTC_DATE', - 'UTC_TIME', - 'UTC_TIMESTAMP', - 'VALUES', - 'VARBINARY', - 'VARCHAR', - 'VARCHARACTER', - 'VARYING', - 'VIRTUAL', - 'WHEN', - 'WHERE', - 'WHILE', - 'WINDOW', - 'WITH', - 'WRITE', - 'XOR', - 'YEAR_MONTH', - 'ZEROFILL', - ]; - } } diff --git a/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php b/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php index 9b60a3f1..19b49fb9 100644 --- a/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php @@ -4,6 +4,8 @@ namespace Doctrine\DBAL\Platforms\Keywords; /** * MySQL 5.7 reserved keywords list. + * + * @deprecated Use {@link MySQLKeywords} instead. */ class MySQL57Keywords extends MySQLKeywords { diff --git a/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php b/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php index dad68ead..fab01864 100644 --- a/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php @@ -6,6 +6,8 @@ namespace Doctrine\DBAL\Platforms\Keywords; /** * PostgreSQL 10.0 reserved keywords list. + * + * @deprecated Use {@link PostgreSQLKeywords} instead. */ class PostgreSQL100Keywords extends PostgreSQL94Keywords { diff --git a/doctrine/dbal/src/Platforms/Keywords/PostgreSQL94Keywords.php b/doctrine/dbal/src/Platforms/Keywords/PostgreSQL94Keywords.php index eeff23ca..88d4ac32 100644 --- a/doctrine/dbal/src/Platforms/Keywords/PostgreSQL94Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/PostgreSQL94Keywords.php @@ -4,122 +4,9 @@ namespace Doctrine\DBAL\Platforms\Keywords; /** * PostgreSQL 9.4 reserved keywords list. + * + * @deprecated Use {@see PostgreSQLKeywords} instead. */ -class PostgreSQL94Keywords extends KeywordList +class PostgreSQL94Keywords extends PostgreSQLKeywords { - /** - * {@inheritdoc} - */ - public function getName() - { - return 'PostgreSQL'; - } - - /** - * {@inheritdoc} - */ - protected function getKeywords() - { - return [ - 'ALL', - 'ANALYSE', - 'ANALYZE', - 'AND', - 'ANY', - 'ARRAY', - 'AS', - 'ASC', - 'ASYMMETRIC', - 'AUTHORIZATION', - 'BINARY', - 'BOTH', - 'CASE', - 'CAST', - 'CHECK', - 'COLLATE', - 'COLLATION', - 'COLUMN', - 'CONCURRENTLY', - 'CONSTRAINT', - 'CREATE', - 'CROSS', - 'CURRENT_CATALOG', - 'CURRENT_DATE', - 'CURRENT_ROLE', - 'CURRENT_SCHEMA', - 'CURRENT_TIME', - 'CURRENT_TIMESTAMP', - 'CURRENT_USER', - 'DEFAULT', - 'DEFERRABLE', - 'DESC', - 'DISTINCT', - 'DO', - 'ELSE', - 'END', - 'EXCEPT', - 'FALSE', - 'FETCH', - 'FOR', - 'FOREIGN', - 'FREEZE', - 'FROM', - 'FULL', - 'GRANT', - 'GROUP', - 'HAVING', - 'ILIKE', - 'IN', - 'INITIALLY', - 'INNER', - 'INTERSECT', - 'INTO', - 'IS', - 'ISNULL', - 'JOIN', - 'LATERAL', - 'LEADING', - 'LEFT', - 'LIKE', - 'LIMIT', - 'LOCALTIME', - 'LOCALTIMESTAMP', - 'NATURAL', - 'NOT', - 'NOTNULL', - 'NULL', - 'OFFSET', - 'ON', - 'ONLY', - 'OR', - 'ORDER', - 'OUTER', - 'OVERLAPS', - 'PLACING', - 'PRIMARY', - 'REFERENCES', - 'RETURNING', - 'RIGHT', - 'SELECT', - 'SESSION_USER', - 'SIMILAR', - 'SOME', - 'SYMMETRIC', - 'TABLE', - 'THEN', - 'TO', - 'TRAILING', - 'TRUE', - 'UNION', - 'UNIQUE', - 'USER', - 'USING', - 'VARIADIC', - 'VERBOSE', - 'WHEN', - 'WHERE', - 'WINDOW', - 'WITH', - ]; - } } diff --git a/doctrine/dbal/src/Platforms/Keywords/PostgreSQLKeywords.php b/doctrine/dbal/src/Platforms/Keywords/PostgreSQLKeywords.php new file mode 100644 index 00000000..47ccfdfb --- /dev/null +++ b/doctrine/dbal/src/Platforms/Keywords/PostgreSQLKeywords.php @@ -0,0 +1,125 @@ +<?php + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Reserved keywords list corresponding to the PostgreSQL database platform of the oldest supported version. + */ +class PostgreSQLKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'PostgreSQL'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return [ + 'ALL', + 'ANALYSE', + 'ANALYZE', + 'AND', + 'ANY', + 'ARRAY', + 'AS', + 'ASC', + 'ASYMMETRIC', + 'AUTHORIZATION', + 'BINARY', + 'BOTH', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLLATION', + 'COLUMN', + 'CONCURRENTLY', + 'CONSTRAINT', + 'CREATE', + 'CROSS', + 'CURRENT_CATALOG', + 'CURRENT_DATE', + 'CURRENT_ROLE', + 'CURRENT_SCHEMA', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'DEFAULT', + 'DEFERRABLE', + 'DESC', + 'DISTINCT', + 'DO', + 'ELSE', + 'END', + 'EXCEPT', + 'FALSE', + 'FETCH', + 'FOR', + 'FOREIGN', + 'FREEZE', + 'FROM', + 'FULL', + 'GRANT', + 'GROUP', + 'HAVING', + 'ILIKE', + 'IN', + 'INITIALLY', + 'INNER', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'LATERAL', + 'LEADING', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'NATURAL', + 'NOT', + 'NOTNULL', + 'NULL', + 'OFFSET', + 'ON', + 'ONLY', + 'OR', + 'ORDER', + 'OUTER', + 'OVERLAPS', + 'PLACING', + 'PRIMARY', + 'REFERENCES', + 'RETURNING', + 'RIGHT', + 'SELECT', + 'SESSION_USER', + 'SIMILAR', + 'SOME', + 'SYMMETRIC', + 'TABLE', + 'THEN', + 'TO', + 'TRAILING', + 'TRUE', + 'UNION', + 'UNIQUE', + 'USER', + 'USING', + 'VARIADIC', + 'VERBOSE', + 'WHEN', + 'WHERE', + 'WINDOW', + 'WITH', + ]; + } +} diff --git a/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php b/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php index a6da7746..bd4faf50 100644 --- a/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php +++ b/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php @@ -43,7 +43,7 @@ class ReservedKeywordsValidator implements Visitor * * @return string[] */ - private function isReservedWord($word) + private function isReservedWord($word): array { if ($word[0] === '`') { $word = str_replace('`', '', $word); @@ -64,10 +64,8 @@ class ReservedKeywordsValidator implements Visitor /** * @param string $asset * @param string[] $violatedPlatforms - * - * @return void */ - private function addViolation($asset, $violatedPlatforms) + private function addViolation($asset, $violatedPlatforms): void { if (count($violatedPlatforms) === 0) { return; diff --git a/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php b/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php index 24e190bf..ebc45c40 100644 --- a/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php @@ -4,210 +4,9 @@ namespace Doctrine\DBAL\Platforms\Keywords; /** * 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 00000000..0ea413ce --- /dev/null +++ b/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php @@ -0,0 +1,214 @@ +<?php + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2012 reserved keyword dictionary. + * Reserved keywords list corresponding to the Microsoft SQL Server database platform of the oldest supported version. + */ +class SQLServerKeywords extends KeywordList +{ + /** + * {@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/MariaDBPlatform.php b/doctrine/dbal/src/Platforms/MariaDBPlatform.php new file mode 100644 index 00000000..e877b70f --- /dev/null +++ b/doctrine/dbal/src/Platforms/MariaDBPlatform.php @@ -0,0 +1,44 @@ +<?php + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Types\Types; +use Doctrine\Deprecations\Deprecation; + +/** + * Provides the behavior, features and SQL dialect of the MariaDB database platform of the oldest supported version. + */ +class MariaDBPlatform extends MySQLPlatform +{ + /** + * {@inheritdoc} + * + * @link https://mariadb.com/kb/en/library/json-data-type/ + */ + public function getJsonTypeDeclarationSQL(array $column): string + { + return 'LONGTEXT'; + } + + /** + * @deprecated Implement {@see createReservedKeywordsList()} instead. + */ + protected function getReservedKeywordsClass(): string + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'MariaDb1027Platform::getReservedKeywordsClass() is deprecated,' + . ' use MariaDb1027Platform::createReservedKeywordsList() instead.' + ); + + return Keywords\MariaDb102Keywords::class; + } + + protected function initializeDoctrineTypeMappings(): void + { + parent::initializeDoctrineTypeMappings(); + + $this->doctrineTypeMapping['json'] = Types::JSON; + } +} diff --git a/doctrine/dbal/src/Platforms/MariaDb1027Platform.php b/doctrine/dbal/src/Platforms/MariaDb1027Platform.php index bb8e0b88..93c7d348 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/Comparator.php b/doctrine/dbal/src/Platforms/MySQL/Comparator.php new file mode 100644 index 00000000..81c54dbc --- /dev/null +++ b/doctrine/dbal/src/Platforms/MySQL/Comparator.php @@ -0,0 +1,66 @@ +<?php + +namespace Doctrine\DBAL\Platforms\MySQL; + +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; +use Doctrine\DBAL\Schema\Comparator as BaseComparator; +use Doctrine\DBAL\Schema\Table; + +use function array_diff_assoc; +use function array_intersect_key; + +/** + * Compares schemas in the context of MySQL platform. + * + * In MySQL, unless specified explicitly, the column's character set and collation are inherited from its containing + * table. So during comparison, an omitted value and the value that matches the default value of table in the + * desired schema must be considered equal. + */ +class Comparator extends BaseComparator +{ + /** + * @internal The comparator can be only instantiated by a schema manager. + */ + public function __construct(AbstractMySQLPlatform $platform) + { + parent::__construct($platform); + } + + /** + * {@inheritDoc} + */ + public function diffTable(Table $fromTable, Table $toTable) + { + return parent::diffTable( + $this->normalizeColumns($fromTable), + $this->normalizeColumns($toTable) + ); + } + + private function normalizeColumns(Table $table): Table + { + $defaults = array_intersect_key($table->getOptions(), [ + 'charset' => null, + 'collation' => null, + ]); + + if ($defaults === []) { + return $table; + } + + $table = clone $table; + + foreach ($table->getColumns() as $column) { + $options = $column->getPlatformOptions(); + $diff = array_diff_assoc($options, $defaults); + + if ($diff === $options) { + continue; + } + + $column->setPlatformOptions($diff); + } + + return $table; + } +} diff --git a/doctrine/dbal/src/Platforms/MySQL57Platform.php b/doctrine/dbal/src/Platforms/MySQL57Platform.php index f29204cf..3975bf2c 100644 --- a/doctrine/dbal/src/Platforms/MySQL57Platform.php +++ b/doctrine/dbal/src/Platforms/MySQL57Platform.php @@ -10,6 +10,9 @@ use Doctrine\Deprecations\Deprecation; /** * 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 { @@ -61,7 +64,7 @@ class MySQL57Platform extends MySQLPlatform /** * {@inheritdoc} * - * @deprecated Implement {@link createReservedKeywordsList()} instead. + * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { diff --git a/doctrine/dbal/src/Platforms/MySQL80Platform.php b/doctrine/dbal/src/Platforms/MySQL80Platform.php index 808e9344..536a79f4 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() { diff --git a/doctrine/dbal/src/Platforms/MySQLPlatform.php b/doctrine/dbal/src/Platforms/MySQLPlatform.php index 379616f2..ae2cc9f2 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 7be7621d..7eabb7fa 100644 --- a/doctrine/dbal/src/Platforms/OraclePlatform.php +++ b/doctrine/dbal/src/Platforms/OraclePlatform.php @@ -35,6 +35,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 +65,20 @@ class OraclePlatform extends AbstractPlatform } /** + * @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': @@ -169,6 +179,19 @@ class OraclePlatform extends AbstractPlatform /** * {@inheritDoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table): string + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $index->getQuotedName($this) + . ' PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; + } + + /** + * {@inheritDoc} * * Need to specifiy minvalue, since start with is hidden in the system and MINVALUE <= START WITH. * Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection @@ -195,10 +218,8 @@ class OraclePlatform extends AbstractPlatform /** * Cache definition for sequences - * - * @return string */ - private function getSequenceCacheSQL(Sequence $sequence) + private function getSequenceCacheSQL(Sequence $sequence): string { if ($sequence->getCache() === 0) { return ' NOCACHE'; @@ -401,10 +422,8 @@ class OraclePlatform extends AbstractPlatform $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; @@ -443,6 +462,7 @@ class OraclePlatform extends AbstractPlatform 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 . ' @@ -466,22 +486,8 @@ class OraclePlatform extends AbstractPlatform } /** - * {@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 @@ -549,6 +555,8 @@ END;'; } /** + * @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. @@ -578,10 +586,8 @@ END;'; * 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 +615,8 @@ END;'; * * 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'); @@ -713,18 +715,6 @@ SQL /** * {@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) { @@ -786,6 +776,14 @@ SQL /** * {@inheritDoc} */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE USER ' . $name; + } + + /** + * {@inheritDoc} + */ public function getDropDatabaseSQL($name) { return 'DROP USER ' . $name . ' CASCADE'; @@ -1008,6 +1006,12 @@ SQL */ 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 +1168,7 @@ SQL /** * {@inheritDoc} * - * @deprecated Implement {@link createReservedKeywordsList()} instead. + * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { diff --git a/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php b/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php index c9ca509d..a9419817 100644 --- a/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php +++ b/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php @@ -9,11 +9,14 @@ use Doctrine\Deprecations\Deprecation; /** * 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 { diff --git a/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php b/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php index bf21477c..2c974c36 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 00000000..477e08af --- /dev/null +++ b/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php @@ -0,0 +1,1276 @@ +<?php + +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\BinaryType; +use Doctrine\DBAL\Types\BlobType; +use Doctrine\DBAL\Types\PhpIntegerMappingType; +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 database platform of the oldest supported version. + */ +class PostgreSQLPlatform extends AbstractPlatform +{ + /** @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} + * + * @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} + */ + 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 {@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} + */ + 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 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 + */ + 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 + ); + } + + /** + * {@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 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 {@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 + { + $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 + */ + 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 ((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(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} + */ + 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 {@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} + */ + 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->isIntegerType($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'); + } + + $oldTypeIsInteger = $this->isIntegerType($columnDiff->fromColumn->getType()); + $newTypeIsInteger = $this->isIntegerType($columnDiff->column->getType()); + + // default should not be changed when switching between integer types and the default comes from a sequence + return $columnDiff->hasChanged('type') + && ! ($oldTypeIsInteger && $newTypeIsInteger && $columnDiff->column->getAutoincrement()); + } + + private function isIntegerType(Type $type): bool + { + return $type instanceof PhpIntegerMappingType; + } + + private function getOldColumnComment(ColumnDiff $columnDiff): ?string + { + return $columnDiff->fromColumn !== null ? $this->getColumnComment($columnDiff->fromColumn) : null; + } + + 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/SQLServer/Comparator.php b/doctrine/dbal/src/Platforms/SQLServer/Comparator.php new file mode 100644 index 00000000..0db1a6a5 --- /dev/null +++ b/doctrine/dbal/src/Platforms/SQLServer/Comparator.php @@ -0,0 +1,56 @@ +<?php + +namespace Doctrine\DBAL\Platforms\SQLServer; + +use Doctrine\DBAL\Platforms\SQLServerPlatform; +use Doctrine\DBAL\Schema\Comparator as BaseComparator; +use Doctrine\DBAL\Schema\Table; + +/** + * Compares schemas in the context of SQL Server platform. + * + * @link https://docs.microsoft.com/en-us/sql/t-sql/statements/collations?view=sql-server-ver15 + */ +class Comparator extends BaseComparator +{ + /** @var string */ + private $databaseCollation; + + /** + * @internal The comparator can be only instantiated by a schema manager. + */ + public function __construct(SQLServerPlatform $platform, string $databaseCollation) + { + parent::__construct($platform); + + $this->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 4db8f055..a8ba2fa0 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( - <<<SQL - IF EXISTS (SELECT * FROM sysobjects WHERE name = '%s') - ALTER TABLE %s DROP CONSTRAINT %s - ELSE - DROP INDEX %s ON %s - SQL - , - $index, - $table, - $index, - $index, - $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 ((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 00000000..732cb636 --- /dev/null +++ b/doctrine/dbal/src/Platforms/SQLServerPlatform.php @@ -0,0 +1,1660 @@ +<?php + +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 database platform + * of the oldest supported version. + */ +class SQLServerPlatform extends AbstractPlatform +{ + /** + * {@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. + */ + 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/pulls/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} + */ + 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 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 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 ($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 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 ((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. + * + * @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 = []; + + 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. + */ + 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 + { + // 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. + * + * @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'; + } + + /** + * {@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 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} + */ + 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) + { + 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 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 {@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} + * + * 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; + } + + 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) + ); + } + + 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/SQLite/Comparator.php b/doctrine/dbal/src/Platforms/SQLite/Comparator.php new file mode 100644 index 00000000..d27ee86d --- /dev/null +++ b/doctrine/dbal/src/Platforms/SQLite/Comparator.php @@ -0,0 +1,53 @@ +<?php + +namespace Doctrine\DBAL\Platforms\SQLite; + +use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Schema\Comparator as BaseComparator; +use Doctrine\DBAL\Schema\Table; + +use function strcasecmp; + +/** + * Compares schemas in the context of SQLite platform. + * + * BINARY is the default column collation and should be ignored if specified explicitly. + */ +class Comparator extends BaseComparator +{ + /** + * @internal The comparator can be only instantiated by a schema manager. + */ + public function __construct(SqlitePlatform $platform) + { + parent::__construct($platform); + } + + /** + * {@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']) || 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 16818261..6d5adda4 100644 --- a/doctrine/dbal/src/Platforms/SqlitePlatform.php +++ b/doctrine/dbal/src/Platforms/SqlitePlatform.php @@ -2,6 +2,7 @@ namespace Doctrine\DBAL\Platforms; +use Doctrine\DBAL\Driver\API\SQLite\UserDefinedFunctions; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\Constraint; @@ -27,7 +28,6 @@ use function sprintf; use function sqrt; use function str_replace; use function strlen; -use function strpos; use function strtolower; use function trim; @@ -48,12 +48,20 @@ class SqlitePlatform extends AbstractPlatform } /** + * @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\')'; @@ -197,9 +205,17 @@ class SqlitePlatform extends AbstractPlatform /** * {@inheritDoc} + * + * @deprecated */ public function prefersIdentityColumns() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pulls/1519', + 'SqlitePlatform::prefersIdentityColumns() is deprecated.' + ); + return true; } @@ -498,22 +514,6 @@ class SqlitePlatform extends AbstractPlatform /** * {@inheritDoc} */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; - } - - /** - * {@inheritDoc} - */ - public function getDropViewSQL($name) - { - return 'DROP VIEW ' . $name; - } - - /** - * {@inheritDoc} - */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey); @@ -537,6 +537,14 @@ class SqlitePlatform extends AbstractPlatform /** * {@inheritDoc} */ + public function supportsCreateDropDatabase() + { + return false; + } + + /** + * {@inheritDoc} + */ public function supportsIdentityColumns() { return true; @@ -563,6 +571,12 @@ class SqlitePlatform extends AbstractPlatform */ 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'; } @@ -580,6 +594,8 @@ class SqlitePlatform extends AbstractPlatform /** * 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 +608,8 @@ class SqlitePlatform extends AbstractPlatform /** * 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 +617,12 @@ class SqlitePlatform extends AbstractPlatform */ 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 +631,7 @@ class SqlitePlatform extends AbstractPlatform */ 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); } /** @@ -691,7 +699,7 @@ class SqlitePlatform extends AbstractPlatform /** * {@inheritDoc} * - * @deprecated Implement {@link createReservedKeywordsList()} instead. + * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { @@ -765,7 +773,7 @@ class SqlitePlatform extends AbstractPlatform 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); @@ -792,6 +800,8 @@ class SqlitePlatform extends AbstractPlatform /** * {@inheritDoc} * + * @deprecated + * * Sqlite Platform emulates schema by underscoring each dot and generating tables * into the default database. * @@ -800,6 +810,12 @@ class SqlitePlatform extends AbstractPlatform */ public function canEmulateSchemas() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4805', + 'SqlitePlatform::canEmulateSchemas() is deprecated.' + ); + return true; } @@ -837,6 +853,8 @@ class SqlitePlatform extends AbstractPlatform /** * {@inheritDoc} + * + * @deprecated */ public function getCreateConstraintSQL(Constraint $constraint, $table) { @@ -1130,7 +1148,7 @@ class SqlitePlatform extends AbstractPlatform /** * @return string[] */ - private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable) + private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable): array { $columns = []; @@ -1170,7 +1188,7 @@ class SqlitePlatform extends AbstractPlatform /** * @return Index[] */ - private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable) + private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable): array { $indexes = $fromTable->getIndexes(); $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable); @@ -1238,7 +1256,7 @@ class SqlitePlatform extends AbstractPlatform /** * @return ForeignKeyConstraint[] */ - private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable) + private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable): array { $foreignKeys = $fromTable->getForeignKeys(); $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable); @@ -1302,7 +1320,7 @@ class SqlitePlatform extends AbstractPlatform /** * @return Index[] */ - private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $fromTable) + private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $fromTable): array { $primaryIndex = []; diff --git a/doctrine/dbal/src/Portability/Connection.php b/doctrine/dbal/src/Portability/Connection.php index a5639792..a22ab92f 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,22 +18,20 @@ 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; 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), + parent::prepare($sql), $this->converter ); } @@ -41,53 +39,8 @@ final class Connection implements ConnectionInterface public function query(string $sql): DriverResult { return new Result( - $this->connection->query($sql), + 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 5763c260..eb76eb78 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 5ae410cd..6d4484f9 100644 --- a/doctrine/dbal/src/Portability/Driver.php +++ b/doctrine/dbal/src/Portability/Driver.php @@ -3,20 +3,18 @@ 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; @@ -25,9 +23,10 @@ final class Driver implements DriverInterface 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 +34,27 @@ final class Driver implements DriverInterface */ public function connect(array $params) { - $connection = $this->driver->connect($params); + $connection = parent::connect($params); $portability = (new OptimizeFlags())( $this->getDatabasePlatform(), $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,7 +63,7 @@ final class Driver implements DriverInterface $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; } @@ -66,25 +72,4 @@ final class Driver implements DriverInterface 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/OptimizeFlags.php b/doctrine/dbal/src/Portability/OptimizeFlags.php index 742eb93f..13367c92 100644 --- a/doctrine/dbal/src/Portability/OptimizeFlags.php +++ b/doctrine/dbal/src/Portability/OptimizeFlags.php @@ -7,9 +7,9 @@ namespace Doctrine\DBAL\Portability; 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 { @@ -20,11 +20,11 @@ final class OptimizeFlags * @var array<string,int> */ private static $platforms = [ - DB2Platform::class => 0, - OraclePlatform::class => Connection::PORTABILITY_EMPTY_TO_NULL, - PostgreSQL94Platform::class => 0, - SqlitePlatform::class => 0, - SQLServer2012Platform::class => 0, + 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 1fa91ab4..d8440b66 100644 --- a/doctrine/dbal/src/Portability/Result.php +++ b/doctrine/dbal/src/Portability/Result.php @@ -4,13 +4,11 @@ declare(strict_types=1); 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; - /** @var Converter */ private $converter; @@ -19,7 +17,8 @@ final class Result implements ResultInterface */ public function __construct(ResultInterface $result, Converter $converter) { - $this->result = $result; + parent::__construct($result); + $this->converter = $converter; } @@ -29,7 +28,7 @@ final class Result implements ResultInterface public function fetchNumeric() { return $this->converter->convertNumeric( - $this->result->fetchNumeric() + parent::fetchNumeric() ); } @@ -39,7 +38,7 @@ final class Result implements ResultInterface public function fetchAssociative() { return $this->converter->convertAssociative( - $this->result->fetchAssociative() + parent::fetchAssociative() ); } @@ -49,7 +48,7 @@ final class Result implements ResultInterface public function fetchOne() { return $this->converter->convertOne( - $this->result->fetchOne() + parent::fetchOne() ); } @@ -59,7 +58,7 @@ final class Result implements ResultInterface public function fetchAllNumeric(): array { return $this->converter->convertAllNumeric( - $this->result->fetchAllNumeric() + parent::fetchAllNumeric() ); } @@ -69,7 +68,7 @@ final class Result implements ResultInterface public function fetchAllAssociative(): array { return $this->converter->convertAllAssociative( - $this->result->fetchAllAssociative() + parent::fetchAllAssociative() ); } @@ -79,22 +78,7 @@ final class Result implements ResultInterface 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 f3f94e41..b104cf78 100644 --- a/doctrine/dbal/src/Portability/Statement.php +++ b/doctrine/dbal/src/Portability/Statement.php @@ -2,18 +2,15 @@ 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; @@ -22,24 +19,9 @@ final class Statement implements DriverStatement */ 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,7 +30,7 @@ final class Statement implements DriverStatement public function execute($params = null): ResultInterface { return new Result( - $this->stmt->execute($params), + parent::execute($params), $this->converter ); } diff --git a/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php b/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php index 8cb53150..1ce11b7b 100644 --- a/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php +++ b/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php @@ -308,7 +308,10 @@ class ExpressionBuilder } /** - * 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 6d74d010..7f30a128 100644 --- a/doctrine/dbal/src/Query/QueryBuilder.php +++ b/doctrine/dbal/src/Query/QueryBuilder.php @@ -327,9 +327,9 @@ class QueryBuilder /** * 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 */ @@ -1315,11 +1315,9 @@ class QueryBuilder } /** - * @return string - * * @throws QueryException */ - private function getSQLForSelect() + private function getSQLForSelect(): string { $query = 'SELECT ' . ($this->sqlParts['distinct'] ? 'DISTINCT ' : '') . implode(', ', $this->sqlParts['select']); @@ -1346,7 +1344,7 @@ class QueryBuilder * * @throws QueryException */ - private function getFromClauses() + private function getFromClauses(): array { $fromClauses = []; $knownAliases = []; @@ -1385,20 +1383,15 @@ class QueryBuilder } } - /** - * @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 +1400,8 @@ class QueryBuilder /** * 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 +1413,8 @@ class QueryBuilder /** * 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,7 +1437,7 @@ class QueryBuilder /** * 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 @@ -1517,11 +1506,9 @@ class QueryBuilder * @param string $fromAlias * @param array<string,true> $knownAliases * - * @return string - * * @throws QueryException */ - private function getSQLForJoins($fromAlias, array &$knownAliases) + private function getSQLForJoins($fromAlias, array &$knownAliases): string { $sql = ''; diff --git a/doctrine/dbal/src/Result.php b/doctrine/dbal/src/Result.php index 59c1065a..dce821c6 100644 --- a/doctrine/dbal/src/Result.php +++ b/doctrine/dbal/src/Result.php @@ -22,7 +22,7 @@ class Result 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) { @@ -170,12 +170,8 @@ class Result */ 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 +182,8 @@ class Result */ 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,12 +223,8 @@ class Result */ 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; } } @@ -287,11 +275,13 @@ class Result * @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 +305,13 @@ class Result * @deprecated This API is deprecated and will be removed after 2022 * * @return list<mixed> + * + * @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/Parser.php b/doctrine/dbal/src/SQL/Parser.php index f1ad6617..b3747e0c 100644 --- a/doctrine/dbal/src/SQL/Parser.php +++ b/doctrine/dbal/src/SQL/Parser.php @@ -70,7 +70,7 @@ final class Parser 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 e6b383a6..69e33760 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 @@ abstract class AbstractAsset * 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 50b9face..e44580b5 100644 --- a/doctrine/dbal/src/Schema/AbstractSchemaManager.php +++ b/doctrine/dbal/src/Schema/AbstractSchemaManager.php @@ -28,6 +28,8 @@ use function strtolower; /** * 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 +43,13 @@ 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,7 +59,7 @@ abstract class AbstractSchemaManager /** * Returns the associated platform. * - * @return AbstractPlatform + * @return T */ public function getDatabasePlatform() { @@ -71,10 +76,18 @@ abstract class AbstractSchemaManager * $result = $sm->tryMethod('dropView', 'view_name'); * </code> * + * @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 +122,7 @@ abstract class AbstractSchemaManager /** * Returns a list of all namespaces in the current database. * - * @deprecated Use {@link listSchemaNames()} instead. + * @deprecated Use {@see listSchemaNames()} instead. * * @return string[] * @@ -419,6 +432,8 @@ abstract class AbstractSchemaManager /** * 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 @@ -460,6 +475,16 @@ abstract class AbstractSchemaManager } /** + * Drops the unique constraint from the given table. + * + * @throws Exception + */ + public function dropUniqueConstraint(string $name, string $tableName): void + { + $this->_execSql($this->_platform->getDropUniqueConstraintSQL($name, $tableName)); + } + + /** * Drops a view. * * @param string $name The name of the view. @@ -519,6 +544,8 @@ abstract class AbstractSchemaManager /** * Creates a constraint on a table. * + * @deprecated Use {@see createIndex()}, {@see createForeignKey()} or {@see createUniqueConstraint()} instead. + * * @param Table|string $table * * @return void @@ -560,6 +587,16 @@ abstract class AbstractSchemaManager } /** + * Creates a unique constraint on a table. + * + * @throws Exception + */ + public function createUniqueConstraint(UniqueConstraint $uniqueConstraint, string $tableName): void + { + $this->_execSql($this->_platform->getCreateUniqueConstraintSQL($uniqueConstraint, $tableName)); + } + + /** * Creates a new view. * * @return void @@ -576,6 +613,10 @@ abstract class AbstractSchemaManager /** * 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 +628,16 @@ abstract class AbstractSchemaManager */ 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 +645,8 @@ abstract class AbstractSchemaManager /** * 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 +655,13 @@ abstract class AbstractSchemaManager */ 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 +669,8 @@ abstract class AbstractSchemaManager /** * 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 +681,13 @@ abstract class AbstractSchemaManager */ 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 +695,21 @@ abstract class AbstractSchemaManager /** * 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 +717,21 @@ abstract class AbstractSchemaManager /** * 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 +739,8 @@ abstract class AbstractSchemaManager /** * 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 +749,13 @@ abstract class AbstractSchemaManager */ 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 +763,48 @@ abstract class AbstractSchemaManager /** * 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($schemaDiff->toSql($this->_platform)); + } + + /** + * Migrates an existing schema to a new schema. + * + * @throws Exception + */ + public function migrateSchema(Schema $toSchema): void + { + $schemaDiff = $this->createComparator() + ->compareSchemas($this->createSchema(), $toSchema); + + $this->alterSchema($schemaDiff); + } + /* alterTable() Methods */ /** @@ -733,7 +861,7 @@ abstract class AbstractSchemaManager /** * 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<int, array<string, mixed>> $namespaces The list of namespace names * in the native DBMS data definition. @@ -771,7 +899,7 @@ abstract class AbstractSchemaManager /** * 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<string, mixed> $namespace The native DBMS namespace definition. * @@ -790,37 +918,6 @@ abstract class AbstractSchemaManager } /** - * @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 * * @return Sequence[] @@ -1175,12 +1272,20 @@ abstract class AbstractSchemaManager * 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 +1299,8 @@ abstract class AbstractSchemaManager * 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 +1316,8 @@ abstract class AbstractSchemaManager } /** + * @internal This method should be only used from within the AbstractSchemaManager class hierarchy. + * * @param string|null $comment * @param string|null $type * @@ -1222,4 +1331,9 @@ abstract class AbstractSchemaManager return str_replace('(DC2Type:' . $type . ')', '', $comment); } + + public function createComparator(): Comparator + { + return new Comparator($this->getDatabasePlatform()); + } } diff --git a/doctrine/dbal/src/Schema/ColumnDiff.php b/doctrine/dbal/src/Schema/ColumnDiff.php index c9c2a52b..83402fd0 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; /** @@ -31,6 +33,15 @@ class ColumnDiff 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; diff --git a/doctrine/dbal/src/Schema/Comparator.php b/doctrine/dbal/src/Schema/Comparator.php index 7ddf5238..7791b29b 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,38 +17,76 @@ use function array_unique; 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 { + /** @var AbstractPlatform|null */ + private $platform; + /** - * @return SchemaDiff - * - * @throws SchemaException + * @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/4659', + 'Not passing a $platform to %s is deprecated.' + . ' Use AbstractSchemaManager::createComparator() to instantiate the comparator.', + __METHOD__ + ); + } + + $this->platform = $platform; + } + + /** + * @param list<mixed> $args + */ + public function __call(string $method, array $args): SchemaDiff + { + if ($method !== 'compareSchemas') { + throw new BadMethodCallException(sprintf('Unknown method "%s"', $method)); + } + + return $this->doCompareSchemas(...$args); + } + + /** + * @param list<mixed> $args */ - public static function compareSchemas(Schema $fromSchema, Schema $toSchema) + public static function __callStatic(string $method, array $args): SchemaDiff { - $c = new self(); + if ($method !== 'compareSchemas') { + throw new BadMethodCallException(sprintf('Unknown method "%s"', $method)); + } - return $c->compare($fromSchema, $toSchema); + $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) - { + private function doCompareSchemas( + Schema $fromSchema, + Schema $toSchema + ) { $diff = new SchemaDiff(); $diff->fromSchema = $fromSchema; @@ -162,12 +204,28 @@ class Comparator } /** + * @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)) { @@ -197,7 +255,7 @@ class Comparator * * @return TableDiff|false * - * @throws SchemaException + * @throws Exception */ public function diffTable(Table $fromTable, Table $toTable) { @@ -227,17 +285,26 @@ class Comparator 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); + $tableDifferences->changedColumns[$column->getName()] = new ColumnDiff( + $column->getName(), + $toColumn, + $changedProperties, + $column + ); - $columnDiff->fromColumn = $column; - $tableDifferences->changedColumns[$column->getName()] = $columnDiff; $changes++; } @@ -315,15 +382,13 @@ class Comparator /** * 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 */ - private function detectColumnRenamings(TableDiff $tableDifferences) + private function detectColumnRenamings(TableDiff $tableDifferences): void { $renameCandidates = []; foreach ($tableDifferences->addedColumns as $addedColumnName => $addedColumn) { foreach ($tableDifferences->removedColumns as $removedColumn) { - if (count($this->diffColumn($addedColumn, $removedColumn)) !== 0) { + if (! $this->columnsEqual($addedColumn, $removedColumn)) { continue; } @@ -337,7 +402,7 @@ class Comparator } [$removedColumn, $addedColumn] = $candidateColumns[0]; - $removedColumnName = strtolower($removedColumn->getName()); + $removedColumnName = $removedColumn->getName(); $addedColumnName = strtolower($addedColumn->getName()); if (isset($tableDifferences->renamedColumns[$removedColumnName])) { @@ -347,7 +412,7 @@ class Comparator $tableDifferences->renamedColumns[$removedColumnName] = $addedColumn; unset( $tableDifferences->addedColumns[$addedColumnName], - $tableDifferences->removedColumns[$removedColumnName] + $tableDifferences->removedColumns[strtolower($removedColumnName)] ); } } @@ -355,10 +420,8 @@ class Comparator /** * 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 */ - private function detectIndexRenamings(TableDiff $tableDifferences) + private function detectIndexRenamings(TableDiff $tableDifferences): void { $renameCandidates = []; @@ -430,10 +493,24 @@ class Comparator } /** + * Compares the definitions of the given columns + * + * @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. * * @return string[] */ diff --git a/doctrine/dbal/src/Schema/Constraint.php b/doctrine/dbal/src/Schema/Constraint.php index 65e239ec..85338c78 100644 --- a/doctrine/dbal/src/Schema/Constraint.php +++ b/doctrine/dbal/src/Schema/Constraint.php @@ -6,6 +6,8 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; /** * Marker interface for constraints. + * + * @deprecated Use {@see ForeignKeyConstraint}, {@see Index} or {@see UniqueConstraint} instead. */ interface Constraint { diff --git a/doctrine/dbal/src/Schema/DB2SchemaManager.php b/doctrine/dbal/src/Schema/DB2SchemaManager.php index 82c287e0..e06baf36 100644 --- a/doctrine/dbal/src/Schema/DB2SchemaManager.php +++ b/doctrine/dbal/src/Schema/DB2SchemaManager.php @@ -8,7 +8,6 @@ use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; use function array_change_key_case; -use function assert; use function preg_match; use function str_replace; use function strpos; @@ -19,6 +18,8 @@ use const CASE_LOWER; /** * IBM Db2 Schema Manager. + * + * @extends AbstractSchemaManager<DB2Platform> */ class DB2SchemaManager extends AbstractSchemaManager { @@ -235,9 +236,7 @@ class DB2SchemaManager extends AbstractSchemaManager { $table = parent::listTableDetails($name); - $platform = $this->_platform; - assert($platform instanceof DB2Platform); - $sql = $platform->getListTableCommentsSQL($name); + $sql = $this->_platform->getListTableCommentsSQL($name); $tableOptions = $this->_conn->fetchAssociative($sql); diff --git a/doctrine/dbal/src/Schema/ForeignKeyConstraint.php b/doctrine/dbal/src/Schema/ForeignKeyConstraint.php index 97618875..852ad0a3 100644 --- a/doctrine/dbal/src/Schema/ForeignKeyConstraint.php +++ b/doctrine/dbal/src/Schema/ForeignKeyConstraint.php @@ -105,6 +105,8 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint * Returns the name of the referencing table * the foreign key constraint is associated with. * + * @deprecated Use the table that contains the foreign key as part of its {@see Table::$_fkConstraints} instead. + * * @return string */ public function getLocalTableName() @@ -116,6 +118,8 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint * Sets the Table instance of the referencing table * the foreign key constraint is associated with. * + * @deprecated Use the table that contains the foreign key as part of its {@see Table::$_fkConstraints} instead. + * * @param Table $table Instance of the referencing table. * * @return void @@ -126,6 +130,8 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint } /** + * @deprecated Use the table that contains the foreign key as part of its {@see Table::$_fkConstraints} instead. + * * @return Table */ public function getLocalTable() @@ -190,6 +196,8 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint /** * {@inheritdoc} * + * @deprecated Use {@see getLocalColumns()} instead. + * * @see getLocalColumns */ public function getColumns() @@ -205,6 +213,8 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint * is a keyword reserved by the platform. * Otherwise the plain unquoted value as inserted is returned. * + * @deprecated Use {@see getQuotedLocalColumns()} instead. + * * @see getQuotedLocalColumns * * @param AbstractPlatform $platform The platform to use for quotation. @@ -357,10 +367,8 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint * on the referenced table the foreign key constraint is associated with. * * @param string $event Name of the database operation/event to return the referential action for. - * - * @return string|null */ - private function onEvent($event) + private function onEvent($event): ?string { if (isset($this->_options[$event])) { $onEvent = strtoupper($this->_options[$event]); diff --git a/doctrine/dbal/src/Schema/Index.php b/doctrine/dbal/src/Schema/Index.php index 508aa403..fd088a30 100644 --- a/doctrine/dbal/src/Schema/Index.php +++ b/doctrine/dbal/src/Schema/Index.php @@ -336,10 +336,8 @@ class Index extends AbstractAsset implements Constraint /** * 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 4d54f715..7c175bb6 100644 --- a/doctrine/dbal/src/Schema/MySQLSchemaManager.php +++ b/doctrine/dbal/src/Schema/MySQLSchemaManager.php @@ -2,13 +2,13 @@ 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\Types\Type; use function array_change_key_case; use function array_shift; -use function array_values; use function assert; use function explode; use function is_string; @@ -22,6 +22,8 @@ use const CASE_LOWER; /** * Schema manager for the MySQL RDBMS. + * + * @extends AbstractSchemaManager<AbstractMySQLPlatform> */ class MySQLSchemaManager extends AbstractSchemaManager { @@ -166,27 +168,27 @@ class MySQLSchemaManager extends AbstractSchemaManager 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': @@ -312,9 +314,9 @@ class MySQLSchemaManager extends AbstractSchemaManager $result = []; foreach ($list as $constraint) { $result[] = new ForeignKeyConstraint( - array_values($constraint['local']), + $constraint['local'], $constraint['foreignTable'], - array_values($constraint['foreign']), + $constraint['foreign'], $constraint['name'], [ 'onDelete' => $constraint['onDelete'], @@ -333,9 +335,7 @@ class MySQLSchemaManager extends AbstractSchemaManager { $table = parent::listTableDetails($name); - $platform = $this->_platform; - assert($platform instanceof MySQLPlatform); - $sql = $platform->getListTableMetadataSQL($name); + $sql = $this->_platform->getListTableMetadataSQL($name); $tableOptions = $this->_conn->fetchAssociative($sql); @@ -349,6 +349,8 @@ class MySQLSchemaManager extends AbstractSchemaManager $table->addOption('collation', $tableOptions['TABLE_COLLATION']); } + $table->addOption('charset', $tableOptions['CHARACTER_SET_NAME']); + if ($tableOptions['AUTO_INCREMENT'] !== null) { $table->addOption('autoincrement', $tableOptions['AUTO_INCREMENT']); } @@ -359,6 +361,11 @@ class MySQLSchemaManager extends AbstractSchemaManager return $table; } + public function createComparator(): Comparator + { + return new MySQL\Comparator($this->getDatabasePlatform()); + } + /** * @return string[]|true[] */ diff --git a/doctrine/dbal/src/Schema/OracleSchemaManager.php b/doctrine/dbal/src/Schema/OracleSchemaManager.php index 13c88524..762492e7 100644 --- a/doctrine/dbal/src/Schema/OracleSchemaManager.php +++ b/doctrine/dbal/src/Schema/OracleSchemaManager.php @@ -8,7 +8,6 @@ use Doctrine\DBAL\Types\Type; use function array_change_key_case; use function array_values; -use function assert; use function is_string; use function preg_match; use function str_replace; @@ -20,6 +19,8 @@ use const CASE_LOWER; /** * Oracle Schema Manager. + * + * @extends AbstractSchemaManager<OraclePlatform> */ class OracleSchemaManager extends AbstractSchemaManager { @@ -257,7 +258,7 @@ class OracleSchemaManager extends AbstractSchemaManager */ public function createDatabase($database) { - $statement = 'CREATE USER ' . $database; + $statement = $this->_platform->getCreateDatabaseSQL($database); $params = $this->_conn->getParams(); @@ -282,8 +283,6 @@ class OracleSchemaManager extends AbstractSchemaManager */ public function dropAutoincrement($table) { - assert($this->_platform instanceof OraclePlatform); - $sql = $this->_platform->getDropAutoincrementSql($table); foreach ($sql as $query) { $this->_conn->executeStatement($query); @@ -309,10 +308,8 @@ class OracleSchemaManager extends AbstractSchemaManager * 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); @@ -328,14 +325,13 @@ class OracleSchemaManager extends AbstractSchemaManager { $table = parent::listTableDetails($name); - $platform = $this->_platform; - assert($platform instanceof OraclePlatform); - $sql = $platform->getListTableCommentsSQL($name); + $sql = $this->_platform->getListTableCommentsSQL($name); $tableOptions = $this->_conn->fetchAssociative($sql); if ($tableOptions !== false) { - $table->addOption('comment', $tableOptions['COMMENTS']); + $tableOptions = array_change_key_case($tableOptions, CASE_LOWER); + $table->addOption('comment', $tableOptions['comments']); } return $table; diff --git a/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php b/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php index abc06af4..03239bde 100644 --- a/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php +++ b/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php @@ -3,7 +3,7 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Exception; -use Doctrine\DBAL\Platforms\PostgreSQL94Platform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; use Doctrine\Deprecations\Deprecation; @@ -29,6 +29,8 @@ use const CASE_LOWER; /** * PostgreSQL Schema Manager. + * + * @extends AbstractSchemaManager<PostgreSQLPlatform> */ class PostgreSQLSchemaManager extends AbstractSchemaManager { @@ -38,7 +40,7 @@ class PostgreSQLSchemaManager extends AbstractSchemaManager /** * Gets all the existing schema names. * - * @deprecated Use {@link listSchemaNames()} instead. + * @deprecated Use {@see listSchemaNames()} instead. * * @return string[] * @@ -73,9 +75,17 @@ 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 +108,8 @@ SQL * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. * * @return string[] + * + * @throws Exception */ public function getExistingSchemaSearchPaths() { @@ -111,6 +123,20 @@ SQL } /** + * 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. * * This is a PostgreSQL only function. @@ -118,6 +144,8 @@ SQL * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. * * @return void + * + * @throws Exception */ public function determineExistingSchemaSearchPaths() { @@ -178,14 +206,6 @@ SQL /** * {@inheritdoc} */ - protected function _getPortableTriggerDefinition($trigger) - { - return $trigger['trigger_name']; - } - - /** - * {@inheritdoc} - */ protected function _getPortableViewDefinition($view) { return new View($view['schemaname'] . '.' . $view['viewname'], $view['definition']); @@ -207,10 +227,9 @@ SQL */ 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']; } @@ -293,7 +312,7 @@ SQL /** * {@inheritdoc} * - * @deprecated Use {@link listSchemaNames()} instead. + * @deprecated Use {@see listSchemaNames()} instead. */ protected function getPortableNamespaceDefinition(array $namespace) { @@ -458,7 +477,7 @@ SQL if ( preg_match( - '([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', + '([A-Za-z]+\(([0-9]+),([0-9]+)\))', $tableColumn['complete_type'], $match ) === 1 @@ -552,9 +571,7 @@ SQL { $table = parent::listTableDetails($name); - $platform = $this->_platform; - assert($platform instanceof PostgreSQL94Platform); - $sql = $platform->getListTableMetadataSQL($name); + $sql = $this->_platform->getListTableMetadataSQL($name); $tableOptions = $this->_conn->fetchAssociative($sql); diff --git a/doctrine/dbal/src/Schema/SQLServerSchemaManager.php b/doctrine/dbal/src/Schema/SQLServerSchemaManager.php index f1836fc5..8a8713f0 100644 --- a/doctrine/dbal/src/Schema/SQLServerSchemaManager.php +++ b/doctrine/dbal/src/Schema/SQLServerSchemaManager.php @@ -3,7 +3,8 @@ 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\Types\Type; use Doctrine\Deprecations\Deprecation; @@ -18,9 +19,14 @@ use function strtok; /** * SQL Server Schema Manager. + * + * @extends AbstractSchemaManager<SQLServerPlatform> */ class SQLServerSchemaManager extends AbstractSchemaManager { + /** @var string|null */ + private $databaseCollation; + /** * {@inheritDoc} */ @@ -221,7 +227,7 @@ SQL /** * {@inheritdoc} * - * @deprecated Use {@link listSchemaNames()} instead. + * @deprecated Use {@see listSchemaNames()} instead. */ protected function getPortableNamespaceDefinition(array $namespace) { @@ -241,7 +247,7 @@ SQL protected function _getPortableViewDefinition($view) { // @todo - return new View($view['name'], ''); + return new View($view['name'], $view['definition']); } /** @@ -271,13 +277,12 @@ SQL { 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 ($this->getColumnConstraints($tableDiff->name, $col->getName()) as $constraint) { $this->_conn->executeStatement( sprintf( 'ALTER TABLE %s DROP CONSTRAINT %s', $tableDiff->name, - $constraint['Name'] + $constraint ) ); } @@ -288,22 +293,32 @@ SQL } /** - * 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<string> * - * @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] + ); } /** @@ -315,9 +330,7 @@ SQL { $table = parent::listTableDetails($name); - $platform = $this->_platform; - assert($platform instanceof SQLServer2012Platform); - $sql = $platform->getListTableMetadataSQL($name); + $sql = $this->_platform->getListTableMetadataSQL($name); $tableOptions = $this->_conn->fetchAssociative($sql); @@ -327,4 +340,32 @@ SQL return $table; } + + /** + * @throws Exception + */ + public function createComparator(): Comparator + { + return new SQLServer\Comparator($this->getDatabasePlatform(), $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; + } } diff --git a/doctrine/dbal/src/Schema/Schema.php b/doctrine/dbal/src/Schema/Schema.php index 08eb2683..9be9e350 100644 --- a/doctrine/dbal/src/Schema/Schema.php +++ b/doctrine/dbal/src/Schema/Schema.php @@ -7,6 +7,7 @@ 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\Deprecations\Deprecation; use function array_keys; use function strpos; @@ -88,10 +89,18 @@ class Schema extends AbstractAsset } /** + * @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 +112,7 @@ class Schema extends AbstractAsset 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 +138,7 @@ class Schema extends AbstractAsset 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); @@ -185,10 +194,8 @@ class Schema extends AbstractAsset /** * @param string $name - * - * @return string */ - private function getFullQualifiedAssetName($name) + private function getFullQualifiedAssetName($name): string { $name = $this->getUnquotedAssetName($name); @@ -199,14 +206,17 @@ class Schema extends AbstractAsset 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 +256,20 @@ class Schema extends AbstractAsset /** * 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); } @@ -431,27 +451,29 @@ class Schema extends AbstractAsset } /** + * @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); } diff --git a/doctrine/dbal/src/Schema/SchemaConfig.php b/doctrine/dbal/src/Schema/SchemaConfig.php index 56d49c4a..92e0701f 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; } diff --git a/doctrine/dbal/src/Schema/SchemaDiff.php b/doctrine/dbal/src/Schema/SchemaDiff.php index 67fb9bb9..50d0d12f 100644 --- a/doctrine/dbal/src/Schema/SchemaDiff.php +++ b/doctrine/dbal/src/Schema/SchemaDiff.php @@ -7,7 +7,10 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use function array_merge; /** - * Schema Diff. + * Differences between two schemas. + * + * The object contains the operations to change the schema stored in $fromSchema + * to a target schema. */ class SchemaDiff { diff --git a/doctrine/dbal/src/Schema/SqliteSchemaManager.php b/doctrine/dbal/src/Schema/SqliteSchemaManager.php index 140ebb3c..3e0e5cfe 100644 --- a/doctrine/dbal/src/Schema/SqliteSchemaManager.php +++ b/doctrine/dbal/src/Schema/SqliteSchemaManager.php @@ -4,15 +4,17 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\SQLite; +use Doctrine\DBAL\Platforms\SqlitePlatform; 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 explode; use function file_exists; use function preg_match; @@ -20,7 +22,6 @@ 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 strpos; use function strtolower; @@ -32,14 +33,24 @@ use const CASE_LOWER; /** * Sqlite SchemaManager. + * + * @extends AbstractSchemaManager<SqlitePlatform> */ class SqliteSchemaManager extends AbstractSchemaManager { /** * {@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 +60,18 @@ class SqliteSchemaManager extends AbstractSchemaManager /** * {@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; @@ -86,9 +106,18 @@ class SqliteSchemaManager extends AbstractSchemaManager /** * {@inheritdoc} + * + * @deprecated Use {@see dropForeignKey()} and {@see createForeignKey()} instead. */ public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4897', + 'SqliteSchemaManager::dropAndCreateForeignKey() is deprecated.' + . ' Use SqliteSchemaManager::dropForeignKey() and SqliteSchemaManager::createForeignKey() instead.' + ); + $tableDiff = $this->getTableDiffForAlterForeignKey($table); $tableDiff->changedForeignKeys[] = $foreignKey; @@ -126,7 +155,7 @@ class SqliteSchemaManager extends AbstractSchemaManager '# (?:CONSTRAINT\s+([^\s]+)\s+)? (?:FOREIGN\s+KEY[^\)]+\)\s*)? - REFERENCES\s+[^\s]+\s+(?:\([^\)]+\))? + REFERENCES\s+[^\s]+\s+(?:\([^)]+\))? (?: [^,]*? (NOT\s+DEFERRABLE|DEFERRABLE) @@ -175,10 +204,7 @@ class SqliteSchemaManager extends AbstractSchemaManager $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, @@ -221,10 +247,7 @@ class SqliteSchemaManager extends AbstractSchemaManager $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']; @@ -431,9 +454,9 @@ class SqliteSchemaManager extends AbstractSchemaManager $result = []; foreach ($list as $constraint) { $result[] = new ForeignKeyConstraint( - array_values($constraint['local']), + $constraint['local'], $constraint['foreignTable'], - array_values($constraint['foreign']), + $constraint['foreign'], $constraint['name'], [ 'onDelete' => $constraint['onDelete'], @@ -450,22 +473,12 @@ class SqliteSchemaManager extends AbstractSchemaManager /** * @param Table|string $table * - * @return TableDiff - * * @throws Exception */ - private function getTableDiffForAlterForeignKey($table) + private function getTableDiffForAlterForeignKey($table): TableDiff { 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; + $table = $this->listTableDetails($table); } $tableDiff = new TableDiff($table->getName()); @@ -569,11 +582,24 @@ SQL return $table; } + public function createComparator(): Comparator + { + return new SQLite\Comparator($this->getDatabasePlatform()); + } + /** * {@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 []; } diff --git a/doctrine/dbal/src/Schema/Table.php b/doctrine/dbal/src/Schema/Table.php index d330d03f..4f591eb6 100644 --- a/doctrine/dbal/src/Schema/Table.php +++ b/doctrine/dbal/src/Schema/Table.php @@ -314,8 +314,6 @@ class Table extends AbstractAsset * @param string[] $flags * @param mixed[] $options * - * @return Index - * * @throws SchemaException */ private function _createIndex( @@ -325,7 +323,7 @@ class Table extends AbstractAsset $isPrimary, array $flags = [], array $options = [] - ) { + ): Index { if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { throw SchemaException::indexNameInvalid($indexName); } diff --git a/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php b/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php index c08fb6fe..730dff77 100644 --- a/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php +++ b/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php @@ -91,8 +91,8 @@ class CreateSchemaSqlCollector extends AbstractVisitor { return array_merge( $this->createNamespaceQueries, - $this->createTableQueries, $this->createSequenceQueries, + $this->createTableQueries, $this->createFkConstraintQueries ); } diff --git a/doctrine/dbal/src/Schema/Visitor/Graphviz.php b/doctrine/dbal/src/Schema/Visitor/Graphviz.php index 299ff0e7..5af4678f 100644 --- a/doctrine/dbal/src/Schema/Visitor/Graphviz.php +++ b/doctrine/dbal/src/Schema/Visitor/Graphviz.php @@ -13,6 +13,8 @@ use function strtolower; /** * Create a Graphviz output of a Schema. + * + * @deprecated */ class Graphviz extends AbstractVisitor { @@ -62,10 +64,7 @@ class Graphviz extends AbstractVisitor ); } - /** - * @return string - */ - private function createTableLabel(Table $table) + private function createTableLabel(Table $table): string { // Start the table $label = '<<TABLE CELLSPACING="0" BORDER="1" ALIGN="LEFT">'; @@ -107,10 +106,8 @@ class Graphviz extends AbstractVisitor /** * @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 +123,8 @@ class Graphviz extends AbstractVisitor * @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/Statement.php b/doctrine/dbal/src/Statement.php index b7183fa3..782feee2 100644 --- a/doctrine/dbal/src/Statement.php +++ b/doctrine/dbal/src/Statement.php @@ -2,8 +2,6 @@ namespace Doctrine\DBAL; -use Doctrine\DBAL\Driver\Exception; -use Doctrine\DBAL\Driver\Statement as DriverStatement; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; use Doctrine\Deprecations\Deprecation; @@ -40,7 +38,7 @@ class Statement /** * The underlying driver statement. * - * @var DriverStatement + * @var Driver\Statement */ protected $stmt; @@ -61,26 +59,19 @@ class Statement /** * Creates a new <tt>Statement</tt> for the given SQL and <tt>Connection</tt>. * - * @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 @@ class Statement try { return $this->stmt->bindValue($param, $value, $bindingType); - } catch (Exception $e) { + } catch (Driver\Exception $e) { throw $this->conn->convertException($e); } } @@ -153,7 +144,7 @@ class Statement } return $this->stmt->bindParam($param, $variable, $type); - } catch (Exception $e) { + } catch (Driver\Exception $e) { throw $this->conn->convertException($e); } } @@ -189,7 +180,7 @@ class Statement $this->stmt->execute($params), $this->conn ); - } catch (Exception $ex) { + } catch (Driver\Exception $ex) { throw $this->conn->convertExceptionDuringQuery($ex, $this->sql, $this->params, $this->types); } finally { if ($logger !== null) { @@ -233,7 +224,7 @@ class Statement /** * Gets the wrapped driver statement. * - * @return DriverStatement + * @return Driver\Statement */ public function getWrappedStatement() { diff --git a/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php b/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php index 875f2a20..a9137206 100644 --- a/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php +++ b/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php @@ -5,19 +5,19 @@ namespace Doctrine\DBAL\Tools\Console\Command; 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; /** @@ -44,7 +44,7 @@ class RunSqlCommand extends Command ->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(<<<EOT @@ -66,6 +66,7 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { $conn = $this->getConnection($input); + $io = new SymfonyStyle($input, $output); $sql = $input->getArgument('sql'); @@ -75,23 +76,19 @@ EOT 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 +103,27 @@ EOT 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/ConsoleRunner.php b/doctrine/dbal/src/Tools/Console/ConsoleRunner.php index 1cc315de..823a1e80 100644 --- a/doctrine/dbal/src/Tools/Console/ConsoleRunner.php +++ b/doctrine/dbal/src/Tools/Console/ConsoleRunner.php @@ -2,13 +2,15 @@ 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. */ @@ -25,7 +27,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); @@ -47,6 +52,8 @@ class ConsoleRunner /** * 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 23dc7a05..00000000 --- a/doctrine/dbal/src/Tools/Dumper.php +++ /dev/null @@ -1,181 +0,0 @@ -<?php - -namespace Doctrine\DBAL\Tools; - -use ArrayIterator; -use ArrayObject; -use DateTimeInterface; -use Doctrine\Common\Collections\Collection; -use Doctrine\Common\Persistence\Proxy; -use stdClass; - -use function array_keys; -use function assert; -use function class_exists; -use function count; -use function end; -use function explode; -use function extension_loaded; -use function get_class; -use function html_entity_decode; -use function ini_set; -use function is_array; -use function is_object; -use function is_string; -use function ob_get_clean; -use function ob_start; -use function strip_tags; -use function strlen; -use function strrpos; -use function substr; -use function var_dump; - -/** - * Static class used to dump the variable to be used on output. - * Simplified port of Util\Debug from doctrine/common. - * - * @internal - */ -final class Dumper -{ - /** - * Private constructor (prevents instantiation). - * - * @codeCoverageIgnore - */ - private function __construct() - { - } - - /** - * Returns a dump of the public, protected and private properties of $var. - * - * @link https://xdebug.org/ - * - * @param mixed $var The variable to dump. - * @param int $maxDepth The maximum nesting level for object properties. - */ - public static function dump($var, int $maxDepth = 2): string - { - $html = ini_set('html_errors', '1'); - assert(is_string($html)); - - if (extension_loaded('xdebug')) { - ini_set('xdebug.var_display_max_depth', (string) $maxDepth); - } - - $var = self::export($var, $maxDepth); - - ob_start(); - var_dump($var); - - try { - $output = ob_get_clean(); - assert(is_string($output)); - - return strip_tags(html_entity_decode($output)); - } finally { - ini_set('html_errors', $html); - } - } - - /** - * @param mixed $var - * - * @return mixed - */ - public static function export($var, int $maxDepth) - { - $isObj = is_object($var); - - if ($var instanceof Collection) { - $var = $var->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/Types/AsciiStringType.php b/doctrine/dbal/src/Types/AsciiStringType.php index e7975748..ab1e0e06 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 f6e4df3b..68a80ef3 100644 --- a/doctrine/dbal/src/Types/BooleanType.php +++ b/doctrine/dbal/src/Types/BooleanType.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\DB2Platform; /** * Type that maps an SQL boolean to a PHP boolean. @@ -49,4 +50,14 @@ class BooleanType extends Type { return ParameterType::BOOLEAN; } + + /** + * @return bool + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + // 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 f1a18742..278734b9 100644 --- a/doctrine/dbal/src/Types/ConversionException.php +++ b/doctrine/dbal/src/Types/ConversionException.php @@ -5,6 +5,8 @@ namespace Doctrine\DBAL\Types; 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 @@ class ConversionException extends Exception * 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 * @@ -98,7 +100,7 @@ class ConversionException extends Exception * * @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); @@ -107,7 +109,7 @@ class ConversionException extends Exception $actualType, $format, $error - )); + ), 0, func_num_args() >= 4 ? func_get_arg(3) : null); } public static function conversionFailedUnserialization(string $format, string $error): self diff --git a/doctrine/dbal/src/Types/DecimalType.php b/doctrine/dbal/src/Types/DecimalType.php index f75d3db2..c70067f2 100644 --- a/doctrine/dbal/src/Types/DecimalType.php +++ b/doctrine/dbal/src/Types/DecimalType.php @@ -5,6 +5,7 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; use function is_float; +use function is_int; use const PHP_VERSION_ID; @@ -34,9 +35,9 @@ class DecimalType extends Type */ 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 && (is_float($value) || is_int($value))) { return (string) $value; } diff --git a/doctrine/dbal/src/Types/JsonType.php b/doctrine/dbal/src/Types/JsonType.php index d17b7f51..d28b8b79 100644 --- a/doctrine/dbal/src/Types/JsonType.php +++ b/doctrine/dbal/src/Types/JsonType.php @@ -3,15 +3,15 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +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 +35,11 @@ class JsonType extends Type 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 +55,11 @@ class JsonType extends Type $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; } /** diff --git a/doctrine/dbal/src/Types/Type.php b/doctrine/dbal/src/Types/Type.php index 03e5b824..c2ae2be5 100644 --- a/doctrine/dbal/src/Types/Type.php +++ b/doctrine/dbal/src/Types/Type.php @@ -12,7 +12,7 @@ use function get_class; /** * 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 { @@ -189,7 +189,7 @@ abstract class Type * 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 +202,7 @@ abstract class Type * Gets the types array map which holds all registered types and the corresponding * type class * - * @return string[] + * @return array<string, string> */ public static function getTypesMap() { @@ -218,10 +218,13 @@ abstract class Type * 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() diff --git a/doctrine/dbal/src/VersionAwarePlatformDriver.php b/doctrine/dbal/src/VersionAwarePlatformDriver.php index b3ec8b84..ffcfcd63 100644 --- a/doctrine/dbal/src/VersionAwarePlatformDriver.php +++ b/doctrine/dbal/src/VersionAwarePlatformDriver.php @@ -11,6 +11,8 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; * 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 2ee7dedc..00000000 --- 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 index a90b0792..156905cd 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 d6682221..22f0cced 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 5cc7ac13..c79e38cd 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 4e0cc21f..f115e43d 100644 --- a/doctrine/deprecations/phpcs.xml +++ b/doctrine/deprecations/phpcs.xml @@ -9,6 +9,8 @@ <!-- Ignore warnings, show progress of the run and show sniff names --> <arg value="nps"/> + <config name="php_version" value="70100"/> + <!-- Directories to be checked --> <file>lib</file> <file>tests</file> diff --git a/doctrine/deprecations/phpunit.xml.dist b/doctrine/deprecations/phpunit.xml.dist deleted file mode 100644 index 4740c060..00000000 --- a/doctrine/deprecations/phpunit.xml.dist +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" ?> -<phpunit bootstrap="vendor/autoload.php"> - <testsuites> - <testsuite name="Doctrine Deprecations"> - <directory>tests</directory> - </testsuite> - </testsuites> -</phpunit> diff --git a/doctrine/deprecations/test_fixtures/src/Foo.php b/doctrine/deprecations/test_fixtures/src/Foo.php deleted file mode 100644 index c4b8ebec..00000000 --- a/doctrine/deprecations/test_fixtures/src/Foo.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace DeprecationTests; - -use Doctrine\Foo\Bar; - -class Foo -{ - public static function triggerDependencyWithDeprecation(): void - { - $bar = new Bar(); - $bar->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 feccd486..00000000 --- a/doctrine/deprecations/test_fixtures/src/RootDeprecation.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -namespace DeprecationTests; - -use Doctrine\Deprecations\Deprecation; - -class RootDeprecation -{ - public static function run() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/deprecations/4444', - 'this is deprecated %s %d', - 'foo', - 1234 - ); - - } -} diff --git a/psr/cache/LICENSE.txt b/psr/cache/LICENSE.txt new file mode 100644 index 00000000..b1c2c97b --- /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 00000000..c8706cee --- /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 00000000..e828fec9 --- /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 00000000..e27f22f8 --- /dev/null +++ b/psr/cache/src/CacheException.php @@ -0,0 +1,10 @@ +<?php + +namespace Psr\Cache; + +/** + * Exception interface for all exceptions thrown by an Implementing Library. + */ +interface CacheException +{ +} diff --git a/psr/cache/src/CacheItemInterface.php b/psr/cache/src/CacheItemInterface.php new file mode 100644 index 00000000..63d05dd1 --- /dev/null +++ b/psr/cache/src/CacheItemInterface.php @@ -0,0 +1,105 @@ +<?php + +namespace Psr\Cache; + +/** + * CacheItemInterface defines an interface for interacting with objects inside a cache. + * + * Each Item object MUST be associated with a specific key, which can be set + * according to the implementing system and is typically passed by the + * Cache\CacheItemPoolInterface object. + * + * The Cache\CacheItemInterface object encapsulates the storage and retrieval of + * cache items. Each Cache\CacheItemInterface is generated by a + * Cache\CacheItemPoolInterface object, which is responsible for any required + * setup as well as associating the object with a unique Key. + * Cache\CacheItemInterface objects MUST be able to store and retrieve any type + * of PHP value defined in the Data section of the specification. + * + * Calling Libraries MUST NOT instantiate Item objects themselves. They may only + * be requested from a Pool object via the getItem() method. Calling Libraries + * SHOULD NOT assume that an Item created by one Implementing Library is + * compatible with a Pool from another Implementing Library. + */ +interface CacheItemInterface +{ + /** + * Returns the key for the current cache item. + * + * The key is loaded by the Implementing Library, but should be available to + * the higher level callers when needed. + * + * @return string + * The key string for this cache item. + */ + public function getKey(); + + /** + * Retrieves the value of the item from the cache associated with this object's key. + * + * The value returned must be identical to the value originally stored by set(). + * + * If isHit() returns false, this method MUST return null. Note that null + * is a legitimate cached value, so the isHit() method SHOULD be used to + * differentiate between "null value was found" and "no value was found." + * + * @return mixed + * The value corresponding to this cache item's key, or null if not found. + */ + public function get(); + + /** + * Confirms if the cache item lookup resulted in a cache hit. + * + * Note: This method MUST NOT have a race condition between calling isHit() + * and calling get(). + * + * @return bool + * True if the request resulted in a cache hit. False otherwise. + */ + public function isHit(); + + /** + * Sets the value represented by this cache item. + * + * The $value argument may be any item that can be serialized by PHP, + * although the method of serialization is left up to the Implementing + * Library. + * + * @param mixed $value + * The serializable value to be stored. + * + * @return static + * The invoked object. + */ + public function set($value); + + /** + * Sets the expiration time for this cache item. + * + * @param \DateTimeInterface|null $expiration + * The point in time after which the item MUST be considered expired. + * If null is passed explicitly, a default value MAY be used. If none is set, + * the value should be stored permanently or for as long as the + * implementation allows. + * + * @return static + * The called object. + */ + public function expiresAt($expiration); + + /** + * Sets the expiration time for this cache item. + * + * @param int|\DateInterval|null $time + * The period of time from the present after which the item MUST be considered + * expired. An integer parameter is understood to be the time in seconds until + * expiration. If null is passed explicitly, a default value MAY be used. + * If none is set, the value should be stored permanently or for as long as the + * implementation allows. + * + * @return static + * The called object. + */ + public function expiresAfter($time); +} diff --git a/psr/cache/src/CacheItemPoolInterface.php b/psr/cache/src/CacheItemPoolInterface.php new file mode 100644 index 00000000..03514196 --- /dev/null +++ b/psr/cache/src/CacheItemPoolInterface.php @@ -0,0 +1,138 @@ +<?php + +namespace Psr\Cache; + +/** + * CacheItemPoolInterface generates CacheItemInterface objects. + * + * The primary purpose of Cache\CacheItemPoolInterface is to accept a key from + * the Calling Library and return the associated Cache\CacheItemInterface object. + * It is also the primary point of interaction with the entire cache collection. + * All configuration and initialization of the Pool is left up to an + * Implementing Library. + */ +interface CacheItemPoolInterface +{ + /** + * Returns a Cache Item representing the specified key. + * + * This method must always return a CacheItemInterface object, even in case of + * a cache miss. It MUST NOT return null. + * + * @param string $key + * The key for which to return the corresponding Cache Item. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return CacheItemInterface + * The corresponding Cache Item. + */ + public function getItem($key); + + /** + * Returns a traversable set of cache items. + * + * @param string[] $keys + * An indexed array of keys of items to retrieve. + * + * @throws InvalidArgumentException + * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return array|\Traversable + * A traversable collection of Cache Items keyed by the cache keys of + * each item. A Cache item will be returned for each key, even if that + * key is not found. However, if no keys are specified then an empty + * traversable MUST be returned instead. + */ + public function getItems(array $keys = array()); + + /** + * Confirms if the cache contains specified cache item. + * + * Note: This method MAY avoid retrieving the cached value for performance reasons. + * This could result in a race condition with CacheItemInterface::get(). To avoid + * such situation use CacheItemInterface::isHit() instead. + * + * @param string $key + * The key for which to check existence. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if item exists in the cache, false otherwise. + */ + public function hasItem($key); + + /** + * Deletes all items in the pool. + * + * @return bool + * True if the pool was successfully cleared. False if there was an error. + */ + public function clear(); + + /** + * Removes the item from the pool. + * + * @param string $key + * The key to delete. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if the item was successfully removed. False if there was an error. + */ + public function deleteItem($key); + + /** + * Removes multiple items from the pool. + * + * @param string[] $keys + * An array of keys that should be removed from the pool. + + * @throws InvalidArgumentException + * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if the items were successfully removed. False if there was an error. + */ + public function deleteItems(array $keys); + + /** + * Persists a cache item immediately. + * + * @param CacheItemInterface $item + * The cache item to save. + * + * @return bool + * True if the item was successfully persisted. False if there was an error. + */ + public function save(CacheItemInterface $item); + + /** + * Sets a cache item to be persisted later. + * + * @param CacheItemInterface $item + * The cache item to save. + * + * @return bool + * False if the item could not be queued or if a commit was attempted and failed. True otherwise. + */ + public function saveDeferred(CacheItemInterface $item); + + /** + * Persists any deferred cache items. + * + * @return bool + * True if all not-yet-saved items were successfully saved or there were none. False otherwise. + */ + public function commit(); +} diff --git a/psr/cache/src/InvalidArgumentException.php b/psr/cache/src/InvalidArgumentException.php new file mode 100644 index 00000000..be7c6fa0 --- /dev/null +++ b/psr/cache/src/InvalidArgumentException.php @@ -0,0 +1,13 @@ +<?php + +namespace Psr\Cache; + +/** + * Exception interface for invalid cache arguments. + * + * Any time an invalid argument is passed into a method it must throw an + * exception class which implements Psr\Cache\InvalidArgumentException. + */ +interface InvalidArgumentException extends CacheException +{ +} |