diff options
author | Christoph Wurst <ChristophWurst@users.noreply.github.com> | 2021-10-21 19:17:21 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-21 19:17:21 +0300 |
commit | 217764f87ec6bbd359c76e57f0f48897d7c47646 (patch) | |
tree | 5b2c8c22b4a63c0a0295315bdd12dc642f759156 | |
parent | 95a491127bf36e55b22ce4508e9c9d7c628fac37 (diff) | |
parent | 51285f0a85f53a534201ea9a189669c2822ce9b8 (diff) |
Merge pull request #837 from nextcloud/dependabot/composer/doctrine/dbal-3.1.3v23.0.0rc2v23.0.0rc1v23.0.0beta3v23.0.0beta2
Bump doctrine/dbal from 3.0.0 to 3.1.3
117 files changed, 2853 insertions, 3601 deletions
@@ -41,6 +41,7 @@ doctrine/collections/composer.json doctrine/common/tests doctrine/common/.travis.yml doctrine/common/composer.json +doctrine/deprecations/tests doctrine/dbal/tests doctrine/dbal/docs doctrine/dbal/psalm.xml diff --git a/composer.json b/composer.json index 32e85c07..f553b88f 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "cweagans/composer-patches": "^1.7", "deepdiver/zipstreamer": "2.0.0", "deepdiver1975/tarstreamer": "v2.0.0", - "doctrine/dbal": "3.0.0", + "doctrine/dbal": "3.1.3", "egulias/email-validator": "3.1.1", "giggsey/libphonenumber-for-php": "^8.12", "guzzlehttp/guzzle": "^7.2", diff --git a/composer.lock b/composer.lock index 43942b5a..61867a16 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": "4d00ce69041cf4588a8790825888e5db", + "content-hash": "7a2c615cf730c6a5dc98776c2ee9b53a", "packages": [ { "name": "aws/aws-sdk-php", @@ -291,16 +291,16 @@ }, { "name": "composer/package-versions-deprecated", - "version": "1.11.99.1", + "version": "1.11.99.4", "source": { "type": "git", "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6" + "reference": "b174585d1fe49ceed21928a945138948cb394600" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/7413f0b55a051e89485c5cb9f765fe24bb02a7b6", - "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", + "reference": "b174585d1fe49ceed21928a945138948cb394600", "shasum": "" }, "require": { @@ -344,7 +344,7 @@ "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.1" + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" }, "funding": [ { @@ -360,7 +360,7 @@ "type": "tidelift" } ], - "time": "2020-11-11T10:22:58+00:00" + "time": "2021-09-13T08:41:34+00:00" }, { "name": "cweagans/composer-patches", @@ -523,16 +523,16 @@ }, { "name": "doctrine/cache", - "version": "1.10.2", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "13e3381b25847283a91948d04640543941309727" + "reference": "331b4d5dbaeab3827976273e9356b3b453c300ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/13e3381b25847283a91948d04640543941309727", - "reference": "13e3381b25847283a91948d04640543941309727", + "url": "https://api.github.com/repos/doctrine/cache/zipball/331b4d5dbaeab3827976273e9356b3b453c300ce", + "reference": "331b4d5dbaeab3827976273e9356b3b453c300ce", "shasum": "" }, "require": { @@ -543,20 +543,19 @@ }, "require-dev": { "alcaeus/mongo-php-adapter": "^1.1", - "doctrine/coding-standard": "^6.0", + "cache/integration-tests": "dev-master", + "doctrine/coding-standard": "^8.0", "mongodb/mongodb": "^1.1", - "phpunit/phpunit": "^7.0", - "predis/predis": "~1.0" + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "predis/predis": "~1.0", + "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" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" @@ -603,7 +602,7 @@ ], "support": { "issues": "https://github.com/doctrine/cache/issues", - "source": "https://github.com/doctrine/cache/tree/1.10.x" + "source": "https://github.com/doctrine/cache/tree/2.1.1" }, "funding": [ { @@ -619,37 +618,40 @@ "type": "tidelift" } ], - "time": "2020-07-07T18:54:01+00:00" + "time": "2021-07-17T14:49:29+00:00" }, { "name": "doctrine/dbal", - "version": "3.0.0", + "version": "3.1.3", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "ee6d1260d5cc20ec506455a585945d7bdb98662c" + "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/ee6d1260d5cc20ec506455a585945d7bdb98662c", - "reference": "ee6d1260d5cc20ec506455a585945d7bdb98662c", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/96b0053775a544b4a6ab47654dac0621be8b4cf8", + "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8", "shasum": "" }, "require": { "composer/package-versions-deprecated": "^1.11.99", - "doctrine/cache": "^1.0", + "doctrine/cache": "^1.0|^2.0", + "doctrine/deprecations": "^0.5.3", "doctrine/event-manager": "^1.0", "php": "^7.3 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^8.1", - "jetbrains/phpstorm-stubs": "^2019.1", - "phpstan/phpstan": "^0.12.40", - "phpstan/phpstan-strict-rules": "^0.12.2", - "phpunit/phpunit": "^9.4", - "psalm/plugin-phpunit": "^0.10.0", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0", - "vimeo/psalm": "^3.17.2" + "doctrine/coding-standard": "9.0.0", + "jetbrains/phpstorm-stubs": "2021.1", + "phpstan/phpstan": "0.12.99", + "phpstan/phpstan-strict-rules": "^0.12.11", + "phpunit/phpunit": "9.5.10", + "psalm/plugin-phpunit": "0.16.1", + "squizlabs/php_codesniffer": "3.6.0", + "symfony/cache": "^5.2|^6.0", + "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", + "vimeo/psalm": "4.10.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -658,11 +660,6 @@ "bin/doctrine-dbal" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\DBAL\\": "src" @@ -714,7 +711,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.0.0" + "source": "https://github.com/doctrine/dbal/tree/3.1.3" }, "funding": [ { @@ -730,7 +727,50 @@ "type": "tidelift" } ], - "time": "2020-11-15T18:20:41+00:00" + "time": "2021-10-02T16:15:05+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "v0.5.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "9504165960a1f83cc1480e2be1dd0a0478561314" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/9504165960a1f83cc1480e2be1dd0a0478561314", + "reference": "9504165960a1f83cc1480e2be1dd0a0478561314", + "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" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v0.5.3" + }, + "time": "2021-03-21T12:59:47+00:00" }, { "name": "doctrine/event-manager", diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php index 9336a93b..8c32a709 100644 --- a/composer/autoload_classmap.php +++ b/composer/autoload_classmap.php @@ -902,37 +902,19 @@ return array( 'Cose\\Key\\RsaKey' => $vendorDir . '/web-auth/cose-lib/src/Key/RsaKey.php', 'Cose\\Key\\SymmetricKey' => $vendorDir . '/web-auth/cose-lib/src/Key/SymmetricKey.php', 'Cose\\Verifier' => $vendorDir . '/web-auth/cose-lib/src/Verifier.php', - 'Doctrine\\Common\\Cache\\ApcCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php', - 'Doctrine\\Common\\Cache\\ApcuCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php', - 'Doctrine\\Common\\Cache\\ArrayCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php', 'Doctrine\\Common\\Cache\\Cache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php', 'Doctrine\\Common\\Cache\\CacheProvider' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php', - 'Doctrine\\Common\\Cache\\ChainCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php', 'Doctrine\\Common\\Cache\\ClearableCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php', - 'Doctrine\\Common\\Cache\\CouchbaseBucketCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseBucketCache.php', - 'Doctrine\\Common\\Cache\\CouchbaseCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php', - 'Doctrine\\Common\\Cache\\ExtMongoDBCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ExtMongoDBCache.php', - 'Doctrine\\Common\\Cache\\FileCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php', - 'Doctrine\\Common\\Cache\\FilesystemCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php', 'Doctrine\\Common\\Cache\\FlushableCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php', - 'Doctrine\\Common\\Cache\\InvalidCacheId' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/InvalidCacheId.php', - 'Doctrine\\Common\\Cache\\LegacyMongoDBCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/LegacyMongoDBCache.php', - 'Doctrine\\Common\\Cache\\MemcacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php', - 'Doctrine\\Common\\Cache\\MemcachedCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php', - 'Doctrine\\Common\\Cache\\MongoDBCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php', 'Doctrine\\Common\\Cache\\MultiDeleteCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiDeleteCache.php', 'Doctrine\\Common\\Cache\\MultiGetCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php', 'Doctrine\\Common\\Cache\\MultiOperationCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiOperationCache.php', 'Doctrine\\Common\\Cache\\MultiPutCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php', - 'Doctrine\\Common\\Cache\\PhpFileCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php', - 'Doctrine\\Common\\Cache\\PredisCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php', - 'Doctrine\\Common\\Cache\\RedisCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php', - 'Doctrine\\Common\\Cache\\SQLite3Cache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php', - 'Doctrine\\Common\\Cache\\Version' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Version.php', - 'Doctrine\\Common\\Cache\\VoidCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php', - 'Doctrine\\Common\\Cache\\WinCacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php', - 'Doctrine\\Common\\Cache\\XcacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php', - 'Doctrine\\Common\\Cache\\ZendDataCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php', + 'Doctrine\\Common\\Cache\\Psr6\\CacheAdapter' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php', + 'Doctrine\\Common\\Cache\\Psr6\\CacheItem' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheItem.php', + 'Doctrine\\Common\\Cache\\Psr6\\DoctrineProvider' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php', + 'Doctrine\\Common\\Cache\\Psr6\\InvalidArgument' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php', + 'Doctrine\\Common\\Cache\\Psr6\\TypedCacheItem' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/TypedCacheItem.php', 'Doctrine\\Common\\EventArgs' => $vendorDir . '/doctrine/event-manager/lib/Doctrine/Common/EventArgs.php', 'Doctrine\\Common\\EventManager' => $vendorDir . '/doctrine/event-manager/lib/Doctrine/Common/EventManager.php', 'Doctrine\\Common\\EventSubscriber' => $vendorDir . '/doctrine/event-manager/lib/Doctrine/Common/EventSubscriber.php', @@ -1026,7 +1008,6 @@ return array( 'Doctrine\\DBAL\\Driver\\SQLSrv\\Connection' => $vendorDir . '/doctrine/dbal/src/Driver/SQLSrv/Connection.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/SQLSrv/Driver.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Exception\\Error' => $vendorDir . '/doctrine/dbal/src/Driver/SQLSrv/Exception/Error.php', - 'Doctrine\\DBAL\\Driver\\SQLSrv\\LastInsertId' => $vendorDir . '/doctrine/dbal/src/Driver/SQLSrv/LastInsertId.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Result' => $vendorDir . '/doctrine/dbal/src/Driver/SQLSrv/Result.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Statement' => $vendorDir . '/doctrine/dbal/src/Driver/SQLSrv/Statement.php', 'Doctrine\\DBAL\\Driver\\ServerInfoAwareConnection' => $vendorDir . '/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php', @@ -1195,6 +1176,8 @@ return array( 'Doctrine\\DBAL\\Types\\VarDateTimeImmutableType' => $vendorDir . '/doctrine/dbal/src/Types/VarDateTimeImmutableType.php', 'Doctrine\\DBAL\\Types\\VarDateTimeType' => $vendorDir . '/doctrine/dbal/src/Types/VarDateTimeType.php', 'Doctrine\\DBAL\\VersionAwarePlatformDriver' => $vendorDir . '/doctrine/dbal/src/VersionAwarePlatformDriver.php', + 'Doctrine\\Deprecations\\Deprecation' => $vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php', + 'Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => $vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php', 'Ds\\Collection' => $vendorDir . '/php-ds/php-ds/src/Collection.php', 'Ds\\Deque' => $vendorDir . '/php-ds/php-ds/src/Deque.php', 'Ds\\Hashable' => $vendorDir . '/php-ds/php-ds/src/Hashable.php', diff --git a/composer/autoload_psr4.php b/composer/autoload_psr4.php index 1ed7d0d5..30c5203b 100644 --- a/composer/autoload_psr4.php +++ b/composer/autoload_psr4.php @@ -75,6 +75,7 @@ return array( 'FG\\' => array($vendorDir . '/fgrosse/phpasn1/lib'), 'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'), 'Ds\\' => array($vendorDir . '/php-ds/php-ds/src'), + 'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations'), 'Doctrine\\DBAL\\' => array($vendorDir . '/doctrine/dbal/src'), 'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'), 'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'), diff --git a/composer/autoload_static.php b/composer/autoload_static.php index 112037af..0248df8f 100644 --- a/composer/autoload_static.php +++ b/composer/autoload_static.php @@ -257,6 +257,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'D' => array ( 'Ds\\' => 3, + 'Doctrine\\Deprecations\\' => 22, 'Doctrine\\DBAL\\' => 14, 'Doctrine\\Common\\Lexer\\' => 22, 'Doctrine\\Common\\Cache\\' => 22, @@ -560,6 +561,10 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 array ( 0 => __DIR__ . '/..' . '/php-ds/php-ds/src', ), + 'Doctrine\\Deprecations\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/deprecations/lib/Doctrine/Deprecations', + ), 'Doctrine\\DBAL\\' => array ( 0 => __DIR__ . '/..' . '/doctrine/dbal/src', @@ -1527,37 +1532,19 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Cose\\Key\\RsaKey' => __DIR__ . '/..' . '/web-auth/cose-lib/src/Key/RsaKey.php', 'Cose\\Key\\SymmetricKey' => __DIR__ . '/..' . '/web-auth/cose-lib/src/Key/SymmetricKey.php', 'Cose\\Verifier' => __DIR__ . '/..' . '/web-auth/cose-lib/src/Verifier.php', - 'Doctrine\\Common\\Cache\\ApcCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php', - 'Doctrine\\Common\\Cache\\ApcuCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php', - 'Doctrine\\Common\\Cache\\ArrayCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php', 'Doctrine\\Common\\Cache\\Cache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php', 'Doctrine\\Common\\Cache\\CacheProvider' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php', - 'Doctrine\\Common\\Cache\\ChainCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php', 'Doctrine\\Common\\Cache\\ClearableCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php', - 'Doctrine\\Common\\Cache\\CouchbaseBucketCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseBucketCache.php', - 'Doctrine\\Common\\Cache\\CouchbaseCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php', - 'Doctrine\\Common\\Cache\\ExtMongoDBCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ExtMongoDBCache.php', - 'Doctrine\\Common\\Cache\\FileCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php', - 'Doctrine\\Common\\Cache\\FilesystemCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php', 'Doctrine\\Common\\Cache\\FlushableCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php', - 'Doctrine\\Common\\Cache\\InvalidCacheId' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/InvalidCacheId.php', - 'Doctrine\\Common\\Cache\\LegacyMongoDBCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/LegacyMongoDBCache.php', - 'Doctrine\\Common\\Cache\\MemcacheCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php', - 'Doctrine\\Common\\Cache\\MemcachedCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php', - 'Doctrine\\Common\\Cache\\MongoDBCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php', 'Doctrine\\Common\\Cache\\MultiDeleteCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiDeleteCache.php', 'Doctrine\\Common\\Cache\\MultiGetCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php', 'Doctrine\\Common\\Cache\\MultiOperationCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiOperationCache.php', 'Doctrine\\Common\\Cache\\MultiPutCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php', - 'Doctrine\\Common\\Cache\\PhpFileCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php', - 'Doctrine\\Common\\Cache\\PredisCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php', - 'Doctrine\\Common\\Cache\\RedisCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php', - 'Doctrine\\Common\\Cache\\SQLite3Cache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php', - 'Doctrine\\Common\\Cache\\Version' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Version.php', - 'Doctrine\\Common\\Cache\\VoidCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php', - 'Doctrine\\Common\\Cache\\WinCacheCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php', - 'Doctrine\\Common\\Cache\\XcacheCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php', - 'Doctrine\\Common\\Cache\\ZendDataCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php', + 'Doctrine\\Common\\Cache\\Psr6\\CacheAdapter' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php', + 'Doctrine\\Common\\Cache\\Psr6\\CacheItem' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheItem.php', + 'Doctrine\\Common\\Cache\\Psr6\\DoctrineProvider' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php', + 'Doctrine\\Common\\Cache\\Psr6\\InvalidArgument' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php', + 'Doctrine\\Common\\Cache\\Psr6\\TypedCacheItem' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/TypedCacheItem.php', 'Doctrine\\Common\\EventArgs' => __DIR__ . '/..' . '/doctrine/event-manager/lib/Doctrine/Common/EventArgs.php', 'Doctrine\\Common\\EventManager' => __DIR__ . '/..' . '/doctrine/event-manager/lib/Doctrine/Common/EventManager.php', 'Doctrine\\Common\\EventSubscriber' => __DIR__ . '/..' . '/doctrine/event-manager/lib/Doctrine/Common/EventSubscriber.php', @@ -1651,7 +1638,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Driver\\SQLSrv\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLSrv/Connection.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLSrv/Driver.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Exception\\Error' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLSrv/Exception/Error.php', - 'Doctrine\\DBAL\\Driver\\SQLSrv\\LastInsertId' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLSrv/LastInsertId.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLSrv/Result.php', 'Doctrine\\DBAL\\Driver\\SQLSrv\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLSrv/Statement.php', 'Doctrine\\DBAL\\Driver\\ServerInfoAwareConnection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php', @@ -1820,6 +1806,8 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Types\\VarDateTimeImmutableType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/VarDateTimeImmutableType.php', 'Doctrine\\DBAL\\Types\\VarDateTimeType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/VarDateTimeType.php', 'Doctrine\\DBAL\\VersionAwarePlatformDriver' => __DIR__ . '/..' . '/doctrine/dbal/src/VersionAwarePlatformDriver.php', + 'Doctrine\\Deprecations\\Deprecation' => __DIR__ . '/..' . '/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php', + 'Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => __DIR__ . '/..' . '/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php', 'Ds\\Collection' => __DIR__ . '/..' . '/php-ds/php-ds/src/Collection.php', 'Ds\\Deque' => __DIR__ . '/..' . '/php-ds/php-ds/src/Deque.php', 'Ds\\Hashable' => __DIR__ . '/..' . '/php-ds/php-ds/src/Hashable.php', diff --git a/composer/installed.json b/composer/installed.json index 2c9049ef..803f729f 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -300,17 +300,17 @@ }, { "name": "composer/package-versions-deprecated", - "version": "1.11.99.1", - "version_normalized": "1.11.99.1", + "version": "1.11.99.4", + "version_normalized": "1.11.99.4", "source": { "type": "git", "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6" + "reference": "b174585d1fe49ceed21928a945138948cb394600" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/7413f0b55a051e89485c5cb9f765fe24bb02a7b6", - "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", + "reference": "b174585d1fe49ceed21928a945138948cb394600", "shasum": "" }, "require": { @@ -325,7 +325,7 @@ "ext-zip": "^1.13", "phpunit/phpunit": "^6.5 || ^7" }, - "time": "2020-11-11T10:22:58+00:00", + "time": "2021-09-13T08:41:34+00:00", "type": "composer-plugin", "extra": { "class": "PackageVersions\\Installer", @@ -356,7 +356,7 @@ "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.1" + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" }, "funding": [ { @@ -544,17 +544,17 @@ }, { "name": "doctrine/cache", - "version": "1.10.2", - "version_normalized": "1.10.2.0", + "version": "2.1.1", + "version_normalized": "2.1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "13e3381b25847283a91948d04640543941309727" + "reference": "331b4d5dbaeab3827976273e9356b3b453c300ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/13e3381b25847283a91948d04640543941309727", - "reference": "13e3381b25847283a91948d04640543941309727", + "url": "https://api.github.com/repos/doctrine/cache/zipball/331b4d5dbaeab3827976273e9356b3b453c300ce", + "reference": "331b4d5dbaeab3827976273e9356b3b453c300ce", "shasum": "" }, "require": { @@ -565,21 +565,20 @@ }, "require-dev": { "alcaeus/mongo-php-adapter": "^1.1", - "doctrine/coding-standard": "^6.0", + "cache/integration-tests": "dev-master", + "doctrine/coding-standard": "^8.0", "mongodb/mongodb": "^1.1", - "phpunit/phpunit": "^7.0", - "predis/predis": "~1.0" + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "predis/predis": "~1.0", + "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" }, - "time": "2020-07-07T18:54:01+00:00", + "time": "2021-07-17T14:49:29+00:00", "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9.x-dev" - } - }, "installation-source": "dist", "autoload": { "psr-4": { @@ -627,7 +626,7 @@ ], "support": { "issues": "https://github.com/doctrine/cache/issues", - "source": "https://github.com/doctrine/cache/tree/1.10.x" + "source": "https://github.com/doctrine/cache/tree/2.1.1" }, "funding": [ { @@ -647,48 +646,46 @@ }, { "name": "doctrine/dbal", - "version": "3.0.0", - "version_normalized": "3.0.0.0", + "version": "3.1.3", + "version_normalized": "3.1.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "ee6d1260d5cc20ec506455a585945d7bdb98662c" + "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/ee6d1260d5cc20ec506455a585945d7bdb98662c", - "reference": "ee6d1260d5cc20ec506455a585945d7bdb98662c", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/96b0053775a544b4a6ab47654dac0621be8b4cf8", + "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8", "shasum": "" }, "require": { "composer/package-versions-deprecated": "^1.11.99", - "doctrine/cache": "^1.0", + "doctrine/cache": "^1.0|^2.0", + "doctrine/deprecations": "^0.5.3", "doctrine/event-manager": "^1.0", "php": "^7.3 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^8.1", - "jetbrains/phpstorm-stubs": "^2019.1", - "phpstan/phpstan": "^0.12.40", - "phpstan/phpstan-strict-rules": "^0.12.2", - "phpunit/phpunit": "^9.4", - "psalm/plugin-phpunit": "^0.10.0", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0", - "vimeo/psalm": "^3.17.2" + "doctrine/coding-standard": "9.0.0", + "jetbrains/phpstorm-stubs": "2021.1", + "phpstan/phpstan": "0.12.99", + "phpstan/phpstan-strict-rules": "^0.12.11", + "phpunit/phpunit": "9.5.10", + "psalm/plugin-phpunit": "0.16.1", + "squizlabs/php_codesniffer": "3.6.0", + "symfony/cache": "^5.2|^6.0", + "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", + "vimeo/psalm": "4.10.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, - "time": "2020-11-15T18:20:41+00:00", + "time": "2021-10-02T16:15:05+00:00", "bin": [ "bin/doctrine-dbal" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" - } - }, "installation-source": "dist", "autoload": { "psr-4": { @@ -741,7 +738,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.0.0" + "source": "https://github.com/doctrine/dbal/tree/3.1.3" }, "funding": [ { @@ -760,6 +757,52 @@ "install-path": "../doctrine/dbal" }, { + "name": "doctrine/deprecations", + "version": "v0.5.3", + "version_normalized": "0.5.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "9504165960a1f83cc1480e2be1dd0a0478561314" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/9504165960a1f83cc1480e2be1dd0a0478561314", + "reference": "9504165960a1f83cc1480e2be1dd0a0478561314", + "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" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "time": "2021-03-21T12:59:47+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v0.5.3" + }, + "install-path": "../doctrine/deprecations" + }, + { "name": "doctrine/event-manager", "version": "1.1.1", "version_normalized": "1.1.1.0", diff --git a/composer/installed.php b/composer/installed.php index ade31e6d..686943ff 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), - 'reference' => 'f43adb17395165d337ee76ca5c1b9cc2e85f666c', + 'reference' => '1d7fff4111e438dd496fb8a5d518ea6b8b910b83', 'name' => 'nextcloud/3rdparty', 'dev' => false, ), @@ -56,12 +56,12 @@ 'dev_requirement' => false, ), 'composer/package-versions-deprecated' => array( - 'pretty_version' => '1.11.99.1', - 'version' => '1.11.99.1', + 'pretty_version' => '1.11.99.4', + 'version' => '1.11.99.4', 'type' => 'composer-plugin', 'install_path' => __DIR__ . '/./package-versions-deprecated', 'aliases' => array(), - 'reference' => '7413f0b55a051e89485c5cb9f765fe24bb02a7b6', + 'reference' => 'b174585d1fe49ceed21928a945138948cb394600', 'dev_requirement' => false, ), 'cweagans/composer-patches' => array( @@ -92,21 +92,30 @@ 'dev_requirement' => false, ), 'doctrine/cache' => array( - 'pretty_version' => '1.10.2', - 'version' => '1.10.2.0', + 'pretty_version' => '2.1.1', + 'version' => '2.1.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/cache', 'aliases' => array(), - 'reference' => '13e3381b25847283a91948d04640543941309727', + 'reference' => '331b4d5dbaeab3827976273e9356b3b453c300ce', 'dev_requirement' => false, ), 'doctrine/dbal' => array( - 'pretty_version' => '3.0.0', - 'version' => '3.0.0.0', + 'pretty_version' => '3.1.3', + 'version' => '3.1.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/dbal', 'aliases' => array(), - 'reference' => 'ee6d1260d5cc20ec506455a585945d7bdb98662c', + 'reference' => '96b0053775a544b4a6ab47654dac0621be8b4cf8', + 'dev_requirement' => false, + ), + 'doctrine/deprecations' => array( + 'pretty_version' => 'v0.5.3', + 'version' => '0.5.3.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../doctrine/deprecations', + 'aliases' => array(), + 'reference' => '9504165960a1f83cc1480e2be1dd0a0478561314', 'dev_requirement' => false, ), 'doctrine/event-manager' => array( @@ -277,7 +286,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), - 'reference' => 'f43adb17395165d337ee76ca5c1b9cc2e85f666c', + 'reference' => '1d7fff4111e438dd496fb8a5d518ea6b8b910b83', 'dev_requirement' => false, ), 'nextcloud/lognormalizer' => array( diff --git a/composer/package-versions-deprecated/README.md b/composer/package-versions-deprecated/README.md index c5f5bba1..7fe2097d 100644 --- a/composer/package-versions-deprecated/README.md +++ b/composer/package-versions-deprecated/README.md @@ -2,4 +2,4 @@ **`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\Versions` class which offers the functionality present here out of the box. +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/phpcs.xml.dist b/composer/package-versions-deprecated/phpcs.xml.dist deleted file mode 100644 index e169c616..00000000 --- a/composer/package-versions-deprecated/phpcs.xml.dist +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0"?> -<ruleset> - <arg name="basepath" value="."/> - <arg name="extensions" value="php"/> - <arg name="parallel" value="80"/> - <arg name="cache" value=".phpcs-cache"/> - <arg name="colors"/> - - <arg value="nps"/> - - <file>src</file> - <file>test</file> - - <rule ref="Doctrine"> - <exclude-pattern>src/PackageVersions/Versions.php</exclude-pattern> - </rule> - - <rule ref="Generic.Strings.UnnecessaryStringConcat.Found"> - <exclude-pattern>src/PackageVersions/Installer.php</exclude-pattern> - </rule> -</ruleset> diff --git a/composer/package-versions-deprecated/src/PackageVersions/Installer.php b/composer/package-versions-deprecated/src/PackageVersions/Installer.php index c0853c16..05bdac9a 100644 --- a/composer/package-versions-deprecated/src/PackageVersions/Installer.php +++ b/composer/package-versions-deprecated/src/PackageVersions/Installer.php @@ -82,7 +82,7 @@ class_exists(InstalledVersions::class); */ public static function rootPackageName() : string { - if (!class_exists(InstalledVersions::class, false) || !InstalledVersions::getRawData()) { + if (!self::composer2ApiUsable()) { return self::ROOT_PACKAGE_NAME; } @@ -100,7 +100,7 @@ class_exists(InstalledVersions::class); */ public static function getVersion(string $packageName): string { - if (class_exists(InstalledVersions::class, false) && InstalledVersions::getRawData()) { + if (self::composer2ApiUsable()) { return InstalledVersions::getPrettyVersion($packageName) . '@' . InstalledVersions::getReference($packageName); } @@ -113,6 +113,27 @@ class_exists(InstalledVersions::class); '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; diff --git a/composer/package-versions-deprecated/src/PackageVersions/Versions.php b/composer/package-versions-deprecated/src/PackageVersions/Versions.php index 6ddb041e..9d9dd6a5 100644 --- a/composer/package-versions-deprecated/src/PackageVersions/Versions.php +++ b/composer/package-versions-deprecated/src/PackageVersions/Versions.php @@ -38,12 +38,13 @@ final class Versions 'beberlei/assert' => 'v3.3.1@5e721d7e937ca3ba2cdec1e1adf195f9e5188372', 'brick/math' => '0.9.2@dff976c2f3487d42c1db75a3b180e2b9f0e72ce0', 'christophwurst/id3parser' => 'v0.1.2@d7f5e9e7db69a24e3111a2033cbdf640f9456f2f', - 'composer/package-versions-deprecated' => '1.11.99.1@7413f0b55a051e89485c5cb9f765fe24bb02a7b6', + 'composer/package-versions-deprecated' => '1.11.99.4@b174585d1fe49ceed21928a945138948cb394600', 'cweagans/composer-patches' => '1.7.1@9888dcc74993c030b75f3dd548bb5e20cdbd740c', 'deepdiver/zipstreamer' => '2.0.0@b8c59647ff34fb97e8937aefb2a65de2bc4b4755', 'deepdiver1975/tarstreamer' => '2.0.0@ad48505d1ab54a8e94e6b1cc5297bbed72e956de', - 'doctrine/cache' => '1.10.2@13e3381b25847283a91948d04640543941309727', - 'doctrine/dbal' => '3.0.0@ee6d1260d5cc20ec506455a585945d7bdb98662c', + 'doctrine/cache' => '2.1.1@331b4d5dbaeab3827976273e9356b3b453c300ce', + 'doctrine/dbal' => '3.1.3@96b0053775a544b4a6ab47654dac0621be8b4cf8', + 'doctrine/deprecations' => 'v0.5.3@9504165960a1f83cc1480e2be1dd0a0478561314', 'doctrine/event-manager' => '1.1.1@41370af6a30faa9dc0368c4a6814d596e81aba7f', 'doctrine/lexer' => '1.2.1@e864bbf5904cb8f5bb334f99209b48018522f042', 'egulias/email-validator' => '3.1.1@c81f18a3efb941d8c4d2e025f6183b5c6d697307', @@ -118,7 +119,7 @@ final class Versions 'web-auth/cose-lib' => 'v3.3.9@ed172d2dc1a6b87b5c644c07c118cd30c1b3819b', 'web-auth/metadata-service' => 'v3.3.9@8488d3a832a38cc81c670fce05de1e515c6e64b1', 'web-auth/webauthn-lib' => 'v3.3.9@04b98ee3d39cb79dad68a7c15c297c085bf66bfe', - 'nextcloud/3rdparty' => 'dev-master@f43adb17395165d337ee76ca5c1b9cc2e85f666c', + 'nextcloud/3rdparty' => 'dev-master@1d7fff4111e438dd496fb8a5d518ea6b8b910b83', ); private function __construct() @@ -133,7 +134,7 @@ final class Versions */ public static function rootPackageName() : string { - if (!class_exists(InstalledVersions::class, false) || !InstalledVersions::getRawData()) { + if (!self::composer2ApiUsable()) { return self::ROOT_PACKAGE_NAME; } @@ -151,7 +152,7 @@ final class Versions */ public static function getVersion(string $packageName): string { - if (class_exists(InstalledVersions::class, false) && InstalledVersions::getRawData()) { + if (self::composer2ApiUsable()) { return InstalledVersions::getPrettyVersion($packageName) . '@' . InstalledVersions::getReference($packageName); } @@ -164,4 +165,25 @@ final class Versions '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; + } } diff --git a/doctrine/cache/UPGRADE-1.11.md b/doctrine/cache/UPGRADE-1.11.md new file mode 100644 index 00000000..a33be230 --- /dev/null +++ b/doctrine/cache/UPGRADE-1.11.md @@ -0,0 +1,15 @@ +# Upgrade to 1.11 + +doctrine/cache will no longer be maintained and all cache implementations have +been marked as deprecated. These implementations will be removed in 2.0, which +will only contain interfaces to provide a lightweight package for backward +compatibility. + +There are two new classes to use in the `Doctrine\Common\Cache\Psr6` namespace: +* The `CacheAdapter` class allows using any Doctrine Cache as PSR-6 cache. This + is useful to provide a forward compatibility layer in libraries that accept + Doctrine cache implementations and switch to PSR-6. +* The `DoctrineProvider` class allows using any PSR-6 cache as Doctrine cache. + 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. diff --git a/doctrine/cache/UPGRADE.md b/doctrine/cache/UPGRADE-1.4.md index e1f8a503..e1f8a503 100644 --- a/doctrine/cache/UPGRADE.md +++ b/doctrine/cache/UPGRADE-1.4.md diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php deleted file mode 100644 index 138d49a5..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use const PHP_VERSION_ID; -use function apc_cache_info; -use function apc_clear_cache; -use function apc_delete; -use function apc_exists; -use function apc_fetch; -use function apc_sma_info; -use function apc_store; - -/** - * APC cache provider. - * - * @deprecated since version 1.6, use ApcuCache instead - * - * @link www.doctrine-project.org - */ -class ApcCache extends CacheProvider -{ - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return apc_fetch($id); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return apc_exists($id); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - return apc_store($id, $data, $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - // apc_delete returns false if the id does not exist - return apc_delete($id) || ! apc_exists($id); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - return apc_clear_cache() && apc_clear_cache('user'); - } - - /** - * {@inheritdoc} - */ - protected function doFetchMultiple(array $keys) - { - return apc_fetch($keys) ?: []; - } - - /** - * {@inheritdoc} - */ - protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) - { - $result = apc_store($keysAndValues, null, $lifetime); - - return empty($result); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $info = apc_cache_info('', true); - $sma = apc_sma_info(); - - // @TODO - Temporary fix @see https://github.com/krakjoe/apcu/pull/42 - if (PHP_VERSION_ID >= 50500) { - $info['num_hits'] = $info['num_hits'] ?? $info['nhits']; - $info['num_misses'] = $info['num_misses'] ?? $info['nmisses']; - $info['start_time'] = $info['start_time'] ?? $info['stime']; - } - - return [ - Cache::STATS_HITS => $info['num_hits'], - Cache::STATS_MISSES => $info['num_misses'], - Cache::STATS_UPTIME => $info['start_time'], - Cache::STATS_MEMORY_USAGE => $info['mem_size'], - Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'], - ]; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php deleted file mode 100644 index a7252136..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php +++ /dev/null @@ -1,106 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use function apcu_cache_info; -use function apcu_clear_cache; -use function apcu_delete; -use function apcu_exists; -use function apcu_fetch; -use function apcu_sma_info; -use function apcu_store; -use function count; - -/** - * APCu cache provider. - * - * @link www.doctrine-project.org - */ -class ApcuCache extends CacheProvider -{ - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return apcu_fetch($id); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return apcu_exists($id); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - return apcu_store($id, $data, $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - // apcu_delete returns false if the id does not exist - return apcu_delete($id) || ! apcu_exists($id); - } - - /** - * {@inheritdoc} - */ - protected function doDeleteMultiple(array $keys) - { - $result = apcu_delete($keys); - - return $result !== false && count($result) !== count($keys); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - return apcu_clear_cache(); - } - - /** - * {@inheritdoc} - */ - protected function doFetchMultiple(array $keys) - { - return apcu_fetch($keys) ?: []; - } - - /** - * {@inheritdoc} - */ - protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) - { - $result = apcu_store($keysAndValues, null, $lifetime); - - return empty($result); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $info = apcu_cache_info(true); - $sma = apcu_sma_info(); - - return [ - Cache::STATS_HITS => $info['num_hits'], - Cache::STATS_MISSES => $info['num_misses'], - Cache::STATS_UPTIME => $info['start_time'], - Cache::STATS_MEMORY_USAGE => $info['mem_size'], - Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'], - ]; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php deleted file mode 100644 index 1beb7098..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use function time; - -/** - * Array cache driver. - * - * @link www.doctrine-project.org - */ -class ArrayCache extends CacheProvider -{ - /** @var array[] $data each element being a tuple of [$data, $expiration], where the expiration is int|bool */ - private $data = []; - - /** @var int */ - private $hitsCount = 0; - - /** @var int */ - private $missesCount = 0; - - /** @var int */ - private $upTime; - - /** - * {@inheritdoc} - */ - public function __construct() - { - $this->upTime = time(); - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - if (! $this->doContains($id)) { - $this->missesCount += 1; - - return false; - } - - $this->hitsCount += 1; - - return $this->data[$id][0]; - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - if (! isset($this->data[$id])) { - return false; - } - - $expiration = $this->data[$id][1]; - - if ($expiration && $expiration < time()) { - $this->doDelete($id); - - return false; - } - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - $this->data[$id] = [$data, $lifeTime ? time() + $lifeTime : false]; - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - unset($this->data[$id]); - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - $this->data = []; - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - return [ - Cache::STATS_HITS => $this->hitsCount, - Cache::STATS_MISSES => $this->missesCount, - Cache::STATS_UPTIME => $this->upTime, - Cache::STATS_MEMORY_USAGE => null, - Cache::STATS_MEMORY_AVAILABLE => null, - ]; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php b/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php index 45697442..4cfab6c0 100644 --- a/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php +++ b/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php @@ -84,7 +84,7 @@ interface Cache * - <b>memory_available</b> * Memory allowed to use for storage. * - * @return array|null An associative array with server's statistics if available, NULL otherwise. + * @return mixed[]|null An associative array with server's statistics if available, NULL otherwise. */ public function getStats(); } diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php b/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php index 43d414f3..180482a7 100644 --- a/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php +++ b/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php @@ -171,7 +171,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M * * @return string The namespaced id. */ - private function getNamespacedId(string $id) : string + private function getNamespacedId(string $id): string { $namespaceVersion = $this->getNamespaceVersion(); @@ -181,7 +181,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M /** * Returns the namespace cache key. */ - private function getNamespaceCacheKey() : string + private function getNamespaceCacheKey(): string { return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); } @@ -189,7 +189,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M /** * Returns the namespace version. */ - private function getNamespaceVersion() : int + private function getNamespaceVersion(): int { if ($this->namespaceVersion !== null) { return $this->namespaceVersion; @@ -204,9 +204,9 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M /** * Default implementation of doFetchMultiple. Each driver that supports multi-get should owerwrite it. * - * @param array $keys Array of keys to retrieve from cache + * @param string[] $keys Array of keys to retrieve from cache * - * @return array Array of values retrieved for the given keys. + * @return mixed[] Array of values retrieved for the given keys. */ protected function doFetchMultiple(array $keys) { @@ -245,9 +245,9 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M /** * Default implementation of doSaveMultiple. Each driver that supports multi-put should override it. * - * @param array $keysAndValues Array of keys and values to save in cache - * @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these - * cache entries (0 => infinite lifeTime). + * @param mixed[] $keysAndValues Array of keys and values to save in cache + * @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these + * cache entries (0 => infinite lifeTime). * * @return bool TRUE if the operation was successful, FALSE if it wasn't. */ @@ -281,7 +281,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M /** * Default implementation of doDeleteMultiple. Each driver that supports multi-delete should override it. * - * @param array $keys Array of keys to delete from cache + * @param string[] $keys Array of keys to delete from cache * * @return bool TRUE if the operation was successful, FALSE if it wasn't */ @@ -319,7 +319,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M /** * Retrieves cached information from the data store. * - * @return array|null An associative array with server's statistics if available, NULL otherwise. + * @return mixed[]|null An associative array with server's statistics if available, NULL otherwise. */ abstract protected function doGetStats(); } diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php deleted file mode 100644 index 8f85845c..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php +++ /dev/null @@ -1,195 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use Traversable; -use function array_values; -use function count; -use function iterator_to_array; - -/** - * Cache provider that allows to easily chain multiple cache providers - */ -class ChainCache extends CacheProvider -{ - /** @var CacheProvider[] */ - private $cacheProviders = []; - - /** @var int */ - private $defaultLifeTimeForDownstreamCacheProviders = 0; - - /** - * @param CacheProvider[] $cacheProviders - */ - public function __construct($cacheProviders = []) - { - $this->cacheProviders = $cacheProviders instanceof Traversable - ? iterator_to_array($cacheProviders, false) - : array_values($cacheProviders); - } - - public function setDefaultLifeTimeForDownstreamCacheProviders(int $defaultLifeTimeForDownstreamCacheProviders) : void - { - $this->defaultLifeTimeForDownstreamCacheProviders = $defaultLifeTimeForDownstreamCacheProviders; - } - - /** - * {@inheritDoc} - */ - public function setNamespace($namespace) - { - parent::setNamespace($namespace); - - foreach ($this->cacheProviders as $cacheProvider) { - $cacheProvider->setNamespace($namespace); - } - } - - /** - * {@inheritDoc} - */ - protected function doFetch($id) - { - foreach ($this->cacheProviders as $key => $cacheProvider) { - if ($cacheProvider->doContains($id)) { - $value = $cacheProvider->doFetch($id); - - // We populate all the previous cache layers (that are assumed to be faster) - for ($subKey = $key - 1; $subKey >= 0; $subKey--) { - $this->cacheProviders[$subKey]->doSave($id, $value, $this->defaultLifeTimeForDownstreamCacheProviders); - } - - return $value; - } - } - - return false; - } - - /** - * {@inheritdoc} - */ - protected function doFetchMultiple(array $keys) - { - /** @var CacheProvider[] $traversedProviders */ - $traversedProviders = []; - $keysCount = count($keys); - $fetchedValues = []; - - foreach ($this->cacheProviders as $key => $cacheProvider) { - $fetchedValues = $cacheProvider->doFetchMultiple($keys); - - // We populate all the previous cache layers (that are assumed to be faster) - if (count($fetchedValues) === $keysCount) { - foreach ($traversedProviders as $previousCacheProvider) { - $previousCacheProvider->doSaveMultiple($fetchedValues, $this->defaultLifeTimeForDownstreamCacheProviders); - } - - return $fetchedValues; - } - - $traversedProviders[] = $cacheProvider; - } - - return $fetchedValues; - } - - /** - * {@inheritDoc} - */ - protected function doContains($id) - { - foreach ($this->cacheProviders as $cacheProvider) { - if ($cacheProvider->doContains($id)) { - return true; - } - } - - return false; - } - - /** - * {@inheritDoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - $stored = true; - - foreach ($this->cacheProviders as $cacheProvider) { - $stored = $cacheProvider->doSave($id, $data, $lifeTime) && $stored; - } - - return $stored; - } - - /** - * {@inheritdoc} - */ - protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) - { - $stored = true; - - foreach ($this->cacheProviders as $cacheProvider) { - $stored = $cacheProvider->doSaveMultiple($keysAndValues, $lifetime) && $stored; - } - - return $stored; - } - - /** - * {@inheritDoc} - */ - protected function doDelete($id) - { - $deleted = true; - - foreach ($this->cacheProviders as $cacheProvider) { - $deleted = $cacheProvider->doDelete($id) && $deleted; - } - - return $deleted; - } - - /** - * {@inheritdoc} - */ - protected function doDeleteMultiple(array $keys) - { - $deleted = true; - - foreach ($this->cacheProviders as $cacheProvider) { - $deleted = $cacheProvider->doDeleteMultiple($keys) && $deleted; - } - - return $deleted; - } - - /** - * {@inheritDoc} - */ - protected function doFlush() - { - $flushed = true; - - foreach ($this->cacheProviders as $cacheProvider) { - $flushed = $cacheProvider->doFlush() && $flushed; - } - - return $flushed; - } - - /** - * {@inheritDoc} - */ - protected function doGetStats() - { - // We return all the stats from all adapters - $stats = []; - - foreach ($this->cacheProviders as $cacheProvider) { - $stats[] = $cacheProvider->doGetStats(); - } - - return $stats; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseBucketCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseBucketCache.php deleted file mode 100644 index b27720c3..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseBucketCache.php +++ /dev/null @@ -1,197 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Doctrine\Common\Cache; - -use Couchbase\Bucket; -use Couchbase\Document; -use Couchbase\Exception; -use RuntimeException; -use function phpversion; -use function serialize; -use function sprintf; -use function substr; -use function time; -use function unserialize; -use function version_compare; - -/** - * Couchbase ^2.3.0 cache provider. - */ -final class CouchbaseBucketCache extends CacheProvider -{ - private const MINIMUM_VERSION = '2.3.0'; - - private const KEY_NOT_FOUND = 13; - - private const MAX_KEY_LENGTH = 250; - - private const THIRTY_DAYS_IN_SECONDS = 2592000; - - /** @var Bucket */ - private $bucket; - - public function __construct(Bucket $bucket) - { - if (version_compare(phpversion('couchbase'), self::MINIMUM_VERSION) < 0) { - // Manager is required to flush cache and pull stats. - throw new RuntimeException(sprintf('ext-couchbase:^%s is required.', self::MINIMUM_VERSION)); - } - - $this->bucket = $bucket; - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - $id = $this->normalizeKey($id); - - try { - $document = $this->bucket->get($id); - } catch (Exception $e) { - return false; - } - - if ($document instanceof Document && $document->value !== false) { - return unserialize($document->value); - } - - return false; - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - $id = $this->normalizeKey($id); - - try { - $document = $this->bucket->get($id); - } catch (Exception $e) { - return false; - } - - if ($document instanceof Document) { - return ! $document->error; - } - - return false; - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - $id = $this->normalizeKey($id); - - $lifeTime = $this->normalizeExpiry($lifeTime); - - try { - $encoded = serialize($data); - - $document = $this->bucket->upsert($id, $encoded, [ - 'expiry' => (int) $lifeTime, - ]); - } catch (Exception $e) { - return false; - } - - if ($document instanceof Document) { - return ! $document->error; - } - - return false; - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - $id = $this->normalizeKey($id); - - try { - $document = $this->bucket->remove($id); - } catch (Exception $e) { - return $e->getCode() === self::KEY_NOT_FOUND; - } - - if ($document instanceof Document) { - return ! $document->error; - } - - return false; - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - $manager = $this->bucket->manager(); - - // Flush does not return with success or failure, and must be enabled per bucket on the server. - // Store a marker item so that we will know if it was successful. - $this->doSave(__METHOD__, true, 60); - - $manager->flush(); - - if ($this->doContains(__METHOD__)) { - $this->doDelete(__METHOD__); - - return false; - } - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $manager = $this->bucket->manager(); - $stats = $manager->info(); - $nodes = $stats['nodes']; - $node = $nodes[0]; - $interestingStats = $node['interestingStats']; - - return [ - Cache::STATS_HITS => $interestingStats['get_hits'], - Cache::STATS_MISSES => $interestingStats['cmd_get'] - $interestingStats['get_hits'], - Cache::STATS_UPTIME => $node['uptime'], - Cache::STATS_MEMORY_USAGE => $interestingStats['mem_used'], - Cache::STATS_MEMORY_AVAILABLE => $node['memoryFree'], - ]; - } - - private function normalizeKey(string $id) : string - { - $normalized = substr($id, 0, self::MAX_KEY_LENGTH); - - if ($normalized === false) { - return $id; - } - - return $normalized; - } - - /** - * Expiry treated as a unix timestamp instead of an offset if expiry is greater than 30 days. - * - * @src https://developer.couchbase.com/documentation/server/4.1/developer-guide/expiry.html - */ - private function normalizeExpiry(int $expiry) : int - { - if ($expiry > self::THIRTY_DAYS_IN_SECONDS) { - return time() + $expiry; - } - - return $expiry; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php deleted file mode 100644 index 2c62c2ea..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use Couchbase; -use function explode; -use function time; - -/** - * Couchbase cache provider. - * - * @deprecated Couchbase SDK 1.x is now deprecated. Use \Doctrine\Common\Cache\CouchbaseBucketCache instead. - * https://developer.couchbase.com/documentation/server/current/sdk/php/compatibility-versions-features.html - * - * @link www.doctrine-project.org - */ -class CouchbaseCache extends CacheProvider -{ - /** @var Couchbase|null */ - private $couchbase; - - /** - * Sets the Couchbase instance to use. - * - * @return void - */ - public function setCouchbase(Couchbase $couchbase) - { - $this->couchbase = $couchbase; - } - - /** - * Gets the Couchbase instance used by the cache. - * - * @return Couchbase|null - */ - public function getCouchbase() - { - return $this->couchbase; - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return $this->couchbase->get($id) ?: false; - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return $this->couchbase->get($id) !== null; - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - if ($lifeTime > 30 * 24 * 3600) { - $lifeTime = time() + $lifeTime; - } - - return $this->couchbase->set($id, $data, (int) $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - return $this->couchbase->delete($id); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - return $this->couchbase->flush(); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $stats = $this->couchbase->getStats(); - $servers = $this->couchbase->getServers(); - $server = explode(':', $servers[0]); - $key = $server[0] . ':11210'; - $stats = $stats[$key]; - - return [ - Cache::STATS_HITS => $stats['get_hits'], - Cache::STATS_MISSES => $stats['get_misses'], - Cache::STATS_UPTIME => $stats['uptime'], - Cache::STATS_MEMORY_USAGE => $stats['bytes'], - Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], - ]; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/ExtMongoDBCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/ExtMongoDBCache.php deleted file mode 100644 index b06f8623..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/ExtMongoDBCache.php +++ /dev/null @@ -1,198 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Doctrine\Common\Cache; - -use DateTime; -use MongoDB\BSON\Binary; -use MongoDB\BSON\UTCDateTime; -use MongoDB\Collection; -use MongoDB\Database; -use MongoDB\Driver\Exception\Exception; -use MongoDB\Model\BSONDocument; -use function serialize; -use function time; -use function unserialize; - -/** - * MongoDB cache provider for ext-mongodb - * - * @internal Do not use - will be removed in 2.0. Use MongoDBCache instead - */ -class ExtMongoDBCache extends CacheProvider -{ - /** @var Database */ - private $database; - - /** @var Collection */ - private $collection; - - /** @var bool */ - private $expirationIndexCreated = false; - - /** - * This provider will default to the write concern and read preference - * options set on the Database instance (or inherited from MongoDB or - * Client). Using an unacknowledged write concern (< 1) may make the return - * values of delete() and save() unreliable. Reading from secondaries may - * make contain() and fetch() unreliable. - * - * @see http://www.php.net/manual/en/mongo.readpreferences.php - * @see http://www.php.net/manual/en/mongo.writeconcerns.php - */ - public function __construct(Collection $collection) - { - // Ensure there is no typemap set - we want to use our own - $this->collection = $collection->withOptions(['typeMap' => null]); - $this->database = new Database($collection->getManager(), $collection->getDatabaseName()); - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::DATA_FIELD, MongoDBCache::EXPIRATION_FIELD]); - - if ($document === null) { - return false; - } - - if ($this->isExpired($document)) { - $this->createExpirationIndex(); - $this->doDelete($id); - - return false; - } - - return unserialize($document[MongoDBCache::DATA_FIELD]->getData()); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::EXPIRATION_FIELD]); - - if ($document === null) { - return false; - } - - if ($this->isExpired($document)) { - $this->createExpirationIndex(); - $this->doDelete($id); - - return false; - } - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - try { - $this->collection->updateOne( - ['_id' => $id], - [ - '$set' => [ - MongoDBCache::EXPIRATION_FIELD => ($lifeTime > 0 ? new UTCDateTime((time() + $lifeTime) * 1000): null), - MongoDBCache::DATA_FIELD => new Binary(serialize($data), Binary::TYPE_GENERIC), - ], - ], - ['upsert' => true] - ); - } catch (Exception $e) { - return false; - } - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - try { - $this->collection->deleteOne(['_id' => $id]); - } catch (Exception $e) { - return false; - } - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - try { - // Use remove() in lieu of drop() to maintain any collection indexes - $this->collection->deleteMany([]); - } catch (Exception $e) { - return false; - } - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $uptime = null; - $memoryUsage = null; - - try { - $serverStatus = $this->database->command([ - 'serverStatus' => 1, - 'locks' => 0, - 'metrics' => 0, - 'recordStats' => 0, - 'repl' => 0, - ])->toArray()[0]; - $uptime = $serverStatus['uptime'] ?? null; - } catch (Exception $e) { - } - - try { - $collStats = $this->database->command(['collStats' => $this->collection->getCollectionName()])->toArray()[0]; - $memoryUsage = $collStats['size'] ?? null; - } catch (Exception $e) { - } - - return [ - Cache::STATS_HITS => null, - Cache::STATS_MISSES => null, - Cache::STATS_UPTIME => $uptime, - Cache::STATS_MEMORY_USAGE => $memoryUsage, - Cache::STATS_MEMORY_AVAILABLE => null, - ]; - } - - /** - * Check if the document is expired. - */ - private function isExpired(BSONDocument $document) : bool - { - return isset($document[MongoDBCache::EXPIRATION_FIELD]) && - $document[MongoDBCache::EXPIRATION_FIELD] instanceof UTCDateTime && - $document[MongoDBCache::EXPIRATION_FIELD]->toDateTime() < new DateTime(); - } - - private function createExpirationIndex() : void - { - if ($this->expirationIndexCreated) { - return; - } - - $this->collection->createIndex([MongoDBCache::EXPIRATION_FIELD => 1], ['background' => true, 'expireAfterSeconds' => 0]); - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php deleted file mode 100644 index 4b485205..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php +++ /dev/null @@ -1,281 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use FilesystemIterator; -use InvalidArgumentException; -use Iterator; -use RecursiveDirectoryIterator; -use RecursiveIteratorIterator; -use const DIRECTORY_SEPARATOR; -use const PATHINFO_DIRNAME; -use function bin2hex; -use function chmod; -use function defined; -use function disk_free_space; -use function file_exists; -use function file_put_contents; -use function gettype; -use function hash; -use function is_dir; -use function is_int; -use function is_writable; -use function mkdir; -use function pathinfo; -use function realpath; -use function rename; -use function rmdir; -use function sprintf; -use function strlen; -use function strrpos; -use function substr; -use function tempnam; -use function unlink; - -/** - * Base file cache driver. - */ -abstract class FileCache extends CacheProvider -{ - /** - * The cache directory. - * - * @var string - */ - protected $directory; - - /** - * The cache file extension. - * - * @var string - */ - private $extension; - - /** @var int */ - private $umask; - - /** @var int */ - private $directoryStringLength; - - /** @var int */ - private $extensionStringLength; - - /** @var bool */ - private $isRunningOnWindows; - - /** - * @param string $directory The cache directory. - * @param string $extension The cache file extension. - * - * @throws InvalidArgumentException - */ - public function __construct($directory, $extension = '', $umask = 0002) - { - // YES, this needs to be *before* createPathIfNeeded() - if (! is_int($umask)) { - throw new InvalidArgumentException(sprintf( - 'The umask parameter is required to be integer, was: %s', - gettype($umask) - )); - } - $this->umask = $umask; - - if (! $this->createPathIfNeeded($directory)) { - throw new InvalidArgumentException(sprintf( - 'The directory "%s" does not exist and could not be created.', - $directory - )); - } - - if (! is_writable($directory)) { - throw new InvalidArgumentException(sprintf( - 'The directory "%s" is not writable.', - $directory - )); - } - - // YES, this needs to be *after* createPathIfNeeded() - $this->directory = realpath($directory); - $this->extension = (string) $extension; - - $this->directoryStringLength = strlen($this->directory); - $this->extensionStringLength = strlen($this->extension); - $this->isRunningOnWindows = defined('PHP_WINDOWS_VERSION_BUILD'); - } - - /** - * Gets the cache directory. - * - * @return string - */ - public function getDirectory() - { - return $this->directory; - } - - /** - * Gets the cache file extension. - * - * @return string - */ - public function getExtension() - { - return $this->extension; - } - - /** - * @param string $id - * - * @return string - */ - protected function getFilename($id) - { - $hash = hash('sha256', $id); - - // This ensures that the filename is unique and that there are no invalid chars in it. - if ($id === '' - || ((strlen($id) * 2 + $this->extensionStringLength) > 255) - || ($this->isRunningOnWindows && ($this->directoryStringLength + 4 + strlen($id) * 2 + $this->extensionStringLength) > 258) - ) { - // Most filesystems have a limit of 255 chars for each path component. On Windows the the whole path is limited - // to 260 chars (including terminating null char). Using long UNC ("\\?\" prefix) does not work with the PHP API. - // And there is a bug in PHP (https://bugs.php.net/bug.php?id=70943) with path lengths of 259. - // So if the id in hex representation would surpass the limit, we use the hash instead. The prefix prevents - // collisions between the hash and bin2hex. - $filename = '_' . $hash; - } else { - $filename = bin2hex($id); - } - - return $this->directory - . DIRECTORY_SEPARATOR - . substr($hash, 0, 2) - . DIRECTORY_SEPARATOR - . $filename - . $this->extension; - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - $filename = $this->getFilename($id); - - return @unlink($filename) || ! file_exists($filename); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - foreach ($this->getIterator() as $name => $file) { - if ($file->isDir()) { - // Remove the intermediate directories which have been created to balance the tree. It only takes effect - // if the directory is empty. If several caches share the same directory but with different file extensions, - // the other ones are not removed. - @rmdir($name); - } elseif ($this->isFilenameEndingWithExtension($name)) { - // If an extension is set, only remove files which end with the given extension. - // If no extension is set, we have no other choice than removing everything. - @unlink($name); - } - } - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $usage = 0; - foreach ($this->getIterator() as $name => $file) { - if ($file->isDir() || ! $this->isFilenameEndingWithExtension($name)) { - continue; - } - - $usage += $file->getSize(); - } - - $free = disk_free_space($this->directory); - - return [ - Cache::STATS_HITS => null, - Cache::STATS_MISSES => null, - Cache::STATS_UPTIME => null, - Cache::STATS_MEMORY_USAGE => $usage, - Cache::STATS_MEMORY_AVAILABLE => $free, - ]; - } - - /** - * Create path if needed. - * - * @return bool TRUE on success or if path already exists, FALSE if path cannot be created. - */ - private function createPathIfNeeded(string $path) : bool - { - if (! is_dir($path)) { - if (@mkdir($path, 0777 & (~$this->umask), true) === false && ! is_dir($path)) { - return false; - } - } - - return true; - } - - /** - * Writes a string content to file in an atomic way. - * - * @param string $filename Path to the file where to write the data. - * @param string $content The content to write - * - * @return bool TRUE on success, FALSE if path cannot be created, if path is not writable or an any other error. - */ - protected function writeFile(string $filename, string $content) : bool - { - $filepath = pathinfo($filename, PATHINFO_DIRNAME); - - if (! $this->createPathIfNeeded($filepath)) { - return false; - } - - if (! is_writable($filepath)) { - return false; - } - - $tmpFile = tempnam($filepath, 'swap'); - @chmod($tmpFile, 0666 & (~$this->umask)); - - if (file_put_contents($tmpFile, $content) !== false) { - @chmod($tmpFile, 0666 & (~$this->umask)); - if (@rename($tmpFile, $filename)) { - return true; - } - - @unlink($tmpFile); - } - - return false; - } - - private function getIterator() : Iterator - { - return new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($this->directory, FilesystemIterator::SKIP_DOTS), - RecursiveIteratorIterator::CHILD_FIRST - ); - } - - /** - * @param string $name The filename - */ - private function isFilenameEndingWithExtension(string $name) : bool - { - return $this->extension === '' - || strrpos($name, $this->extension) === (strlen($name) - $this->extensionStringLength); - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php deleted file mode 100644 index 8f34c9cb..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use const PHP_EOL; -use function fclose; -use function fgets; -use function fopen; -use function is_file; -use function serialize; -use function time; -use function unserialize; - -/** - * Filesystem cache driver. - */ -class FilesystemCache extends FileCache -{ - public const EXTENSION = '.doctrinecache.data'; - - /** - * {@inheritdoc} - */ - public function __construct($directory, $extension = self::EXTENSION, $umask = 0002) - { - parent::__construct($directory, $extension, $umask); - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - $data = ''; - $lifetime = -1; - $filename = $this->getFilename($id); - - if (! is_file($filename)) { - return false; - } - - $resource = fopen($filename, 'r'); - $line = fgets($resource); - - if ($line !== false) { - $lifetime = (int) $line; - } - - if ($lifetime !== 0 && $lifetime < time()) { - fclose($resource); - - return false; - } - - while (($line = fgets($resource)) !== false) { - $data .= $line; - } - - fclose($resource); - - return unserialize($data); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - $lifetime = -1; - $filename = $this->getFilename($id); - - if (! is_file($filename)) { - return false; - } - - $resource = fopen($filename, 'r'); - $line = fgets($resource); - - if ($line !== false) { - $lifetime = (int) $line; - } - - fclose($resource); - - return $lifetime === 0 || $lifetime > time(); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - if ($lifeTime > 0) { - $lifeTime = time() + $lifeTime; - } - - $data = serialize($data); - $filename = $this->getFilename($id); - - return $this->writeFile($filename, $lifeTime . PHP_EOL . $data); - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/InvalidCacheId.php b/doctrine/cache/lib/Doctrine/Common/Cache/InvalidCacheId.php deleted file mode 100644 index 0a7fe659..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/InvalidCacheId.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php -declare(strict_types=1); - -namespace Doctrine\Common\Cache; - -use InvalidArgumentException; -use function sprintf; - -final class InvalidCacheId extends InvalidArgumentException -{ - public static function exceedsMaxLength($id, int $maxLength) : self - { - return new self(sprintf('Cache id "%s" exceeds maximum length %d', $id, $maxLength)); - } - - public static function containsUnauthorizedCharacter($id, string $character) : self - { - return new self(sprintf('Cache id "%s" contains unauthorized character "%s"', $id, $character)); - } - - public static function containsControlCharacter($id) : self - { - return new self(sprintf('Cache id "%s" contains at least one control character', $id)); - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/LegacyMongoDBCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/LegacyMongoDBCache.php deleted file mode 100644 index fe5dc4b8..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/LegacyMongoDBCache.php +++ /dev/null @@ -1,175 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use MongoBinData; -use MongoCollection; -use MongoCursorException; -use MongoDate; -use const E_USER_DEPRECATED; -use function serialize; -use function time; -use function trigger_error; -use function unserialize; - -/** - * MongoDB cache provider. - * - * @internal Do not use - will be removed in 2.0. Use MongoDBCache instead - */ -class LegacyMongoDBCache extends CacheProvider -{ - /** @var MongoCollection */ - private $collection; - - /** @var bool */ - private $expirationIndexCreated = false; - - /** - * This provider will default to the write concern and read preference - * options set on the MongoCollection instance (or inherited from MongoDB or - * MongoClient). Using an unacknowledged write concern (< 1) may make the - * return values of delete() and save() unreliable. Reading from secondaries - * may make contain() and fetch() unreliable. - * - * @see http://www.php.net/manual/en/mongo.readpreferences.php - * @see http://www.php.net/manual/en/mongo.writeconcerns.php - */ - public function __construct(MongoCollection $collection) - { - @trigger_error('Using the legacy MongoDB cache provider is deprecated and will be removed in 2.0', E_USER_DEPRECATED); - $this->collection = $collection; - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::DATA_FIELD, MongoDBCache::EXPIRATION_FIELD]); - - if ($document === null) { - return false; - } - - if ($this->isExpired($document)) { - $this->createExpirationIndex(); - $this->doDelete($id); - - return false; - } - - return unserialize($document[MongoDBCache::DATA_FIELD]->bin); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::EXPIRATION_FIELD]); - - if ($document === null) { - return false; - } - - if ($this->isExpired($document)) { - $this->createExpirationIndex(); - $this->doDelete($id); - - return false; - } - - return true; - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - try { - $result = $this->collection->update( - ['_id' => $id], - [ - '$set' => [ - MongoDBCache::EXPIRATION_FIELD => ($lifeTime > 0 ? new MongoDate(time() + $lifeTime) : null), - MongoDBCache::DATA_FIELD => new MongoBinData(serialize($data), MongoBinData::BYTE_ARRAY), - ], - ], - ['upsert' => true, 'multiple' => false] - ); - } catch (MongoCursorException $e) { - return false; - } - - return ($result['ok'] ?? 1) == 1; - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - $result = $this->collection->remove(['_id' => $id]); - - return ($result['ok'] ?? 1) == 1; - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - // Use remove() in lieu of drop() to maintain any collection indexes - $result = $this->collection->remove(); - - return ($result['ok'] ?? 1) == 1; - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $serverStatus = $this->collection->db->command([ - 'serverStatus' => 1, - 'locks' => 0, - 'metrics' => 0, - 'recordStats' => 0, - 'repl' => 0, - ]); - - $collStats = $this->collection->db->command(['collStats' => 1]); - - return [ - Cache::STATS_HITS => null, - Cache::STATS_MISSES => null, - Cache::STATS_UPTIME => $serverStatus['uptime'] ?? null, - Cache::STATS_MEMORY_USAGE => $collStats['size'] ?? null, - Cache::STATS_MEMORY_AVAILABLE => null, - ]; - } - - /** - * Check if the document is expired. - * - * @param array $document - */ - private function isExpired(array $document) : bool - { - return isset($document[MongoDBCache::EXPIRATION_FIELD]) && - $document[MongoDBCache::EXPIRATION_FIELD] instanceof MongoDate && - $document[MongoDBCache::EXPIRATION_FIELD]->sec < time(); - } - - private function createExpirationIndex() : void - { - if ($this->expirationIndexCreated) { - return; - } - - $this->expirationIndexCreated = true; - $this->collection->createIndex([MongoDBCache::EXPIRATION_FIELD => 1], ['background' => true, 'expireAfterSeconds' => 0]); - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php deleted file mode 100644 index 42bbd2ce..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php +++ /dev/null @@ -1,104 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use Memcache; -use function time; - -/** - * Memcache cache provider. - * - * @deprecated - * - * @link www.doctrine-project.org - */ -class MemcacheCache extends CacheProvider -{ - /** @var Memcache|null */ - private $memcache; - - /** - * Sets the memcache instance to use. - * - * @return void - */ - public function setMemcache(Memcache $memcache) - { - $this->memcache = $memcache; - } - - /** - * Gets the memcache instance used by the cache. - * - * @return Memcache|null - */ - public function getMemcache() - { - return $this->memcache; - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return $this->memcache->get($id); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - $flags = null; - $this->memcache->get($id, $flags); - - //if memcache has changed the value of "flags", it means the value exists - return $flags !== null; - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - if ($lifeTime > 30 * 24 * 3600) { - $lifeTime = time() + $lifeTime; - } - - return $this->memcache->set($id, $data, 0, (int) $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - // Memcache::delete() returns false if entry does not exist - return $this->memcache->delete($id) || ! $this->doContains($id); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - return $this->memcache->flush(); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $stats = $this->memcache->getStats(); - - return [ - Cache::STATS_HITS => $stats['get_hits'], - Cache::STATS_MISSES => $stats['get_misses'], - Cache::STATS_UPTIME => $stats['uptime'], - Cache::STATS_MEMORY_USAGE => $stats['bytes'], - Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], - ]; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php deleted file mode 100644 index da966ae2..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php +++ /dev/null @@ -1,170 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use Memcached; -use function array_keys; -use function preg_match; -use function strlen; -use function strpos; -use function time; - -/** - * Memcached cache provider. - * - * @link www.doctrine-project.org - */ -class MemcachedCache extends CacheProvider -{ - public const CACHE_ID_MAX_LENGTH = 250; - - /** @var Memcached|null */ - private $memcached; - - /** - * Sets the memcache instance to use. - * - * @return void - */ - public function setMemcached(Memcached $memcached) - { - $this->memcached = $memcached; - } - - /** - * Gets the memcached instance used by the cache. - * - * @return Memcached|null - */ - public function getMemcached() - { - return $this->memcached; - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return $this->memcached->get($id); - } - - /** - * {@inheritdoc} - */ - protected function doFetchMultiple(array $keys) - { - return $this->memcached->getMulti($keys) ?: []; - } - - /** - * {@inheritdoc} - */ - protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) - { - foreach (array_keys($keysAndValues) as $id) { - $this->validateCacheId($id); - } - - if ($lifetime > 30 * 24 * 3600) { - $lifetime = time() + $lifetime; - } - - return $this->memcached->setMulti($keysAndValues, $lifetime); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - $this->memcached->get($id); - - return $this->memcached->getResultCode() === Memcached::RES_SUCCESS; - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - $this->validateCacheId($id); - - if ($lifeTime > 30 * 24 * 3600) { - $lifeTime = time() + $lifeTime; - } - - return $this->memcached->set($id, $data, (int) $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDeleteMultiple(array $keys) - { - return $this->memcached->deleteMulti($keys) - || $this->memcached->getResultCode() === Memcached::RES_NOTFOUND; - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - return $this->memcached->delete($id) - || $this->memcached->getResultCode() === Memcached::RES_NOTFOUND; - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - return $this->memcached->flush(); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $stats = $this->memcached->getStats(); - $servers = $this->memcached->getServerList(); - $key = $servers[0]['host'] . ':' . $servers[0]['port']; - $stats = $stats[$key]; - - return [ - Cache::STATS_HITS => $stats['get_hits'], - Cache::STATS_MISSES => $stats['get_misses'], - Cache::STATS_UPTIME => $stats['uptime'], - Cache::STATS_MEMORY_USAGE => $stats['bytes'], - Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], - ]; - } - - /** - * Validate the cache id - * - * @see https://github.com/memcached/memcached/blob/1.5.12/doc/protocol.txt#L41-L49 - * - * @param string $id - * - * @return void - * - * @throws InvalidCacheId - */ - private function validateCacheId($id) - { - if (strlen($id) > self::CACHE_ID_MAX_LENGTH) { - throw InvalidCacheId::exceedsMaxLength($id, self::CACHE_ID_MAX_LENGTH); - } - - if (strpos($id, ' ') !== false) { - throw InvalidCacheId::containsUnauthorizedCharacter($id, ' '); - } - - if (preg_match('/[\t\r\n]/', $id) === 1) { - throw InvalidCacheId::containsControlCharacter($id); - } - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php deleted file mode 100644 index 861e4dda..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php +++ /dev/null @@ -1,112 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use InvalidArgumentException; -use MongoCollection; -use MongoDB\Collection; -use const E_USER_DEPRECATED; -use function trigger_error; - -/** - * MongoDB cache provider. - */ -class MongoDBCache extends CacheProvider -{ - /** - * The data field will store the serialized PHP value. - */ - public const DATA_FIELD = 'd'; - - /** - * The expiration field will store a MongoDate value indicating when the - * cache entry should expire. - * - * With MongoDB 2.2+, entries can be automatically deleted by MongoDB by - * indexing this field with the "expireAfterSeconds" option equal to zero. - * This will direct MongoDB to regularly query for and delete any entries - * whose date is older than the current time. Entries without a date value - * in this field will be ignored. - * - * The cache provider will also check dates on its own, in case expired - * entries are fetched before MongoDB's TTLMonitor pass can expire them. - * - * @see http://docs.mongodb.org/manual/tutorial/expire-data/ - */ - public const EXPIRATION_FIELD = 'e'; - - /** @var CacheProvider */ - private $provider; - - /** - * This provider will default to the write concern and read preference - * options set on the collection instance (or inherited from MongoDB or - * MongoClient). Using an unacknowledged write concern (< 1) may make the - * return values of delete() and save() unreliable. Reading from secondaries - * may make contain() and fetch() unreliable. - * - * @see http://www.php.net/manual/en/mongo.readpreferences.php - * @see http://www.php.net/manual/en/mongo.writeconcerns.php - * - * @param MongoCollection|Collection $collection - */ - public function __construct($collection) - { - if ($collection instanceof MongoCollection) { - @trigger_error('Using a MongoCollection instance for creating a cache adapter is deprecated and will be removed in 2.0', E_USER_DEPRECATED); - $this->provider = new LegacyMongoDBCache($collection); - } elseif ($collection instanceof Collection) { - $this->provider = new ExtMongoDBCache($collection); - } else { - throw new InvalidArgumentException('Invalid collection given - expected a MongoCollection or MongoDB\Collection instance'); - } - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return $this->provider->doFetch($id); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return $this->provider->doContains($id); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - return $this->provider->doSave($id, $data, $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - return $this->provider->doDelete($id); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - return $this->provider->doFlush(); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - return $this->provider->doGetStats(); - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php index 6f059c16..b7c06d39 100644 --- a/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php +++ b/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php @@ -14,9 +14,9 @@ interface MultiPutCache /** * Returns a boolean value indicating if the operation succeeded. * - * @param array $keysAndValues Array of keys and values to save in cache - * @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these - * cache entries (0 => infinite lifeTime). + * @param mixed[] $keysAndValues Array of keys and values to save in cache + * @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these + * cache entries (0 => infinite lifeTime). * * @return bool TRUE if the operation was successful, FALSE if it wasn't. */ diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php deleted file mode 100644 index c0619991..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php +++ /dev/null @@ -1,118 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use function is_object; -use function method_exists; -use function restore_error_handler; -use function serialize; -use function set_error_handler; -use function sprintf; -use function time; -use function var_export; - -/** - * Php file cache driver. - */ -class PhpFileCache extends FileCache -{ - public const EXTENSION = '.doctrinecache.php'; - - /** - * @var callable - * - * This is cached in a local static variable to avoid instantiating a closure each time we need an empty handler - */ - private static $emptyErrorHandler; - - /** - * {@inheritdoc} - */ - public function __construct($directory, $extension = self::EXTENSION, $umask = 0002) - { - parent::__construct($directory, $extension, $umask); - - self::$emptyErrorHandler = static function () { - }; - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - $value = $this->includeFileForId($id); - - if ($value === null) { - return false; - } - - if ($value['lifetime'] !== 0 && $value['lifetime'] < time()) { - return false; - } - - return $value['data']; - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - $value = $this->includeFileForId($id); - - if ($value === null) { - return false; - } - - return $value['lifetime'] === 0 || $value['lifetime'] > time(); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - if ($lifeTime > 0) { - $lifeTime = time() + $lifeTime; - } - - $filename = $this->getFilename($id); - - $value = [ - 'lifetime' => $lifeTime, - 'data' => $data, - ]; - - if (is_object($data) && method_exists($data, '__set_state')) { - $value = var_export($value, true); - $code = sprintf('<?php return %s;', $value); - } else { - $value = var_export(serialize($value), true); - $code = sprintf('<?php return unserialize(%s);', $value); - } - - return $this->writeFile($filename, $code); - } - - /** - * @return array|null - */ - private function includeFileForId(string $id) : ?array - { - $fileName = $this->getFilename($id); - - // note: error suppression is still faster than `file_exists`, `is_file` and `is_readable` - set_error_handler(self::$emptyErrorHandler); - - $value = include $fileName; - - restore_error_handler(); - - if (! isset($value['lifetime'])) { - return null; - } - - return $value; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php deleted file mode 100644 index 6430d52d..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php +++ /dev/null @@ -1,143 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use Predis\ClientInterface; -use function array_combine; -use function array_filter; -use function array_map; -use function call_user_func_array; -use function serialize; -use function unserialize; - -/** - * Predis cache provider. - */ -class PredisCache extends CacheProvider -{ - /** @var ClientInterface */ - private $client; - - public function __construct(ClientInterface $client) - { - $this->client = $client; - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - $result = $this->client->get($id); - if ($result === null) { - return false; - } - - return unserialize($result); - } - - /** - * {@inheritdoc} - */ - protected function doFetchMultiple(array $keys) - { - $fetchedItems = call_user_func_array([$this->client, 'mget'], $keys); - - return array_map('unserialize', array_filter(array_combine($keys, $fetchedItems))); - } - - /** - * {@inheritdoc} - */ - protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) - { - if ($lifetime) { - $success = true; - - // Keys have lifetime, use SETEX for each of them - foreach ($keysAndValues as $key => $value) { - $response = (string) $this->client->setex($key, $lifetime, serialize($value)); - - if ($response == 'OK') { - continue; - } - - $success = false; - } - - return $success; - } - - // No lifetime, use MSET - $response = $this->client->mset(array_map(static function ($value) { - return serialize($value); - }, $keysAndValues)); - - return (string) $response == 'OK'; - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return (bool) $this->client->exists($id); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - $data = serialize($data); - if ($lifeTime > 0) { - $response = $this->client->setex($id, $lifeTime, $data); - } else { - $response = $this->client->set($id, $data); - } - - return $response === true || $response == 'OK'; - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - return $this->client->del($id) >= 0; - } - - /** - * {@inheritdoc} - */ - protected function doDeleteMultiple(array $keys) - { - return $this->client->del($keys) >= 0; - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - $response = $this->client->flushdb(); - - return $response === true || $response == 'OK'; - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $info = $this->client->info(); - - return [ - Cache::STATS_HITS => $info['Stats']['keyspace_hits'], - Cache::STATS_MISSES => $info['Stats']['keyspace_misses'], - Cache::STATS_UPTIME => $info['Server']['uptime_in_seconds'], - Cache::STATS_MEMORY_USAGE => $info['Memory']['used_memory'], - Cache::STATS_MEMORY_AVAILABLE => false, - ]; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php new file mode 100644 index 00000000..d3693b7c --- /dev/null +++ b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php @@ -0,0 +1,340 @@ +<?php + +namespace Doctrine\Common\Cache\Psr6; + +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Cache\ClearableCache; +use Doctrine\Common\Cache\MultiDeleteCache; +use Doctrine\Common\Cache\MultiGetCache; +use Doctrine\Common\Cache\MultiPutCache; +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\DoctrineProvider as SymfonyDoctrineProvider; + +use function array_key_exists; +use function assert; +use function count; +use function current; +use function get_class; +use function gettype; +use function is_object; +use function is_string; +use function microtime; +use function sprintf; +use function strpbrk; + +use const PHP_VERSION_ID; + +final class CacheAdapter implements CacheItemPoolInterface +{ + private const RESERVED_CHARACTERS = '{}()/\@:'; + + /** @var Cache */ + private $cache; + + /** @var array<CacheItem|TypedCacheItem> */ + private $deferredItems = []; + + public static function wrap(Cache $cache): CacheItemPoolInterface + { + if ($cache instanceof DoctrineProvider && ! $cache->getNamespace()) { + return $cache->getPool(); + } + + if ($cache instanceof SymfonyDoctrineProvider && ! $cache->getNamespace()) { + $getPool = function () { + // phpcs:ignore Squiz.Scope.StaticThisUsage.Found + return $this->pool; + }; + + return $getPool->bindTo($cache, SymfonyDoctrineProvider::class)(); + } + + return new self($cache); + } + + private function __construct(Cache $cache) + { + $this->cache = $cache; + } + + /** @internal */ + public function getCache(): Cache + { + return $this->cache; + } + + /** + * {@inheritDoc} + */ + public function getItem($key): CacheItemInterface + { + assert(self::validKey($key)); + + if (isset($this->deferredItems[$key])) { + $this->commit(); + } + + $value = $this->cache->fetch($key); + + if (PHP_VERSION_ID >= 80000) { + if ($value !== false) { + return new TypedCacheItem($key, $value, true); + } + + return new TypedCacheItem($key, null, false); + } + + if ($value !== false) { + return new CacheItem($key, $value, true); + } + + return new CacheItem($key, null, false); + } + + /** + * {@inheritDoc} + */ + public function getItems(array $keys = []): array + { + if ($this->deferredItems) { + $this->commit(); + } + + assert(self::validKeys($keys)); + + $values = $this->doFetchMultiple($keys); + $items = []; + + if (PHP_VERSION_ID >= 80000) { + foreach ($keys as $key) { + if (array_key_exists($key, $values)) { + $items[$key] = new TypedCacheItem($key, $values[$key], true); + } else { + $items[$key] = new TypedCacheItem($key, null, false); + } + } + + return $items; + } + + foreach ($keys as $key) { + if (array_key_exists($key, $values)) { + $items[$key] = new CacheItem($key, $values[$key], true); + } else { + $items[$key] = new CacheItem($key, null, false); + } + } + + return $items; + } + + /** + * {@inheritDoc} + */ + public function hasItem($key): bool + { + assert(self::validKey($key)); + + if (isset($this->deferredItems[$key])) { + $this->commit(); + } + + return $this->cache->contains($key); + } + + public function clear(): bool + { + $this->deferredItems = []; + + if (! $this->cache instanceof ClearableCache) { + return false; + } + + return $this->cache->deleteAll(); + } + + /** + * {@inheritDoc} + */ + public function deleteItem($key): bool + { + assert(self::validKey($key)); + unset($this->deferredItems[$key]); + + return $this->cache->delete($key); + } + + /** + * {@inheritDoc} + */ + public function deleteItems(array $keys): bool + { + foreach ($keys as $key) { + assert(self::validKey($key)); + unset($this->deferredItems[$key]); + } + + return $this->doDeleteMultiple($keys); + } + + public function save(CacheItemInterface $item): bool + { + return $this->saveDeferred($item) && $this->commit(); + } + + public function saveDeferred(CacheItemInterface $item): bool + { + if (! $item instanceof CacheItem && ! $item instanceof TypedCacheItem) { + return false; + } + + $this->deferredItems[$item->getKey()] = $item; + + return true; + } + + public function commit(): bool + { + if (! $this->deferredItems) { + return true; + } + + $now = microtime(true); + $itemsCount = 0; + $byLifetime = []; + $expiredKeys = []; + + foreach ($this->deferredItems as $key => $item) { + $lifetime = ($item->getExpiry() ?? $now) - $now; + + if ($lifetime < 0) { + $expiredKeys[] = $key; + + continue; + } + + ++$itemsCount; + $byLifetime[(int) $lifetime][$key] = $item->get(); + } + + $this->deferredItems = []; + + switch (count($expiredKeys)) { + case 0: + break; + case 1: + $this->cache->delete(current($expiredKeys)); + break; + default: + $this->doDeleteMultiple($expiredKeys); + break; + } + + if ($itemsCount === 1) { + return $this->cache->save($key, $item->get(), (int) $lifetime); + } + + $success = true; + foreach ($byLifetime as $lifetime => $values) { + $success = $this->doSaveMultiple($values, $lifetime) && $success; + } + + return $success; + } + + public function __destruct() + { + $this->commit(); + } + + /** + * @param mixed $key + */ + private static function validKey($key): bool + { + if (! is_string($key)) { + throw new InvalidArgument(sprintf('Cache key must be string, "%s" given.', is_object($key) ? get_class($key) : gettype($key))); + } + + if ($key === '') { + throw new InvalidArgument('Cache key length must be greater than zero.'); + } + + if (strpbrk($key, self::RESERVED_CHARACTERS) !== false) { + throw new InvalidArgument(sprintf('Cache key "%s" contains reserved characters "%s".', $key, self::RESERVED_CHARACTERS)); + } + + return true; + } + + /** + * @param mixed[] $keys + */ + private static function validKeys(array $keys): bool + { + foreach ($keys as $key) { + self::validKey($key); + } + + return true; + } + + /** + * @param mixed[] $keys + */ + private function doDeleteMultiple(array $keys): bool + { + if ($this->cache instanceof MultiDeleteCache) { + return $this->cache->deleteMultiple($keys); + } + + $success = true; + foreach ($keys as $key) { + $success = $this->cache->delete($key) && $success; + } + + return $success; + } + + /** + * @param mixed[] $keys + * + * @return mixed[] + */ + private function doFetchMultiple(array $keys): array + { + if ($this->cache instanceof MultiGetCache) { + return $this->cache->fetchMultiple($keys); + } + + $values = []; + foreach ($keys as $key) { + $value = $this->cache->fetch($key); + if (! $value) { + continue; + } + + $values[$key] = $value; + } + + return $values; + } + + /** + * @param mixed[] $keysAndValues + */ + private function doSaveMultiple(array $keysAndValues, int $lifetime = 0): bool + { + if ($this->cache instanceof MultiPutCache) { + return $this->cache->saveMultiple($keysAndValues, $lifetime); + } + + $success = true; + foreach ($keysAndValues as $key => $value) { + $success = $this->cache->save($key, $value, $lifetime) && $success; + } + + return $success; + } +} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheItem.php b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheItem.php new file mode 100644 index 00000000..0b6f0a28 --- /dev/null +++ b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheItem.php @@ -0,0 +1,118 @@ +<?php + +namespace Doctrine\Common\Cache\Psr6; + +use DateInterval; +use DateTime; +use DateTimeInterface; +use Psr\Cache\CacheItemInterface; +use TypeError; + +use function get_class; +use function gettype; +use function is_int; +use function is_object; +use function microtime; +use function sprintf; + +final class CacheItem implements CacheItemInterface +{ + /** @var string */ + private $key; + /** @var mixed */ + private $value; + /** @var bool */ + private $isHit; + /** @var float|null */ + private $expiry; + + /** + * @internal + * + * @param mixed $data + */ + public function __construct(string $key, $data, bool $isHit) + { + $this->key = $key; + $this->value = $data; + $this->isHit = $isHit; + } + + public function getKey(): string + { + return $this->key; + } + + /** + * {@inheritDoc} + * + * @return mixed + */ + public function get() + { + return $this->value; + } + + public function isHit(): bool + { + return $this->isHit; + } + + /** + * {@inheritDoc} + */ + public function set($value): self + { + $this->value = $value; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function expiresAt($expiration): self + { + if ($expiration === null) { + $this->expiry = null; + } elseif ($expiration instanceof DateTimeInterface) { + $this->expiry = (float) $expiration->format('U.u'); + } else { + throw new TypeError(sprintf( + 'Expected $expiration to be an instance of DateTimeInterface or null, got %s', + is_object($expiration) ? get_class($expiration) : gettype($expiration) + )); + } + + return $this; + } + + /** + * {@inheritDoc} + */ + public function expiresAfter($time): self + { + if ($time === null) { + $this->expiry = null; + } elseif ($time instanceof DateInterval) { + $this->expiry = microtime(true) + DateTime::createFromFormat('U', 0)->add($time)->format('U.u'); + } elseif (is_int($time)) { + $this->expiry = $time + microtime(true); + } else { + throw new TypeError(sprintf( + 'Expected $time to be either an integer, an instance of DateInterval or null, got %s', + is_object($time) ? get_class($time) : gettype($time) + )); + } + + return $this; + } + + /** + * @internal + */ + public function getExpiry(): ?float + { + return $this->expiry; + } +} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php new file mode 100644 index 00000000..c15497e5 --- /dev/null +++ b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php @@ -0,0 +1,125 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Common\Cache\Psr6; + +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Cache\CacheProvider; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\DoctrineAdapter as SymfonyDoctrineAdapter; + +use function rawurlencode; + +/** + * This class was copied from the Symfony Framework, see the original copyright + * notice above. The code is distributed subject to the license terms in + * https://github.com/symfony/symfony/blob/ff0cf61278982539c49e467db9ab13cbd342f76d/LICENSE + */ +final class DoctrineProvider extends CacheProvider +{ + /** @var CacheItemPoolInterface */ + private $pool; + + public static function wrap(CacheItemPoolInterface $pool): Cache + { + if ($pool instanceof CacheAdapter) { + return $pool->getCache(); + } + + if ($pool instanceof SymfonyDoctrineAdapter) { + $getCache = function () { + // phpcs:ignore Squiz.Scope.StaticThisUsage.Found + return $this->provider; + }; + + return $getCache->bindTo($pool, SymfonyDoctrineAdapter::class)(); + } + + return new self($pool); + } + + private function __construct(CacheItemPoolInterface $pool) + { + $this->pool = $pool; + } + + /** @internal */ + public function getPool(): CacheItemPoolInterface + { + return $this->pool; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $item = $this->pool->getItem(rawurlencode($id)); + + return $item->isHit() ? $item->get() : false; + } + + /** + * {@inheritdoc} + * + * @return bool + */ + protected function doContains($id) + { + return $this->pool->hasItem(rawurlencode($id)); + } + + /** + * {@inheritdoc} + * + * @return bool + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $item = $this->pool->getItem(rawurlencode($id)); + + if (0 < $lifeTime) { + $item->expiresAfter($lifeTime); + } + + return $this->pool->save($item->set($data)); + } + + /** + * {@inheritdoc} + * + * @return bool + */ + protected function doDelete($id) + { + return $this->pool->deleteItem(rawurlencode($id)); + } + + /** + * {@inheritdoc} + * + * @return bool + */ + protected function doFlush() + { + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + * + * @return array|null + */ + protected function doGetStats() + { + return null; + } +} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php new file mode 100644 index 00000000..196f1bca --- /dev/null +++ b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php @@ -0,0 +1,13 @@ +<?php + +namespace Doctrine\Common\Cache\Psr6; + +use InvalidArgumentException; +use Psr\Cache\InvalidArgumentException as PsrInvalidArgumentException; + +/** + * @internal + */ +final class InvalidArgument extends InvalidArgumentException implements PsrInvalidArgumentException +{ +} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/TypedCacheItem.php b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/TypedCacheItem.php new file mode 100644 index 00000000..60f37967 --- /dev/null +++ b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/TypedCacheItem.php @@ -0,0 +1,99 @@ +<?php + +namespace Doctrine\Common\Cache\Psr6; + +use DateInterval; +use DateTime; +use DateTimeInterface; +use Psr\Cache\CacheItemInterface; +use TypeError; + +use function get_debug_type; +use function is_int; +use function microtime; +use function sprintf; + +final class TypedCacheItem implements CacheItemInterface +{ + private ?float $expiry = null; + + /** + * @internal + */ + public function __construct( + private string $key, + private mixed $value, + private bool $isHit, + ) { + } + + public function getKey(): string + { + return $this->key; + } + + public function get(): mixed + { + return $this->value; + } + + public function isHit(): bool + { + return $this->isHit; + } + + public function set(mixed $value): static + { + $this->value = $value; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function expiresAt($expiration): static + { + if ($expiration === null) { + $this->expiry = null; + } elseif ($expiration instanceof DateTimeInterface) { + $this->expiry = (float) $expiration->format('U.u'); + } else { + throw new TypeError(sprintf( + 'Expected $expiration to be an instance of DateTimeInterface or null, got %s', + get_debug_type($expiration) + )); + } + + return $this; + } + + /** + * {@inheritDoc} + */ + public function expiresAfter($time): static + { + if ($time === null) { + $this->expiry = null; + } elseif ($time instanceof DateInterval) { + $this->expiry = microtime(true) + DateTime::createFromFormat('U', 0)->add($time)->format('U.u'); + } elseif (is_int($time)) { + $this->expiry = $time + microtime(true); + } else { + throw new TypeError(sprintf( + 'Expected $time to be either an integer, an instance of DateInterval or null, got %s', + get_debug_type($time) + )); + } + + return $this; + } + + /** + * @internal + */ + public function getExpiry(): ?float + { + return $this->expiry; + } +} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php deleted file mode 100644 index 32457058..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php +++ /dev/null @@ -1,181 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use Redis; -use function array_combine; -use function array_diff_key; -use function array_fill_keys; -use function array_filter; -use function array_keys; -use function count; -use function defined; -use function extension_loaded; -use function is_bool; - -/** - * Redis cache provider. - * - * @link www.doctrine-project.org - */ -class RedisCache extends CacheProvider -{ - /** @var Redis|null */ - private $redis; - - /** - * Sets the redis instance to use. - * - * @return void - */ - public function setRedis(Redis $redis) - { - $redis->setOption(Redis::OPT_SERIALIZER, $this->getSerializerValue()); - $this->redis = $redis; - } - - /** - * Gets the redis instance used by the cache. - * - * @return Redis|null - */ - public function getRedis() - { - return $this->redis; - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return $this->redis->get($id); - } - - /** - * {@inheritdoc} - */ - protected function doFetchMultiple(array $keys) - { - $fetchedItems = array_combine($keys, $this->redis->mget($keys)); - - // Redis mget returns false for keys that do not exist. So we need to filter those out unless it's the real data. - $keysToFilter = array_keys(array_filter($fetchedItems, static function ($item) : bool { - return $item === false; - })); - - if ($keysToFilter) { - $multi = $this->redis->multi(Redis::PIPELINE); - foreach ($keysToFilter as $key) { - $multi->exists($key); - } - $existItems = array_filter($multi->exec()); - $missedItemKeys = array_diff_key($keysToFilter, $existItems); - $fetchedItems = array_diff_key($fetchedItems, array_fill_keys($missedItemKeys, true)); - } - - return $fetchedItems; - } - - /** - * {@inheritdoc} - */ - protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) - { - if ($lifetime) { - // Keys have lifetime, use SETEX for each of them - $multi = $this->redis->multi(Redis::PIPELINE); - foreach ($keysAndValues as $key => $value) { - $multi->setex($key, $lifetime, $value); - } - $succeeded = array_filter($multi->exec()); - - return count($succeeded) == count($keysAndValues); - } - - // No lifetime, use MSET - return (bool) $this->redis->mset($keysAndValues); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - $exists = $this->redis->exists($id); - - if (is_bool($exists)) { - return $exists; - } - - return $exists > 0; - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - if ($lifeTime > 0) { - return $this->redis->setex($id, $lifeTime, $data); - } - - return $this->redis->set($id, $data); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - return $this->redis->del($id) >= 0; - } - - /** - * {@inheritdoc} - */ - protected function doDeleteMultiple(array $keys) - { - return $this->redis->del($keys) >= 0; - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - return $this->redis->flushDB(); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $info = $this->redis->info(); - - return [ - Cache::STATS_HITS => $info['keyspace_hits'], - Cache::STATS_MISSES => $info['keyspace_misses'], - Cache::STATS_UPTIME => $info['uptime_in_seconds'], - Cache::STATS_MEMORY_USAGE => $info['used_memory'], - Cache::STATS_MEMORY_AVAILABLE => false, - ]; - } - - /** - * Returns the serializer constant to use. If Redis is compiled with - * igbinary support, that is used. Otherwise the default PHP serializer is - * used. - * - * @return int One of the Redis::SERIALIZER_* constants - */ - protected function getSerializerValue() - { - if (defined('Redis::SERIALIZER_IGBINARY') && extension_loaded('igbinary')) { - return Redis::SERIALIZER_IGBINARY; - } - - return Redis::SERIALIZER_PHP; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php b/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php deleted file mode 100644 index 56deb077..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php +++ /dev/null @@ -1,206 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use SQLite3; -use SQLite3Result; -use const SQLITE3_ASSOC; -use const SQLITE3_BLOB; -use const SQLITE3_TEXT; -use function array_search; -use function implode; -use function serialize; -use function sprintf; -use function time; -use function unserialize; - -/** - * SQLite3 cache provider. - */ -class SQLite3Cache extends CacheProvider -{ - /** - * The ID field will store the cache key. - */ - public const ID_FIELD = 'k'; - - /** - * The data field will store the serialized PHP value. - */ - public const DATA_FIELD = 'd'; - - /** - * The expiration field will store a date value indicating when the - * cache entry should expire. - */ - public const EXPIRATION_FIELD = 'e'; - - /** @var SQLite3 */ - private $sqlite; - - /** @var string */ - private $table; - - /** - * Calling the constructor will ensure that the database file and table - * exist and will create both if they don't. - * - * @param string $table - */ - public function __construct(SQLite3 $sqlite, $table) - { - $this->sqlite = $sqlite; - $this->table = (string) $table; - - $this->ensureTableExists(); - } - - private function ensureTableExists() : void - { - $this->sqlite->exec( - sprintf( - 'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)', - $this->table, - static::ID_FIELD, - static::DATA_FIELD, - static::EXPIRATION_FIELD - ) - ); - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - $item = $this->findById($id); - - if (! $item) { - return false; - } - - return unserialize($item[self::DATA_FIELD]); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return $this->findById($id, false) !== null; - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - $statement = $this->sqlite->prepare(sprintf( - 'INSERT OR REPLACE INTO %s (%s) VALUES (:id, :data, :expire)', - $this->table, - implode(',', $this->getFields()) - )); - - $statement->bindValue(':id', $id); - $statement->bindValue(':data', serialize($data), SQLITE3_BLOB); - $statement->bindValue(':expire', $lifeTime > 0 ? time() + $lifeTime : null); - - return $statement->execute() instanceof SQLite3Result; - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - [$idField] = $this->getFields(); - - $statement = $this->sqlite->prepare(sprintf( - 'DELETE FROM %s WHERE %s = :id', - $this->table, - $idField - )); - - $statement->bindValue(':id', $id); - - return $statement->execute() instanceof SQLite3Result; - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - return $this->sqlite->exec(sprintf('DELETE FROM %s', $this->table)); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - // no-op. - } - - /** - * Find a single row by ID. - * - * @param mixed $id - * - * @return array|null - */ - private function findById($id, bool $includeData = true) : ?array - { - [$idField] = $fields = $this->getFields(); - - if (! $includeData) { - $key = array_search(static::DATA_FIELD, $fields); - unset($fields[$key]); - } - - $statement = $this->sqlite->prepare(sprintf( - 'SELECT %s FROM %s WHERE %s = :id LIMIT 1', - implode(',', $fields), - $this->table, - $idField - )); - - $statement->bindValue(':id', $id, SQLITE3_TEXT); - - $item = $statement->execute()->fetchArray(SQLITE3_ASSOC); - - if ($item === false) { - return null; - } - - if ($this->isExpired($item)) { - $this->doDelete($id); - - return null; - } - - return $item; - } - - /** - * Gets an array of the fields in our table. - * - * @return array - */ - private function getFields() : array - { - return [static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD]; - } - - /** - * Check if the item is expired. - * - * @param array $item - */ - private function isExpired(array $item) : bool - { - return isset($item[static::EXPIRATION_FIELD]) && - $item[self::EXPIRATION_FIELD] !== null && - $item[self::EXPIRATION_FIELD] < time(); - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Version.php b/doctrine/cache/lib/Doctrine/Common/Cache/Version.php deleted file mode 100644 index 834b5b7d..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/Version.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -class Version -{ - public const VERSION = '1.9.0-DEV'; -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php deleted file mode 100644 index b58f613a..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -/** - * Void cache driver. The cache could be of use in tests where you don`t need to cache anything. - * - * @link www.doctrine-project.org - */ -class VoidCache extends CacheProvider -{ - /** - * {@inheritDoc} - */ - protected function doFetch($id) - { - return false; - } - - /** - * {@inheritDoc} - */ - protected function doContains($id) - { - return false; - } - - /** - * {@inheritDoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - return true; - } - - /** - * {@inheritDoc} - */ - protected function doDelete($id) - { - return true; - } - - /** - * {@inheritDoc} - */ - protected function doFlush() - { - return true; - } - - /** - * {@inheritDoc} - */ - protected function doGetStats() - { - return; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php deleted file mode 100644 index 972f92f9..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php +++ /dev/null @@ -1,106 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use function count; -use function is_array; -use function wincache_ucache_clear; -use function wincache_ucache_delete; -use function wincache_ucache_exists; -use function wincache_ucache_get; -use function wincache_ucache_info; -use function wincache_ucache_meminfo; -use function wincache_ucache_set; - -/** - * WinCache cache provider. - * - * @link www.doctrine-project.org - */ -class WinCacheCache extends CacheProvider -{ - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return wincache_ucache_get($id); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return wincache_ucache_exists($id); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - return wincache_ucache_set($id, $data, $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - return wincache_ucache_delete($id); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - return wincache_ucache_clear(); - } - - /** - * {@inheritdoc} - */ - protected function doFetchMultiple(array $keys) - { - return wincache_ucache_get($keys); - } - - /** - * {@inheritdoc} - */ - protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) - { - $result = wincache_ucache_set($keysAndValues, null, $lifetime); - - return empty($result); - } - - /** - * {@inheritdoc} - */ - protected function doDeleteMultiple(array $keys) - { - $result = wincache_ucache_delete($keys); - - return is_array($result) && count($result) !== count($keys); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $info = wincache_ucache_info(); - $meminfo = wincache_ucache_meminfo(); - - return [ - Cache::STATS_HITS => $info['total_hit_count'], - Cache::STATS_MISSES => $info['total_miss_count'], - Cache::STATS_UPTIME => $info['total_cache_uptime'], - Cache::STATS_MEMORY_USAGE => $meminfo['memory_total'], - Cache::STATS_MEMORY_AVAILABLE => $meminfo['memory_free'], - ]; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php deleted file mode 100644 index 9b3a485e..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php +++ /dev/null @@ -1,104 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use BadMethodCallException; -use const XC_TYPE_VAR; -use function ini_get; -use function serialize; -use function unserialize; -use function xcache_clear_cache; -use function xcache_get; -use function xcache_info; -use function xcache_isset; -use function xcache_set; -use function xcache_unset; - -/** - * Xcache cache driver. - * - * @deprecated - * - * @link www.doctrine-project.org - */ -class XcacheCache extends CacheProvider -{ - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return $this->doContains($id) ? unserialize(xcache_get($id)) : false; - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return xcache_isset($id); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - return xcache_set($id, serialize($data), (int) $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - return xcache_unset($id); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - $this->checkAuthorization(); - - xcache_clear_cache(XC_TYPE_VAR); - - return true; - } - - /** - * Checks that xcache.admin.enable_auth is Off. - * - * @return void - * - * @throws BadMethodCallException When xcache.admin.enable_auth is On. - */ - protected function checkAuthorization() - { - if (ini_get('xcache.admin.enable_auth')) { - throw new BadMethodCallException( - 'To use all features of \Doctrine\Common\Cache\XcacheCache, ' - . 'you must set "xcache.admin.enable_auth" to "Off" in your php.ini.' - ); - } - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $this->checkAuthorization(); - - $info = xcache_info(XC_TYPE_VAR, 0); - - return [ - Cache::STATS_HITS => $info['hits'], - Cache::STATS_MISSES => $info['misses'], - Cache::STATS_UPTIME => null, - Cache::STATS_MEMORY_USAGE => $info['size'], - Cache::STATS_MEMORY_AVAILABLE => $info['avail'], - ]; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php deleted file mode 100644 index a6b67ef8..00000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -namespace Doctrine\Common\Cache; - -use function zend_shm_cache_clear; -use function zend_shm_cache_delete; -use function zend_shm_cache_fetch; -use function zend_shm_cache_store; - -/** - * Zend Data Cache cache driver. - * - * @link www.doctrine-project.org - */ -class ZendDataCache extends CacheProvider -{ - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return zend_shm_cache_fetch($id); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return zend_shm_cache_fetch($id) !== false; - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - return zend_shm_cache_store($id, $data, $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - return zend_shm_cache_delete($id); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - $namespace = $this->getNamespace(); - if (empty($namespace)) { - return zend_shm_cache_clear(); - } - - return zend_shm_cache_clear($namespace); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - return null; - } -} diff --git a/doctrine/dbal/README.md b/doctrine/dbal/README.md index 259f7c62..43caa5a5 100644 --- a/doctrine/dbal/README.md +++ b/doctrine/dbal/README.md @@ -1,11 +1,11 @@ # Doctrine DBAL -| [Master][Master] | [2.12][2.12] | -|:----------------:|:----------:| -| [![Build status][Master image]][Master] | [![Build status][2.12 image]][2.12] | -| [![GitHub Actions][GA master image]][GA master] | [![GitHub Actions][GA 2.12 image]][GA 2.12] | -| [![AppVeyor][AppVeyor master image]][AppVeyor master] | [![AppVeyor][AppVeyor 2.12 image]][AppVeyor 2.12] | -| [![Code Coverage][Coverage image]][CodeCov Master] | [![Code Coverage][Coverage 2.12 image]][CodeCov 2.12] | +| [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 | Powerful database abstraction layer with many features for database schema introspection, schema management and PDO abstraction. @@ -15,20 +15,28 @@ Powerful database abstraction layer with many features for database schema intro * [Documentation](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/) * [Issue Tracker](https://github.com/doctrine/dbal/issues) - [Master image]: https://img.shields.io/travis/doctrine/dbal/master.svg?style=flat-square - [Coverage image]: https://codecov.io/gh/doctrine/dbal/branch/master/graph/badge.svg - [Master]: https://travis-ci.org/doctrine/dbal - [CodeCov Master]: https://codecov.io/gh/doctrine/dbal/branch/master - [AppVeyor master]: https://ci.appveyor.com/project/doctrine/dbal/branch/master - [AppVeyor master image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/master?svg=true - [GA master]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3Amaster - [GA master image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg + [Coverage image]: https://codecov.io/gh/doctrine/dbal/branch/4.0.x/graph/badge.svg + [4.0]: https://github.com/doctrine/dbal/tree/4.0.x + [CodeCov 4.0]: https://codecov.io/gh/doctrine/dbal/branch/4.0.x + [AppVeyor 4.0]: https://ci.appveyor.com/project/doctrine/dbal/branch/4.0.x + [AppVeyor 4.0 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/4.0.x?svg=true + [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 - [2.12 image]: https://img.shields.io/travis/doctrine/dbal/2.12.x.svg?style=flat-square - [Coverage 2.12 image]: https://codecov.io/gh/doctrine/dbal/branch/2.12.x/graph/badge.svg - [2.12]: https://github.com/doctrine/dbal/tree/2.12.x - [CodeCov 2.12]: https://codecov.io/gh/doctrine/dbal/branch/2.12.x - [AppVeyor 2.12]: https://ci.appveyor.com/project/doctrine/dbal/branch/2.12.x - [AppVeyor 2.12 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/2.12.x?svg=true - [GA 2.12]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A2.12.x - [GA 2.12 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=2.12.x + [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 diff --git a/doctrine/dbal/composer.json b/doctrine/dbal/composer.json index 09095660..013b6b8d 100644 --- a/doctrine/dbal/composer.json +++ b/doctrine/dbal/composer.json @@ -33,27 +33,27 @@ "require": { "php": "^7.3 || ^8.0", "composer/package-versions-deprecated": "^1.11.99", - "doctrine/cache": "^1.0", + "doctrine/cache": "^1.0|^2.0", + "doctrine/deprecations": "^0.5.3", "doctrine/event-manager": "^1.0" }, "require-dev": { - "doctrine/coding-standard": "^8.1", - "jetbrains/phpstorm-stubs": "^2019.1", - "phpstan/phpstan": "^0.12.40", - "phpstan/phpstan-strict-rules": "^0.12.2", - "phpunit/phpunit": "^9.4", - "psalm/plugin-phpunit": "^0.10.0", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0", - "vimeo/psalm": "^3.17.2" + "doctrine/coding-standard": "9.0.0", + "jetbrains/phpstorm-stubs": "2021.1", + "phpstan/phpstan": "0.12.99", + "phpstan/phpstan-strict-rules": "^0.12.11", + "phpunit/phpunit": "9.5.10", + "psalm/plugin-phpunit": "0.16.1", + "squizlabs/php_codesniffer": "3.6.0", + "symfony/cache": "^5.2|^6.0", + "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", + "vimeo/psalm": "4.10.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, "bin": ["bin/doctrine-dbal"], "config": { - "platform": { - "php": "7.3.0" - }, "sort-packages": true }, "autoload": { @@ -61,10 +61,5 @@ }, "autoload-dev": { "psr-4": { "Doctrine\\DBAL\\Tests\\": "tests" } - }, - "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" - } } } diff --git a/doctrine/dbal/src/Cache/ArrayResult.php b/doctrine/dbal/src/Cache/ArrayResult.php index f8fa8344..e2dc6e11 100644 --- a/doctrine/dbal/src/Cache/ArrayResult.php +++ b/doctrine/dbal/src/Cache/ArrayResult.php @@ -98,10 +98,6 @@ final class ArrayResult implements Result public function rowCount(): int { - if ($this->data === null) { - return 0; - } - return count($this->data); } diff --git a/doctrine/dbal/src/Cache/QueryCacheProfile.php b/doctrine/dbal/src/Cache/QueryCacheProfile.php index 3d5b814d..44f5868c 100644 --- a/doctrine/dbal/src/Cache/QueryCacheProfile.php +++ b/doctrine/dbal/src/Cache/QueryCacheProfile.php @@ -20,7 +20,7 @@ class QueryCacheProfile private $resultCacheDriver; /** @var int */ - private $lifetime = 0; + private $lifetime; /** @var string|null */ private $cacheKey; diff --git a/doctrine/dbal/src/Connection.php b/doctrine/dbal/src/Connection.php index 9477b54e..46648423 100644 --- a/doctrine/dbal/src/Connection.php +++ b/doctrine/dbal/src/Connection.php @@ -21,6 +21,7 @@ use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\SQL\Parser; use Doctrine\DBAL\Types\Type; +use Doctrine\Deprecations\Deprecation; use Throwable; use Traversable; @@ -35,6 +36,8 @@ use function key; /** * 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 */ class Connection { @@ -66,7 +69,11 @@ class Connection /** @var EventManager */ protected $_eventManager; - /** @var ExpressionBuilder */ + /** + * @deprecated Use {@link createExpressionBuilder()} instead. + * + * @var ExpressionBuilder + */ protected $_expr; /** @@ -84,9 +91,9 @@ class Connection private $transactionNestingLevel = 0; /** - * The currently active transaction isolation level. + * The currently active transaction isolation level or NULL before it has been determined. * - * @var int + * @var int|null */ private $transactionIsolationLevel; @@ -100,15 +107,16 @@ class Connection /** * The parameters used during creation of the Connection instance. * - * @var mixed[] + * @var array<string,mixed> + * @phpstan-var array<string,mixed> + * @psalm-var Params */ - private $params = []; + private $params; /** - * The DatabasePlatform object that provides information about the - * database platform used by the connection. + * The database platform object used by the connection or NULL before it's initialized. * - * @var AbstractPlatform + * @var AbstractPlatform|null */ private $platform; @@ -121,6 +129,8 @@ class Connection /** * The schema manager. * + * @deprecated Use {@link createSchemaManager()} instead. + * * @var AbstractSchemaManager|null */ protected $_schemaManager; @@ -144,10 +154,12 @@ class Connection * * @internal The connection can be only instantiated by the driver manager. * - * @param mixed[] $params The connection parameters. - * @param Driver $driver The driver to use. - * @param Configuration|null $config The configuration, optional. - * @param EventManager|null $eventManager The event manager, optional. + * @param array<string,mixed> $params The connection parameters. + * @param Driver $driver The driver to use. + * @param Configuration|null $config The configuration, optional. + * @param EventManager|null $eventManager The event manager, optional. + * @psalm-param Params $params + * @phpstan-param array<string,mixed> $params * * @throws Exception */ @@ -180,7 +192,7 @@ class Connection $this->_config = $config; $this->_eventManager = $eventManager; - $this->_expr = new Query\Expression\ExpressionBuilder($this); + $this->_expr = $this->createExpressionBuilder(); $this->autoCommit = $config->getAutoCommit(); } @@ -190,7 +202,9 @@ class Connection * * @internal * - * @return mixed[] + * @return array<string,mixed> + * @psalm-return Params + * @phpstan-return array<string,mixed> */ public function getParams() { @@ -257,19 +271,37 @@ class Connection public function getDatabasePlatform() { if ($this->platform === null) { - $this->detectDatabasePlatform(); + $this->platform = $this->detectDatabasePlatform(); + $this->platform->setEventManager($this->_eventManager); } return $this->platform; } /** + * Creates an expression builder for the connection. + */ + public function createExpressionBuilder(): ExpressionBuilder + { + return new ExpressionBuilder($this); + } + + /** * Gets the ExpressionBuilder for the connection. * + * @deprecated Use {@link createExpressionBuilder()} instead. + * * @return ExpressionBuilder */ public function getExpressionBuilder() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4515', + 'Connection::getExpressionBuilder() is deprecated,' + . ' use Connection::createExpressionBuilder() instead.' + ); + return $this->_expr; } @@ -293,8 +325,6 @@ class Connection throw $this->convertException($e); } - $this->transactionNestingLevel = 0; - if ($this->autoCommit === false) { $this->beginTransaction(); } @@ -314,19 +344,17 @@ class Connection * * @throws Exception If an invalid platform was specified for this connection. */ - private function detectDatabasePlatform(): void + private function detectDatabasePlatform(): AbstractPlatform { $version = $this->getDatabasePlatformVersion(); if ($version !== null) { assert($this->_driver instanceof VersionAwarePlatformDriver); - $this->platform = $this->_driver->createDatabasePlatformForVersion($version); - } else { - $this->platform = $this->_driver->getDatabasePlatform(); + return $this->_driver->createDatabasePlatformForVersion($version); } - $this->platform->setEventManager($this->_eventManager); + return $this->_driver->getDatabasePlatform(); } /** @@ -364,23 +392,21 @@ class Connection // The database to connect to might not yet exist. // Retry detection without database name connection parameter. - $databaseName = $this->params['dbname']; - $this->params['dbname'] = null; + $params = $this->params; + + unset($this->params['dbname']); try { $this->connect(); } catch (Exception $fallbackException) { // Either the platform does not support database-less connections // or something else went wrong. - // Reset connection parameters and rethrow the original exception. - $this->params['dbname'] = $databaseName; - throw $originalException; + } finally { + $this->params = $params; } - // Reset connection parameters. - $this->params['dbname'] = $databaseName; - $serverVersion = $this->getServerVersion(); + $serverVersion = $this->getServerVersion(); // Close "temporary" connection to allow connecting to the real database again. $this->close(); @@ -549,10 +575,10 @@ class Connection /** * Adds condition based on the criteria to the query components * - * @param mixed[] $criteria Map of key columns to their values - * @param string[] $columns Column names - * @param mixed[] $values Column values - * @param string[] $conditions Key conditions + * @param array<string,mixed> $criteria Map of key columns to their values + * @param string[] $columns Column names + * @param mixed[] $values Column values + * @param string[] $conditions Key conditions * * @throws Exception */ @@ -613,7 +639,8 @@ class Connection */ public function close() { - $this->_conn = null; + $this->_conn = null; + $this->transactionNestingLevel = 0; } /** @@ -733,7 +760,7 @@ class Connection { $typeValues = []; - foreach ($columnList as $columnIndex => $columnName) { + foreach ($columnList as $columnName) { $typeValues[] = $types[$columnName] ?? ParameterType::STRING; } @@ -836,9 +863,9 @@ class Connection * to the first column and the values being an associative array representing the rest of the columns * and their values. * - * @param string $query SQL query - * @param list<mixed>|array<string, mixed> $params Query parameters - * @param array<int, int|string>|array<string, int|string> $types Parameter types + * @param string $query SQL query + * @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 array<mixed,array<string,mixed>> * @@ -1427,11 +1454,13 @@ class Connection */ public function createSavepoint($savepoint) { - if (! $this->getDatabasePlatform()->supportsSavepoints()) { + $platform = $this->getDatabasePlatform(); + + if (! $platform->supportsSavepoints()) { throw ConnectionException::savepointsNotSupported(); } - $this->executeStatement($this->platform->createSavePoint($savepoint)); + $this->executeStatement($platform->createSavePoint($savepoint)); } /** @@ -1445,15 +1474,17 @@ class Connection */ public function releaseSavepoint($savepoint) { - if (! $this->getDatabasePlatform()->supportsSavepoints()) { + $platform = $this->getDatabasePlatform(); + + if (! $platform->supportsSavepoints()) { throw ConnectionException::savepointsNotSupported(); } - if (! $this->platform->supportsReleaseSavepoints()) { + if (! $platform->supportsReleaseSavepoints()) { return; } - $this->executeStatement($this->platform->releaseSavePoint($savepoint)); + $this->executeStatement($platform->releaseSavePoint($savepoint)); } /** @@ -1467,11 +1498,13 @@ class Connection */ public function rollbackSavepoint($savepoint) { - if (! $this->getDatabasePlatform()->supportsSavepoints()) { + $platform = $this->getDatabasePlatform(); + + if (! $platform->supportsSavepoints()) { throw ConnectionException::savepointsNotSupported(); } - $this->executeStatement($this->platform->rollbackSavePoint($savepoint)); + $this->executeStatement($platform->rollbackSavePoint($savepoint)); } /** @@ -1491,20 +1524,39 @@ class Connection } /** + * Creates a SchemaManager that can be used to inspect or change the + * database schema through the connection. + * + * @throws Exception + */ + public function createSchemaManager(): AbstractSchemaManager + { + return $this->_driver->getSchemaManager( + $this, + $this->getDatabasePlatform() + ); + } + + /** * Gets the SchemaManager that can be used to inspect or change the * database schema through the connection. * + * @deprecated Use {@link createSchemaManager()} instead. + * * @return AbstractSchemaManager * * @throws Exception */ public function getSchemaManager() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4515', + 'Connection::getSchemaManager() is deprecated, use Connection::createSchemaManager() instead.' + ); + if ($this->_schemaManager === null) { - $this->_schemaManager = $this->_driver->getSchemaManager( - $this, - $this->getDatabasePlatform() - ); + $this->_schemaManager = $this->createSchemaManager(); } return $this->_schemaManager; diff --git a/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php b/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php index 8d52d859..9d38f904 100644 --- a/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php +++ b/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php @@ -8,6 +8,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\Connection as DriverConnection; use Doctrine\DBAL\Driver\Exception as DriverException; +use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Event\ConnectionEventArgs; use Doctrine\DBAL\Events; use Doctrine\DBAL\Exception; @@ -56,6 +57,7 @@ use function count; * * Instantiation through the DriverManager looks like: * + * @psalm-import-type Params from DriverManager * @example * * $conn = DriverManager::getConnection(array( @@ -93,7 +95,9 @@ class PrimaryReadReplicaConnection extends Connection * * @internal The connection can be only instantiated by the driver manager. * - * @param mixed[] $params + * @param array<string,mixed> $params + * @psalm-param Params $params + * @phpstan-param array<string,mixed> $params * * @throws Exception * @throws InvalidArgumentException @@ -112,9 +116,12 @@ class PrimaryReadReplicaConnection extends Connection throw new InvalidArgumentException('You have to configure at least one replica.'); } - $params['primary']['driver'] = $params['driver']; - foreach ($params['replica'] as $replicaKey => $replica) { - $params['replica'][$replicaKey]['driver'] = $params['driver']; + if (isset($params['driver'])) { + $params['primary']['driver'] = $params['driver']; + + foreach ($params['replica'] as $replicaKey => $replica) { + $params['replica'][$replicaKey]['driver'] = $params['driver']; + } } $this->keepReplica = (bool) ($params['keepReplica'] ?? false); diff --git a/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php index 04f58516..9abc0c16 100644 --- a/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php @@ -27,8 +27,8 @@ use Doctrine\DBAL\Query; final class ExceptionConverter implements ExceptionConverterInterface { /** - * @link https://dev.mysql.com/doc/refman/8.0/en/client-error-reference.html - * @link https://dev.mysql.com/doc/refman/8.0/en/server-error-reference.html + * @link https://dev.mysql.com/doc/mysql-errors/8.0/en/client-error-reference.html + * @link https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html */ public function convert(Exception $exception, ?Query $query): DriverException { diff --git a/doctrine/dbal/src/Driver/IBMDB2/Statement.php b/doctrine/dbal/src/Driver/IBMDB2/Statement.php index 1820a735..c655ea74 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Statement.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Statement.php @@ -125,7 +125,7 @@ final class Statement implements StatementInterface $params = []; - foreach ($this->bindParam as $column => $value) { + foreach ($this->bindParam as $value) { $params[] = $value; } } diff --git a/doctrine/dbal/src/Driver/Mysqli/Connection.php b/doctrine/dbal/src/Driver/Mysqli/Connection.php index eb964a9e..bec38011 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Connection.php +++ b/doctrine/dbal/src/Driver/Mysqli/Connection.php @@ -11,6 +11,7 @@ use Doctrine\DBAL\Driver\Statement as DriverStatement; use Doctrine\DBAL\ParameterType; use mysqli; +use function assert; use function floor; use function mysqli_init; use function stripos; @@ -45,6 +46,7 @@ final class Connection implements ServerInfoAwareConnection iterable $postInitializers = [] ) { $connection = mysqli_init(); + assert($connection !== false); foreach ($preInitializers as $initializer) { $initializer->initialize($connection); diff --git a/doctrine/dbal/src/Driver/Mysqli/Result.php b/doctrine/dbal/src/Driver/Mysqli/Result.php index 9d6665e9..11e00f0b 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Result.php +++ b/doctrine/dbal/src/Driver/Mysqli/Result.php @@ -131,10 +131,7 @@ final class Result implements ResultInterface return false; } - $row = array_combine($this->columnNames, $values); - assert(is_array($row)); - - return $row; + return array_combine($this->columnNames, $values); } /** diff --git a/doctrine/dbal/src/Driver/Mysqli/Statement.php b/doctrine/dbal/src/Driver/Mysqli/Statement.php index b3a5ca87..9f802e11 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Statement.php +++ b/doctrine/dbal/src/Driver/Mysqli/Statement.php @@ -43,7 +43,7 @@ final class Statement implements StatementInterface /** @var mysqli_stmt */ protected $_stmt; - /** @var mixed[] */ + /** @var mixed[]|null */ protected $_bindedValues; /** @var string */ diff --git a/doctrine/dbal/src/Driver/OCI8/Connection.php b/doctrine/dbal/src/Driver/OCI8/Connection.php index 3921b956..1bc5c62c 100644 --- a/doctrine/dbal/src/Driver/OCI8/Connection.php +++ b/doctrine/dbal/src/Driver/OCI8/Connection.php @@ -115,6 +115,8 @@ final class Connection implements ServerInfoAwareConnection /** * {@inheritdoc} * + * @param string|null $name + * * @return int|false */ public function lastInsertId($name = null) diff --git a/doctrine/dbal/src/Driver/OCI8/Result.php b/doctrine/dbal/src/Driver/OCI8/Result.php index 5cbf007a..8f77da75 100644 --- a/doctrine/dbal/src/Driver/OCI8/Result.php +++ b/doctrine/dbal/src/Driver/OCI8/Result.php @@ -4,10 +4,13 @@ declare(strict_types=1); namespace Doctrine\DBAL\Driver\OCI8; +use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Driver\FetchUtils; +use Doctrine\DBAL\Driver\OCI8\Exception\Error; use Doctrine\DBAL\Driver\Result as ResultInterface; use function oci_cancel; +use function oci_error; use function oci_fetch_all; use function oci_fetch_array; use function oci_num_fields; @@ -112,13 +115,18 @@ final class Result implements ResultInterface /** * @return mixed|false + * + * @throws Exception */ private function fetch(int $mode) { - return oci_fetch_array( - $this->statement, - $mode | OCI_RETURN_NULLS | OCI_RETURN_LOBS - ); + $result = oci_fetch_array($this->statement, $mode | OCI_RETURN_NULLS | OCI_RETURN_LOBS); + + if ($result === false && oci_error($this->statement) !== false) { + throw Error::new($this->statement); + } + + return $result; } /** diff --git a/doctrine/dbal/src/Driver/OCI8/Statement.php b/doctrine/dbal/src/Driver/OCI8/Statement.php index a1621c7e..1f3afda2 100644 --- a/doctrine/dbal/src/Driver/OCI8/Statement.php +++ b/doctrine/dbal/src/Driver/OCI8/Statement.php @@ -101,7 +101,7 @@ final class Statement implements StatementInterface assert($lob !== false); - $lob->writetemporary($variable, OCI_TEMP_BLOB); + $lob->writeTemporary($variable, OCI_TEMP_BLOB); $variable =& $lob; } diff --git a/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php b/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php index 40cd2446..d2da1625 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php +++ b/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php @@ -6,8 +6,11 @@ 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 { /** @var PDOStatement */ @@ -24,10 +27,22 @@ final class Statement implements StatementInterface /** * {@inheritdoc} * - * @param mixed $driverOptions + * @param string|int $param + * @param mixed $variable + * @param int $type + * @param int|null $length + * @param mixed $driverOptions The usage of the argument is deprecated. */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null) { + if (func_num_args() > 4) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4533', + 'The $driverOptions argument of Statement::bindParam() is deprecated.' + ); + } + switch ($type) { case ParameterType::LARGE_OBJECT: case ParameterType::BINARY: diff --git a/doctrine/dbal/src/Driver/PDO/Statement.php b/doctrine/dbal/src/Driver/PDO/Statement.php index 7107fa2d..1461239e 100644 --- a/doctrine/dbal/src/Driver/PDO/Statement.php +++ b/doctrine/dbal/src/Driver/PDO/Statement.php @@ -7,12 +7,14 @@ use Doctrine\DBAL\Driver\Exception\UnknownParameterType; use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use PDO; use PDOException; use PDOStatement; use function array_slice; use function func_get_args; +use function func_num_args; final class Statement implements StatementInterface { @@ -58,12 +60,20 @@ final class Statement implements StatementInterface * @param mixed $variable * @param int $type * @param int|null $length - * @param mixed $driverOptions + * @param mixed $driverOptions The usage of the argument is deprecated. * * @return bool */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null) { + if (func_num_args() > 4) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4533', + 'The $driverOptions argument of Statement::bindParam() is deprecated.' + ); + } + $type = $this->convertParamType($type); try { diff --git a/doctrine/dbal/src/Driver/SQLSrv/Connection.php b/doctrine/dbal/src/Driver/SQLSrv/Connection.php index 902a1167..e37ea900 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Connection.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Connection.php @@ -27,9 +27,6 @@ final class Connection implements ServerInfoAwareConnection /** @var resource */ protected $conn; - /** @var LastInsertId */ - protected $lastInsertId; - /** * @internal The connection can be only instantiated by its driver. * @@ -50,8 +47,7 @@ final class Connection implements ServerInfoAwareConnection throw Error::new(); } - $this->conn = $conn; - $this->lastInsertId = new LastInsertId(); + $this->conn = $conn; } /** @@ -66,7 +62,7 @@ final class Connection implements ServerInfoAwareConnection public function prepare(string $sql): DriverStatement { - return new Statement($this->conn, $sql, $this->lastInsertId); + return new Statement($this->conn, $sql); } public function query(string $sql): ResultInterface diff --git a/doctrine/dbal/src/Driver/SQLSrv/LastInsertId.php b/doctrine/dbal/src/Driver/SQLSrv/LastInsertId.php deleted file mode 100644 index 0948e587..00000000 --- a/doctrine/dbal/src/Driver/SQLSrv/LastInsertId.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -namespace Doctrine\DBAL\Driver\SQLSrv; - -/** - * Last Id Data Container. - * - * @internal - */ -final class LastInsertId -{ - /** @var int */ - private $id; - - /** - * @param int $id - * - * @return void - */ - public function setId($id) - { - $this->id = $id; - } - - /** - * @return int - */ - public function getId() - { - return $this->id; - } -} diff --git a/doctrine/dbal/src/Driver/SQLSrv/Result.php b/doctrine/dbal/src/Driver/SQLSrv/Result.php index 7fe5ca19..0e24002c 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Result.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Result.php @@ -80,10 +80,6 @@ final class Result implements ResultInterface public function rowCount(): int { - if ($this->statement === null) { - return 0; - } - $count = sqlsrv_rows_affected($this->statement); if ($count !== false) { @@ -95,10 +91,6 @@ final class Result implements ResultInterface public function columnCount(): int { - if ($this->statement === null) { - return 0; - } - $count = sqlsrv_num_fields($this->statement); if ($count !== false) { diff --git a/doctrine/dbal/src/Driver/SQLSrv/Statement.php b/doctrine/dbal/src/Driver/SQLSrv/Statement.php index ba2e29f9..8267bc0d 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Statement.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Statement.php @@ -11,9 +11,6 @@ use Doctrine\DBAL\ParameterType; use function assert; use function is_int; use function sqlsrv_execute; -use function sqlsrv_fetch; -use function sqlsrv_get_field; -use function sqlsrv_next_result; use function SQLSRV_PHPTYPE_STREAM; use function SQLSRV_PHPTYPE_STRING; use function sqlsrv_prepare; @@ -62,13 +59,6 @@ final class Statement implements StatementInterface private $types = []; /** - * The last insert ID. - * - * @var LastInsertId|null - */ - private $lastInsertId; - - /** * Append to any INSERT query to retrieve the last insert id. */ private const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;'; @@ -79,7 +69,7 @@ final class Statement implements StatementInterface * @param resource $conn * @param string $sql */ - public function __construct($conn, $sql, ?LastInsertId $lastInsertId = null) + public function __construct($conn, $sql) { $this->conn = $conn; $this->sql = $sql; @@ -88,8 +78,7 @@ final class Statement implements StatementInterface return; } - $this->sql .= self::LAST_INSERT_ID_SQL; - $this->lastInsertId = $lastInsertId; + $this->sql .= self::LAST_INSERT_ID_SQL; } /** @@ -144,12 +133,6 @@ final class Statement implements StatementInterface throw Error::new(); } - if ($this->lastInsertId !== null) { - sqlsrv_next_result($this->stmt); - sqlsrv_fetch($this->stmt); - $this->lastInsertId->setId(sqlsrv_get_field($this->stmt, 0)); - } - return new Result($this->stmt); } diff --git a/doctrine/dbal/src/DriverManager.php b/doctrine/dbal/src/DriverManager.php index dd02fcd7..58a0cc57 100644 --- a/doctrine/dbal/src/DriverManager.php +++ b/doctrine/dbal/src/DriverManager.php @@ -25,15 +25,54 @@ use function strpos; use function substr; /** - * Factory for creating Doctrine\DBAL\Connection instances. + * Factory for creating {@link Connection} instances. + * + * @psalm-type OverrideParams = array{ + * charset?: string, + * dbname?: string, + * default_dbname?: string, + * driver?: key-of<self::DRIVER_MAP>, + * driverClass?: class-string<Driver>, + * driverOptions?: array<mixed>, + * host?: string, + * password?: string, + * path?: string, + * pdo?: \PDO, + * platform?: Platforms\AbstractPlatform, + * port?: int, + * user?: string, + * } + * @psalm-type Params = array{ + * charset?: string, + * dbname?: string, + * default_dbname?: string, + * driver?: key-of<self::DRIVER_MAP>, + * driverClass?: class-string<Driver>, + * driverOptions?: array<mixed>, + * host?: string, + * keepSlave?: bool, + * keepReplica?: bool, + * master?: OverrideParams, + * memory?: bool, + * password?: string, + * path?: string, + * pdo?: \PDO, + * platform?: Platforms\AbstractPlatform, + * port?: int, + * primary?: OverrideParams, + * replica?: array<OverrideParams>, + * sharding?: array<string,mixed>, + * slaves?: array<OverrideParams>, + * user?: string, + * wrapperClass?: class-string<Connection>, + * } */ 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 {@link DriverManager::getConnection()}. */ private const DRIVER_MAP = [ 'pdo_mysql' => PDO\MySQL\Driver::class, @@ -103,13 +142,39 @@ final class DriverManager * <b>driverClass</b>: * The driver class to use. * - * @param array{wrapperClass?: class-string<T>} $params - * @param Configuration|null $config The configuration to use. - * @param EventManager|null $eventManager The event manager to use. + * @param array<string,mixed> $params + * @param Configuration|null $config The configuration to use. + * @param EventManager|null $eventManager The event manager to use. + * @psalm-param array{ + * charset?: string, + * dbname?: string, + * default_dbname?: string, + * driver?: key-of<self::DRIVER_MAP>, + * driverClass?: class-string<Driver>, + * driverOptions?: array<mixed>, + * host?: string, + * keepSlave?: bool, + * keepReplica?: bool, + * master?: OverrideParams, + * memory?: bool, + * password?: string, + * path?: string, + * pdo?: \PDO, + * platform?: Platforms\AbstractPlatform, + * port?: int, + * primary?: OverrideParams, + * replica?: array<OverrideParams>, + * sharding?: array<string,mixed>, + * slaves?: array<OverrideParams>, + * user?: string, + * wrapperClass?: class-string<T>, + * } $params + * @phpstan-param array<string,mixed> $params + * + * @psalm-return ($params is array{wrapperClass:mixed} ? T : Connection) * * @throws Exception * - * @psalm-return ($params is array{wrapperClass:mixed} ? T : Connection) * @template T of Connection */ public static function getConnection( @@ -139,24 +204,7 @@ final class DriverManager } } - if (isset($params['driverClass'])) { - if (! in_array(Driver::class, class_implements($params['driverClass']), true)) { - throw Exception::invalidDriverClass($params['driverClass']); - } - - /** @var class-string<Driver> $driverClass */ - $driverClass = $params['driverClass']; - } elseif (isset($params['driver'])) { - if (! isset(self::DRIVER_MAP[$params['driver']])) { - throw Exception::unknownDriver($params['driver'], array_keys(self::DRIVER_MAP)); - } - - $driverClass = self::DRIVER_MAP[$params['driver']]; - } else { - throw Exception::driverRequired(); - } - - $driver = new $driverClass(); + $driver = self::createDriver($params); foreach ($config->getMiddlewares() as $middleware) { $driver = $middleware->wrap($driver); @@ -186,6 +234,38 @@ final class DriverManager } /** + * @param array<string,mixed> $params + * @psalm-param Params $params + * @phpstan-param array<string,mixed> $params + * + * @throws Exception + */ + private static function createDriver(array $params): Driver + { + if (isset($params['driverClass'])) { + $interfaces = class_implements($params['driverClass']); + + if ($interfaces === false || ! in_array(Driver::class, $interfaces, true)) { + throw Exception::invalidDriverClass($params['driverClass']); + } + + return new $params['driverClass'](); + } + + if (isset($params['driver'])) { + if (! isset(self::DRIVER_MAP[$params['driver']])) { + throw Exception::unknownDriver($params['driver'], array_keys(self::DRIVER_MAP)); + } + + $class = self::DRIVER_MAP[$params['driver']]; + + return new $class(); + } + + throw Exception::driverRequired(); + } + + /** * Normalizes the given connection URL path. * * @return string The normalized connection URL path @@ -201,9 +281,13 @@ final class DriverManager * updated list of parameters. * * @param mixed[] $params The list of parameters. + * @psalm-param Params $params + * @phpstan-param array<string,mixed> $params * * @return mixed[] A modified list of parameters with info from a database * URL extracted into indidivual parameter parts. + * @psalm-return Params + * @phpstan-return array<string,mixed> * * @throws Exception */ diff --git a/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php index 30a1a9ca..e61a48d0 100644 --- a/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php +++ b/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php @@ -5,6 +5,7 @@ namespace Doctrine\DBAL\Event; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\Deprecations\Deprecation; use function array_merge; use function func_get_args; @@ -67,6 +68,15 @@ class SchemaAlterTableAddColumnEventArgs extends SchemaEventArgs */ public function addSql($sql) { + if (is_array($sql)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3580', + 'Passing multiple SQL statements as an array to SchemaAlterTableAddColumnEventaArrgs::addSql() ' . + 'is deprecated. Pass each statement as an individual argument instead.' + ); + } + $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); return $this; diff --git a/doctrine/dbal/src/Logging/LoggerChain.php b/doctrine/dbal/src/Logging/LoggerChain.php index 27b8516e..9b44dc0e 100644 --- a/doctrine/dbal/src/Logging/LoggerChain.php +++ b/doctrine/dbal/src/Logging/LoggerChain.php @@ -8,7 +8,7 @@ namespace Doctrine\DBAL\Logging; class LoggerChain implements SQLLogger { /** @var iterable<SQLLogger> */ - private $loggers = []; + private $loggers; /** * @param iterable<SQLLogger> $loggers diff --git a/doctrine/dbal/src/Platforms/AbstractPlatform.php b/doctrine/dbal/src/Platforms/AbstractPlatform.php index 042fae29..e7f0819b 100644 --- a/doctrine/dbal/src/Platforms/AbstractPlatform.php +++ b/doctrine/dbal/src/Platforms/AbstractPlatform.php @@ -30,6 +30,7 @@ use Doctrine\DBAL\SQL\Parser; use Doctrine\DBAL\TransactionIsolationLevel; use Doctrine\DBAL\Types; use Doctrine\DBAL\Types\Type; +use Doctrine\Deprecations\Deprecation; use InvalidArgumentException; use UnexpectedValueException; @@ -58,9 +59,6 @@ use function strlen; use function strpos; use function strtolower; use function strtoupper; -use function trigger_error; - -use const E_USER_DEPRECATED; /** * Base class for all DatabasePlatforms. The DatabasePlatforms are the central @@ -245,12 +243,14 @@ abstract class AbstractPlatform if ($column['length'] > $maxLength) { if ($maxLength > 0) { - @trigger_error(sprintf( + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3187', 'Binary column length %d is greater than supported by the platform (%d).' . ' Reduce the column length or use a BLOB column instead.', $column['length'], $maxLength - ), E_USER_DEPRECATED); + ); } return $this->getBlobTypeDeclarationSQL($column); @@ -1352,13 +1352,21 @@ abstract class AbstractPlatform /** * Returns the SQL snippet to drop an existing database. * - * @param string $database The name of the database that should be dropped. + * @param string $name The name of the database that should be dropped. * * @return string */ - public function getDropDatabaseSQL($database) + public function getDropDatabaseSQL($name) { - return 'DROP DATABASE ' . $database; + return 'DROP DATABASE ' . $name; + } + + /** + * Returns the SQL snippet to drop a schema. + */ + public function getDropSchemaSQL(string $schemaName): string + { + return 'DROP SCHEMA ' . $schemaName; } /** @@ -1768,6 +1776,8 @@ abstract class AbstractPlatform 'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().' ); } + } elseif ($constraint instanceof UniqueConstraint) { + $query .= ' UNIQUE'; } elseif ($constraint instanceof ForeignKeyConstraint) { $query .= ' FOREIGN KEY'; @@ -2742,12 +2752,21 @@ abstract class AbstractPlatform /** * Returns the SQL statement for retrieving the namespaces defined in the database. * + * @deprecated Use {@link AbstractSchemaManager::listSchemaNames()} instead. + * * @return string * * @throws Exception If not supported on this platform. */ public function getListNamespacesSQL() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'AbstractPlatform::getListNamespacesSQL() is deprecated,' + . ' use AbstractSchemaManager::listSchemaNames() instead.' + ); + throw Exception::notSupported(__METHOD__); } @@ -2910,13 +2929,13 @@ abstract class AbstractPlatform /** * Returns the SQL to create a new database. * - * @param string $database The name of the database that should be created. + * @param string $name The name of the database that should be created. * * @return string * * @throws Exception If not supported on this platform. */ - public function getCreateDatabaseSQL($database) + public function getCreateDatabaseSQL($name) { throw Exception::notSupported(__METHOD__); } @@ -3480,31 +3499,51 @@ abstract class AbstractPlatform final public function getReservedKeywordsList() { // Check for an existing instantiation of the keywords class. - if ($this->_keywords !== null) { - return $this->_keywords; + if ($this->_keywords === null) { + // Store the instance so it doesn't need to be generated on every request. + $this->_keywords = $this->createReservedKeywordsList(); } + return $this->_keywords; + } + + /** + * Creates an instance of the reserved keyword list of this platform. + * + * This method will become @abstract in DBAL 4.0.0. + * + * @throws Exception + */ + protected function createReservedKeywordsList(): KeywordList + { $class = $this->getReservedKeywordsClass(); $keywords = new $class(); if (! $keywords instanceof KeywordList) { throw Exception::notSupported(__METHOD__); } - // Store the instance so it doesn't need to be generated on every request. - $this->_keywords = $keywords; - return $keywords; } /** * Returns the class name of the reserved keywords list. * + * @deprecated Implement {@link createReservedKeywordsList()} instead. + * * @return string + * @psalm-return class-string<KeywordList> * * @throws Exception If not supported on this platform. */ protected function getReservedKeywordsClass() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'AbstractPlatform::getReservedKeywordsClass() is deprecated,' + . ' use AbstractPlatform::createReservedKeywordsList() instead.' + ); + throw Exception::notSupported(__METHOD__); } diff --git a/doctrine/dbal/src/Platforms/DB2Platform.php b/doctrine/dbal/src/Platforms/DB2Platform.php index 16d4839f..03631054 100644 --- a/doctrine/dbal/src/Platforms/DB2Platform.php +++ b/doctrine/dbal/src/Platforms/DB2Platform.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; +use Doctrine\Deprecations\Deprecation; use function array_merge; use function count; @@ -290,6 +291,7 @@ class DB2Platform extends AbstractPlatform c.colname, c.colno, c.typename, + c.codepage, c.nulls, c.length, c.scale, @@ -413,17 +415,17 @@ class DB2Platform extends AbstractPlatform /** * {@inheritDoc} */ - public function getCreateDatabaseSQL($database) + public function getCreateDatabaseSQL($name) { - return 'CREATE DATABASE ' . $database; + return 'CREATE DATABASE ' . $name; } /** * {@inheritDoc} */ - public function getDropDatabaseSQL($database) + public function getDropDatabaseSQL($name) { - return 'DROP DATABASE ' . $database; + return 'DROP DATABASE ' . $name; } /** @@ -896,9 +898,18 @@ class DB2Platform extends AbstractPlatform /** * {@inheritDoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'DB2Platform::getReservedKeywordsClass() is deprecated,' + . ' use DB2Platform::createReservedKeywordsList() instead.' + ); + return Keywords\DB2Keywords::class; } diff --git a/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php b/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php index ff03432e..a6da7746 100644 --- a/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php +++ b/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php @@ -17,7 +17,7 @@ use function str_replace; class ReservedKeywordsValidator implements Visitor { /** @var KeywordList[] */ - private $keywordLists = []; + private $keywordLists; /** @var string[] */ private $violations = []; diff --git a/doctrine/dbal/src/Platforms/MariaDb1027Platform.php b/doctrine/dbal/src/Platforms/MariaDb1027Platform.php index 054f6539..9fa84e98 100644 --- a/doctrine/dbal/src/Platforms/MariaDb1027Platform.php +++ b/doctrine/dbal/src/Platforms/MariaDb1027Platform.php @@ -3,6 +3,7 @@ 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. @@ -21,8 +22,18 @@ final class MariaDb1027Platform extends MySQLPlatform 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; } diff --git a/doctrine/dbal/src/Platforms/MySQL57Platform.php b/doctrine/dbal/src/Platforms/MySQL57Platform.php index a3ec5044..f29204cf 100644 --- a/doctrine/dbal/src/Platforms/MySQL57Platform.php +++ b/doctrine/dbal/src/Platforms/MySQL57Platform.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\SQL\Parser; use Doctrine\DBAL\Types\Types; +use Doctrine\Deprecations\Deprecation; /** * Provides the behavior, features and SQL dialect of the MySQL 5.7 (5.7.9 GA) database platform. @@ -59,9 +60,18 @@ class MySQL57Platform extends MySQLPlatform /** * {@inheritdoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'MySQL57Platform::getReservedKeywordsClass() is deprecated,' + . ' use MySQL57Platform::createReservedKeywordsList() instead.' + ); + return Keywords\MySQL57Keywords::class; } diff --git a/doctrine/dbal/src/Platforms/MySQL80Platform.php b/doctrine/dbal/src/Platforms/MySQL80Platform.php index f6d4be9d..808e9344 100644 --- a/doctrine/dbal/src/Platforms/MySQL80Platform.php +++ b/doctrine/dbal/src/Platforms/MySQL80Platform.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Platforms; +use Doctrine\Deprecations\Deprecation; + /** * Provides the behavior, features and SQL dialect of the MySQL 8.0 (8.0 GA) database platform. */ @@ -9,9 +11,18 @@ class MySQL80Platform extends MySQL57Platform { /** * {@inheritdoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'MySQL80Platform::getReservedKeywordsClass() is deprecated,' + . ' use MySQL80Platform::createReservedKeywordsList() instead.' + ); + return Keywords\MySQL80Keywords::class; } } diff --git a/doctrine/dbal/src/Platforms/MySQLPlatform.php b/doctrine/dbal/src/Platforms/MySQLPlatform.php index d9607c3e..06bc5975 100644 --- a/doctrine/dbal/src/Platforms/MySQLPlatform.php +++ b/doctrine/dbal/src/Platforms/MySQLPlatform.php @@ -11,6 +11,7 @@ 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; @@ -32,8 +33,6 @@ 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. - * - * @todo Rename: MySQLPlatform */ class MySQLPlatform extends AbstractPlatform { @@ -1105,9 +1104,18 @@ SQL /** * {@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; } diff --git a/doctrine/dbal/src/Platforms/OraclePlatform.php b/doctrine/dbal/src/Platforms/OraclePlatform.php index cbbfb07d..7be7621d 100644 --- a/doctrine/dbal/src/Platforms/OraclePlatform.php +++ b/doctrine/dbal/src/Platforms/OraclePlatform.php @@ -11,6 +11,7 @@ use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\TransactionIsolationLevel; use Doctrine\DBAL\Types\BinaryType; +use Doctrine\Deprecations\Deprecation; use InvalidArgumentException; use function array_merge; @@ -506,8 +507,10 @@ class OraclePlatform extends AbstractPlatform $sql[] = "DECLARE constraints_Count NUMBER; BEGIN - SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = '" . $unquotedTableName - . "' AND CONSTRAINT_TYPE = 'P'; + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count + FROM USER_CONSTRAINTS + WHERE TABLE_NAME = '" . $unquotedTableName . "' + AND CONSTRAINT_TYPE = 'P'; IF constraints_Count = 0 OR constraints_Count = '' THEN EXECUTE IMMEDIATE '" . $this->getCreateConstraintSQL($idx, $quotedTableName) . "'; END IF; @@ -528,7 +531,6 @@ DECLARE last_Sequence NUMBER; last_InsertID NUMBER; BEGIN - SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.' . $quotedName . ' = 0) THEN SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; ELSE @@ -539,6 +541,7 @@ BEGIN WHILE (last_InsertID > last_Sequence) LOOP SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; END LOOP; + SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; END IF; END;'; @@ -783,9 +786,9 @@ SQL /** * {@inheritDoc} */ - public function getDropDatabaseSQL($database) + public function getDropDatabaseSQL($name) { - return 'DROP USER ' . $database . ' CASCADE'; + return 'DROP USER ' . $name . ' CASCADE'; } /** @@ -1160,9 +1163,18 @@ SQL /** * {@inheritDoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'OraclePlatform::getReservedKeywordsClass() is deprecated,' + . ' use OraclePlatform::createReservedKeywordsList() instead.' + ); + return Keywords\OracleKeywords::class; } diff --git a/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php b/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php index f5610056..c9ca509d 100644 --- a/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php +++ b/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php @@ -5,14 +5,25 @@ declare(strict_types=1); namespace Doctrine\DBAL\Platforms; use Doctrine\DBAL\Platforms\Keywords\PostgreSQL100Keywords; +use Doctrine\Deprecations\Deprecation; /** * Provides the behavior, features and SQL dialect of the PostgreSQL 10.0 database platform. */ class PostgreSQL100Platform extends PostgreSQL94Platform { + /** + * @deprecated Implement {@link createReservedKeywordsList()} instead. + */ protected function getReservedKeywordsClass(): string { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'PostgreSQL100Platform::getReservedKeywordsClass() is deprecated,' + . ' use PostgreSQL100Platform::createReservedKeywordsList() instead.' + ); + return PostgreSQL100Keywords::class; } diff --git a/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php b/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php index a00a43e2..bf21477c 100644 --- a/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php +++ b/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php @@ -14,6 +14,7 @@ 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; @@ -227,9 +228,18 @@ class PostgreSQL94Platform extends AbstractPlatform /** * {@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\_%' @@ -775,6 +785,12 @@ SQL } } + 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); @@ -864,12 +880,15 @@ SQL return $this->doConvertBooleans( $item, - static function ($boolean) { - if ($boolean === null) { + /** + * @param mixed $value + */ + static function ($value) { + if ($value === null) { return 'NULL'; } - return $boolean === true ? 'true' : 'false'; + return $value === true ? 'true' : 'false'; } ); } @@ -885,8 +904,11 @@ SQL return $this->doConvertBooleans( $item, - static function ($boolean): ?int { - return $boolean === null ? null : (int) $boolean; + /** + * @param mixed $value + */ + static function ($value): ?int { + return $value === null ? null : (int) $value; } ); } @@ -896,7 +918,7 @@ SQL */ public function convertFromBoolean($item) { - if (in_array(strtolower($item), $this->booleanLiterals['false'], true)) { + if ($item !== null && in_array(strtolower($item), $this->booleanLiterals['false'], true)) { return false; } @@ -1168,9 +1190,18 @@ SQL /** * {@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; } diff --git a/doctrine/dbal/src/Platforms/SQLServer2012Platform.php b/doctrine/dbal/src/Platforms/SQLServer2012Platform.php index 5e7c428d..d94784d6 100644 --- a/doctrine/dbal/src/Platforms/SQLServer2012Platform.php +++ b/doctrine/dbal/src/Platforms/SQLServer2012Platform.php @@ -12,6 +12,7 @@ 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; @@ -282,11 +283,11 @@ class SQLServer2012Platform extends AbstractPlatform return sprintf( <<<SQL -IF EXISTS (SELECT * FROM sysobjects WHERE name = '%s') - ALTER TABLE %s DROP CONSTRAINT %s -ELSE - DROP INDEX %s ON %s -SQL + IF EXISTS (SELECT * FROM sysobjects WHERE name = '%s') + ALTER TABLE %s DROP CONSTRAINT %s + ELSE + DROP INDEX %s ON %s + SQL , $index, $table, @@ -524,12 +525,15 @@ SQL } $columnDef = $column->toArray(); - $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); - + $addColumnSql = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); if (isset($columnDef['default'])) { - $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + $addColumnSql .= ' CONSTRAINT ' . + $this->generateDefaultConstraintName($diff->name, $column->getQuotedName($this)) . + $this->getDefaultValueDeclarationSQL($columnDef); } + $queryParts[] = $addColumnSql; + $comment = $this->getColumnComment($column); if (empty($comment) && ! is_numeric($comment)) { @@ -1157,9 +1161,18 @@ SQL /** * {@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')"; } @@ -1337,20 +1350,7 @@ SQL return $query; } - // Queries using OFFSET... FETCH MUST have an ORDER BY clause - // 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); - $orderByPos = false; - if ($matchesCount > 0) { - $orderByPos = $matches[0][$matchesCount - 1][1]; - } - - if ( - $orderByPos === false - || substr_count($query, '(', $orderByPos) !== substr_count($query, ')', $orderByPos) - ) { + 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 @@ -1470,6 +1470,7 @@ SQL 'bigint' => 'bigint', 'binary' => 'binary', 'bit' => 'boolean', + 'blob' => 'blob', 'char' => 'string', 'date' => 'date', 'datetime' => 'datetime', @@ -1564,9 +1565,18 @@ SQL /** * {@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; } @@ -1575,7 +1585,7 @@ SQL */ public function quoteSingleIdentifier($str) { - return '[' . str_replace(']', '][', $str) . ']'; + return '[' . str_replace(']', ']]', $str) . ']'; } /** @@ -1661,10 +1671,10 @@ SQL { 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 + 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) @@ -1675,16 +1685,47 @@ SQL { 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 + 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/SqlitePlatform.php b/doctrine/dbal/src/Platforms/SqlitePlatform.php index 6d85de4e..5dc1747c 100644 --- a/doctrine/dbal/src/Platforms/SqlitePlatform.php +++ b/doctrine/dbal/src/Platforms/SqlitePlatform.php @@ -3,16 +3,22 @@ namespace Doctrine\DBAL\Platforms; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\Constraint; use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\TransactionIsolationLevel; use Doctrine\DBAL\Types; +use Doctrine\Deprecations\Deprecation; +use function array_combine; +use function array_keys; use function array_merge; +use function array_search; use function array_unique; use function array_values; use function implode; @@ -684,9 +690,18 @@ class SqlitePlatform extends AbstractPlatform /** * {@inheritDoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'SqlitePlatform::getReservedKeywordsClass() is deprecated,' + . ' use SqlitePlatform::createReservedKeywordsList() instead.' + ); + return Keywords\SQLiteKeywords::class; } @@ -905,12 +920,7 @@ class SqlitePlatform extends AbstractPlatform continue; } - $oldColumnName = strtolower($oldColumnName); - if (isset($columns[$oldColumnName])) { - unset($columns[$oldColumnName]); - } - - $columns[strtolower($column->getName())] = $column; + $columns = $this->replaceColumn($diff->name, $columns, $oldColumnName, $column); if (! isset($newColumnNames[$oldColumnName])) { continue; @@ -924,11 +934,7 @@ class SqlitePlatform extends AbstractPlatform continue; } - if (isset($columns[$oldColumnName])) { - unset($columns[$oldColumnName]); - } - - $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column; + $columns = $this->replaceColumn($diff->name, $columns, $oldColumnName, $columnDiff->column); if (! isset($newColumnNames[$oldColumnName])) { continue; @@ -997,6 +1003,34 @@ class SqlitePlatform extends AbstractPlatform } /** + * Replace the column with the given name with the new column. + * + * @param string $tableName + * @param array<string,Column> $columns + * @param string $columnName + * + * @return array<string,Column> + * + * @throws Exception + */ + private function replaceColumn($tableName, array $columns, $columnName, Column $column): array + { + $keys = array_keys($columns); + $index = array_search(strtolower($columnName), $keys, true); + + if ($index === false) { + throw SchemaException::columnDoesNotExist($columnName, $tableName); + } + + $values = array_values($columns); + + $keys[$index] = strtolower($column->getName()); + $values[$index] = $column; + + return array_combine($keys, $values); + } + + /** * @return string[]|false * * @throws Exception diff --git a/doctrine/dbal/src/Portability/Converter.php b/doctrine/dbal/src/Portability/Converter.php index eba144a1..5763c260 100644 --- a/doctrine/dbal/src/Portability/Converter.php +++ b/doctrine/dbal/src/Portability/Converter.php @@ -38,21 +38,17 @@ final class Converter */ public function __construct(bool $convertEmptyStringToNull, bool $rightTrimString, ?int $case) { - $id = static function ($value) { - return $value; - }; - $convertValue = $this->createConvertValue($convertEmptyStringToNull, $rightTrimString); $convertNumeric = $this->createConvertRow($convertValue, null); $convertAssociative = $this->createConvertRow($convertValue, $case); - $this->convertNumeric = $this->createConvert($convertNumeric, $id); - $this->convertAssociative = $this->createConvert($convertAssociative, $id); - $this->convertOne = $this->createConvert($convertValue, $id); + $this->convertNumeric = $this->createConvert($convertNumeric, [self::class, 'id']); + $this->convertAssociative = $this->createConvert($convertAssociative, [self::class, 'id']); + $this->convertOne = $this->createConvert($convertValue, [self::class, 'id']); - $this->convertAllNumeric = $this->createConvertAll($convertNumeric, $id); - $this->convertAllAssociative = $this->createConvertAll($convertAssociative, $id); - $this->convertFirstColumn = $this->createConvertAll($convertValue, $id); + $this->convertAllNumeric = $this->createConvertAll($convertNumeric, [self::class, 'id']); + $this->convertAllAssociative = $this->createConvertAll($convertAssociative, [self::class, 'id']); + $this->convertFirstColumn = $this->createConvertAll($convertValue, [self::class, 'id']); } /** @@ -116,6 +112,51 @@ final class Converter } /** + * @param T $value + * + * @return T + * + * @template T + */ + private static function id($value) + { + return $value; + } + + /** + * @param T $value + * + * @return T|null + * + * @template T + */ + private static function convertEmptyStringToNull($value) + { + if ($value === '') { + return null; + } + + return $value; + } + + /** + * @param T $value + * + * @return T|string + * @psalm-return (T is string ? string : T) + * + * @template T + */ + private static function rightTrimString($value) + { + if (! is_string($value)) { + return $value; + } + + return rtrim($value); + } + + /** * Creates a function that will convert each individual value retrieved from the database * * @param bool $convertEmptyStringToNull Whether each empty string should be converted to NULL @@ -128,23 +169,11 @@ final class Converter $functions = []; if ($convertEmptyStringToNull) { - $functions[] = static function ($value) { - if ($value === '') { - return null; - } - - return $value; - }; + $functions[] = [self::class, 'convertEmptyStringToNull']; } if ($rightTrimString) { - $functions[] = static function ($value) { - if (! is_string($value)) { - return $value; - } - - return rtrim($value); - }; + $functions[] = [self::class, 'rightTrimString']; } return $this->compose(...$functions); @@ -188,13 +217,20 @@ final class Converter return $id; } - return static function ($value) use ($function) { - if ($value === false) { - return false; - } + return /** + * @param T $value + * + * @psalm-return (T is false ? false : T) + * + * @template T + */ + static function ($value) use ($function) { + if ($value === false) { + return false; + } - return $function($value); - }; + return $function($value); + }; } /** @@ -228,9 +264,11 @@ final class Converter /** * Creates a composition of the given set of functions * - * @param callable ...$functions The functions to compose + * @param callable(T):T ...$functions The functions to compose * - * @return callable|null The composition or NULL if an empty set is provided + * @return callable(T):T|null + * + * @template T */ private function compose(callable ...$functions): ?callable { @@ -239,9 +277,16 @@ final class Converter return $item; } - return static function ($value) use ($carry, $item) { - return $item($carry($value)); - }; + return /** + * @param T $value + * + * @return T + * + * @template T + */ + static function ($value) use ($carry, $item) { + return $item($carry($value)); + }; }); } } diff --git a/doctrine/dbal/src/Query/Expression/CompositeExpression.php b/doctrine/dbal/src/Query/Expression/CompositeExpression.php index 4e551e95..85de9ae9 100644 --- a/doctrine/dbal/src/Query/Expression/CompositeExpression.php +++ b/doctrine/dbal/src/Query/Expression/CompositeExpression.php @@ -3,6 +3,8 @@ namespace Doctrine\DBAL\Query\Expression; use Countable; +use Doctrine\Deprecations\Deprecation; +use ReturnTypeWillChange; use function array_merge; use function count; @@ -48,6 +50,12 @@ class CompositeExpression implements Countable $this->type = $type; $this->addMultiple($parts); + + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3864', + 'Do not use CompositeExpression constructor directly, use static and() and or() factory methods.' + ); } /** @@ -79,6 +87,12 @@ class CompositeExpression implements Countable */ public function addMultiple(array $parts = []) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3844', + 'CompositeExpression::addMultiple() is deprecated, use CompositeExpression::with() instead.' + ); + foreach ($parts as $part) { $this->add($part); } @@ -97,6 +111,12 @@ class CompositeExpression implements Countable */ public function add($part) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3844', + 'CompositeExpression::add() is deprecated, use CompositeExpression::with() instead.' + ); + if ($part === null) { return $this; } @@ -130,6 +150,7 @@ class CompositeExpression implements Countable * * @return int */ + #[ReturnTypeWillChange] public function count() { return count($this->parts); diff --git a/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php b/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php index f12ef0fa..8cb53150 100644 --- a/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php +++ b/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL\Query\Expression; use Doctrine\DBAL\Connection; +use Doctrine\Deprecations\Deprecation; use function func_get_arg; use function func_get_args; @@ -71,6 +72,12 @@ class ExpressionBuilder */ public function andX($x = null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3851', + 'ExpressionBuilder::andX() is deprecated, use ExpressionBuilder::and() instead.' + ); + return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); } @@ -84,6 +91,12 @@ class ExpressionBuilder */ public function orX($x = null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/3851', + 'ExpressionBuilder::orX() is deprecated, use ExpressionBuilder::or() instead.' + ); + return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); } @@ -243,8 +256,8 @@ class ExpressionBuilder /** * Creates a LIKE() comparison expression with the given arguments. * - * @param string $x Field in string format to be inspected by LIKE() comparison. - * @param mixed $y Argument to be used in LIKE() comparison. + * @param string $x The expression to be inspected by the LIKE comparison + * @param mixed $y The pattern to compare against * * @return string */ @@ -257,8 +270,8 @@ class ExpressionBuilder /** * Creates a NOT LIKE() comparison expression with the given arguments. * - * @param string $x Field in string format to be inspected by NOT LIKE() comparison. - * @param mixed $y Argument to be used in NOT LIKE() comparison. + * @param string $x The expression to be inspected by the NOT LIKE comparison + * @param mixed $y The pattern to compare against * * @return string */ @@ -269,10 +282,10 @@ class ExpressionBuilder } /** - * Creates a IN () comparison expression with the given arguments. + * Creates an IN () comparison expression with the given arguments. * - * @param string $x The field in string format to be inspected by IN() comparison. - * @param string|string[] $y The placeholder or the array of values to be used by IN() comparison. + * @param string $x The SQL expression to be matched against the set. + * @param string|string[] $y The SQL expression or an array of SQL expressions representing the set. * * @return string */ @@ -284,8 +297,8 @@ class ExpressionBuilder /** * Creates a NOT IN () comparison expression with the given arguments. * - * @param string $x The expression to be inspected by NOT IN() comparison. - * @param string|string[] $y The placeholder or the array of values to be used by NOT IN() comparison. + * @param string $x The SQL expression to be matched against the set. + * @param string|string[] $y The SQL expression or an array of SQL expressions representing the set. * * @return string */ diff --git a/doctrine/dbal/src/Query/QueryBuilder.php b/doctrine/dbal/src/Query/QueryBuilder.php index 86e509ca..246cde1a 100644 --- a/doctrine/dbal/src/Query/QueryBuilder.php +++ b/doctrine/dbal/src/Query/QueryBuilder.php @@ -10,6 +10,7 @@ use Doctrine\DBAL\Query\Expression\ExpressionBuilder; use Doctrine\DBAL\Result; use Doctrine\DBAL\Statement; use Doctrine\DBAL\Types\Type; +use Doctrine\Deprecations\Deprecation; use function array_key_exists; use function array_keys; @@ -83,7 +84,7 @@ class QueryBuilder /** * The complete SQL string for this query. * - * @var string + * @var string|null */ private $sql; @@ -120,7 +121,7 @@ class QueryBuilder * * @var int */ - private $firstResult; + private $firstResult = 0; /** * The maximum number of results to retrieve or NULL to retrieve all results. @@ -198,8 +199,136 @@ class QueryBuilder } /** + * Prepares and executes an SQL query and returns the first row of the result + * as an associative array. + * + * @return array<string, mixed>|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchAssociative() + { + return $this->connection->fetchAssociative($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as a numerically indexed array. + * + * @return array<int, mixed>|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchNumeric() + { + return $this->connection->fetchNumeric($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the value of a single column + * of the first row of the result. + * + * @return mixed|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchOne() + { + return $this->connection->fetchOne($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the result as an array of numeric arrays. + * + * @return array<int,array<int,mixed>> + * + * @throws Exception + */ + public function fetchAllNumeric(): array + { + return $this->connection->fetchAllNumeric($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the result as an array of associative arrays. + * + * @return array<int,array<string,mixed>> + * + * @throws Exception + */ + public function fetchAllAssociative(): array + { + return $this->connection->fetchAllAssociative($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the result as an associative array with the keys + * mapped to the first column and the values mapped to the second column. + * + * @return array<mixed,mixed> + * + * @throws Exception + */ + public function fetchAllKeyValue(): array + { + return $this->connection->fetchAllKeyValue($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the result as an associative array with the keys mapped + * to the first column and the values being an associative array representing the rest of the columns + * and their values. + * + * @return array<mixed,array<string,mixed>> + * + * @throws Exception + */ + public function fetchAllAssociativeIndexed(): array + { + return $this->connection->fetchAllAssociativeIndexed($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the result as an array of the first column values. + * + * @return array<int,mixed> + * + * @throws Exception + */ + public function fetchFirstColumn(): array + { + return $this->connection->fetchFirstColumn($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Executes an SQL query (SELECT) and returns a Result. + * + * @throws Exception + */ + public function executeQuery(): Result + { + return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Executes an SQL statement and returns the number of affected rows. + * + * Should be used for INSERT, UPDATE and DELETE + * + * @return int The number of affected rows. + * + * @throws Exception + */ + public function executeStatement(): int + { + return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); + } + + /** * Executes this query using the bound parameters and their types. * + * @deprecated Use {@link executeQuery()} or {@link executeStatement()} instead. + * * @return Result|int * * @throws Exception @@ -207,9 +336,21 @@ class QueryBuilder public function execute() { if ($this->type === self::SELECT) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4578', + 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeQuery() for SQL queries instead.' + ); + return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4578', + 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeStatement() for SQL statements instead.' + ); + return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); } @@ -269,7 +410,7 @@ class QueryBuilder * * @param int|string $key Parameter position or name * @param mixed $value Parameter value - * @param int|string|Type|null $type One of the {@link ParameterType} constants or DBAL type + * @param int|string|Type|null $type Parameter type * * @return $this This QueryBuilder instance. */ @@ -483,6 +624,15 @@ class QueryBuilder return $this; } + if (is_array($select)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3837', + 'Passing an array for the first argument to QueryBuilder::select() is deprecated, ' . + 'pass each value as an individual variadic argument instead.' + ); + } + $selects = is_array($select) ? $select : func_get_args(); return $this->add('select', $selects); @@ -533,6 +683,15 @@ class QueryBuilder return $this; } + if (is_array($select)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3837', + 'Passing an array for the first argument to QueryBuilder::addSelect() is deprecated, ' . + 'pass each value as an individual variadic argument instead.' + ); + } + $selects = is_array($select) ? $select : func_get_args(); return $this->add('select', $selects, true); @@ -905,6 +1064,15 @@ class QueryBuilder return $this; } + if (is_array($groupBy)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3837', + 'Passing an array for the first argument to QueryBuilder::groupBy() is deprecated, ' . + 'pass each value as an individual variadic argument instead.' + ); + } + $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); return $this->add('groupBy', $groupBy, false); @@ -934,6 +1102,15 @@ class QueryBuilder return $this; } + if (is_array($groupBy)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3837', + 'Passing an array for the first argument to QueryBuilder::addGroupBy() is deprecated, ' . + 'pass each value as an individual variadic argument instead.' + ); + } + $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); return $this->add('groupBy', $groupBy, true); @@ -1213,7 +1390,7 @@ class QueryBuilder */ private function isLimitQuery() { - return $this->maxResults !== null || $this->firstResult !== null; + return $this->maxResults !== null || $this->firstResult !== 0; } /** diff --git a/doctrine/dbal/src/Schema/AbstractAsset.php b/doctrine/dbal/src/Schema/AbstractAsset.php index 8f0c1d46..e6b383a6 100644 --- a/doctrine/dbal/src/Schema/AbstractAsset.php +++ b/doctrine/dbal/src/Schema/AbstractAsset.php @@ -24,7 +24,7 @@ use function substr; abstract class AbstractAsset { /** @var string */ - protected $_name; + protected $_name = ''; /** * Namespace of the asset. If none isset the default namespace is assumed. @@ -102,7 +102,7 @@ abstract class AbstractAsset } /** - * The normalized name is full-qualified and lowerspaced. Lowerspacing is + * The normalized name is full-qualified and lower-cased. Lower-casing is * actually wrong, but we have to do it to keep our sanity. If you are * using database objects that only differentiate in the casing (FOO vs * Foo) then you will NOT be able to use Doctrine Schema abstraction. diff --git a/doctrine/dbal/src/Schema/AbstractSchemaManager.php b/doctrine/dbal/src/Schema/AbstractSchemaManager.php index 6b7d162e..50b9face 100644 --- a/doctrine/dbal/src/Schema/AbstractSchemaManager.php +++ b/doctrine/dbal/src/Schema/AbstractSchemaManager.php @@ -8,6 +8,7 @@ use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; use Doctrine\DBAL\Events; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\Deprecations\Deprecation; use Throwable; use function array_filter; @@ -19,6 +20,7 @@ use function call_user_func_array; use function count; use function func_get_args; use function is_callable; +use function is_string; use function preg_match; use function str_replace; use function strtolower; @@ -107,12 +109,21 @@ abstract class AbstractSchemaManager /** * Returns a list of all namespaces in the current database. * + * @deprecated Use {@link listSchemaNames()} instead. + * * @return string[] * * @throws Exception */ public function listNamespaceNames() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'AbstractSchemaManager::listNamespaceNames() is deprecated,' + . ' use AbstractSchemaManager::listSchemaNames() instead.' + ); + $sql = $this->_platform->getListNamespacesSQL(); $namespaces = $this->_conn->fetchAllAssociative($sql); @@ -121,6 +132,18 @@ abstract class AbstractSchemaManager } /** + * Returns a list of the names of all schemata in the current database. + * + * @return list<string> + * + * @throws Exception + */ + public function listSchemaNames(): array + { + throw Exception::notSupported(__METHOD__); + } + + /** * Lists the available sequences for this connection. * * @param string|null $database @@ -205,6 +228,15 @@ abstract class AbstractSchemaManager */ public function tablesExist($names) { + if (is_string($names)) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/3580', + 'The usage of a string $tableNames in AbstractSchemaManager::tablesExist() is deprecated. ' . + 'Pass a one-element array instead.' + ); + } + $names = array_map('strtolower', (array) $names); return count($names) === count(array_intersect($names, array_map('strtolower', $this->listTableNames()))); @@ -342,6 +374,16 @@ abstract class AbstractSchemaManager } /** + * Drops a schema. + * + * @throws Exception + */ + public function dropSchema(string $schemaName): void + { + $this->_execSql($this->_platform->getDropSchemaSQL($schemaName)); + } + + /** * Drops the given table. * * @param string $name The name of the table to drop. @@ -646,9 +688,7 @@ abstract class AbstractSchemaManager */ public function alterTable(TableDiff $tableDiff) { - $queries = $this->_platform->getAlterTableSQL($tableDiff); - - foreach ($queries as $ddlQuery) { + foreach ($this->_platform->getAlterTableSQL($tableDiff) as $ddlQuery) { $this->_execSql($ddlQuery); } } @@ -693,6 +733,8 @@ 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. + * * @param array<int, array<string, mixed>> $namespaces The list of namespace names * in the native DBMS data definition. * @@ -700,6 +742,13 @@ abstract class AbstractSchemaManager */ protected function getPortableNamespacesList(array $namespaces) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'AbstractSchemaManager::getPortableNamespacesList() is deprecated,' + . ' use AbstractSchemaManager::listSchemaNames() instead.' + ); + $namespacesList = []; foreach ($namespaces as $namespace) { @@ -722,12 +771,21 @@ abstract class AbstractSchemaManager /** * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition. * + * @deprecated Use {@link listSchemaNames()} instead. + * * @param array<string, mixed> $namespace The native DBMS namespace definition. * * @return mixed */ protected function getPortableNamespaceDefinition(array $namespace) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'AbstractSchemaManager::getPortableNamespaceDefinition() is deprecated,' + . ' use AbstractSchemaManager::listSchemaNames() instead.' + ); + return $namespace; } @@ -1059,10 +1117,10 @@ abstract class AbstractSchemaManager */ public function createSchema() { - $namespaces = []; + $schemaNames = []; if ($this->_platform->supportsSchemas()) { - $namespaces = $this->listNamespaceNames(); + $schemaNames = $this->listNamespaceNames(); } $sequences = []; @@ -1073,7 +1131,7 @@ abstract class AbstractSchemaManager $tables = $this->listTables(); - return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces); + return new Schema($tables, $sequences, $this->createSchemaConfig(), $schemaNames); } /** diff --git a/doctrine/dbal/src/Schema/DB2SchemaManager.php b/doctrine/dbal/src/Schema/DB2SchemaManager.php index d956d5b7..82c287e0 100644 --- a/doctrine/dbal/src/Schema/DB2SchemaManager.php +++ b/doctrine/dbal/src/Schema/DB2SchemaManager.php @@ -5,6 +5,7 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Types\Types; use function array_change_key_case; use function assert; @@ -69,11 +70,19 @@ class DB2SchemaManager extends AbstractSchemaManager switch (strtolower($tableColumn['typename'])) { case 'varchar': + if ($tableColumn['codepage'] === 0) { + $type = Types::BINARY; + } + $length = $tableColumn['length']; $fixed = false; break; case 'character': + if ($tableColumn['codepage'] === 0) { + $type = Types::BINARY; + } + $length = $tableColumn['length']; $fixed = true; break; @@ -96,7 +105,7 @@ class DB2SchemaManager extends AbstractSchemaManager 'fixed' => (bool) $fixed, 'default' => $default, 'autoincrement' => (bool) $tableColumn['autoincrement'], - 'notnull' => (bool) ($tableColumn['nulls'] === 'N'), + 'notnull' => $tableColumn['nulls'] === 'N', 'scale' => null, 'precision' => null, 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' diff --git a/doctrine/dbal/src/Schema/Index.php b/doctrine/dbal/src/Schema/Index.php index 947ec896..508aa403 100644 --- a/doctrine/dbal/src/Schema/Index.php +++ b/doctrine/dbal/src/Schema/Index.php @@ -43,7 +43,7 @@ class Index extends AbstractAsset implements Constraint * @todo $_flags should eventually be refactored into options * @var mixed[] */ - private $options = []; + private $options; /** * @param string $name diff --git a/doctrine/dbal/src/Schema/MySQLSchemaManager.php b/doctrine/dbal/src/Schema/MySQLSchemaManager.php index b27cdcb0..4d54f715 100644 --- a/doctrine/dbal/src/Schema/MySQLSchemaManager.php +++ b/doctrine/dbal/src/Schema/MySQLSchemaManager.php @@ -91,7 +91,10 @@ class MySQLSchemaManager extends AbstractSchemaManager $v['flags'] = ['SPATIAL']; } - $v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null; + // Ignore prohibited prefix `length` for spatial index + if (strpos($v['index_type'], 'SPATIAL') === false) { + $v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null; + } $tableIndexes[$k] = $v; } diff --git a/doctrine/dbal/src/Schema/OracleSchemaManager.php b/doctrine/dbal/src/Schema/OracleSchemaManager.php index 757af8bb..e24f79ff 100644 --- a/doctrine/dbal/src/Schema/OracleSchemaManager.php +++ b/doctrine/dbal/src/Schema/OracleSchemaManager.php @@ -154,6 +154,11 @@ class OracleSchemaManager extends AbstractSchemaManager $fixed = false; break; + case 'raw': + $length = $tableColumn['data_length']; + $fixed = true; + break; + case 'char': case 'nchar': $length = $tableColumn['char_length']; @@ -162,7 +167,7 @@ class OracleSchemaManager extends AbstractSchemaManager } $options = [ - 'notnull' => (bool) ($tableColumn['nullable'] === 'N'), + 'notnull' => $tableColumn['nullable'] === 'N', 'fixed' => (bool) $fixed, 'unsigned' => (bool) $unsigned, 'default' => $tableColumn['data_default'], @@ -249,18 +254,23 @@ class OracleSchemaManager extends AbstractSchemaManager */ public function createDatabase($database) { - $params = $this->_conn->getParams(); - $username = $database; - $password = $params['password']; + $statement = 'CREATE USER ' . $database; - $query = 'CREATE USER ' . $username . ' IDENTIFIED BY ' . $password; - $this->_conn->executeStatement($query); + $params = $this->_conn->getParams(); - $query = 'GRANT DBA TO ' . $username; - $this->_conn->executeStatement($query); + if (isset($params['password'])) { + $statement .= ' IDENTIFIED BY ' . $params['password']; + } + + $this->_conn->executeStatement($statement); + + $statement = 'GRANT DBA TO ' . $database; + $this->_conn->executeStatement($statement); } /** + * @internal The method should be only used from within the OracleSchemaManager class hierarchy. + * * @param string $table * * @return bool diff --git a/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php b/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php index 9868359f..e353b53e 100644 --- a/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php +++ b/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; +use Doctrine\Deprecations\Deprecation; use function array_change_key_case; use function array_filter; @@ -32,20 +33,42 @@ use const CASE_LOWER; */ class PostgreSQLSchemaManager extends AbstractSchemaManager { - /** @var string[] */ + /** @var string[]|null */ private $existingSchemaPaths; /** * Gets all the existing schema names. * + * @deprecated Use {@link listSchemaNames()} instead. + * * @return string[] * * @throws Exception */ public function getSchemaNames() { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'PostgreSQLSchemaManager::getSchemaNames() is deprecated,' + . ' use PostgreSQLSchemaManager::listSchemaNames() instead.' + ); + + return $this->listNamespaceNames(); + } + + /** + * {@inheritDoc} + */ + public function listSchemaNames(): array + { return $this->_conn->fetchFirstColumn( - "SELECT nspname FROM pg_namespace WHERE nspname !~ '^pg_.*' AND nspname != 'information_schema'" + <<<'SQL' +SELECT schema_name +FROM information_schema.schemata +WHERE schema_name NOT LIKE 'pg\_%' +AND schema_name != 'information_schema' +SQL ); } @@ -73,6 +96,8 @@ class PostgreSQLSchemaManager extends AbstractSchemaManager * * This is a PostgreSQL only function. * + * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. + * * @return string[] */ public function getExistingSchemaSearchPaths() @@ -89,11 +114,13 @@ class PostgreSQLSchemaManager extends AbstractSchemaManager * * This is a PostgreSQL only function. * + * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. + * * @return void */ public function determineExistingSchemaSearchPaths() { - $names = $this->getSchemaNames(); + $names = $this->listSchemaNames(); $paths = $this->getSchemaSearchPaths(); $this->existingSchemaPaths = array_filter($paths, static function ($v) use ($names): bool { @@ -106,11 +133,8 @@ class PostgreSQLSchemaManager extends AbstractSchemaManager */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) { - $onUpdate = null; - $onDelete = null; - $localColumns = []; - $foreignColumns = []; - $foreignTable = null; + $onUpdate = null; + $onDelete = null; if ( preg_match( @@ -267,9 +291,18 @@ class PostgreSQLSchemaManager extends AbstractSchemaManager /** * {@inheritdoc} + * + * @deprecated Use {@link listSchemaNames()} instead. */ protected function getPortableNamespaceDefinition(array $namespace) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'PostgreSQLSchemaManager::getPortableNamespaceDefinition() is deprecated,' + . ' use PostgreSQLSchemaManager::listSchemaNames() instead.' + ); + return $namespace['nspname']; } diff --git a/doctrine/dbal/src/Schema/SQLServerSchemaManager.php b/doctrine/dbal/src/Schema/SQLServerSchemaManager.php index a33bd45c..a448dd14 100644 --- a/doctrine/dbal/src/Schema/SQLServerSchemaManager.php +++ b/doctrine/dbal/src/Schema/SQLServerSchemaManager.php @@ -5,6 +5,7 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\SQLServer2012Platform; use Doctrine\DBAL\Types\Type; +use Doctrine\Deprecations\Deprecation; use PDOException; use function assert; @@ -22,6 +23,20 @@ use function strtok; class SQLServerSchemaManager extends AbstractSchemaManager { /** + * {@inheritDoc} + */ + public function listSchemaNames(): array + { + return $this->_conn->fetchFirstColumn( + <<<'SQL' +SELECT name +FROM sys.schemas +WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys') +SQL + ); + } + + /** * {@inheritdoc} */ protected function _getPortableSequenceDefinition($sequence) @@ -64,6 +79,13 @@ class SQLServerSchemaManager extends AbstractSchemaManager } break; + + case 'varbinary': + if ($length === -1) { + $dbType = 'blob'; + } + + break; } if ($dbType === 'char' || $dbType === 'nchar' || $dbType === 'binary') { @@ -85,7 +107,7 @@ class SQLServerSchemaManager extends AbstractSchemaManager 'comment' => $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null, ]; - if ($length !== 0 && ($type === 'text' || $type === 'string')) { + if ($length !== 0 && ($type === 'text' || $type === 'string' || $type === 'binary')) { $options['length'] = $length; } @@ -199,9 +221,18 @@ class SQLServerSchemaManager extends AbstractSchemaManager /** * {@inheritdoc} + * + * @deprecated Use {@link listSchemaNames()} instead. */ protected function getPortableNamespaceDefinition(array $namespace) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'SQLServerSchemaManager::getPortableNamespaceDefinition() is deprecated,' + . ' use SQLServerSchemaManager::listSchemaNames() instead.' + ); + return $namespace['name']; } diff --git a/doctrine/dbal/src/Schema/SchemaConfig.php b/doctrine/dbal/src/Schema/SchemaConfig.php index b8c3502f..56d49c4a 100644 --- a/doctrine/dbal/src/Schema/SchemaConfig.php +++ b/doctrine/dbal/src/Schema/SchemaConfig.php @@ -13,7 +13,7 @@ class SchemaConfig /** @var int */ protected $maxIdentifierLength = 63; - /** @var string */ + /** @var string|null */ protected $name; /** @var mixed[] */ @@ -58,7 +58,7 @@ class SchemaConfig /** * Gets the default namespace of schema objects. * - * @return string + * @return string|null */ public function getName() { diff --git a/doctrine/dbal/src/Schema/Sequence.php b/doctrine/dbal/src/Schema/Sequence.php index 1a48def8..a634e84f 100644 --- a/doctrine/dbal/src/Schema/Sequence.php +++ b/doctrine/dbal/src/Schema/Sequence.php @@ -67,7 +67,7 @@ class Sequence extends AbstractAsset public function setAllocationSize($allocationSize) { if ($allocationSize > 0) { - $this->allocationSize = (int) $allocationSize; + $this->allocationSize = $allocationSize; } else { $this->allocationSize = 1; } @@ -83,7 +83,7 @@ class Sequence extends AbstractAsset public function setInitialValue($initialValue) { if ($initialValue > 0) { - $this->initialValue = (int) $initialValue; + $this->initialValue = $initialValue; } else { $this->initialValue = 1; } diff --git a/doctrine/dbal/src/Schema/SqliteSchemaManager.php b/doctrine/dbal/src/Schema/SqliteSchemaManager.php index 857f90b0..140ebb3c 100644 --- a/doctrine/dbal/src/Schema/SqliteSchemaManager.php +++ b/doctrine/dbal/src/Schema/SqliteSchemaManager.php @@ -52,13 +52,12 @@ class SqliteSchemaManager extends AbstractSchemaManager */ public function createDatabase($database) { - $params = $this->_conn->getParams(); - $driver = $params['driver']; - $options = [ - 'driver' => $driver, - 'path' => $database, - ]; - $conn = DriverManager::getConnection($options); + $params = $this->_conn->getParams(); + + $params['path'] = $database; + unset($params['memory']); + + $conn = DriverManager::getConnection($params); $conn->connect(); $conn->close(); } @@ -181,15 +180,23 @@ class SqliteSchemaManager extends AbstractSchemaManager $this->_conn->quote($tableName) )); - usort($indexArray, static function ($a, $b) { - if ($a['pk'] === $b['pk']) { - return $a['cid'] - $b['cid']; + usort( + $indexArray, + /** + * @param array<string,mixed> $a + * @param array<string,mixed> $b + */ + static function (array $a, array $b): int { + if ($a['pk'] === $b['pk']) { + return $a['cid'] - $b['cid']; + } + + return $a['pk'] - $b['pk']; } + ); - return $a['pk'] - $b['pk']; - }); foreach ($indexArray as $indexColumnRow) { - if ($indexColumnRow['pk'] === '0') { + if ($indexColumnRow['pk'] === 0 || $indexColumnRow['pk'] === '0') { continue; } @@ -240,7 +247,7 @@ class SqliteSchemaManager extends AbstractSchemaManager $autoincrementCount = 0; foreach ($tableColumns as $tableColumn) { - if ($tableColumn['pk'] === '0') { + if ($tableColumn['pk'] === 0 || $tableColumn['pk'] === '0') { continue; } @@ -362,7 +369,7 @@ class SqliteSchemaManager extends AbstractSchemaManager $options = [ 'length' => $length, - 'unsigned' => (bool) $unsigned, + 'unsigned' => $unsigned, 'fixed' => $fixed, 'notnull' => $notnull, 'default' => $default, @@ -412,7 +419,12 @@ class SqliteSchemaManager extends AbstractSchemaManager ]; } - $list[$name]['local'][] = $value['from']; + $list[$name]['local'][] = $value['from']; + + if ($value['to'] === null) { + continue; + } + $list[$name]['foreign'][] = $value['to']; } @@ -556,4 +568,13 @@ SQL return $table; } + + /** + * {@inheritDoc} + */ + public function getSchemaSearchPaths() + { + // 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 181e5dec..d330d03f 100644 --- a/doctrine/dbal/src/Schema/Table.php +++ b/doctrine/dbal/src/Schema/Table.php @@ -11,8 +11,6 @@ use function array_filter; use function array_keys; use function array_merge; use function in_array; -use function is_numeric; -use function is_string; use function preg_match; use function strlen; use function strtolower; @@ -688,7 +686,7 @@ class Table extends AbstractAsset { $name = $this->normalizeIdentifier($name); - if (! $this->hasForeignKey($name)) { + if (! $this->hasUniqueConstraint($name)) { throw SchemaException::uniqueConstraintDoesNotExist($name, $this->_name); } @@ -737,7 +735,7 @@ class Table extends AbstractAsset */ private function filterColumns(array $columnNames, bool $reverse = false): array { - return array_filter($this->_columns, static function ($columnName) use ($columnNames, $reverse): bool { + return array_filter($this->_columns, static function (string $columnName) use ($columnNames, $reverse): bool { return in_array($columnName, $columnNames, true) !== $reverse; }, ARRAY_FILTER_USE_KEY); } @@ -967,11 +965,7 @@ class Table extends AbstractAsset throw SchemaException::indexNameInvalid($indexName); } - foreach ($columnNames as $columnName => $indexColOptions) { - if (is_numeric($columnName) && is_string($indexColOptions)) { - $columnName = $indexColOptions; - } - + foreach ($columnNames as $columnName) { if (! $this->hasColumn($columnName)) { throw SchemaException::columnDoesNotExist($columnName, $this->_name); } diff --git a/doctrine/dbal/src/Schema/UniqueConstraint.php b/doctrine/dbal/src/Schema/UniqueConstraint.php index 87a5f6e3..cb91becb 100644 --- a/doctrine/dbal/src/Schema/UniqueConstraint.php +++ b/doctrine/dbal/src/Schema/UniqueConstraint.php @@ -34,7 +34,7 @@ class UniqueConstraint extends AbstractAsset implements Constraint * * @var mixed[] */ - private $options = []; + private $options; /** * @param string[] $columns diff --git a/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php b/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php index 9f778109..1b77268b 100644 --- a/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php +++ b/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php @@ -32,7 +32,7 @@ class DropSchemaSqlCollector extends AbstractVisitor public function __construct(AbstractPlatform $platform) { $this->platform = $platform; - $this->clearQueries(); + $this->initializeQueries(); } /** @@ -68,9 +68,7 @@ class DropSchemaSqlCollector extends AbstractVisitor */ public function clearQueries() { - $this->constraints = new SplObjectStorage(); - $this->sequences = new SplObjectStorage(); - $this->tables = new SplObjectStorage(); + $this->initializeQueries(); } /** @@ -98,4 +96,11 @@ class DropSchemaSqlCollector extends AbstractVisitor return $sql; } + + private function initializeQueries(): void + { + $this->constraints = new SplObjectStorage(); + $this->sequences = new SplObjectStorage(); + $this->tables = new SplObjectStorage(); + } } diff --git a/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php b/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php index 001ea2a0..509eac6f 100644 --- a/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php +++ b/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php @@ -20,7 +20,7 @@ use Doctrine\DBAL\Schema\Table; */ class RemoveNamespacedAssets extends AbstractVisitor { - /** @var Schema */ + /** @var Schema|null */ private $schema; /** @@ -36,6 +36,10 @@ class RemoveNamespacedAssets extends AbstractVisitor */ public function acceptTable(Table $table) { + if ($this->schema === null) { + return; + } + if ($table->isInDefaultNamespace($this->schema->getName())) { return; } @@ -48,6 +52,10 @@ class RemoveNamespacedAssets extends AbstractVisitor */ public function acceptSequence(Sequence $sequence) { + if ($this->schema === null) { + return; + } + if ($sequence->isInDefaultNamespace($this->schema->getName())) { return; } @@ -60,6 +68,10 @@ class RemoveNamespacedAssets extends AbstractVisitor */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { + if ($this->schema === null) { + return; + } + // The table may already be deleted in a previous // RemoveNamespacedAssets#acceptTable call. Removing Foreign keys that // point to nowhere. diff --git a/doctrine/dbal/src/Statement.php b/doctrine/dbal/src/Statement.php index d7a541a8..b7183fa3 100644 --- a/doctrine/dbal/src/Statement.php +++ b/doctrine/dbal/src/Statement.php @@ -6,7 +6,9 @@ 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; +use function func_num_args; use function is_string; /** @@ -146,7 +148,11 @@ class Statement $this->types[$param] = $type; try { - return $this->stmt->bindParam($param, $variable, $type, $length); + if (func_num_args() > 3) { + return $this->stmt->bindParam($param, $variable, $type, $length); + } + + return $this->stmt->bindParam($param, $variable, $type); } catch (Exception $e) { throw $this->conn->convertException($e); } @@ -155,12 +161,20 @@ class Statement /** * Executes the statement with the currently bound parameters. * + * @deprecated Statement::execute() is deprecated, use Statement::executeQuery() or executeStatement() instead + * * @param mixed[]|null $params * * @throws Exception */ public function execute($params = null): Result { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4580', + 'Statement::execute() is deprecated, use Statement::executeQuery() or Statement::executeStatement() instead' + ); + if ($params !== null) { $this->params = $params; } @@ -185,6 +199,38 @@ class Statement } /** + * Executes the statement with the currently bound parameters and return result. + * + * @param mixed[] $params + * + * @throws Exception + */ + public function executeQuery(array $params = []): Result + { + if ($params === []) { + $params = null; // Workaround as long execute() exists and used internally. + } + + return $this->execute($params); + } + + /** + * Executes the statement with the currently bound parameters and return affected rows. + * + * @param mixed[] $params + * + * @throws Exception + */ + public function executeStatement(array $params = []): int + { + if ($params === []) { + $params = null; // Workaround as long execute() exists and used internally. + } + + return $this->execute($params)->rowCount(); + } + + /** * Gets the wrapped driver statement. * * @return DriverStatement diff --git a/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php b/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php index a06e2d0f..8cff50f9 100644 --- a/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php +++ b/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php @@ -17,6 +17,7 @@ use Doctrine\DBAL\Platforms\Keywords\ReservedKeywordsValidator; use Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords; use Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords; use Doctrine\DBAL\Tools\Console\ConnectionProvider; +use Doctrine\Deprecations\Deprecation; use InvalidArgumentException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -32,19 +33,8 @@ use function is_string; class ReservedWordsCommand extends Command { - /** @var array<string,class-string<KeywordList>> */ - private $keywordListClasses = [ - 'db2' => DB2Keywords::class, - 'mysql' => MySQLKeywords::class, - 'mysql57' => MySQL57Keywords::class, - 'mysql80' => MySQL80Keywords::class, - 'mariadb102' => MariaDb102Keywords::class, - 'oracle' => OracleKeywords::class, - 'pgsql' => PostgreSQL94Keywords::class, - 'pgsql100' => PostgreSQL100Keywords::class, - 'sqlite' => SQLiteKeywords::class, - 'sqlserver' => SQLServer2012Keywords::class, - ]; + /** @var array<string,KeywordList> */ + private $keywordLists; /** @var ConnectionProvider */ private $connectionProvider; @@ -53,6 +43,27 @@ class ReservedWordsCommand extends Command { parent::__construct(); $this->connectionProvider = $connectionProvider; + + $this->keywordLists = [ + 'db2' => new DB2Keywords(), + 'mariadb102' => new MariaDb102Keywords(), + 'mysql' => new MySQLKeywords(), + 'mysql57' => new MySQL57Keywords(), + 'mysql80' => new MySQL80Keywords(), + 'oracle' => new OracleKeywords(), + 'pgsql' => new PostgreSQL94Keywords(), + 'pgsql100' => new PostgreSQL100Keywords(), + 'sqlite' => new SQLiteKeywords(), + 'sqlserver' => new SQLServer2012Keywords(), + ]; + } + + /** + * Add or replace a keyword list. + */ + public function setKeywordList(string $name, KeywordList $keywordList): void + { + $this->keywordLists[$name] = $keywordList; } /** @@ -65,7 +76,14 @@ class ReservedWordsCommand extends Command */ public function setKeywordListClass($name, $class) { - $this->keywordListClasses[$name] = $class; + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'ReservedWordsCommand::setKeywordListClass() is deprecated,' + . ' use ReservedWordsCommand::setKeywordList() instead.' + ); + + $this->keywordLists[$name] = new $class(); } /** @return void */ @@ -87,8 +105,7 @@ class ReservedWordsCommand extends Command Checks if the current database contains tables and columns with names that are identifiers in this dialect or in other SQL dialects. -By default SQLite, MySQL, PostgreSQL, Microsoft SQL Server and Oracle -keywords are checked: +By default all supported platform keywords are checked: <info>%command.full_name%</info> @@ -99,17 +116,16 @@ pass them to the command: The following keyword lists are currently shipped with Doctrine: + * db2 + * mariadb102 * mysql * mysql57 * mysql80 - * mariadb102 + * oracle * pgsql * pgsql100 * sqlite - * oracle * sqlserver - * sqlserver2012 - * db2 (Not checked by default) EOT ); } @@ -132,20 +148,19 @@ EOT } if (count($keywordLists) === 0) { - $keywordLists = array_keys($this->keywordListClasses); + $keywordLists = array_keys($this->keywordLists); } $keywords = []; foreach ($keywordLists as $keywordList) { - if (! isset($this->keywordListClasses[$keywordList])) { + if (! isset($this->keywordLists[$keywordList])) { throw new InvalidArgumentException( "There exists no keyword list with name '" . $keywordList . "'. " . - 'Known lists: ' . implode(', ', array_keys($this->keywordListClasses)) + 'Known lists: ' . implode(', ', array_keys($this->keywordLists)) ); } - $class = $this->keywordListClasses[$keywordList]; - $keywords[] = new $class(); + $keywords[] = $this->keywordLists[$keywordList]; } $output->write( diff --git a/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php b/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php index a4ba7d3b..4ccb3260 100644 --- a/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php +++ b/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php @@ -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.', '7'), new InputOption('force-fetch', null, InputOption::VALUE_NONE, 'Forces fetching the result.'), ]) ->setHelp(<<<EOT diff --git a/doctrine/dbal/src/Tools/Console/ConsoleRunner.php b/doctrine/dbal/src/Tools/Console/ConsoleRunner.php index 92998188..1cc315de 100644 --- a/doctrine/dbal/src/Tools/Console/ConsoleRunner.php +++ b/doctrine/dbal/src/Tools/Console/ConsoleRunner.php @@ -25,9 +25,7 @@ class ConsoleRunner */ public static function run(ConnectionProvider $connectionProvider, $commands = []) { - $cli = new Application('Doctrine Command Line Interface', Versions::getVersion( - Versions::rootPackageName() - )); + $cli = new Application('Doctrine Command Line Interface', Versions::getVersion('doctrine/dbal')); $cli->setCatchExceptions(true); self::addCommands($cli, $connectionProvider); diff --git a/doctrine/dbal/src/Tools/Dumper.php b/doctrine/dbal/src/Tools/Dumper.php index c1e72aa3..23dc7a05 100644 --- a/doctrine/dbal/src/Tools/Dumper.php +++ b/doctrine/dbal/src/Tools/Dumper.php @@ -86,8 +86,7 @@ final class Dumper */ public static function export($var, int $maxDepth) { - $return = null; - $isObj = is_object($var); + $isObj = is_object($var); if ($var instanceof Collection) { $var = $var->toArray(); diff --git a/doctrine/dbal/src/Types/DecimalType.php b/doctrine/dbal/src/Types/DecimalType.php index be76b7c8..f75d3db2 100644 --- a/doctrine/dbal/src/Types/DecimalType.php +++ b/doctrine/dbal/src/Types/DecimalType.php @@ -4,6 +4,10 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use function is_float; + +use const PHP_VERSION_ID; + /** * Type that maps an SQL DECIMAL to a PHP string. */ @@ -30,6 +34,12 @@ class DecimalType extends Type */ public function convertToPHPValue($value, AbstractPlatform $platform) { + // Some drivers starting from PHP 8.1 can represent decimals as float + // See also: https://github.com/doctrine/dbal/pull/4818 + if (PHP_VERSION_ID >= 80100 && is_float($value)) { + return (string) $value; + } + return $value; } } diff --git a/doctrine/dbal/src/Types/Type.php b/doctrine/dbal/src/Types/Type.php index f86a423e..03e5b824 100644 --- a/doctrine/dbal/src/Types/Type.php +++ b/doctrine/dbal/src/Types/Type.php @@ -108,9 +108,6 @@ abstract class Type */ abstract public function getName(); - /** - * @internal This method is only to be used within DBAL for forward compatibility purposes. Do not use directly. - */ final public static function getTypeRegistry(): TypeRegistry { if (self::$typeRegistry === null) { diff --git a/doctrine/dbal/src/Types/TypeRegistry.php b/doctrine/dbal/src/Types/TypeRegistry.php index fdae6d60..ce33b951 100644 --- a/doctrine/dbal/src/Types/TypeRegistry.php +++ b/doctrine/dbal/src/Types/TypeRegistry.php @@ -12,8 +12,6 @@ use function in_array; /** * The type registry is responsible for holding a map of all known DBAL types. * The types are stored using the flyweight pattern so that one type only exists as exactly one instance. - * - * @internal TypeRegistry exists for forward compatibility, its API should not be considered stable. */ final class TypeRegistry { diff --git a/doctrine/dbal/static-analysis/driver-manager-retrieves-correct-connection-type.php b/doctrine/dbal/static-analysis/driver-manager-retrieves-correct-connection-type.php new file mode 100644 index 00000000..566db247 --- /dev/null +++ b/doctrine/dbal/static-analysis/driver-manager-retrieves-correct-connection-type.php @@ -0,0 +1,19 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\StaticAnalysis\DBAL; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DriverManager; + +final class MyConnection extends Connection +{ +} + +function makeMeACustomConnection(): MyConnection +{ + return DriverManager::getConnection([ + 'wrapperClass' => MyConnection::class, + ]); +} diff --git a/doctrine/deprecations/.gitignore b/doctrine/deprecations/.gitignore new file mode 100644 index 00000000..2ee7dedc --- /dev/null +++ b/doctrine/deprecations/.gitignore @@ -0,0 +1,3 @@ +vendor +.phpcs-cache +composer.lock diff --git a/doctrine/deprecations/README.md b/doctrine/deprecations/README.md new file mode 100644 index 00000000..d6682221 --- /dev/null +++ b/doctrine/deprecations/README.md @@ -0,0 +1,147 @@ +# Doctrine Deprecations + +A small (side-effect free by default) layer on top of +`trigger_error(E_USER_DEPRECATED)` or PSR-3 logging. + +- no side-effects by default, making it a perfect fit for libraries that don't know how the error handler works they operate under +- options to avoid having to rely on error handlers global state by using PSR-3 logging +- deduplicate deprecation messages to avoid excessive triggering and reduce overhead + +We recommend to collect Deprecations using a PSR logger instead of relying on +the global error handler. + +## Usage from consumer perspective: + +Enable Doctrine deprecations to be sent to a PSR3 logger: + +```php +\Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger); +``` + +Enable Doctrine deprecations to be sent as `@trigger_error($message, E_USER_DEPRECATED)` +messages. + +```php +\Doctrine\Deprecations\Deprecation::enableWithTriggerError(); +``` + +If you only want to enable deprecation tracking, without logging or calling `trigger_error` then call: + +```php +\Doctrine\Deprecations\Deprecation::enableTrackingDeprecations(); +``` + +Tracking is enabled with all three modes and provides access to all triggered +deprecations and their individual count: + +```php +$deprecations = \Doctrine\Deprecations\Deprecation::getTriggeredDeprecations(); + +foreach ($deprecations as $identifier => $count) { + echo $identifier . " was triggered " . $count . " times\n"; +} +``` + +### Suppressing Specific Deprecations + +Disable triggering about specific deprecations: + +```php +\Doctrine\Deprecations\Deprecation::ignoreDeprecations("https://link/to/deprecations-description-identifier"); +``` + +Disable all deprecations from a package + +```php +\Doctrine\Deprecations\Deprecation::ignorePackage("doctrine/orm"); +``` + +### Other Operations + +When used within PHPUnit or other tools that could collect multiple instances of the same deprecations +the deduplication can be disabled: + +```php +\Doctrine\Deprecations\Deprecation::withoutDeduplication(); +``` + +Disable deprecation tracking again: + +```php +\Doctrine\Deprecations\Deprecation::disable(); +``` + +## Usage from a library/producer perspective: + +When you want to unconditionally trigger a deprecation even when called +from the library itself then the `trigger` method is the way to go: + +```php +\Doctrine\Deprecations\Deprecation::trigger( + "doctrine/orm", + "https://link/to/deprecations-description", + "message" +); +``` + +If variable arguments are provided at the end, they are used with `sprintf` on +the message. + +```php +\Doctrine\Deprecations\Deprecation::trigger( + "doctrine/orm", + "https://github.com/doctrine/orm/issue/1234", + "message %s %d", + "foo", + 1234 +); +``` + +When you want to trigger a deprecation only when it is called by a function +outside of the current package, but not trigger when the package itself is the cause, +then use: + +```php +\Doctrine\Deprecations\Deprecation::triggerIfCalledFromOutside( + "doctrine/orm", + "https://link/to/deprecations-description", + "message" +); +``` + +Based on the issue link each deprecation message is only triggered once per +request. + +A limited stacktrace is included in the deprecation message to find the +offending location. + +Note: A producer/library should never call `Deprecation::enableWith` methods +and leave the decision how to handle deprecations to application and +frameworks. + +## Usage in PHPUnit tests + +There is a `VerifyDeprecations` trait that you can use to make assertions on +the occurrence of deprecations within a test. + +```php +use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; + +class MyTest extends TestCase +{ + use VerifyDeprecations; + + public function testSomethingDeprecation() + { + $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234'); + + triggerTheCodeWithDeprecation(); + } +} +``` + +## What is a deprecation identifier? + +An identifier for deprecations is just a link to any resource, most often a +Github Issue or Pull Request explaining the deprecation and potentially its +alternative. diff --git a/doctrine/deprecations/composer.json b/doctrine/deprecations/composer.json new file mode 100644 index 00000000..5cc7ac13 --- /dev/null +++ b/doctrine/deprecations/composer.json @@ -0,0 +1,27 @@ +{ + "name": "doctrine/deprecations", + "type": "library", + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "license": "MIT", + "require": { + "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" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "autoload": { + "psr-4": {"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"} + }, + "autoload-dev": { + "psr-4": { + "DeprecationTests\\": "test_fixtures/src", + "Doctrine\\Foo\\": "test_fixtures/vendor/doctrine/foo" + } + } +} diff --git a/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php b/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php new file mode 100644 index 00000000..1029372f --- /dev/null +++ b/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php @@ -0,0 +1,266 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\Deprecations; + +use Psr\Log\LoggerInterface; + +use function array_key_exists; +use function array_reduce; +use function debug_backtrace; +use function sprintf; +use function strpos; +use function strrpos; +use function substr; +use function trigger_error; + +use const DEBUG_BACKTRACE_IGNORE_ARGS; +use const DIRECTORY_SEPARATOR; +use const E_USER_DEPRECATED; + +/** + * Manages Deprecation logging in different ways. + * + * By default triggered exceptions are not logged. + * + * To enable different deprecation logging mechanisms you can call the + * following methods: + * + * - Minimal collection of deprecations via getTriggeredDeprecations() + * \Doctrine\Deprecations\Deprecation::enableTrackingDeprecations(); + * + * - Uses @trigger_error with E_USER_DEPRECATED + * \Doctrine\Deprecations\Deprecation::enableWithTriggerError(); + * + * - Sends deprecation messages via a PSR-3 logger + * \Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger); + * + * Packages that trigger deprecations should use the `trigger()` or + * `triggerIfCalledFromOutside()` methods. + */ +class Deprecation +{ + private const TYPE_NONE = 0; + private const TYPE_TRACK_DEPRECATIONS = 1; + private const TYPE_TRIGGER_ERROR = 2; + private const TYPE_PSR_LOGGER = 4; + + /** @var int */ + private static $type = self::TYPE_NONE; + + /** @var LoggerInterface|null */ + private static $logger; + + /** @var array<string,bool> */ + private static $ignoredPackages = []; + + /** @var array<string,int> */ + private static $ignoredLinks = []; + + /** @var bool */ + private static $deduplication = true; + + /** + * Trigger a deprecation for the given package and identfier. + * + * The link should point to a Github issue or Wiki entry detailing the + * deprecation. It is additionally used to de-duplicate the trigger of the + * same deprecation during a request. + * + * @param mixed $args + */ + public static function trigger(string $package, string $link, string $message, ...$args): void + { + if (self::$type === self::TYPE_NONE) { + return; + } + + if (array_key_exists($link, self::$ignoredLinks)) { + self::$ignoredLinks[$link]++; + } else { + self::$ignoredLinks[$link] = 1; + } + + if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) { + return; + } + + if (isset(self::$ignoredPackages[$package])) { + return; + } + + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + + $message = sprintf($message, ...$args); + + self::delegateTriggerToBackend($message, $backtrace, $link, $package); + } + + /** + * Trigger a deprecation for the given package and identifier when called from outside. + * + * "Outside" means we assume that $package is currently installed as a + * dependency and the caller is not a file in that package. When $package + * is installed as a root package then deprecations triggered from the + * tests folder are also considered "outside". + * + * This deprecation method assumes that you are using Composer to install + * the dependency and are using the default /vendor/ folder and not a + * Composer plugin to change the install location. The assumption is also + * that $package is the exact composer packge name. + * + * Compared to {@link trigger()} this method causes some overhead when + * deprecation tracking is enabled even during deduplication, because it + * needs to call {@link debug_backtrace()} + * + * @param mixed $args + */ + public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args): void + { + if (self::$type === self::TYPE_NONE) { + return; + } + + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + + // first check that the caller is not from a tests folder, in which case we always let deprecations pass + if (strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === false) { + $path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package . DIRECTORY_SEPARATOR; + + if (strpos($backtrace[0]['file'], $path) === false) { + return; + } + + if (strpos($backtrace[1]['file'], $path) !== false) { + return; + } + } + + if (array_key_exists($link, self::$ignoredLinks)) { + self::$ignoredLinks[$link]++; + } else { + self::$ignoredLinks[$link] = 1; + } + + if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) { + return; + } + + if (isset(self::$ignoredPackages[$package])) { + return; + } + + $message = sprintf($message, ...$args); + + self::delegateTriggerToBackend($message, $backtrace, $link, $package); + } + + /** + * @param array<mixed> $backtrace + */ + private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void + { + if ((self::$type & self::TYPE_PSR_LOGGER) > 0) { + $context = [ + 'file' => $backtrace[0]['file'], + 'line' => $backtrace[0]['line'], + 'package' => $package, + 'link' => $link, + ]; + + self::$logger->notice($message, $context); + } + + if (! ((self::$type & self::TYPE_TRIGGER_ERROR) > 0)) { + return; + } + + $message .= sprintf( + ' (%s:%d called by %s:%d, %s, package %s)', + self::basename($backtrace[0]['file']), + $backtrace[0]['line'], + self::basename($backtrace[1]['file']), + $backtrace[1]['line'], + $link, + $package + ); + + @trigger_error($message, E_USER_DEPRECATED); + } + + /** + * A non-local-aware version of PHPs basename function. + */ + private static function basename(string $filename): string + { + $pos = strrpos($filename, DIRECTORY_SEPARATOR); + + if ($pos === false) { + return $filename; + } + + return substr($filename, $pos + 1); + } + + public static function enableTrackingDeprecations(): void + { + self::$type |= self::TYPE_TRACK_DEPRECATIONS; + } + + public static function enableWithTriggerError(): void + { + self::$type |= self::TYPE_TRIGGER_ERROR; + } + + public static function enableWithPsrLogger(LoggerInterface $logger): void + { + self::$type |= self::TYPE_PSR_LOGGER; + self::$logger = $logger; + } + + public static function withoutDeduplication(): void + { + self::$deduplication = false; + } + + public static function disable(): void + { + self::$type = self::TYPE_NONE; + self::$logger = null; + self::$deduplication = true; + + foreach (self::$ignoredLinks as $link => $count) { + self::$ignoredLinks[$link] = 0; + } + } + + public static function ignorePackage(string $packageName): void + { + self::$ignoredPackages[$packageName] = true; + } + + public static function ignoreDeprecations(string ...$links): void + { + foreach ($links as $link) { + self::$ignoredLinks[$link] = 0; + } + } + + public static function getUniqueTriggeredDeprecationsCount(): int + { + return array_reduce(self::$ignoredLinks, static function (int $carry, int $count) { + return $carry + $count; + }, 0); + } + + /** + * Returns each triggered deprecation link identifier and the amount of occurrences. + * + * @return array<string,int> + */ + public static function getTriggeredDeprecations(): array + { + return self::$ignoredLinks; + } +} diff --git a/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php b/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php new file mode 100644 index 00000000..4c3366a9 --- /dev/null +++ b/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php @@ -0,0 +1,66 @@ +<?php + +declare(strict_types=1); + +namespace Doctrine\Deprecations\PHPUnit; + +use Doctrine\Deprecations\Deprecation; + +use function sprintf; + +trait VerifyDeprecations +{ + /** @var array<string,int> */ + private $doctrineDeprecationsExpectations = []; + + /** @var array<string,int> */ + private $doctrineNoDeprecationsExpectations = []; + + public function expectDeprecationWithIdentifier(string $identifier): void + { + $this->doctrineDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; + } + + public function expectNoDeprecationWithIdentifier(string $identifier): void + { + $this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; + } + + /** + * @before + */ + public function enableDeprecationTracking(): void + { + Deprecation::enableTrackingDeprecations(); + } + + /** + * @after + */ + public function verifyDeprecationsAreTriggered(): void + { + foreach ($this->doctrineDeprecationsExpectations as $identifier => $expectation) { + $actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; + + $this->assertTrue( + $actualCount > $expectation, + sprintf( + "Expected deprecation with identifier '%s' was not triggered by code executed in test.", + $identifier + ) + ); + } + + foreach ($this->doctrineNoDeprecationsExpectations as $identifier => $expectation) { + $actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; + + $this->assertTrue( + $actualCount === $expectation, + sprintf( + "Expected deprecation with identifier '%s' was triggered by code executed in test, but expected not to.", + $identifier + ) + ); + } + } +} diff --git a/doctrine/deprecations/phpcs.xml b/doctrine/deprecations/phpcs.xml new file mode 100644 index 00000000..4e0cc21f --- /dev/null +++ b/doctrine/deprecations/phpcs.xml @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<ruleset> + <arg name="basepath" value="."/> + <arg name="extensions" value="php"/> + <arg name="parallel" value="80"/> + <arg name="cache" value=".phpcs-cache"/> + <arg name="colors"/> + + <!-- Ignore warnings, show progress of the run and show sniff names --> + <arg value="nps"/> + + <!-- Directories to be checked --> + <file>lib</file> + <file>tests</file> + + <!-- Include full Doctrine Coding Standard --> + <rule ref="Doctrine"> + <exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint" /> + </rule> +</ruleset> diff --git a/doctrine/deprecations/phpunit.xml.dist b/doctrine/deprecations/phpunit.xml.dist new file mode 100644 index 00000000..4740c060 --- /dev/null +++ b/doctrine/deprecations/phpunit.xml.dist @@ -0,0 +1,8 @@ +<?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 new file mode 100644 index 00000000..c4b8ebec --- /dev/null +++ b/doctrine/deprecations/test_fixtures/src/Foo.php @@ -0,0 +1,22 @@ +<?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 new file mode 100644 index 00000000..feccd486 --- /dev/null +++ b/doctrine/deprecations/test_fixtures/src/RootDeprecation.php @@ -0,0 +1,20 @@ +<?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 + ); + + } +} |