diff --git a/.doctrine-project.json b/.doctrine-project.json index 7b97cb4512..f68f2fd0bc 100644 --- a/.doctrine-project.json +++ b/.doctrine-project.json @@ -11,21 +11,27 @@ "slug": "latest", "upcoming": true }, + { + "name": "2.11", + "branchName": "2.11.x", + "slug": "2.11", + "current": true, + "aliases": [ + "current", + "stable" + ] + }, { "name": "2.10", - "branchName": "master", + "branchName": "2.10.x", "slug": "2.10", - "upcoming": true + "maintained": false }, { "name": "2.9", "branchName": "2.9", "slug": "2.9", - "current": true, - "aliases": [ - "current", - "stable" - ] + "maintained": false }, { "name": "2.8", diff --git a/.gitattributes b/.gitattributes index 81a3abf96a..97a6be4d77 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,14 +1,15 @@ /.appveyor.yml export-ignore +/composer.lock export-ignore +/docs export-ignore +/.doctrine-project.json export-ignore /.gitattributes export-ignore /.github export-ignore /.gitignore export-ignore -/.travis.yml export-ignore -/build.properties export-ignore -/build.xml export-ignore -/composer.lock export-ignore -/docs export-ignore /phpcs.xml.dist export-ignore /phpstan.neon.dist export-ignore /phpunit.xml.dist export-ignore /run-all.sh export-ignore +/SECURITY.md export-ignore /tests export-ignore +/.travis.yml export-ignore +/UPGRADE.md export-ignore diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml new file mode 100644 index 0000000000..0a8bf324d9 --- /dev/null +++ b/.github/workflows/release-on-milestone-closed.yml @@ -0,0 +1,45 @@ +name: "Automatic Releases" + +on: + milestone: + types: + - "closed" + +jobs: + release: + name: "Git tag, release & create merge-up PR" + runs-on: "ubuntu-20.04" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Release" + uses: "laminas/automatic-releases@1.0.1" + with: + command-name: "laminas:automatic-releases:release" + env: + "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Create Merge-Up Pull Request" + uses: "laminas/automatic-releases@1.0.1" + with: + command-name: "laminas:automatic-releases:create-merge-up-pull-request" + env: + "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Create and/or Switch to new Release Branch" + uses: "laminas/automatic-releases@v1" + with: + command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor" + env: + "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} diff --git a/.travis.yml b/.travis.yml index 82b463431d..4621a9afa9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,43 +33,6 @@ jobs: php: 7.3 env: DB=sqlite - - stage: Test - php: 7.2 - env: DB=mysql.docker IMAGE=mysql:8.0 - - stage: Test - php: 7.2 - env: DB=mysqli.docker IMAGE=mysql:8.0 - - stage: Test - php: 7.2 - env: DB=mariadb.docker IMAGE=mariadb:10.3 - - stage: Test - php: 7.2 - env: DB=mariadb.mysqli.docker IMAGE=mariadb:10.3 - - stage: Test - php: 7.2 - env: DB=pgsql POSTGRESQL_VERSION=11.0 - sudo: required - before_script: - - bash ./tests/travis/install-postgres-11.sh - - stage: Test - php: 7.2 - env: DB=sqlite - - stage: Test - php: 7.2 - env: DB=sqlsrv - sudo: required - before_script: - - bash ./tests/travis/install-sqlsrv-dependencies.sh - - bash ./tests/travis/install-mssql-sqlsrv.sh - - bash ./tests/travis/install-mssql.sh - - stage: Test - php: 7.2 - env: DB=pdo_sqlsrv - sudo: required - before_script: - - bash ./tests/travis/install-sqlsrv-dependencies.sh - - bash ./tests/travis/install-mssql-pdo_sqlsrv.sh - - bash ./tests/travis/install-mssql.sh - stage: Test php: 7.3 env: DB=mysql.docker IMAGE=mysql:5.7 diff --git a/README.md b/README.md index 157c5d29a5..58c5bc3170 100644 --- a/README.md +++ b/README.md @@ -40,12 +40,12 @@ $table->addUniqueIndex( ## Original README from doctrine/dbal: -| [Master][Master] | [2.10][2.10] | +| [Master][Master] | [2.11][2.11] | |:----------------:|:----------:| -| [![Build status][Master image]][Master] | [![Build status][2.10 image]][2.10] | -| [![GitHub Actions][GA master image]][GA master] | [![GitHub Actions][GA 2.10 image]][GA 2.10] | -| [![AppVeyor][AppVeyor master image]][AppVeyor master] | [![AppVeyor][AppVeyor 2.10 image]][AppVeyor 2.10] -| [![Code Coverage][Coverage image]][CodeCov Master] | [![Code Coverage][Coverage 2.10 image]][CodeCov 2.10] | +| [![Build status][Master image]][Master] | [![Build status][2.11 image]][2.11] | +| [![GitHub Actions][GA master image]][GA master] | [![GitHub Actions][GA 2.11 image]][GA 2.11] | +| [![AppVeyor][AppVeyor master image]][AppVeyor master] | [![AppVeyor][AppVeyor 2.11 image]][AppVeyor 2.11] | +| [![Code Coverage][Coverage image]][CodeCov Master] | [![Code Coverage][Coverage 2.11 image]][CodeCov 2.11] | Powerful database abstraction layer with many features for database schema introspection, schema management and PDO abstraction. @@ -64,11 +64,11 @@ Powerful database abstraction layer with many features for database schema intro [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 - [2.10 image]: https://img.shields.io/travis/doctrine/dbal/2.10.x.svg?style=flat-square - [Coverage 2.10 image]: https://codecov.io/gh/doctrine/dbal/branch/2.10.x/graph/badge.svg - [2.10]: https://github.com/doctrine/dbal/tree/2.10.x - [CodeCov 2.10]: https://codecov.io/gh/doctrine/dbal/branch/2.10.x - [AppVeyor 2.10]: https://ci.appveyor.com/project/doctrine/dbal/branch/2.10.x - [AppVeyor 2.10 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/2.10.x?svg=true - [GA 2.10]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A2.10.x - [GA 2.10 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=2.10.x + [2.11 image]: https://img.shields.io/travis/doctrine/dbal/2.11.x.svg?style=flat-square + [Coverage 2.11 image]: https://codecov.io/gh/doctrine/dbal/branch/2.11.x/graph/badge.svg + [2.11]: https://github.com/doctrine/dbal/tree/2.11.x + [CodeCov 2.11]: https://codecov.io/gh/doctrine/dbal/branch/2.11.x + [AppVeyor 2.11]: https://ci.appveyor.com/project/doctrine/dbal/branch/2.11.x + [AppVeyor 2.11 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/2.11.x?svg=true + [GA 2.11]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A2.11.x + [GA 2.11 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=2.11.x diff --git a/UPGRADE.md b/UPGRADE.md index 205bf99197..9eb98d764a 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,3 +1,243 @@ +# Upgrade to 2.11 + +## Deprecated the functionality of dropping client connections when dropping a database + +The corresponding `getDisallowDatabaseConnectionsSQL()` and `getCloseActiveDatabaseConnectionsSQL` methods +of the `PostgreSqlPlatform` class have been deprecated. + +## Deprecated `Synchronizer` package + +The `Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer` interface and all its implementations are deprecated. + +## Deprecated usage of wrapper-level components as implementations of driver-level interfaces + +The usage of the wrapper `Connection` and `Statement` classes as implementations of the `Driver\Connection` and `Driver\Statement` interfaces is deprecated. + +## Deprecations in the wrapper `Connection` class + +1. The `executeUpdate()` method has been deprecated in favor of `executeStatement()`. +2. The `query()` method has been deprecated in favor of `executeQuery()`. +3. The `exec()` method has been deprecated in favor of `executeStatement()`. + +Note that `PrimaryReplicaConnection::query()` ensures connection to the primary instance while `executeQuery()` doesn't. + +Depending on the desired behavior: + +- If the statement doesn't have to be executed on the primary instance, use `executeQuery()`. +- If the statement has to be executed on the primary instance and yields rows (e.g. `SELECT`), prepend `executeQuery()` with `ensureConnectedToPrimary()`. +- Otherwise, use `executeStatement()`. + +## PDO-related classes outside of the PDO namespace are deprecated + +The following PDO-related classes outside of the PDO namespace have been deprecated in favor of their counterparts in the PDO namespace: + +- `PDOMySql\Driver` → `PDO\MySQL\Driver` +- `PDOOracle\Driver` → `PDO\OCI\Driver` +- `PDOPgSql\Driver` → `PDO\PgSQL\Driver` +- `PDOSqlite\Driver` → `PDO\SQLite\Driver` +- `PDOSqlsrv\Driver` → `PDO\SQLSrv\Driver` +- `PDOSqlsrv\Connection` → `PDO\SQLSrv\Connection` +- `PDOSqlsrv\Statement` → `PDO\SQLSrv\Statement` + +## Deprecations in driver-level exception handling + +1. The `ExceptionConverterDriver` interface and the usage of the `convertException()` method on the `Driver` objects are deprecated. +2. The `driverException()` and `driverExceptionDuringQuery()` factory methods of the `DBALException` class are deprecated. +3. Relying on the wrapper layer handling non-driver exceptions is deprecated. + +## `DBALException` factory method deprecations + +1. `DBALException::invalidPlatformType()` is deprecated as unused as of v2.7.0. +2. `DBALException::invalidPdoInstance()` as passing a PDO instance via configuration is deprecated. + +## Deprecated `AbstractPlatform` methods. + +1. `fixSchemaElementName()`. +2. `getSQLResultCasing()`. +3. `prefersSequences()`. +4. `supportsForeignKeyOnUpdate()`. + +##`ServerInfoAwareConnection::requiresQueryForServerVersion()` is deprecated. + +The `ServerInfoAwareConnection::requiresQueryForServerVersion()` method has been deprecated as an implementation detail which is the same for almost all supported drivers. + +## Connection and Statement constructors are marked internal + +1. Driver connection objects can be only created by the corresponding drivers. +2. Wrapper connection objects can be only created by the driver manager. +3. The driver and wrapper connection objects can be only created by the corresponding connection objects. + +Additionally, the `SQLSrv\LastInsertId` class has been marked internal. + +## The `PingableConnection` interface is deprecated + +The wrapper connection will automatically handle the lost connection if the driver supports reporting it. + +## `DriverException::getErrorCode()` is deprecated + +The `DriverException::getErrorCode()` is deprecated as redundant and inconsistently supported by drivers. Use `::getCode()` or `::getSQLState()` instead. + +## Non-interface driver methods have been marked internal + +The non-interface methods of driver-level classes have been marked internal: + +- `OCI8Connection::getExecuteMode()` +- `OCI8Statement::convertPositionalToNamedPlaceholders()` + +## Deprecated `DBALException` + +The `Doctrine\DBAL\DBALException` class has been deprecated in favor of `Doctrine\DBAL\Exception`. + +## Inconsistently and ambiguously named driver-level classes are deprecated + +The following classes under the `Driver` namespace have been deprecated in favor of their consistently named counterparts: + +- `DriverException` → `Exception` +- `AbstractDriverException` → `AbstractException` +- `IBMDB2\DB2Driver` → `IBMDB2\Driver` +- `IBMDB2\DB2Connection` → `IBMDB2\Connection` +- `IBMDB2\DB2Statement` → `IBMDB2\Statement` +- `Mysqli\MysqliConnection` → `Mysqli\Connection` +- `Mysqli\MysqliStatement` → `Mysqli\Statement` +- `OCI8\OCI8Connection` → `OCI8\Connection` +- `OCI8\OCI8Statement` → `OCI8\Statement` +- `SQLSrv\SQLSrvConnection` → `SQLSrv\Connection` +- `SQLSrv\SQLSrvStatement` → `SQLSrv\Statement` +- `PDOConnection` → `PDO\Connection` +- `PDOStatement` → `PDO\Statement` + +All driver-specific exception classes have been deprecated: + +- `IBMDB2\DB2Exception` +- `Mysqli\MysqliException` +- `OCI8\OCI8Exception` +- `PDOException` +- `SQLSrv\SQLSrvException` + +A driver-level exception should be only identified as a subtype of `Driver\Exception`. +Internal driver-level exception implementations may use `Driver\AbstractException` as the base class. +Driver-specific exception handling has to be implemented either in the driver or based on the type of the `Driver` implementation. + +The `Driver\AbstractException` class has been marked internal. + +## `Connection::getParams()` has been marked internal + +Consumers of the Connection class should not rely on connection parameters stored in the connection object. If needed, they should be obtained from a different source, e.g. application configuration. + +## Deprecated `Doctrine\DBAL\Driver::getDatabase()` + +- The usage of `Doctrine\DBAL\Driver::getDatabase()` is deprecated. Please use `Doctrine\DBAL\Connection::getDatabase()` instead. +- The behavior of the SQLite connection returning the database file path as the database is deprecated and shouldn't be relied upon. + +## Deprecated `Portability\Connection::PORTABILITY_{PLATFORM}` constants + +The platform-specific portability mode flags are meant to be used only by the portability layer internally to optimize +the user-provided mode for the current database platform. + +## Deprecated `MasterSlaveConnection` use `PrimaryReadReplicaConnection` + +The `Doctrine\DBAL\Connections\MasterSlaveConnection` class is renamed to `Doctrine\DBAL\Connections\PrimaryReadReplicaConnection`. +In addition its configuration parameters `master`, `slaves` and `keepSlave` are renamed to `primary`, `replica` and `keepReplica`. + +Before: + + $connection = DriverManager::getConnection( + 'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection', + 'driver' => 'pdo_mysql', + 'master' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), + 'slaves' => array( + array('user' => 'replica1', 'password', 'host' => '', 'dbname' => ''), + array('user' => 'replica2', 'password', 'host' => '', 'dbname' => ''), + ), + 'keepSlave' => true, + )); + $connection->connect('slave'); + $connection->connect('master'); + $connection->isConnectedToMaster(); + +After: + + $connection = DriverManager::getConnection(array( + 'wrapperClass' => 'Doctrine\DBAL\Connections\PrimaryReadReplicaConnection', + 'driver' => 'pdo_mysql', + 'primary' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), + 'replica' => array( + array('user' => 'replica1', 'password', 'host' => '', 'dbname' => ''), + array('user' => 'replica2', 'password', 'host' => '', 'dbname' => ''), + ) + 'keepReplica' => true, + )); + $connection->ensureConnectedToReplica(); + $connection->ensureConnectedToPrimary(); + $connection->isConnectedToPrimary(); + +## Deprecated `ArrayStatement` and `ResultCacheStatement` classes. + +The `ArrayStatement` and `ResultCacheStatement` classes are deprecated. In a future major release they will be renamed and marked internal as implementation details of the caching layer. + +## Deprecated `ResultStatement` interface + +1. The `ResultStatement` interface is deprecated. Use the `Driver\Result` and `Abstraction\Result` interfaces instead. +2. `ResultStatement::closeCursor()` is deprecated in favor of `Result::free()`. + +## Deprecated `FetchMode` and the corresponding methods + +1. The `FetchMode` class and the `setFetchMode()` method of the `Connection` and `Statement` interfaces are deprecated. +2. The `Statement::fetch()` method is deprecated in favor of `Result::fetchNumeric()`, `::fetchAssociative()` and `::fetchOne()`. +3. The `Statement::fetchAll()` method is deprecated in favor of `Result::fetchAllNumeric()`, `::fetchAllAssociative()` and `::fetchFirstColumn()`. +4. The `Statement::fetchColumn()` method is deprecated in favor of `Result::fetchOne()`. +5. The `Connection::fetchArray()` and `fetchAssoc()` method are deprecated in favor of `fetchNumeric()` and `fetchAssociative()` respectively. +6. The `StatementIterator` class and the usage of a `Statement` object as `Traversable` is deprecated in favor of `Result::iterateNumeric()`, `::iterateAssociative()` and `::iterateColumn()`. +7. Fetching data in mixed mode (`FetchMode::MIXED`) is deprecated. + +## Deprecated `Connection::project()` + +The `Connection::project()` method is deprecated. Implement data transformation outside of DBAL. + +## Deprecated `Statement::errorCode()` and `errorInfo()` + +The `Statement::errorCode()` and `errorInfo()` methods are deprecated. The error information is available via exceptions. + +## Deprecated `EchoSQLLogger` + +The `EchoSQLLogger` class is deprecated. Implement your logger with the desired logic. + +## Deprecated database platforms: + +1. PostgreSQL 9.3 and older +2. MariaDB 10.0 and older +3. SQL Server 2008 and older +4. SQL Anywhere 12 and older +5. Drizzle +6. Azure SQL Database + +## Deprecated database drivers: + +1. PDO-based IBM DB2 driver +2. Drizzle MySQL driver + +## Deprecated `Doctrine\DBAL\Sharding` package + +The sharding functionality in DBAL has been effectively unmaintained for a long time. + +## Deprecated `Doctrine\DBAL\Version` class + +The usage of the `Doctrine\DBAL\Version` class is deprecated as internal implementation detail. Please refrain from checking the DBAL version at runtime. + +## Deprecated `ExpressionBuilder` methods + +The usage of the `andX()` and `orX()` methods of the `ExpressionBuilder` class has been deprecated. Use `and()` and `or()` instead. + +## Deprecated `CompositeExpression` methods + +- The usage of the `add()` and `addMultiple()` methods of the `CompositeExpression` class has been deprecated. Use `with()` instead, which returns a new instance. +In the future, the `add*()` methods will be removed and the class will be effectively immutable. +- The usage of the `CompositeExpression` constructor has been deprecated. Use the `and()` / `or()` factory methods. + +## Deprecated calling `QueryBuilder` methods with an array argument + +Calling the `select()`, `addSelect()`, `groupBy()` and `addGroupBy()` methods with an array argument is deprecated. + # Upgrade to 2.10 ## Deprecated `Doctrine\DBAL\Event\ConnectionEventArgs` methods diff --git a/bin/doctrine-dbal.php b/bin/doctrine-dbal.php index f3e064ffd6..2f0c65be87 100644 --- a/bin/doctrine-dbal.php +++ b/bin/doctrine-dbal.php @@ -1,5 +1,6 @@ - - - ../../lib - - - - - - performance - - + + + ../../lib/Doctrine + + diff --git a/ci/github/phpunit.pdo-oci.xml b/ci/github/phpunit.pdo-oci.xml index 94fe38c282..900e5ce10b 100644 --- a/ci/github/phpunit.pdo-oci.xml +++ b/ci/github/phpunit.pdo-oci.xml @@ -1,6 +1,6 @@ - - - ../../lib - - - - - - performance - - + + + ../../lib/Doctrine + + diff --git a/composer.json b/composer.json index 0c61fc462b..e3c2668799 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ {"name": "Jonathan Wage", "email": "jonwage@gmail.com"} ], "require": { - "php": "^7.2", + "php": "^7.3", "ext-pdo": "*", "doctrine/cache": "^1.0", "doctrine/event-manager": "^1.0" @@ -42,7 +42,7 @@ "jetbrains/phpstorm-stubs": "^2019.1", "nikic/php-parser": "^4.4", "phpstan/phpstan": "^0.12.40", - "phpunit/phpunit": "^8.5.5", + "phpunit/phpunit": "^9.3", "psalm/plugin-phpunit": "^0.10.0", "symfony/console": "^2.0.5|^3.0|^4.0|^5.0", "vimeo/psalm": "^3.14.2" @@ -54,7 +54,7 @@ "config": { "sort-packages": true, "platform": { - "php": "7.2.0" + "php": "7.3.0" } }, "autoload": { @@ -65,8 +65,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "3.0.x-dev" + "dev-master": "4.0.x-dev" } } } diff --git a/composer.lock b/composer.lock index 60d0bcc18c..c0e3434b50 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": "0f85745785af604940cdf6c65474abe0", + "content-hash": "2a7bb91be55d10f401c5f03dd7f235bc", "packages": [ { "name": "doctrine/cache", @@ -78,6 +78,10 @@ "cache", "caching" ], + "support": { + "issues": "https://github.com/doctrine/cache/issues", + "source": "https://github.com/doctrine/cache/tree/v1.7.1" + }, "time": "2017-08-25T07:02:50+00:00" }, { @@ -152,6 +156,10 @@ "eventdispatcher", "eventmanager" ], + "support": { + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/master" + }, "time": "2018-06-11T11:59:03+00:00" } ], @@ -232,6 +240,11 @@ "non-blocking", "promise" ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/master" + }, "funding": [ { "url": "https://github.com/amphp", @@ -304,6 +317,11 @@ "non-blocking", "stream" ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/master" + }, "time": "2020-06-29T18:35:05+00:00" }, { @@ -359,6 +377,10 @@ } ], "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.10.99.1" + }, "funding": [ { "url": "https://packagist.com", @@ -434,6 +456,11 @@ "validation", "versioning" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/1.5.1" + }, "time": "2020-01-13T12:06:48+00:00" }, { @@ -478,6 +505,11 @@ "Xdebug", "performance" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/1.4.3" + }, "funding": [ { "url": "https://packagist.com", @@ -558,6 +590,10 @@ "stylecheck", "tests" ], + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, "time": "2020-06-25T14:57:39+00:00" }, { @@ -614,24 +650,28 @@ "standard", "style" ], + "support": { + "issues": "https://github.com/doctrine/coding-standard/issues", + "source": "https://github.com/doctrine/coding-standard/tree/8.1.x" + }, "time": "2020-07-05T20:35:22+00:00" }, { "name": "doctrine/instantiator", - "version": "1.2.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "f350df0268e904597e3bd9c4685c53e0e333feea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea", + "reference": "f350df0268e904597e3bd9c4685c53e0e333feea", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^6.0", @@ -670,7 +710,25 @@ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.3.x" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-05-29T17:27:14+00:00" }, { "name": "felixfbecker/advanced-json-rpc", @@ -711,6 +769,10 @@ } ], "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/master" + }, "time": "2020-03-11T15:21:41+00:00" }, { @@ -758,6 +820,10 @@ "php", "server" ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.4.0" + }, "time": "2019-06-23T21:03:50+00:00" }, { @@ -802,24 +868,27 @@ "stubs", "type" ], + "support": { + "source": "https://github.com/JetBrains/phpstorm-stubs/tree/master" + }, "time": "2019-12-05T16:56:26+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.3", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "replace": { "myclabs/deep-copy": "self.version" @@ -850,7 +919,17 @@ "object", "object graph" ], - "time": "2019-08-09T12:45:53+00:00" + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.x" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-06-29T13:22:24+00:00" }, { "name": "netresearch/jsonmapper", @@ -896,20 +975,25 @@ } ], "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/master" + }, "time": "2020-04-16T18:48:43+00:00" }, { "name": "nikic/php-parser", - "version": "v4.4.0", + "version": "v4.9.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "bd43ec7152eaaab3bd8c6d0aa95ceeb1df8ee120" + "reference": "aaee038b912e567780949787d5fe1977be11a778" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/bd43ec7152eaaab3bd8c6d0aa95ceeb1df8ee120", - "reference": "bd43ec7152eaaab3bd8c6d0aa95ceeb1df8ee120", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/aaee038b912e567780949787d5fe1977be11a778", + "reference": "aaee038b912e567780949787d5fe1977be11a778", "shasum": "" }, "require": { @@ -917,8 +1001,8 @@ "php": ">=7.0" }, "require-dev": { - "ircmaxell/php-yacc": "0.0.5", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0" + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" @@ -926,7 +1010,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.9-dev" } }, "autoload": { @@ -948,7 +1032,11 @@ "parser", "php" ], - "time": "2020-04-10T16:34:50+00:00" + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/master" + }, + "time": "2020-08-18T19:48:01+00:00" }, { "name": "openlss/lib-array2xml", @@ -997,32 +1085,37 @@ "xml", "xml conversion" ], + "support": { + "issues": "https://github.com/nullivex/lib-array2xml/issues", + "source": "https://github.com/nullivex/lib-array2xml/tree/master" + }, "time": "2019-03-29T20:06:56+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1052,24 +1145,28 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/master" + }, + "time": "2020-06-27T14:33:11+00:00" }, { "name": "phar-io/version", - "version": "2.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "url": "https://api.github.com/repos/phar-io/version/zipball/c6bb6825def89e0a32220f88337f8ceaf1975fa0", + "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -1099,7 +1196,11 @@ } ], "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/master" + }, + "time": "2020-06-27T14:39:04+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -1148,6 +1249,10 @@ "reflection", "static analysis" ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, "time": "2020-06-27T09:03:43+00:00" }, { @@ -1200,6 +1305,10 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + }, "time": "2020-08-15T11:14:08+00:00" }, { @@ -1245,37 +1354,41 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.x" + }, "time": "2020-06-27T10:12:23+00:00" }, { "name": "phpspec/prophecy", - "version": "1.9.0", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203" + "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203", - "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b20034be5efcdab4fb60ca3a29cba2949aead160", + "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2", + "phpdocumentor/reflection-docblock": "^5.0", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "phpspec/phpspec": "^6.0", + "phpunit/phpunit": "^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.11.x-dev" } }, "autoload": { @@ -1308,7 +1421,11 @@ "spy", "stub" ], - "time": "2019-10-03T11:07:50+00:00" + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/master" + }, + "time": "2020-07-08T12:44:21+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -1357,6 +1474,10 @@ "MIT" ], "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/master" + }, "time": "2020-04-13T16:28:46+00:00" }, { @@ -1421,40 +1542,44 @@ }, { "name": "phpunit/php-code-coverage", - "version": "7.0.8", + "version": "9.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "aa0d179a13284c7420fc281fc32750e6cc7c9e2f" + "reference": "ee24e82baca11d7d6fb3513e127d6000f541cf90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aa0d179a13284c7420fc281fc32750e6cc7c9e2f", - "reference": "aa0d179a13284c7420fc281fc32750e6cc7c9e2f", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ee24e82baca11d7d6fb3513e127d6000f541cf90", + "reference": "ee24e82baca11d7d6fb3513e127d6000f541cf90", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-xmlwriter": "*", - "php": "^7.2", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.1.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^4.2.2", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1.3" + "nikic/php-parser": "^4.7", + "php": "^7.3 || ^8.0", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^8.2.2" + "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-xdebug": "^2.7.2" + "ext-pcov": "*", + "ext-xdebug": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "7.0-dev" + "dev-master": "9.0-dev" } }, "autoload": { @@ -1480,32 +1605,42 @@ "testing", "xunit" ], - "time": "2019-09-17T06:24:36+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-08-07T04:12:30+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "2.0.2", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" + "reference": "25fefc5b19835ca653877fe081644a3f8c1d915e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/25fefc5b19835ca653877fe081644a3f8c1d915e", + "reference": "25fefc5b19835ca653877fe081644a3f8c1d915e", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1530,26 +1665,48 @@ "filesystem", "iterator" ], - "time": "2018-09-13T20:33:42+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-07-11T05:18:21+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", + "name": "phpunit/php-invoker", + "version": "3.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "7a85b66acc48cacffdf87dadd3694e7123674298" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/7a85b66acc48cacffdf87dadd3694e7123674298", + "reference": "7a85b66acc48cacffdf87dadd3694e7123674298", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -1566,37 +1723,47 @@ "role": "lead" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "template" + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2015-06-21T13:50:34+00:00" + "time": "2020-08-06T07:04:15+00:00" }, { - "name": "phpunit/php-timer", - "version": "2.1.2", + "name": "phpunit/php-text-template", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "6ff9c8ea4d3212b88fcf74e25e516e2c51c99324" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/6ff9c8ea4d3212b88fcf74e25e516e2c51c99324", + "reference": "6ff9c8ea4d3212b88fcf74e25e516e2c51c99324", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1615,38 +1782,47 @@ "role": "lead" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "timer" + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2019-06-07T04:22:29+00:00" + "time": "2020-06-26T11:55:37+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "3.1.1", + "name": "phpunit/php-timer", + "version": "5.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "cc49734779cbb302bf51a44297dab8c4bbf941e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/cc49734779cbb302bf51a44297dab8c4bbf941e7", + "reference": "cc49734779cbb302bf51a44297dab8c4bbf941e7", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": "^7.1" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^9.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -1661,65 +1837,77 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "tokenizer" + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "abandoned": true, - "time": "2019-09-17T06:23:10+00:00" + "time": "2020-06-26T11:58:13+00:00" }, { "name": "phpunit/phpunit", - "version": "8.5.5", + "version": "9.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "63dda3b212a0025d380a745f91bdb4d8c985adb7" + "reference": "eacb57f3857cb6706550bd39ea500f9b1097b0bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/63dda3b212a0025d380a745f91bdb4d8c985adb7", - "reference": "63dda3b212a0025d380a745f91bdb4d8c985adb7", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/eacb57f3857cb6706550bd39ea500f9b1097b0bf", + "reference": "eacb57f3857cb6706550bd39ea500f9b1097b0bf", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.2.0", + "doctrine/instantiator": "^1.3.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.9.1", - "phar-io/manifest": "^1.0.3", - "phar-io/version": "^2.0.1", - "php": "^7.2", - "phpspec/prophecy": "^1.8.1", - "phpunit/php-code-coverage": "^7.0.7", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1.2", - "sebastian/comparator": "^3.0.2", - "sebastian/diff": "^3.0.2", - "sebastian/environment": "^4.2.2", - "sebastian/exporter": "^3.1.1", - "sebastian/global-state": "^3.0.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0.1", - "sebastian/type": "^1.1.3", - "sebastian/version": "^2.0.1" + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.1", + "phar-io/version": "^3.0.2", + "php": "^7.3 || ^8.0", + "phpspec/prophecy": "^1.11.1", + "phpunit/php-code-coverage": "^9.0", + "phpunit/php-file-iterator": "^3.0.4", + "phpunit/php-invoker": "^3.1", + "phpunit/php-text-template": "^2.0.2", + "phpunit/php-timer": "^5.0.1", + "sebastian/code-unit": "^1.0.5", + "sebastian/comparator": "^4.0.3", + "sebastian/diff": "^4.0.2", + "sebastian/environment": "^5.1.2", + "sebastian/exporter": "^4.0.2", + "sebastian/global-state": "^5.0", + "sebastian/object-enumerator": "^4.0.2", + "sebastian/resource-operations": "^3.0.2", + "sebastian/type": "^2.2.1", + "sebastian/version": "^3.0.1" }, "require-dev": { - "ext-pdo": "*" + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" }, "suggest": { "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0.0" + "ext-xdebug": "*" }, "bin": [ "phpunit" @@ -1727,12 +1915,15 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "8.5-dev" + "dev-master": "9.3-dev" } }, "autoload": { "classmap": [ "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1753,6 +1944,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.3.2" + }, "funding": [ { "url": "https://phpunit.de/donate.html", @@ -1763,20 +1958,20 @@ "type": "github" } ], - "time": "2020-05-22T13:51:52+00:00" + "time": "2020-08-07T09:12:30+00:00" }, { "name": "psalm/plugin-phpunit", - "version": "0.10.0", + "version": "0.10.1", "source": { "type": "git", "url": "https://github.com/psalm/psalm-plugin-phpunit.git", - "reference": "12b3978cea0e9ad3aa3a127a93d3b8a1ce0eb2af" + "reference": "138998ffd32b76a2e69eb1ff94ef2bf110967273" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/12b3978cea0e9ad3aa3a127a93d3b8a1ce0eb2af", - "reference": "12b3978cea0e9ad3aa3a127a93d3b8a1ce0eb2af", + "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/138998ffd32b76a2e69eb1ff94ef2bf110967273", + "reference": "138998ffd32b76a2e69eb1ff94ef2bf110967273", "shasum": "" }, "require": { @@ -1790,7 +1985,7 @@ "require-dev": { "codeception/codeception": "^4.0.3", "squizlabs/php_codesniffer": "^3.3.1", - "weirdan/codeception-psalm-module": "^0.4.0" + "weirdan/codeception-psalm-module": "^0.7.1" }, "type": "psalm-plugin", "extra": { @@ -1814,7 +2009,11 @@ } ], "description": "Psalm plugin for PHPUnit", - "time": "2020-04-05T22:49:26+00:00" + "support": { + "issues": "https://github.com/psalm/psalm-plugin-phpunit/issues", + "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.10.1" + }, + "time": "2020-05-24T20:30:10+00:00" }, { "name": "psr/container", @@ -1863,6 +2062,10 @@ "container-interop", "psr" ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/master" + }, "time": "2017-02-14T16:28:37+00:00" }, { @@ -1910,32 +2113,91 @@ "psr", "psr-3" ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.3" + }, "time": "2020-03-23T09:12:05+00:00" }, + { + "name": "sebastian/code-unit", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "c1e2df332c905079980b119c4db103117e5e5c90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/c1e2df332c905079980b119c4db103117e5e5c90", + "reference": "c1e2df332c905079980b119c4db103117e5e5c90", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:50:45+00:00" + }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "reference": "ee51f9bb0c6d8a43337055db3120829fa14da819" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ee51f9bb0c6d8a43337055db3120829fa14da819", + "reference": "ee51f9bb0c6d8a43337055db3120829fa14da819", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1955,34 +2217,44 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:04:00+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.2", + "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + "reference": "dcc580eadfaa4e7f9d2cf9ae1922134ea962e14f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/dcc580eadfaa4e7f9d2cf9ae1922134ea962e14f", + "reference": "dcc580eadfaa4e7f9d2cf9ae1922134ea962e14f", "shasum": "" }, "require": { - "php": "^7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" + "php": "^7.3 || ^8.0", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1995,6 +2267,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -2006,10 +2282,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", @@ -2019,33 +2291,100 @@ "compare", "equality" ], - "time": "2018-07-12T15:12:46+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:05:46+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "33fcd6a26656c6546f70871244ecba4b4dced097" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/33fcd6a26656c6546f70871244ecba4b4dced097", + "reference": "33fcd6a26656c6546f70871244ecba4b4dced097", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-07-25T14:01:34+00:00" }, { "name": "sebastian/diff", - "version": "3.0.2", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + "reference": "1e90b4cf905a7d06c420b1d2e9d11a4dc8a13113" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/1e90b4cf905a7d06c420b1d2e9d11a4dc8a13113", + "reference": "1e90b4cf905a7d06c420b1d2e9d11a4dc8a13113", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" + "phpunit/phpunit": "^9.0", + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2058,13 +2397,13 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", @@ -2075,27 +2414,37 @@ "unidiff", "unified diff" ], - "time": "2019-02-04T06:01:07+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-30T04:46:02+00:00" }, { "name": "sebastian/environment", - "version": "4.2.2", + "version": "5.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" + "reference": "0a757cab9d5b7ef49a619f1143e6c9c1bc0fe9d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/0a757cab9d5b7ef49a619f1143e6c9c1bc0fe9d2", + "reference": "0a757cab9d5b7ef49a619f1143e6c9c1bc0fe9d2", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.5" + "phpunit/phpunit": "^9.0" }, "suggest": { "ext-posix": "*" @@ -2103,7 +2452,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -2128,34 +2477,44 @@ "environment", "hhvm" ], - "time": "2019-05-05T09:05:15+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:07:24+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.2", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" + "reference": "571d721db4aec847a0e59690b954af33ebf9f023" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/571d721db4aec847a0e59690b954af33ebf9f023", + "reference": "571d721db4aec847a0e59690b954af33ebf9f023", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" + "php": "^7.3 || ^8.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2195,30 +2554,40 @@ "export", "exporter" ], - "time": "2019-09-14T09:02:43+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:08:55+00:00" }, { "name": "sebastian/global-state", - "version": "3.0.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" + "reference": "22ae663c951bdc39da96603edc3239ed3a299097" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/22ae663c951bdc39da96603edc3239ed3a299097", + "reference": "22ae663c951bdc39da96603edc3239ed3a299097", "shasum": "" }, "require": { - "php": "^7.2", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": "^7.3 || ^8.0", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^8.0" + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" @@ -2226,7 +2595,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -2249,34 +2618,101 @@ "keywords": [ "global state" ], - "time": "2019-02-01T05:30:01+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-08-07T04:09:03+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e02bf626f404b5daec382a7b8a6a4456e49017e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e02bf626f404b5daec382a7b8a6a4456e49017e5", + "reference": "e02bf626f404b5daec382a7b8a6a4456e49017e5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-07-22T18:33:42+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.3", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "reference": "074fed2d0a6d08e1677dd8ce9d32aecb384917b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/074fed2d0a6d08e1677dd8ce9d32aecb384917b8", + "reference": "074fed2d0a6d08e1677dd8ce9d32aecb384917b8", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": "^7.3 || ^8.0", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2296,32 +2732,42 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:11:32+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "127a46f6b057441b201253526f81d5406d6c7840" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/127a46f6b057441b201253526f81d5406d6c7840", + "reference": "127a46f6b057441b201253526f81d5406d6c7840", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2341,32 +2787,42 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:12:55+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "062231bf61d2b9448c4fa5a7643b5e1829c11d63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/062231bf61d2b9448c4fa5a7643b5e1829c11d63", + "reference": "062231bf61d2b9448c4fa5a7643b5e1829c11d63", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2379,14 +2835,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" @@ -2394,29 +2850,42 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:14:17+00:00" }, { "name": "sebastian/resource-operations", - "version": "2.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + "reference": "0653718a5a629b065e91f774595267f8dc32e213" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0653718a5a629b065e91f774595267f8dc32e213", + "reference": "0653718a5a629b065e91f774595267f8dc32e213", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2436,32 +2905,42 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:16:22+00:00" }, { "name": "sebastian/type", - "version": "1.1.3", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3" + "reference": "86991e2b33446cd96e648c18bcdb1e95afb2c05a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3", - "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/86991e2b33446cd96e648c18bcdb1e95afb2c05a", + "reference": "86991e2b33446cd96e648c18bcdb1e95afb2c05a", "shasum": "" }, "require": { - "php": "^7.2" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^8.2" + "phpunit/phpunit": "^9.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.2-dev" } }, "autoload": { @@ -2482,29 +2961,39 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", - "time": "2019-07-02T08:10:15+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/2.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-07-05T08:31:53+00:00" }, { "name": "sebastian/version", - "version": "2.0.1", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "reference": "626586115d0ed31cb71483be55beb759b5af5a3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/626586115d0ed31cb71483be55beb759b5af5a3c", + "reference": "626586115d0ed31cb71483be55beb759b5af5a3c", "shasum": "" }, "require": { - "php": ">=5.6" + "php": "^7.3 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2525,7 +3014,17 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:18:43+00:00" }, { "name": "slevomat/coding-standard", @@ -2572,6 +3071,10 @@ "MIT" ], "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "support": { + "issues": "https://github.com/slevomat/coding-standard/issues", + "source": "https://github.com/slevomat/coding-standard/tree/6.3.10" + }, "funding": [ { "url": "https://github.com/kukulich", @@ -2633,6 +3136,11 @@ "phpcs", "standards" ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, "time": "2020-04-17T01:09:41+00:00" }, { @@ -2709,6 +3217,23 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/console/tree/v4.4.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-03-30T11:41:10+00:00" }, { @@ -2765,6 +3290,9 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/debug/tree/4.0" + }, "time": "2018-02-28T21:50:02+00:00" }, { @@ -2827,6 +3355,9 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.18.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2900,6 +3431,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/master" + }, "time": "2019-11-27T14:18:11+00:00" }, { @@ -2958,6 +3492,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.13.0" + }, "time": "2019-11-27T16:25:15+00:00" }, { @@ -3016,27 +3553,30 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v1.1.8" + }, "time": "2019-10-14T12:27:06+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.3", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" + "reference": "75a63c33a8577608444246075ea0af0d052e452a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", + "reference": "75a63c33a8577608444246075ea0af0d052e452a", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -3056,7 +3596,17 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-06-13T22:48:21+00:00" + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/master" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2020-07-12T23:59:07+00:00" }, { "name": "vimeo/psalm", @@ -3153,6 +3703,10 @@ "inspection", "php" ], + "support": { + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm/tree/3.14.2" + }, "time": "2020-08-22T14:01:26+00:00" }, { @@ -3202,6 +3756,10 @@ "check", "validate" ], + "support": { + "issues": "https://github.com/webmozart/assert/issues", + "source": "https://github.com/webmozart/assert/tree/master" + }, "time": "2020-07-08T17:02:28+00:00" }, { @@ -3249,6 +3807,10 @@ } ], "description": "A PHP implementation of Ant's glob.", + "support": { + "issues": "https://github.com/webmozart/glob/issues", + "source": "https://github.com/webmozart/glob/tree/master" + }, "time": "2015-12-29T11:14:33+00:00" }, { @@ -3295,6 +3857,10 @@ } ], "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", + "support": { + "issues": "https://github.com/webmozart/path-util/issues", + "source": "https://github.com/webmozart/path-util/tree/2.3.0" + }, "time": "2015-12-17T08:42:14+00:00" } ], @@ -3304,12 +3870,12 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.2", + "php": "^7.3", "ext-pdo": "*" }, "platform-dev": [], "platform-overrides": { - "php": "7.2.0" + "php": "7.3.0" }, "plugin-api-version": "2.0.0" } diff --git a/docs/en/explanation/implicit-indexes.rst b/docs/en/explanation/implicit-indexes.rst new file mode 100644 index 0000000000..f68edbe3ff --- /dev/null +++ b/docs/en/explanation/implicit-indexes.rst @@ -0,0 +1,62 @@ +Implicit indexes +================ + +Ever noticed the DBAL creating indexes you did not remember asking for, +with names such as ``IDX_885DBAFAA76ED395``? In this document, we will +distinguish three types of indexes: + +user-defined indexes + indexes you did ask for + +DBAL-defined indexes + indexes you did not ask for, created on your behalf by the DBAL + +RDBMS-defined indexes + indexes you did not ask for, created on your behalf by the RDBMS + +RDBMS-defined indexes can be created by some database platforms when you +create a foreign key: they will create an index on the referencing +table, using the referencing columns. + +The rationale behind this is that these indexes improve performance, for +instance for checking that a delete operation can be performed on a +referenced table without violating the constraint in the referencing +table. + +Here are some database platforms that are known to create indexes when +creating a foreign key: + +- `MySQL `_ +- `MariaDB `_ + +These platforms can drop an existing implicit index once it is fulfilled +by a newly created user-defined index. + +Some other will not do so, on grounds that such indexes are not always +needed, and can be created in many different ways. They instead leave +that responsibility to the user: + +- `PostgreSQL `_ +- `SQLite `_ +- `SQL Server `_ + +Regardless of the behavior of the platform, the DBAL will create an +index for you and will automatically pick an index name that obeys +string length constraints of the platform you are using. That way, +differences between platforms are reduced because you always end up with +an index. + +This is a detail, but these indexes will be prefixed with ``IDX_``, and +typically look like this: + +.. code-block:: sql + + CREATE INDEX IDX_885DBAFAA76ED395 ON posts (user_id) + +In the case of MariaDB and MySQL, the creation of that DBAL-defined +index will result in the RDBMS-defined index being dropped. + +You can still explicitly create such indexes yourself, and the DBAL will +notice when your index fulfills the indexing and constraint needs of the +implicit index it would create, and will refrain from doing so, much +like some platforms drop indexes that are redundant as explained above. diff --git a/docs/en/reference/caching.rst b/docs/en/reference/caching.rst index 5a2daf0b31..ee2ed63516 100644 --- a/docs/en/reference/caching.rst +++ b/docs/en/reference/caching.rst @@ -35,15 +35,13 @@ the default cache instance: new QueryCacheProfile(0, "some key", $cache); In order for the data to actually be cached its necessary to ensure that the entire -result set is read (the easiest way to ensure this is to use ``fetchAll``) and the statement -object is closed: +result set is read (the easiest way to ensure this is to use one of the ``fetchAll*()`` methods): :: executeCacheQuery($query, $params, $types, new QueryCacheProfile(0, "some key")); - $data = $stmt->fetchAll(); - $stmt->closeCursor(); // at this point the result is cached + $data = $stmt->fetchAllAssociative(); .. warning:: diff --git a/docs/en/reference/data-retrieval-and-manipulation.rst b/docs/en/reference/data-retrieval-and-manipulation.rst index 31397a532b..8a88deb6a6 100644 --- a/docs/en/reference/data-retrieval-and-manipulation.rst +++ b/docs/en/reference/data-retrieval-and-manipulation.rst @@ -41,7 +41,7 @@ the query until there are no more rows: fetch()) { + while (($row = $stmt->fetchAssociative()) !== false) { echo $row['headline']; } @@ -142,7 +142,7 @@ use prepared statements: SQL query, bind the given params with their binding types and execute the query. This method returns the executed prepared statement for iteration and is useful for SELECT statements. -- ``executeUpdate($sql, $params, $types)`` - Create a prepared statement for the passed +- ``executeStatement($sql, $params, $types)`` - Create a prepared statement for the passed SQL query, bind the given params with their binding types and execute the query. This method returns the number of affected rows by the executed query and is useful for UPDATE, DELETE and INSERT statements. @@ -170,7 +170,7 @@ of this query using the fetch API of a statement: The fetch API of a prepared statement obviously works only for ``SELECT`` queries. If you find it tedious to write all the prepared statement code you can alternatively use -the ``Doctrine\DBAL\Connection#executeQuery()`` and ``Doctrine\DBAL\Connection#executeUpdate()`` +the ``Doctrine\DBAL\Connection#executeQuery()`` and ``Doctrine\DBAL\Connection#executeStatement()`` methods. See the API section below on details how to use them. Additionally there are lots of convenience methods for data-retrieval and manipulation @@ -208,7 +208,7 @@ which means this code works independent of the database you are using. .. note:: Be aware this type conversion only works with ``Statement#bindValue()``, - ``Connection#executeQuery()`` and ``Connection#executeUpdate()``. It + ``Connection#executeQuery()`` and ``Connection#executeStatement()``. It is not supported to pass a doctrine type name to ``Statement#bindParam()``, because this would not work with binding by reference. @@ -286,7 +286,7 @@ This is much more complicated and is ugly to write generically. .. note:: The parameter list support only works with ``Doctrine\DBAL\Connection::executeQuery()`` - and ``Doctrine\DBAL\Connection::executeUpdate()``, NOT with the binding methods of + and ``Doctrine\DBAL\Connection::executeStatement()``, NOT with the binding methods of a prepared statement. API @@ -308,7 +308,7 @@ Prepare a given SQL statement and return the prepare('SELECT * FROM user'); $statement->execute(); - $users = $statement->fetchAll(); + $users = $statement->fetchAllAssociative(); /* array( @@ -319,7 +319,7 @@ Prepare a given SQL statement and return the ) */ -executeUpdate() +executeStatement() ~~~~~~~~~~~~~~~ Executes a prepared statement with the given SQL and parameters and @@ -328,7 +328,7 @@ returns the affected rows count: .. code-block:: php executeUpdate('UPDATE user SET username = ? WHERE id = ?', array('jwage', 1)); + $count = $conn->executeStatement('UPDATE user SET username = ? WHERE id = ?', array('jwage', 1)); echo $count; // 1 The ``$types`` variable contains the PDO or Doctrine Type constants @@ -346,7 +346,7 @@ parameters to the execute method, then returning the statement: executeQuery('SELECT * FROM user WHERE username = ?', array('jwage')); - $user = $statement->fetch(); + $user = $statement->fetchAssociative(); /* array( @@ -360,15 +360,15 @@ to perform necessary type conversions between actual input parameters and expected database values. See the :ref:`Types ` section for more information. -fetchAll() -~~~~~~~~~~ +fetchAllAssociative() +~~~~~~~~~~~~~~~~~~~~~ Execute the query and fetch all results into an array: .. code-block:: php fetchAll('SELECT * FROM user'); + $users = $conn->fetchAllAssociative('SELECT * FROM user'); /* array( @@ -379,15 +379,15 @@ Execute the query and fetch all results into an array: ) */ -fetchArray() -~~~~~~~~~~~~ +fetchNumeric() +~~~~~~~~~~~~~~ Numeric index retrieval of first result row of the given query: .. code-block:: php fetchArray('SELECT * FROM user WHERE username = ?', array('jwage')); + $user = $conn->fetchNumeric('SELECT * FROM user WHERE username = ?', array('jwage')); /* array( @@ -396,26 +396,26 @@ Numeric index retrieval of first result row of the given query: ) */ -fetchColumn() -~~~~~~~~~~~~~ +fetchOne() +~~~~~~~~~~ -Retrieve only the given column of the first result row. +Retrieve only the value of the first column of the first result row. .. code-block:: php fetchColumn('SELECT username FROM user WHERE id = ?', array(1), 0); + $username = $conn->fetchOne('SELECT username FROM user WHERE id = ?', array(1), 0); echo $username; // jwage -fetchAssoc() -~~~~~~~~~~~~ +fetchAssociative() +~~~~~~~~~~~~~~~~~~ -Retrieve assoc row of the first result row. +Retrieve associative array of the first result row. .. code-block:: php fetchAssoc('SELECT * FROM user WHERE username = ?', array('jwage')); + $user = $conn->fetchAssociative('SELECT * FROM user WHERE username = ?', array('jwage')); /* array( 'username' => 'jwage', diff --git a/docs/en/reference/platforms.rst b/docs/en/reference/platforms.rst index 32ef74304e..2b6516e269 100644 --- a/docs/en/reference/platforms.rst +++ b/docs/en/reference/platforms.rst @@ -50,25 +50,25 @@ Oracle Microsoft SQL Server ^^^^^^^^^^^^^^^^^^^^ -- ``SQLServerPlatform`` for version 2000 and above. -- ``SQLServer2005Platform`` for version 2005 and above. -- ``SQLServer2008Platform`` for version 2008 and above. +- ``SQLServerPlatform`` for version 2000 and above (deprecated). +- ``SQLServer2005Platform`` for version 2005 and above (deprecated). +- ``SQLServer2008Platform`` for version 2008 and above (deprecated). - ``SQLServer2012Platform`` for version 2012 and above. PostgreSQL ^^^^^^^^^^ -- ``PostgreSqlPlatform`` for all versions. -- ``PostgreSQL91Platform`` for version 9.1 and above. -- ``PostgreSQL92Platform`` for version 9.2 and above. +- ``PostgreSqlPlatform`` for version 9.0 and below (deprecated). +- ``PostgreSQL91Platform`` for version 9.1 and above (deprecated). +- ``PostgreSQL92Platform`` for version 9.2 and above (deprecated). - ``PostgreSQL94Platform`` for version 9.4 and above. SAP Sybase SQL Anywhere ^^^^^^^^^^^^^^^^^^^^^^^ -- ``SQLAnywherePlatform`` for version 10 and above. -- ``SQLAnywhere11Platform`` for version 11 and above. -- ``SQLAnywhere12Platform`` for version 12 and above. +- ``SQLAnywherePlatform`` for version 10 and above (deprecated). +- ``SQLAnywhere11Platform`` for version 11 and above (deprecated). +- ``SQLAnywhere12Platform`` for version 12 and above (deprecated). - ``SQLAnywhere16Platform`` for version 16 and above. SQLite @@ -79,7 +79,7 @@ SQLite Drizzle ^^^^^^ -- ``DrizzlePlatform`` for all versions. +- ``DrizzlePlatform`` for all versions (deprecated). It is highly encouraged to use the platform class that matches your database vendor and version best. Otherwise it is not guaranteed @@ -113,4 +113,3 @@ all the different database vendors, for example MySQL BIGINT and Oracle NUMBER should be handled as integer. Doctrine 2 offers a powerful way to abstract the database to php and back conversion, which is described in the next section. - diff --git a/docs/en/reference/query-builder.rst b/docs/en/reference/query-builder.rst index 059a70bef5..3c201df41d 100644 --- a/docs/en/reference/query-builder.rst +++ b/docs/en/reference/query-builder.rst @@ -332,13 +332,13 @@ Most notably you can use expressions to build nested And-/Or statements: ->select('id', 'name') ->from('users') ->where( - $queryBuilder->expr()->andX( + $queryBuilder->expr()->and( $queryBuilder->expr()->eq('username', '?'), $queryBuilder->expr()->eq('email', '?') ) ); -The ``andX()`` and ``orX()`` methods accept an arbitrary amount +The ``and()`` and ``or()`` methods accept an arbitrary amount of arguments and can be nested in each other. There is a bunch of methods to create comparisons and other SQL snippets diff --git a/docs/en/reference/security.rst b/docs/en/reference/security.rst index 82f159c87f..89e1963692 100644 --- a/docs/en/reference/security.rst +++ b/docs/en/reference/security.rst @@ -135,7 +135,7 @@ are using just the DBAL there are also helper methods which simplify the usage q $sql = "SELECT * FROM users WHERE username = ?"; $stmt = $connection->executeQuery($sql, array($_GET['username'])); -There is also ``executeUpdate`` which does not return a statement but the number of affected rows. +There is also ``executeStatement`` which does not return a statement but the number of affected rows. Besides binding parameters you can also pass the type of the variable. This allows Doctrine or the underlying vendor to not only escape but also cast the value to the correct type. See the docs on querying and DQL in the diff --git a/docs/en/reference/sharding.rst b/docs/en/reference/sharding.rst index 2b2f804da5..8780593a0b 100644 --- a/docs/en/reference/sharding.rst +++ b/docs/en/reference/sharding.rst @@ -269,8 +269,8 @@ you have to sort the data in the application. $sql = "SELECT * FROM customers"; $rows = $shardManager->queryAll($sql, $params); -Schema Operations: SchemaSynchronizer Interface ------------------------------------------------ +Schema Operations: SchemaSynchronizer Interface (deprecated) +------------------------------------------------------------ Schema Operations in a sharding architecture are tricky. You have to perform them on all databases instances (shards) at the same time. Also Doctrine diff --git a/docs/en/reference/sharding_azure_tutorial.rst b/docs/en/reference/sharding_azure_tutorial.rst index 71a4fab559..cb8e85d488 100644 --- a/docs/en/reference/sharding_azure_tutorial.rst +++ b/docs/en/reference/sharding_azure_tutorial.rst @@ -263,7 +263,7 @@ operation for us: 'LastName' => 'Brehm', )); - $conn->executeUpdate("DECLARE @orderId INT + $conn->executeStatement("DECLARE @orderId INT DECLARE @customerId INT diff --git a/docs/en/reference/types.rst b/docs/en/reference/types.rst index f4916a6967..ee9214c20c 100644 --- a/docs/en/reference/types.rst +++ b/docs/en/reference/types.rst @@ -157,6 +157,13 @@ or ``null`` if no data is present. This can lead to type inconsistencies when reverse engineering the type from the database. +ascii_string +++++++++++++ + +Similar to the ``string`` type but for binding non-unicode data. This type +should be used with database vendors where a binding type mismatch +can trigger an implicit cast and lead to performance problems. + text ++++ @@ -607,6 +614,9 @@ Please also notice the mapping specific footnotes for additional information. | | | | +----------------------------------------------------------+ | | | | | ``NCHAR(n)`` [4]_ | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ +| **ascii_string** | ``string`` | **SQL Server** | | ``VARCHAR(n)`` | +| | | | | ``CHAR(n)`` | ++-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ | **text** | ``string`` | **MySQL** | *all* | ``TINYTEXT`` [17]_ | | | | | +----------------------------------------------------------+ | | | | | ``TEXT`` [18]_ | diff --git a/docs/en/sidebar.rst b/docs/en/sidebar.rst index 0935c8dc17..4ada9f1caf 100644 --- a/docs/en/sidebar.rst +++ b/docs/en/sidebar.rst @@ -20,3 +20,5 @@ reference/caching reference/known-vendor-issues reference/upgrading + + explanation/implicit-indexes diff --git a/docs/examples/sharding/insert_data.php b/docs/examples/sharding/insert_data.php index 57aeda6c9f..9d6e5aabcf 100644 --- a/docs/examples/sharding/insert_data.php +++ b/docs/examples/sharding/insert_data.php @@ -90,7 +90,7 @@ 'LastName' => 'Brehm', )); -$conn->executeUpdate(" +$conn->executeStatement(" DECLARE @orderId INT DECLARE @customerId INT diff --git a/lib/Doctrine/DBAL/Abstraction/Result.php b/lib/Doctrine/DBAL/Abstraction/Result.php new file mode 100644 index 0000000000..42ff3419e3 --- /dev/null +++ b/lib/Doctrine/DBAL/Abstraction/Result.php @@ -0,0 +1,43 @@ +> + * + * @throws Exception + */ + public function iterateNumeric(): Traversable; + + /** + * Returns an iterator over the result set rows represented as associative arrays. + * + * @return Traversable> + * + * @throws Exception + */ + public function iterateAssociative(): Traversable; + + /** + * Returns an iterator over the values of the first column of the result set. + * + * @return Traversable + * + * @throws Exception + */ + public function iterateColumn(): Traversable; +} diff --git a/lib/Doctrine/DBAL/Cache/ArrayStatement.php b/lib/Doctrine/DBAL/Cache/ArrayStatement.php index ef64e7cccf..58d0a59e36 100644 --- a/lib/Doctrine/DBAL/Cache/ArrayStatement.php +++ b/lib/Doctrine/DBAL/Cache/ArrayStatement.php @@ -3,6 +3,8 @@ namespace Doctrine\DBAL\Cache; use ArrayIterator; +use Doctrine\DBAL\Driver\FetchUtils; +use Doctrine\DBAL\Driver\Result; use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\FetchMode; use InvalidArgumentException; @@ -14,7 +16,10 @@ use function count; use function reset; -class ArrayStatement implements IteratorAggregate, ResultStatement +/** + * @deprecated + */ +class ArrayStatement implements IteratorAggregate, ResultStatement, Result { /** @var mixed[] */ private $data; @@ -43,14 +48,24 @@ public function __construct(array $data) /** * {@inheritdoc} + * + * @deprecated Use free() instead. */ public function closeCursor() { - unset($this->data); + $this->free(); return true; } + /** + * {@inheritdoc} + */ + public function rowCount() + { + return count($this->data); + } + /** * {@inheritdoc} */ @@ -61,6 +76,8 @@ public function columnCount() /** * {@inheritdoc} + * + * @deprecated Use one of the fetch- or iterate-related methods. */ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) { @@ -75,6 +92,8 @@ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) /** * {@inheritdoc} + * + * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. */ public function getIterator() { @@ -85,6 +104,8 @@ public function getIterator() /** * {@inheritdoc} + * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. */ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) { @@ -116,6 +137,8 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} + * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { @@ -129,6 +152,8 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n /** * {@inheritdoc} + * + * @deprecated Use fetchOne() instead. */ public function fetchColumn($columnIndex = 0) { @@ -137,4 +162,81 @@ public function fetchColumn($columnIndex = 0) // TODO: verify that return false is the correct behavior return $row[$columnIndex] ?? false; } + + /** + * {@inheritdoc} + */ + public function fetchNumeric() + { + $row = $this->doFetch(); + + if ($row === false) { + return false; + } + + return array_values($row); + } + + /** + * {@inheritdoc} + */ + public function fetchAssociative() + { + return $this->doFetch(); + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + $row = $this->doFetch(); + + if ($row === false) { + return false; + } + + return reset($row); + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + return FetchUtils::fetchAllNumeric($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + return FetchUtils::fetchAllAssociative($this); + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + + public function free(): void + { + $this->data = []; + } + + /** + * @return mixed|false + */ + private function doFetch() + { + if (! isset($this->data[$this->num])) { + return false; + } + + return $this->data[$this->num++]; + } } diff --git a/lib/Doctrine/DBAL/Cache/CacheException.php b/lib/Doctrine/DBAL/Cache/CacheException.php index db5680812e..3db115bdfa 100644 --- a/lib/Doctrine/DBAL/Cache/CacheException.php +++ b/lib/Doctrine/DBAL/Cache/CacheException.php @@ -2,12 +2,12 @@ namespace Doctrine\DBAL\Cache; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; /** * @psalm-immutable */ -class CacheException extends DBALException +class CacheException extends Exception { /** * @return CacheException diff --git a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php index f7a137fd81..34381e23b1 100644 --- a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php +++ b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -4,6 +4,9 @@ use ArrayIterator; use Doctrine\Common\Cache\Cache; +use Doctrine\DBAL\Driver\Exception; +use Doctrine\DBAL\Driver\FetchUtils; +use Doctrine\DBAL\Driver\Result; use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\FetchMode; @@ -11,6 +14,7 @@ use IteratorAggregate; use PDO; +use function array_map; use function array_merge; use function array_values; use function assert; @@ -28,8 +32,10 @@ * * Also you have to realize that the cache will load the whole result into memory at once to ensure 2. * This means that the memory usage for cached results might increase by using this feature. + * + * @deprecated */ -class ResultCacheStatement implements IteratorAggregate, ResultStatement +class ResultCacheStatement implements IteratorAggregate, ResultStatement, Result { /** @var Cache */ private $resultCache; @@ -46,14 +52,7 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement /** @var ResultStatement */ private $statement; - /** - * Did we reach the end of the statement? - * - * @var bool - */ - private $emptied = false; - - /** @var mixed[] */ + /** @var array>|null */ private $data; /** @var int */ @@ -75,23 +74,12 @@ public function __construct(ResultStatement $stmt, Cache $resultCache, $cacheKey /** * {@inheritdoc} + * + * @deprecated Use free() instead. */ public function closeCursor() { - $this->statement->closeCursor(); - if (! $this->emptied || $this->data === null) { - return true; - } - - $data = $this->resultCache->fetch($this->cacheKey); - if (! $data) { - $data = []; - } - - $data[$this->realKey] = $this->data; - - $this->resultCache->save($this->cacheKey, $data, $this->lifetime); - unset($this->data); + $this->free(); return true; } @@ -106,6 +94,8 @@ public function columnCount() /** * {@inheritdoc} + * + * @deprecated Use one of the fetch- or iterate-related methods. */ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) { @@ -116,6 +106,8 @@ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) /** * {@inheritdoc} + * + * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. */ public function getIterator() { @@ -126,6 +118,8 @@ public function getIterator() /** * {@inheritdoc} + * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. */ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) { @@ -159,32 +153,45 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX throw new InvalidArgumentException('Invalid fetch-style given for caching result.'); } - $this->emptied = true; + $this->saveToCache(); return false; } /** * {@inheritdoc} + * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { - $data = $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs); + $data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE, $fetchArgument, $ctorArgs); + + $this->data = $data; - if ($fetchMode === FetchMode::COLUMN) { - foreach ($data as $key => $value) { - $data[$key] = [$value]; + $this->saveToCache(); + + if ($fetchMode === FetchMode::NUMERIC) { + foreach ($data as $i => $row) { + $data[$i] = array_values($row); + } + } elseif ($fetchMode === FetchMode::MIXED) { + foreach ($data as $i => $row) { + $data[$i] = array_merge($row, array_values($row)); + } + } elseif ($fetchMode === FetchMode::COLUMN) { + foreach ($data as $i => $row) { + $data[$i] = reset($row); } } - $this->data = $data; - $this->emptied = true; - - return $this->data; + return $data; } /** * {@inheritdoc} + * + * @deprecated Use fetchOne() instead. */ public function fetchColumn($columnIndex = 0) { @@ -194,6 +201,76 @@ public function fetchColumn($columnIndex = 0) return $row[$columnIndex] ?? false; } + /** + * {@inheritdoc} + */ + public function fetchNumeric() + { + $row = $this->doFetch(); + + if ($row === false) { + return false; + } + + return array_values($row); + } + + /** + * {@inheritdoc} + */ + public function fetchAssociative() + { + return $this->doFetch(); + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + if ($this->statement instanceof Result) { + $data = $this->statement->fetchAllAssociative(); + } else { + $data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + $this->store($data); + + return array_map('array_values', $this->data); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + if ($this->statement instanceof Result) { + $data = $this->statement->fetchAllAssociative(); + } else { + $data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + $this->store($data); + + return $data; + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + /** * Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement * executed by the corresponding object. @@ -211,4 +288,61 @@ public function rowCount() return $this->statement->rowCount(); } + + public function free(): void + { + $this->data = null; + } + + /** + * @return array|false + * + * @throws Exception + */ + private function doFetch() + { + if ($this->data === null) { + $this->data = []; + } + + if ($this->statement instanceof Result) { + $row = $this->statement->fetchAssociative(); + } else { + $row = $this->statement->fetch(FetchMode::ASSOCIATIVE); + } + + if ($row !== false) { + $this->data[] = $row; + + return $row; + } + + $this->saveToCache(); + + return false; + } + + /** + * @param array> $data + */ + private function store(array $data): void + { + $this->data = $data; + } + + private function saveToCache(): void + { + if ($this->data === null) { + return; + } + + $data = $this->resultCache->fetch($this->cacheKey); + if (! $data) { + $data = []; + } + + $data[$this->realKey] = $this->data; + + $this->resultCache->save($this->cacheKey, $data, $this->lifetime); + } } diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index a233fd1084..82ef5d8017 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -4,6 +4,7 @@ use Closure; use Doctrine\Common\EventManager; +use Doctrine\DBAL\Abstraction\Result; use Doctrine\DBAL\Cache\ArrayStatement; use Doctrine\DBAL\Cache\CacheException; use Doctrine\DBAL\Cache\QueryCacheProfile; @@ -12,14 +13,15 @@ use Doctrine\DBAL\Driver\PingableConnection; use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Doctrine\DBAL\Exception\ConnectionLost; use Doctrine\DBAL\Exception\InvalidArgumentException; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Types\Type; -use Exception; use Throwable; +use Traversable; use function array_key_exists; use function assert; @@ -95,13 +97,6 @@ class Connection implements DriverConnection /** @var ExpressionBuilder */ protected $_expr; - /** - * Whether or not a connection has been established. - * - * @var bool - */ - private $isConnected = false; - /** * The current auto-commit mode of this connection. * @@ -172,12 +167,14 @@ class Connection implements DriverConnection /** * Initializes a new instance of the Connection class. * + * @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. * - * @throws DBALException + * @throws Exception */ public function __construct( array $params, @@ -189,14 +186,13 @@ public function __construct( $this->params = $params; if (isset($params['pdo'])) { - $this->_conn = $params['pdo']; - $this->isConnected = true; + $this->_conn = $params['pdo']; unset($this->params['pdo']); } if (isset($params['platform'])) { if (! $params['platform'] instanceof Platforms\AbstractPlatform) { - throw DBALException::invalidPlatformType($params['platform']); + throw Exception::invalidPlatformType($params['platform']); } $this->platform = $params['platform']; @@ -222,6 +218,8 @@ public function __construct( /** * Gets the parameters used during instantiation. * + * @internal + * * @return mixed[] */ public function getParams() @@ -322,7 +320,7 @@ public function getEventManager() * * @return AbstractPlatform * - * @throws DBALException + * @throws Exception */ public function getDatabasePlatform() { @@ -351,7 +349,7 @@ public function getExpressionBuilder() */ public function connect() { - if ($this->isConnected) { + if ($this->_conn !== null) { return false; } @@ -359,8 +357,7 @@ public function connect() $user = $this->params['user'] ?? null; $password = $this->params['password'] ?? null; - $this->_conn = $this->_driver->connect($this->params, $user, $password, $driverOptions); - $this->isConnected = true; + $this->_conn = $this->_driver->connect($this->params, $user, $password, $driverOptions); $this->transactionNestingLevel = 0; @@ -381,7 +378,7 @@ public function connect() * * Evaluates custom platform class and version in order to set the correct platform. * - * @throws DBALException If an invalid platform was specified for this connection. + * @throws Exception If an invalid platform was specified for this connection. */ private function detectDatabasePlatform(): void { @@ -408,7 +405,7 @@ private function detectDatabasePlatform(): void * * @return string|null * - * @throws Exception + * @throws Throwable */ private function getDatabasePlatformVersion() { @@ -519,7 +516,7 @@ public function setAutoCommit($autoCommit) $this->autoCommit = $autoCommit; // Commit all currently active transactions if any when switching auto-commit mode. - if ($this->isConnected !== true || $this->transactionNestingLevel === 0) { + if ($this->_conn === null || $this->transactionNestingLevel === 0) { return; } @@ -529,6 +526,8 @@ public function setAutoCommit($autoCommit) /** * Sets the fetch mode. * + * @deprecated Use one of the fetch- or iterate-related methods. + * * @param int $fetchMode * * @return void @@ -542,13 +541,15 @@ public function setFetchMode($fetchMode) * Prepares and executes an SQL query and returns the first row of the result * as an associative array. * + * @deprecated Use fetchAllAssociative() + * * @param string $sql The query SQL * @param mixed[] $params The query parameters * @param int[]|string[] $types The query parameter types * * @return mixed[]|false False is returned if no rows are found. * - * @throws DBALException + * @throws Exception */ public function fetchAssoc($sql, array $params = [], array $types = []) { @@ -559,6 +560,8 @@ public function fetchAssoc($sql, array $params = [], array $types = []) * Prepares and executes an SQL query and returns the first row of the result * as a numerically indexed array. * + * @deprecated Use fetchAllNumeric() + * * @param string $sql The query SQL * @param mixed[] $params The query parameters * @param int[]|string[] $types The query parameter types @@ -574,6 +577,8 @@ public function fetchArray($sql, array $params = [], array $types = []) * Prepares and executes an SQL query and returns the value of a single column * of the first row of the result. * + * @deprecated Use fetchOne() instead. + * * @param string $sql The query SQL * @param mixed[] $params The query parameters * @param int $column The 0-indexed column number to retrieve @@ -581,13 +586,94 @@ public function fetchArray($sql, array $params = [], array $types = []) * * @return mixed|false False is returned if no rows are found. * - * @throws DBALException + * @throws Exception */ public function fetchColumn($sql, array $params = [], $column = 0, array $types = []) { return $this->executeQuery($sql, $params, $types)->fetchColumn($column); } + /** + * Prepares and executes an SQL query and returns the first row of the result + * as an associative array. + * + * @param string $query The SQL query. + * @param array|array $params The prepared statement params. + * @param array|array $types The query parameter types. + * + * @return array|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchAssociative(string $query, array $params = [], array $types = []) + { + try { + $stmt = $this->executeQuery($query, $params, $types); + + if ($stmt instanceof Result) { + return $stmt->fetchAssociative(); + } + + return $stmt->fetch(FetchMode::ASSOCIATIVE); + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as a numerically indexed array. + * + * @param string $query The SQL query to be executed. + * @param array|array $params The prepared statement params. + * @param array|array $types The query parameter types. + * + * @return array|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchNumeric(string $query, array $params = [], array $types = []) + { + try { + $stmt = $this->executeQuery($query, $params, $types); + + if ($stmt instanceof Result) { + return $stmt->fetchNumeric(); + } + + return $stmt->fetch(FetchMode::NUMERIC); + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the value of a single column + * of the first row of the result. + * + * @param string $query The SQL query to be executed. + * @param array|array $params The prepared statement params. + * @param array|array $types The query parameter types. + * + * @return mixed|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchOne(string $query, array $params = [], array $types = []) + { + try { + $stmt = $this->executeQuery($query, $params, $types); + + if ($stmt instanceof Result) { + return $stmt->fetchOne(); + } + + return $stmt->fetch(FetchMode::COLUMN); + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $query, $params, $types); + } + } + /** * Whether an actual connection to the database is established. * @@ -595,7 +681,7 @@ public function fetchColumn($sql, array $params = [], $column = 0, array $types */ public function isConnected() { - return $this->isConnected; + return $this->_conn !== null; } /** @@ -616,7 +702,7 @@ public function isTransactionActive() * @param mixed[] $values Column values * @param string[] $conditions Key conditions * - * @throws DBALException + * @throws Exception */ private function addIdentifierCondition( array $identifier, @@ -649,8 +735,7 @@ private function addIdentifierCondition( * * @return int The number of affected rows. * - * @throws DBALException - * @throws InvalidArgumentException + * @throws Exception */ public function delete($table, array $identifier, array $types = []) { @@ -662,7 +747,7 @@ public function delete($table, array $identifier, array $types = []) $this->addIdentifierCondition($identifier, $columns, $values, $conditions); - return $this->executeUpdate( + return $this->executeStatement( 'DELETE FROM ' . $table . ' WHERE ' . implode(' AND ', $conditions), $values, is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types @@ -677,8 +762,6 @@ public function delete($table, array $identifier, array $types = []) public function close() { $this->_conn = null; - - $this->isConnected = false; } /** @@ -692,7 +775,7 @@ public function setTransactionIsolation($level) { $this->transactionIsolationLevel = $level; - return $this->executeUpdate($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level)); + return $this->executeStatement($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level)); } /** @@ -721,7 +804,7 @@ public function getTransactionIsolation() * * @return int The number of affected rows. * - * @throws DBALException + * @throws Exception */ public function update($table, array $data, array $identifier, array $types = []) { @@ -742,7 +825,7 @@ public function update($table, array $data, array $identifier, array $types = [] $sql = 'UPDATE ' . $table . ' SET ' . implode(', ', $set) . ' WHERE ' . implode(' AND ', $conditions); - return $this->executeUpdate($sql, $values, $types); + return $this->executeStatement($sql, $values, $types); } /** @@ -756,12 +839,12 @@ public function update($table, array $data, array $identifier, array $types = [] * * @return int The number of affected rows. * - * @throws DBALException + * @throws Exception */ public function insert($table, array $data, array $types = []) { if (empty($data)) { - return $this->executeUpdate('INSERT INTO ' . $table . ' () VALUES ()'); + return $this->executeStatement('INSERT INTO ' . $table . ' () VALUES ()'); } $columns = []; @@ -774,7 +857,7 @@ public function insert($table, array $data, array $types = []) $set[] = '?'; } - return $this->executeUpdate( + return $this->executeStatement( 'INSERT INTO ' . $table . ' (' . implode(', ', $columns) . ')' . ' VALUES (' . implode(', ', $set) . ')', $values, @@ -835,6 +918,8 @@ public function quote($value, $type = ParameterType::STRING) /** * Prepares and executes an SQL query and returns the result as an associative array. * + * @deprecated Use fetchAllAssociative() + * * @param string $sql The SQL query. * @param mixed[] $params The query parameters. * @param int[]|string[] $types The query parameter types. @@ -846,6 +931,169 @@ public function fetchAll($sql, array $params = [], $types = []) return $this->executeQuery($sql, $params, $types)->fetchAll(); } + /** + * Prepares and executes an SQL query and returns the result as an array of numeric arrays. + * + * @param string $query The SQL query. + * @param array|array $params The query parameters. + * @param array|array $types The query parameter types. + * + * @return array> + * + * @throws Exception + */ + public function fetchAllNumeric(string $query, array $params = [], array $types = []): array + { + try { + $stmt = $this->executeQuery($query, $params, $types); + + if ($stmt instanceof Result) { + return $stmt->fetchAllNumeric(); + } + + return $stmt->fetchAll(FetchMode::NUMERIC); + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the result as an array of associative arrays. + * + * @param string $query The SQL query. + * @param array|array $params The query parameters. + * @param array|array $types The query parameter types. + * + * @return array> + * + * @throws Exception + */ + public function fetchAllAssociative(string $query, array $params = [], array $types = []): array + { + try { + $stmt = $this->executeQuery($query, $params, $types); + + if ($stmt instanceof Result) { + return $stmt->fetchAllAssociative(); + } + + return $stmt->fetchAll(FetchMode::ASSOCIATIVE); + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the result as an array of the first column values. + * + * @param string $query The SQL query. + * @param array|array $params The query parameters. + * @param array|array $types The query parameter types. + * + * @return array + * + * @throws Exception + */ + public function fetchFirstColumn(string $query, array $params = [], array $types = []): array + { + try { + $stmt = $this->executeQuery($query, $params, $types); + + if ($stmt instanceof Result) { + return $stmt->fetchFirstColumn(); + } + + return $stmt->fetchAll(FetchMode::COLUMN); + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the result as an iterator over rows represented as numeric arrays. + * + * @param string $query The SQL query. + * @param array|array $params The query parameters. + * @param array|array $types The query parameter types. + * + * @return Traversable> + * + * @throws Exception + */ + public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable + { + try { + $stmt = $this->executeQuery($query, $params, $types); + + if ($stmt instanceof Result) { + yield from $stmt->iterateNumeric(); + } else { + while (($row = $stmt->fetch(FetchMode::NUMERIC)) !== false) { + yield $row; + } + } + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the result as an iterator over rows represented + * as associative arrays. + * + * @param string $query The SQL query. + * @param array|array $params The query parameters. + * @param array|array $types The query parameter types. + * + * @return Traversable> + * + * @throws Exception + */ + public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable + { + try { + $stmt = $this->executeQuery($query, $params, $types); + + if ($stmt instanceof Result) { + yield from $stmt->iterateAssociative(); + } else { + while (($row = $stmt->fetch(FetchMode::ASSOCIATIVE)) !== false) { + yield $row; + } + } + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the result as an iterator over the first column values. + * + * @param string $query The SQL query. + * @param array|array $params The query parameters. + * @param array|array $types The query parameter types. + * + * @return Traversable + * + * @throws Exception + */ + public function iterateColumn(string $query, array $params = [], array $types = []): Traversable + { + try { + $stmt = $this->executeQuery($query, $params, $types); + + if ($stmt instanceof Result) { + yield from $stmt->iterateColumn(); + } else { + while (($value = $stmt->fetch(FetchMode::COLUMN)) !== false) { + yield $value; + } + } + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $query, $params, $types); + } + } + /** * Prepares an SQL statement. * @@ -853,14 +1101,14 @@ public function fetchAll($sql, array $params = [], $types = []) * * @return Statement The prepared statement. * - * @throws DBALException + * @throws Exception */ public function prepare($sql) { try { $stmt = new Statement($sql, $this); - } catch (Throwable $ex) { - throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $sql); + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $sql); } $stmt->setFetchMode($this->defaultFetchMode); @@ -881,7 +1129,7 @@ public function prepare($sql) * * @return ResultStatement The executed statement. * - * @throws DBALException + * @throws Exception */ public function executeQuery($sql, array $params = [], $types = [], ?QueryCacheProfile $qcp = null) { @@ -910,12 +1158,12 @@ public function executeQuery($sql, array $params = [], $types = [], ?QueryCacheP } else { $stmt = $connection->query($sql); } - } catch (Throwable $ex) { - throw DBALException::driverExceptionDuringQuery( - $this->_driver, - $ex, + } catch (Throwable $e) { + $this->handleExceptionDuringQuery( + $e, $sql, - $this->resolveParams($params, $types) + $params, + $types ); } @@ -948,7 +1196,7 @@ public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp) throw CacheException::noResultDriverConfigured(); } - $connectionParams = $this->getParams(); + $connectionParams = $this->params; unset($connectionParams['platform']); [$cacheKey, $realKey] = $qcp->generateCacheKeys($sql, $params, $types, $connectionParams); @@ -984,6 +1232,8 @@ public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp) * Executes an, optionally parametrized, SQL query and returns the result, * applying a given projection/transformation function on each row of the result. * + * @deprecated + * * @param string $sql The SQL query to execute. * @param mixed[] $params The parameters, if any. * @param Closure $function The transformation function that is applied on each row. @@ -1009,9 +1259,11 @@ public function project($sql, array $params, Closure $function) /** * Executes an SQL statement, returning a result set as a Statement object. * + * @deprecated Use {@link executeQuery()} instead. + * * @return \Doctrine\DBAL\Driver\Statement * - * @throws DBALException + * @throws Exception */ public function query() { @@ -1026,8 +1278,8 @@ public function query() try { $statement = $connection->query(...$args); - } catch (Throwable $ex) { - throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $args[0]); + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $args[0]); } $statement->setFetchMode($this->defaultFetchMode); @@ -1045,15 +1297,42 @@ public function query() * * This method supports PDO binding types as well as DBAL mapping types. * + * @deprecated Use {@link executeStatement()} instead. + * * @param string $sql The SQL query. * @param array $params The query parameters. * @param array $types The parameter types. * * @return int The number of affected rows. * - * @throws DBALException + * @throws Exception */ public function executeUpdate($sql, array $params = [], array $types = []) + { + return $this->executeStatement($sql, $params, $types); + } + + /** + * Executes an SQL statement with the given parameters and returns the number of affected rows. + * + * Could be used for: + * - DML statements: INSERT, UPDATE, DELETE, etc. + * - DDL statements: CREATE, DROP, ALTER, etc. + * - DCL statements: GRANT, REVOKE, etc. + * - Session control statements: ALTER SESSION, SET, DECLARE, etc. + * - Other statements that don't yield a row set. + * + * This method supports PDO binding types as well as DBAL mapping types. + * + * @param string $sql The statement SQL + * @param array $params The query parameters + * @param array $types The parameter types + * + * @return int The number of affected rows. + * + * @throws Exception + */ + public function executeStatement($sql, array $params = [], array $types = []) { $connection = $this->getWrappedConnection(); @@ -1079,12 +1358,12 @@ public function executeUpdate($sql, array $params = [], array $types = []) } else { $result = $connection->exec($sql); } - } catch (Throwable $ex) { - throw DBALException::driverExceptionDuringQuery( - $this->_driver, - $ex, + } catch (Throwable $e) { + $this->handleExceptionDuringQuery( + $e, $sql, - $this->resolveParams($params, $types) + $params, + $types ); } @@ -1098,11 +1377,13 @@ public function executeUpdate($sql, array $params = [], array $types = []) /** * Executes an SQL statement and return the number of affected rows. * + * @deprecated Use {@link executeStatement()} instead. + * * @param string $sql * * @return int The number of affected rows. * - * @throws DBALException + * @throws Exception */ public function exec($sql) { @@ -1115,8 +1396,8 @@ public function exec($sql) try { $result = $connection->exec($sql); - } catch (Throwable $ex) { - throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $sql); + } catch (Throwable $e) { + $this->handleExceptionDuringQuery($e, $sql); } if ($logger) { @@ -1139,6 +1420,8 @@ public function getTransactionNestingLevel() /** * Fetches the SQLSTATE associated with the last database operation. * + * @deprecated The error information is available via exceptions. + * * @return string|null The last error code. */ public function errorCode() @@ -1148,6 +1431,8 @@ public function errorCode() /** * {@inheritDoc} + * + * @deprecated The error information is available via exceptions. */ public function errorInfo() { @@ -1183,7 +1468,6 @@ public function lastInsertId($name = null) * * @return mixed The value returned by $func * - * @throws Exception * @throws Throwable */ public function transactional(Closure $func) @@ -1194,10 +1478,6 @@ public function transactional(Closure $func) $this->commit(); return $res; - } catch (Exception $e) { - $this->rollBack(); - - throw $e; } catch (Throwable $e) { $this->rollBack(); @@ -1688,6 +1968,8 @@ public function createQueryBuilder() * It is responsibility of the developer to handle this case * and abort the request or reconnect manually: * + * @deprecated + * * @return bool * * @example @@ -1718,4 +2000,59 @@ public function ping() return false; } } + + /** + * @internal + * + * @param array|array $params + * @param array|array $types + * + * @throws Exception + * + * @psalm-return never-return + */ + public function handleExceptionDuringQuery(Throwable $e, string $sql, array $params = [], array $types = []): void + { + $this->throw( + Exception::driverExceptionDuringQuery( + $this->_driver, + $e, + $sql, + $this->resolveParams($params, $types) + ) + ); + } + + /** + * @internal + * + * @throws Exception + * + * @psalm-return never-return + */ + public function handleDriverException(Throwable $e): void + { + $this->throw( + Exception::driverException( + $this->_driver, + $e + ) + ); + } + + /** + * @internal + * + * @throws Exception + * + * @psalm-return never-return + */ + private function throw(Exception $e): void + { + if ($e instanceof ConnectionLost) { + $this->close(); + } + + throw $e; + } } diff --git a/lib/Doctrine/DBAL/ConnectionException.php b/lib/Doctrine/DBAL/ConnectionException.php index f1914f5294..8426ca2889 100644 --- a/lib/Doctrine/DBAL/ConnectionException.php +++ b/lib/Doctrine/DBAL/ConnectionException.php @@ -5,7 +5,7 @@ /** * @psalm-immutable */ -class ConnectionException extends DBALException +class ConnectionException extends Exception { /** * @return ConnectionException diff --git a/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php b/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php index 2ba37bfad2..0e97267643 100644 --- a/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php +++ b/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php @@ -4,89 +4,23 @@ use Doctrine\Common\EventManager; use Doctrine\DBAL\Configuration; -use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver; -use Doctrine\DBAL\Driver\Connection as DriverConnection; -use Doctrine\DBAL\Event\ConnectionEventArgs; -use Doctrine\DBAL\Events; use InvalidArgumentException; -use function array_rand; -use function assert; -use function count; -use function func_get_args; +use function sprintf; +use function trigger_error; + +use const E_USER_DEPRECATED; /** - * Master-Slave Connection - * - * Connection can be used with master-slave setups. - * - * Important for the understanding of this connection should be how and when - * it picks the slave or master. - * - * 1. Slave if master was never picked before and ONLY if 'getWrappedConnection' - * or 'executeQuery' is used. - * 2. Master picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint', - * 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or - * 'prepare' is called. - * 3. If master was picked once during the lifetime of the connection it will always get picked afterwards. - * 4. One slave connection is randomly picked ONCE during a request. - * - * ATTENTION: You can write to the slave with this connection if you execute a write query without - * opening up a transaction. For example: - * - * $conn = DriverManager::getConnection(...); - * $conn->executeQuery("DELETE FROM table"); - * - * Be aware that Connection#executeQuery is a method specifically for READ - * operations only. - * - * This connection is limited to slave operations using the - * Connection#executeQuery operation only, because it wouldn't be compatible - * with the ORM or SchemaManager code otherwise. Both use all the other - * operations in a context where writes could happen to a slave, which makes - * this restricted approach necessary. - * - * You can manually connect to the master at any time by calling: - * - * $conn->connect('master'); - * - * Instantiation through the DriverManager looks like: - * - * @example - * - * $conn = DriverManager::getConnection(array( - * 'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection', - * 'driver' => 'pdo_mysql', - * 'master' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), - * 'slaves' => array( - * array('user' => 'slave1', 'password', 'host' => '', 'dbname' => ''), - * array('user' => 'slave2', 'password', 'host' => '', 'dbname' => ''), - * ) - * )); - * - * You can also pass 'driverOptions' and any other documented option to each of this drivers - * to pass additional information. + * @deprecated Use PrimaryReadReplicaConnection instead */ -class MasterSlaveConnection extends Connection +class MasterSlaveConnection extends PrimaryReadReplicaConnection { /** - * Master and slave connection (one of the randomly picked slaves). + * Creates Primary Replica Connection. * - * @var DriverConnection[]|null[] - */ - protected $connections = ['master' => null, 'slave' => null]; - - /** - * You can keep the slave connection and then switch back to it - * during the request if you know what you are doing. - * - * @var bool - */ - protected $keepSlave = false; - - /** - * Creates Master Slave Connection. + * @internal The connection can be only instantiated by the driver manager. * * @param mixed[] $params * @@ -98,289 +32,73 @@ public function __construct( ?Configuration $config = null, ?EventManager $eventManager = null ) { - if (! isset($params['slaves'], $params['master'])) { - throw new InvalidArgumentException('master or slaves configuration missing'); - } + $this->deprecated(self::class, PrimaryReadReplicaConnection::class); - if (count($params['slaves']) === 0) { - throw new InvalidArgumentException('You have to configure at least one slaves.'); - } + if (isset($params['master'])) { + $this->deprecated('Params key "master"', '"primary"'); - $params['master']['driver'] = $params['driver']; - foreach ($params['slaves'] as $slaveKey => $slave) { - $params['slaves'][$slaveKey]['driver'] = $params['driver']; + $params['primary'] = $params['master']; + unset($params['master']); } - $this->keepSlave = (bool) ($params['keepSlave'] ?? false); + if (isset($params['slaves'])) { + $this->deprecated('Params key "slaves"', '"replica"'); - parent::__construct($params, $driver, $config, $eventManager); - } - - /** - * Checks if the connection is currently towards the master or not. - * - * @return bool - */ - public function isConnectedToMaster() - { - return $this->_conn !== null && $this->_conn === $this->connections['master']; - } - - /** - * @param string|null $connectionName - * - * @return bool - */ - public function connect($connectionName = null) - { - $requestedConnectionChange = ($connectionName !== null); - $connectionName = $connectionName ?: 'slave'; - - if ($connectionName !== 'slave' && $connectionName !== 'master') { - throw new InvalidArgumentException('Invalid option to connect(), only master or slave allowed.'); - } - - // If we have a connection open, and this is not an explicit connection - // change request, then abort right here, because we are already done. - // This prevents writes to the slave in case of "keepSlave" option enabled. - if ($this->_conn !== null && ! $requestedConnectionChange) { - return false; - } - - $forceMasterAsSlave = false; - - if ($this->getTransactionNestingLevel() > 0) { - $connectionName = 'master'; - $forceMasterAsSlave = true; + $params['replica'] = $params['slaves']; + unset($params['slaves']); } - if (isset($this->connections[$connectionName])) { - $this->_conn = $this->connections[$connectionName]; + if (isset($params['keepSlave'])) { + $this->deprecated('Params key "keepSlave"', '"keepReplica"'); - if ($forceMasterAsSlave && ! $this->keepSlave) { - $this->connections['slave'] = $this->_conn; - } - - return false; + $params['keepReplica'] = $params['keepSlave']; + unset($params['keepSlave']); } - if ($connectionName === 'master') { - $this->connections['master'] = $this->_conn = $this->connectTo($connectionName); - - // Set slave connection to master to avoid invalid reads - if (! $this->keepSlave) { - $this->connections['slave'] = $this->connections['master']; - } - } else { - $this->connections['slave'] = $this->_conn = $this->connectTo($connectionName); - } - - if ($this->_eventManager->hasListeners(Events::postConnect)) { - $eventArgs = new ConnectionEventArgs($this); - $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); - } - - return true; + parent::__construct($params, $driver, $config, $eventManager); } /** - * Connects to a specific connection. - * - * @param string $connectionName - * - * @return DriverConnection + * Checks if the connection is currently towards the primary or not. */ - protected function connectTo($connectionName) + public function isConnectedToMaster(): bool { - $params = $this->getParams(); - - $driverOptions = $params['driverOptions'] ?? []; + $this->deprecated('isConnectedtoMaster()', 'isConnectedToPrimary()'); - $connectionParams = $this->chooseConnectionConfiguration($connectionName, $params); - - $user = $connectionParams['user'] ?? null; - $password = $connectionParams['password'] ?? null; - - return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); + return $this->isConnectedToPrimary(); } /** - * @param string $connectionName - * @param mixed[] $params + * @param string|null $connectionName * - * @return mixed + * @return bool */ - protected function chooseConnectionConfiguration($connectionName, $params) + public function connect($connectionName = null) { if ($connectionName === 'master') { - return $params['master']; - } - - $config = $params['slaves'][array_rand($params['slaves'])]; - - if (! isset($config['charset']) && isset($params['master']['charset'])) { - $config['charset'] = $params['master']['charset']; - } - - return $config; - } - - /** - * {@inheritDoc} - */ - public function executeUpdate($sql, array $params = [], array $types = []) - { - $this->connect('master'); - - return parent::executeUpdate($sql, $params, $types); - } - - /** - * {@inheritDoc} - */ - public function beginTransaction() - { - $this->connect('master'); - - return parent::beginTransaction(); - } - - /** - * {@inheritDoc} - */ - public function commit() - { - $this->connect('master'); - - return parent::commit(); - } - - /** - * {@inheritDoc} - */ - public function rollBack() - { - $this->connect('master'); + $connectionName = 'primary'; - return parent::rollBack(); - } - - /** - * {@inheritDoc} - */ - public function delete($table, array $identifier, array $types = []) - { - $this->connect('master'); - - return parent::delete($table, $identifier, $types); - } - - /** - * {@inheritDoc} - */ - public function close() - { - unset($this->connections['master'], $this->connections['slave']); - - parent::close(); - - $this->_conn = null; - $this->connections = ['master' => null, 'slave' => null]; - } - - /** - * {@inheritDoc} - */ - public function update($table, array $data, array $identifier, array $types = []) - { - $this->connect('master'); - - return parent::update($table, $data, $identifier, $types); - } - - /** - * {@inheritDoc} - */ - public function insert($table, array $data, array $types = []) - { - $this->connect('master'); - - return parent::insert($table, $data, $types); - } - - /** - * {@inheritDoc} - */ - public function exec($sql) - { - $this->connect('master'); - - return parent::exec($sql); - } - - /** - * {@inheritDoc} - */ - public function createSavepoint($savepoint) - { - $this->connect('master'); - - parent::createSavepoint($savepoint); - } - - /** - * {@inheritDoc} - */ - public function releaseSavepoint($savepoint) - { - $this->connect('master'); - - parent::releaseSavepoint($savepoint); - } - - /** - * {@inheritDoc} - */ - public function rollbackSavepoint($savepoint) - { - $this->connect('master'); - - parent::rollbackSavepoint($savepoint); - } - - /** - * {@inheritDoc} - */ - public function query() - { - $this->connect('master'); - assert($this->_conn instanceof DriverConnection); - - $args = func_get_args(); - - $logger = $this->getConfiguration()->getSQLLogger(); - if ($logger) { - $logger->startQuery($args[0]); + $this->deprecated('connect("master")', 'ensureConnectedToPrimary()'); } - $statement = $this->_conn->query(...$args); + if ($connectionName === 'slave') { + $connectionName = 'replica'; - $statement->setFetchMode($this->defaultFetchMode); - - if ($logger) { - $logger->stopQuery(); + $this->deprecated('connect("slave")', 'ensureConnectedToReplica()'); } - return $statement; + return $this->performConnect($connectionName); } - /** - * {@inheritDoc} - */ - public function prepare($sql) + private function deprecated(string $thing, string $instead): void { - $this->connect('master'); - - return parent::prepare($sql); + @trigger_error( + sprintf( + '%s is deprecated since doctrine/dbal 2.11 and will be removed in 3.0, use %s instead.', + $thing, + $instead + ), + E_USER_DEPRECATED + ); } } diff --git a/lib/Doctrine/DBAL/Connections/PrimaryReadReplicaConnection.php b/lib/Doctrine/DBAL/Connections/PrimaryReadReplicaConnection.php new file mode 100644 index 0000000000..78cb755d80 --- /dev/null +++ b/lib/Doctrine/DBAL/Connections/PrimaryReadReplicaConnection.php @@ -0,0 +1,435 @@ +executeQuery("DELETE FROM table"); + * + * Be aware that Connection#executeQuery is a method specifically for READ + * operations only. + * + * Use Connection#executeStatement for any SQL statement that changes/updates + * state in the database (UPDATE, INSERT, DELETE or DDL statements). + * + * This connection is limited to replica operations using the + * Connection#executeQuery operation only, because it wouldn't be compatible + * with the ORM or SchemaManager code otherwise. Both use all the other + * operations in a context where writes could happen to a replica, which makes + * this restricted approach necessary. + * + * You can manually connect to the primary at any time by calling: + * + * $conn->ensureConnectedToPrimary(); + * + * Instantiation through the DriverManager looks like: + * + * @example + * + * $conn = DriverManager::getConnection(array( + * 'wrapperClass' => 'Doctrine\DBAL\Connections\PrimaryReadReplicaConnection', + * 'driver' => 'pdo_mysql', + * 'primary' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), + * 'replica' => array( + * array('user' => 'replica1', 'password', 'host' => '', 'dbname' => ''), + * array('user' => 'replica2', 'password', 'host' => '', 'dbname' => ''), + * ) + * )); + * + * You can also pass 'driverOptions' and any other documented option to each of this drivers + * to pass additional information. + */ +class PrimaryReadReplicaConnection extends Connection +{ + /** + * Primary and Replica connection (one of the randomly picked replicas). + * + * @var DriverConnection[]|null[] + */ + protected $connections = ['primary' => null, 'replica' => null]; + + /** + * You can keep the replica connection and then switch back to it + * during the request if you know what you are doing. + * + * @var bool + */ + protected $keepReplica = false; + + /** + * Creates Primary Replica Connection. + * + * @internal The connection can be only instantiated by the driver manager. + * + * @param mixed[] $params + * + * @throws InvalidArgumentException + */ + public function __construct( + array $params, + Driver $driver, + ?Configuration $config = null, + ?EventManager $eventManager = null + ) { + if (! isset($params['replica'], $params['primary'])) { + throw new InvalidArgumentException('primary or replica configuration missing'); + } + + if (count($params['replica']) === 0) { + 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']; + } + + $this->keepReplica = (bool) ($params['keepReplica'] ?? false); + + parent::__construct($params, $driver, $config, $eventManager); + } + + /** + * Checks if the connection is currently towards the primary or not. + */ + public function isConnectedToPrimary(): bool + { + return $this->_conn !== null && $this->_conn === $this->connections['primary']; + } + + /** + * @param string|null $connectionName + * + * @return bool + */ + public function connect($connectionName = null) + { + if ($connectionName !== null) { + throw new InvalidArgumentException( + 'Passing a connection name as first argument is not supported anymore.' + . ' Use ensureConnectedToPrimary()/ensureConnectedToReplica() instead.' + ); + } + + return $this->performConnect(); + } + + protected function performConnect(?string $connectionName = null): bool + { + $requestedConnectionChange = ($connectionName !== null); + $connectionName = $connectionName ?: 'replica'; + + if ($connectionName !== 'replica' && $connectionName !== 'primary') { + throw new InvalidArgumentException('Invalid option to connect(), only primary or replica allowed.'); + } + + // If we have a connection open, and this is not an explicit connection + // change request, then abort right here, because we are already done. + // This prevents writes to the replica in case of "keepReplica" option enabled. + if ($this->_conn !== null && ! $requestedConnectionChange) { + return false; + } + + $forcePrimaryAsReplica = false; + + if ($this->getTransactionNestingLevel() > 0) { + $connectionName = 'primary'; + $forcePrimaryAsReplica = true; + } + + if (isset($this->connections[$connectionName])) { + $this->_conn = $this->connections[$connectionName]; + + if ($forcePrimaryAsReplica && ! $this->keepReplica) { + $this->connections['replica'] = $this->_conn; + } + + return false; + } + + if ($connectionName === 'primary') { + $this->connections['primary'] = $this->_conn = $this->connectTo($connectionName); + + // Set replica connection to primary to avoid invalid reads + if (! $this->keepReplica) { + $this->connections['replica'] = $this->connections['primary']; + } + } else { + $this->connections['replica'] = $this->_conn = $this->connectTo($connectionName); + } + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Connects to the primary node of the database cluster. + * + * All following statements after this will be executed against the primary node. + */ + public function ensureConnectedToPrimary(): bool + { + return $this->performConnect('primary'); + } + + /** + * Connects to a replica node of the database cluster. + * + * All following statements after this will be executed against the replica node, + * unless the keepReplica option is set to false and a primary connection + * was already opened. + */ + public function ensureConnectedToReplica(): bool + { + return $this->performConnect('replica'); + } + + /** + * Connects to a specific connection. + * + * @param string $connectionName + * + * @return DriverConnection + */ + protected function connectTo($connectionName) + { + $params = $this->getParams(); + + $driverOptions = $params['driverOptions'] ?? []; + + $connectionParams = $this->chooseConnectionConfiguration($connectionName, $params); + + $user = $connectionParams['user'] ?? null; + $password = $connectionParams['password'] ?? null; + + return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); + } + + /** + * @param string $connectionName + * @param mixed[] $params + * + * @return mixed + */ + protected function chooseConnectionConfiguration($connectionName, $params) + { + if ($connectionName === 'primary') { + return $params['primary']; + } + + $config = $params['replica'][array_rand($params['replica'])]; + + if (! isset($config['charset']) && isset($params['primary']['charset'])) { + $config['charset'] = $params['primary']['charset']; + } + + return $config; + } + + /** + * {@inheritDoc} + * + * @deprecated Use {@link executeStatement()} instead. + */ + public function executeUpdate($sql, array $params = [], array $types = []) + { + $this->ensureConnectedToPrimary(); + + return parent::executeUpdate($sql, $params, $types); + } + + /** + * {@inheritDoc} + */ + public function executeStatement($sql, array $params = [], array $types = []) + { + $this->ensureConnectedToPrimary(); + + return parent::executeStatement($sql, $params, $types); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + $this->ensureConnectedToPrimary(); + + return parent::beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() + { + $this->ensureConnectedToPrimary(); + + return parent::commit(); + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + $this->ensureConnectedToPrimary(); + + return parent::rollBack(); + } + + /** + * {@inheritDoc} + */ + public function delete($table, array $identifier, array $types = []) + { + $this->ensureConnectedToPrimary(); + + return parent::delete($table, $identifier, $types); + } + + /** + * {@inheritDoc} + */ + public function close() + { + unset($this->connections['primary'], $this->connections['replica']); + + parent::close(); + + $this->_conn = null; + $this->connections = ['primary' => null, 'replica' => null]; + } + + /** + * {@inheritDoc} + */ + public function update($table, array $data, array $identifier, array $types = []) + { + $this->ensureConnectedToPrimary(); + + return parent::update($table, $data, $identifier, $types); + } + + /** + * {@inheritDoc} + */ + public function insert($table, array $data, array $types = []) + { + $this->ensureConnectedToPrimary(); + + return parent::insert($table, $data, $types); + } + + /** + * {@inheritDoc} + */ + public function exec($statement) + { + $this->ensureConnectedToPrimary(); + + return parent::exec($statement); + } + + /** + * {@inheritDoc} + */ + public function createSavepoint($savepoint) + { + $this->ensureConnectedToPrimary(); + + parent::createSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function releaseSavepoint($savepoint) + { + $this->ensureConnectedToPrimary(); + + parent::releaseSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function rollbackSavepoint($savepoint) + { + $this->ensureConnectedToPrimary(); + + parent::rollbackSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function query() + { + $this->ensureConnectedToPrimary(); + assert($this->_conn instanceof DriverConnection); + + $args = func_get_args(); + + $logger = $this->getConfiguration()->getSQLLogger(); + if ($logger) { + $logger->startQuery($args[0]); + } + + $statement = $this->_conn->query(...$args); + + $statement->setFetchMode($this->defaultFetchMode); + + if ($logger) { + $logger->stopQuery(); + } + + return $statement; + } + + /** + * {@inheritDoc} + */ + public function prepare($statement) + { + $this->ensureConnectedToPrimary(); + + return parent::prepare($statement); + } +} diff --git a/lib/Doctrine/DBAL/DBALException.php b/lib/Doctrine/DBAL/DBALException.php index e7ae39ce55..b9a33cf1f7 100644 --- a/lib/Doctrine/DBAL/DBALException.php +++ b/lib/Doctrine/DBAL/DBALException.php @@ -2,12 +2,11 @@ namespace Doctrine\DBAL; -use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface; +use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException; use Doctrine\DBAL\Driver\ExceptionConverterDriver; use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; -use Exception; use Throwable; use function array_map; @@ -24,23 +23,28 @@ use function sprintf; /** + * @deprecated Use {@link Exception} instead + * * @psalm-immutable */ -class DBALException extends Exception +class DBALException extends \Exception { /** * @param string $method * - * @return DBALException + * @return Exception */ public static function notSupported($method) { - return new self(sprintf("Operation '%s' is not supported by platform.", $method)); + return new Exception(sprintf("Operation '%s' is not supported by platform.", $method)); } + /** + * @deprecated Use {@link invalidPlatformType()} instead. + */ public static function invalidPlatformSpecified(): self { - return new self( + return new Exception( "Invalid 'platform' option specified, need to give an instance of " . AbstractPlatform::class . '.' ); } @@ -51,7 +55,7 @@ public static function invalidPlatformSpecified(): self public static function invalidPlatformType($invalidPlatform): self { if (is_object($invalidPlatform)) { - return new self( + return new Exception( sprintf( "Option 'platform' must be a subtype of '%s', instance of '%s' given", AbstractPlatform::class, @@ -60,7 +64,7 @@ public static function invalidPlatformType($invalidPlatform): self ); } - return new self( + return new Exception( sprintf( "Option 'platform' must be an object and subtype of '%s'. Got '%s'", AbstractPlatform::class, @@ -75,11 +79,11 @@ public static function invalidPlatformType($invalidPlatform): self * @param string $version The invalid platform version given. * @param string $expectedFormat The expected platform version format. * - * @return DBALException + * @return Exception */ public static function invalidPlatformVersionSpecified($version, $expectedFormat) { - return new self( + return new Exception( sprintf( 'Invalid platform version "%s" specified. ' . 'The platform version has to be specified in the format: "%s".', @@ -90,11 +94,13 @@ public static function invalidPlatformVersionSpecified($version, $expectedFormat } /** - * @return DBALException + * @deprecated Passing a PDO instance in connection parameters is deprecated. + * + * @return Exception */ public static function invalidPdoInstance() { - return new self( + return new Exception( "The 'pdo' option was used in DriverManager::getConnection() but no " . 'instance of PDO was given.' ); @@ -103,12 +109,12 @@ public static function invalidPdoInstance() /** * @param string|null $url The URL that was provided in the connection parameters (if any). * - * @return DBALException + * @return Exception */ public static function driverRequired($url = null) { if ($url) { - return new self( + return new Exception( sprintf( "The options 'driver' or 'driverClass' are mandatory if a connection URL without scheme " . 'is given to DriverManager::getConnection(). Given URL: %s', @@ -117,7 +123,7 @@ public static function driverRequired($url = null) ); } - return new self("The options 'driver' or 'driverClass' are mandatory if no PDO " . + return new Exception("The options 'driver' or 'driverClass' are mandatory if no PDO " . 'instance is given to DriverManager::getConnection().'); } @@ -125,19 +131,21 @@ public static function driverRequired($url = null) * @param string $unknownDriverName * @param string[] $knownDrivers * - * @return DBALException + * @return Exception */ public static function unknownDriver($unknownDriverName, array $knownDrivers) { - return new self("The given 'driver' " . $unknownDriverName . ' is unknown, ' . + return new Exception("The given 'driver' " . $unknownDriverName . ' is unknown, ' . 'Doctrine currently supports only the following drivers: ' . implode(', ', $knownDrivers)); } /** + * @deprecated + * * @param string $sql * @param mixed[] $params * - * @return self + * @return Exception */ public static function driverExceptionDuringQuery(Driver $driver, Throwable $driverEx, $sql, array $params = []) { @@ -152,7 +160,9 @@ public static function driverExceptionDuringQuery(Driver $driver, Throwable $dri } /** - * @return self + * @deprecated + * + * @return Exception */ public static function driverException(Driver $driver, Throwable $driverEx) { @@ -160,7 +170,7 @@ public static function driverException(Driver $driver, Throwable $driverEx) } /** - * @return self + * @return Exception */ private static function wrapException(Driver $driver, Throwable $driverEx, string $msg) { @@ -168,11 +178,11 @@ private static function wrapException(Driver $driver, Throwable $driverEx, strin return $driverEx; } - if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof DriverExceptionInterface) { + if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof DeprecatedDriverException) { return $driver->convertException($msg, $driverEx); } - return new self($msg, 0, $driverEx); + return new Exception($msg, 0, $driverEx); } /** @@ -204,22 +214,22 @@ private static function formatParameters(array $params) /** * @param string $wrapperClass * - * @return DBALException + * @return Exception */ public static function invalidWrapperClass($wrapperClass) { - return new self("The given 'wrapperClass' " . $wrapperClass . ' has to be a ' . + return new Exception("The given 'wrapperClass' " . $wrapperClass . ' has to be a ' . 'subtype of \Doctrine\DBAL\Connection.'); } /** * @param string $driverClass * - * @return DBALException + * @return Exception */ public static function invalidDriverClass($driverClass) { - return new self( + return new Exception( "The given 'driverClass' " . $driverClass . ' has to implement the ' . Driver::class . ' interface.' ); } @@ -227,49 +237,49 @@ public static function invalidDriverClass($driverClass) /** * @param string $tableName * - * @return DBALException + * @return Exception */ public static function invalidTableName($tableName) { - return new self('Invalid table name specified: ' . $tableName); + return new Exception('Invalid table name specified: ' . $tableName); } /** * @param string $tableName * - * @return DBALException + * @return Exception */ public static function noColumnsSpecifiedForTable($tableName) { - return new self('No columns specified for table ' . $tableName); + return new Exception('No columns specified for table ' . $tableName); } /** - * @return DBALException + * @return Exception */ public static function limitOffsetInvalid() { - return new self('Invalid Offset in Limit Query, it has to be larger than or equal to 0.'); + return new Exception('Invalid Offset in Limit Query, it has to be larger than or equal to 0.'); } /** * @param string $name * - * @return DBALException + * @return Exception */ public static function typeExists($name) { - return new self('Type ' . $name . ' already exists.'); + return new Exception('Type ' . $name . ' already exists.'); } /** * @param string $name * - * @return DBALException + * @return Exception */ public static function unknownColumnType($name) { - return new self('Unknown column type "' . $name . '" requested. Any Doctrine type that you use has ' . + return new Exception('Unknown column type "' . $name . '" requested. Any Doctrine type that you use has ' . 'to be registered with \Doctrine\DBAL\Types\Type::addType(). You can get a list of all the ' . 'known types with \Doctrine\DBAL\Types\Type::getTypesMap(). If this error occurs during database ' . 'introspection then you might have forgotten to register all database types for a Doctrine Type. Use ' . @@ -281,23 +291,23 @@ public static function unknownColumnType($name) /** * @param string $name * - * @return DBALException + * @return Exception */ public static function typeNotFound($name) { - return new self('Type to be overwritten ' . $name . ' does not exist.'); + return new Exception('Type to be overwritten ' . $name . ' does not exist.'); } public static function typeNotRegistered(Type $type): self { - return new self( + return new Exception( sprintf('Type of the class %s@%s is not registered.', get_class($type), spl_object_hash($type)) ); } public static function typeAlreadyRegistered(Type $type): self { - return new self( + return new Exception( sprintf('Type of the class %s@%s is already registered.', get_class($type), spl_object_hash($type)) ); } diff --git a/lib/Doctrine/DBAL/Driver.php b/lib/Doctrine/DBAL/Driver.php index 57493e633b..6f8afbf3cf 100644 --- a/lib/Doctrine/DBAL/Driver.php +++ b/lib/Doctrine/DBAL/Driver.php @@ -53,6 +53,8 @@ public function getName(); /** * Gets the name of the database connected to for this driver. * + * @deprecated Use Connection::getDatabase() instead. + * * @return string The name of the database. */ public function getDatabase(Connection $conn); diff --git a/lib/Doctrine/DBAL/Driver/AbstractDB2Driver.php b/lib/Doctrine/DBAL/Driver/AbstractDB2Driver.php index 45e29827fd..72590a895a 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractDB2Driver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractDB2Driver.php @@ -4,6 +4,8 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\DriverException as TheDriverException; +use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Schema\DB2SchemaManager; @@ -14,6 +16,8 @@ abstract class AbstractDB2Driver implements Driver { /** * {@inheritdoc} + * + * @deprecated Use Connection::getDatabase() instead. */ public function getDatabase(Connection $conn) { @@ -37,4 +41,14 @@ public function getSchemaManager(Connection $conn) { return new DB2SchemaManager($conn); } + + /** + * @param string $message + * + * @return DriverException + */ + public function convertException($message, TheDriverException $exception) + { + return new DriverException($message, $exception); + } } diff --git a/lib/Doctrine/DBAL/Driver/AbstractDriverException.php b/lib/Doctrine/DBAL/Driver/AbstractDriverException.php index f57de38f09..bf104545e4 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractDriverException.php +++ b/lib/Doctrine/DBAL/Driver/AbstractDriverException.php @@ -2,55 +2,11 @@ namespace Doctrine\DBAL\Driver; -use Exception; - /** - * Abstract base implementation of the {@link DriverException} interface. + * @deprecated * * @psalm-immutable */ -abstract class AbstractDriverException extends Exception implements DriverException +class AbstractDriverException extends AbstractException { - /** - * The driver specific error code. - * - * @var int|string|null - */ - private $errorCode; - - /** - * The SQLSTATE of the driver. - * - * @var string|null - */ - private $sqlState; - - /** - * @param string $message The driver error message. - * @param string|null $sqlState The SQLSTATE the driver is in at the time the error occurred, if any. - * @param int|string|null $errorCode The driver specific error code if any. - */ - public function __construct($message, $sqlState = null, $errorCode = null) - { - parent::__construct($message); - - $this->errorCode = $errorCode; - $this->sqlState = $sqlState; - } - - /** - * {@inheritdoc} - */ - public function getErrorCode() - { - return $this->errorCode; - } - - /** - * {@inheritdoc} - */ - public function getSQLState() - { - return $this->sqlState; - } } diff --git a/lib/Doctrine/DBAL/Driver/AbstractException.php b/lib/Doctrine/DBAL/Driver/AbstractException.php new file mode 100644 index 0000000000..65a9708ad6 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/AbstractException.php @@ -0,0 +1,60 @@ +errorCode = $errorCode; + $this->sqlState = $sqlState; + } + + /** + * {@inheritdoc} + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * {@inheritdoc} + */ + public function getSQLState() + { + return $this->sqlState; + } +} diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index 7b454df588..edb33b89a4 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -3,9 +3,22 @@ namespace Doctrine\DBAL\Driver; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Exception\ConnectionException; +use Doctrine\DBAL\Exception\ConnectionLost; +use Doctrine\DBAL\Exception\DeadlockException; +use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; +use Doctrine\DBAL\Exception\InvalidFieldNameException; +use Doctrine\DBAL\Exception\LockWaitTimeoutException; +use Doctrine\DBAL\Exception\NonUniqueFieldNameException; +use Doctrine\DBAL\Exception\NotNullConstraintViolationException; +use Doctrine\DBAL\Exception\SyntaxErrorException; +use Doctrine\DBAL\Exception\TableExistsException; +use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\MySQL80Platform; @@ -26,47 +39,49 @@ abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver, /** * {@inheritdoc} * + * @deprecated + * * @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 */ - public function convertException($message, DriverException $exception) + public function convertException($message, DeprecatedDriverException $exception) { switch ($exception->getErrorCode()) { case '1213': - return new Exception\DeadlockException($message, $exception); + return new DeadlockException($message, $exception); case '1205': - return new Exception\LockWaitTimeoutException($message, $exception); + return new LockWaitTimeoutException($message, $exception); case '1050': - return new Exception\TableExistsException($message, $exception); + return new TableExistsException($message, $exception); case '1051': case '1146': - return new Exception\TableNotFoundException($message, $exception); + return new TableNotFoundException($message, $exception); case '1216': case '1217': case '1451': case '1452': case '1701': - return new Exception\ForeignKeyConstraintViolationException($message, $exception); + return new ForeignKeyConstraintViolationException($message, $exception); case '1062': case '1557': case '1569': case '1586': - return new Exception\UniqueConstraintViolationException($message, $exception); + return new UniqueConstraintViolationException($message, $exception); case '1054': case '1166': case '1611': - return new Exception\InvalidFieldNameException($message, $exception); + return new InvalidFieldNameException($message, $exception); case '1052': case '1060': case '1110': - return new Exception\NonUniqueFieldNameException($message, $exception); + return new NonUniqueFieldNameException($message, $exception); case '1064': case '1149': @@ -80,7 +95,7 @@ public function convertException($message, DriverException $exception) case '1541': case '1554': case '1626': - return new Exception\SyntaxErrorException($message, $exception); + return new SyntaxErrorException($message, $exception); case '1044': case '1045': @@ -94,7 +109,10 @@ public function convertException($message, DriverException $exception) case '1429': case '2002': case '2005': - return new Exception\ConnectionException($message, $exception); + return new ConnectionException($message, $exception); + + case '2006': + return new ConnectionLost($message, $exception); case '1048': case '1121': @@ -104,16 +122,16 @@ public function convertException($message, DriverException $exception) case '1263': case '1364': case '1566': - return new Exception\NotNullConstraintViolationException($message, $exception); + return new NotNullConstraintViolationException($message, $exception); } - return new Exception\DriverException($message, $exception); + return new DriverException($message, $exception); } /** * {@inheritdoc} * - * @throws DBALException + * @throws Exception */ public function createDatabasePlatformForVersion($version) { @@ -142,7 +160,7 @@ public function createDatabasePlatformForVersion($version) * * @param string $versionString Version string returned by the driver, i.e. '5.7.10' * - * @throws DBALException + * @throws Exception */ private function getOracleMysqlVersionNumber(string $versionString): string { @@ -153,7 +171,7 @@ private function getOracleMysqlVersionNumber(string $versionString): string $versionParts ) ) { - throw DBALException::invalidPlatformVersionSpecified( + throw Exception::invalidPlatformVersionSpecified( $versionString, '..' ); @@ -176,7 +194,7 @@ private function getOracleMysqlVersionNumber(string $versionString): string * * @param string $versionString Version string as returned by mariadb server, i.e. '5.5.5-Mariadb-10.0.8-xenial' * - * @throws DBALException + * @throws Exception */ private function getMariaDbMysqlVersionNumber(string $versionString): string { @@ -187,7 +205,7 @@ private function getMariaDbMysqlVersionNumber(string $versionString): string $versionParts ) ) { - throw DBALException::invalidPlatformVersionSpecified( + throw Exception::invalidPlatformVersionSpecified( $versionString, '^(?:5\.5\.5-)?(mariadb-)?..' ); @@ -198,6 +216,8 @@ private function getMariaDbMysqlVersionNumber(string $versionString): string /** * {@inheritdoc} + * + * @deprecated Use Connection::getDatabase() instead. */ public function getDatabase(Connection $conn) { diff --git a/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php b/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php index edbb3e8f8c..7172c70e5a 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php @@ -6,7 +6,17 @@ use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\AbstractOracleDriver\EasyConnectString; -use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException; +use Doctrine\DBAL\Exception\ConnectionException; +use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; +use Doctrine\DBAL\Exception\InvalidFieldNameException; +use Doctrine\DBAL\Exception\NonUniqueFieldNameException; +use Doctrine\DBAL\Exception\NotNullConstraintViolationException; +use Doctrine\DBAL\Exception\SyntaxErrorException; +use Doctrine\DBAL\Exception\TableExistsException; +use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Platforms\Oracle121Platform; use Doctrine\DBAL\Platforms\Oracle122Platform; use Doctrine\DBAL\Platforms\OraclePlatform; @@ -51,49 +61,53 @@ public function createDatabasePlatformForVersion($version) { /** * {@inheritdoc} + * + * @deprecated */ - public function convertException($message, DriverException $exception) + public function convertException($message, DeprecatedDriverException $exception) { switch ($exception->getErrorCode()) { case '1': case '2299': case '38911': - return new Exception\UniqueConstraintViolationException($message, $exception); + return new UniqueConstraintViolationException($message, $exception); case '904': - return new Exception\InvalidFieldNameException($message, $exception); + return new InvalidFieldNameException($message, $exception); case '918': case '960': - return new Exception\NonUniqueFieldNameException($message, $exception); + return new NonUniqueFieldNameException($message, $exception); case '923': - return new Exception\SyntaxErrorException($message, $exception); + return new SyntaxErrorException($message, $exception); case '942': - return new Exception\TableNotFoundException($message, $exception); + return new TableNotFoundException($message, $exception); case '955': - return new Exception\TableExistsException($message, $exception); + return new TableExistsException($message, $exception); case '1017': case '12545': - return new Exception\ConnectionException($message, $exception); + return new ConnectionException($message, $exception); case '1400': - return new Exception\NotNullConstraintViolationException($message, $exception); + return new NotNullConstraintViolationException($message, $exception); case '2266': case '2291': case '2292': - return new Exception\ForeignKeyConstraintViolationException($message, $exception); + return new ForeignKeyConstraintViolationException($message, $exception); } - return new Exception\DriverException($message, $exception); + return new DriverException($message, $exception); } /** * {@inheritdoc} + * + * @deprecated Use Connection::getDatabase() instead. */ public function getDatabase(Connection $conn) { diff --git a/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php index 1a33b6609f..b961025554 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php @@ -3,9 +3,20 @@ namespace Doctrine\DBAL\Driver; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Exception\ConnectionException; +use Doctrine\DBAL\Exception\DeadlockException; +use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; +use Doctrine\DBAL\Exception\InvalidFieldNameException; +use Doctrine\DBAL\Exception\NonUniqueFieldNameException; +use Doctrine\DBAL\Exception\NotNullConstraintViolationException; +use Doctrine\DBAL\Exception\SyntaxErrorException; +use Doctrine\DBAL\Exception\TableExistsException; +use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Platforms\PostgreSQL100Platform; use Doctrine\DBAL\Platforms\PostgreSQL91Platform; use Doctrine\DBAL\Platforms\PostgreSQL92Platform; @@ -27,49 +38,51 @@ abstract class AbstractPostgreSQLDriver implements Driver, ExceptionConverterDri /** * {@inheritdoc} * + * @deprecated + * * @link http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html */ - public function convertException($message, DriverException $exception) + public function convertException($message, DeprecatedDriverException $exception) { $sqlState = $exception->getSQLState(); switch ($sqlState) { case '40001': case '40P01': - return new Exception\DeadlockException($message, $exception); + return new DeadlockException($message, $exception); case '0A000': // Foreign key constraint violations during a TRUNCATE operation // are considered "feature not supported" in PostgreSQL. if (strpos($exception->getMessage(), 'truncate') !== false) { - return new Exception\ForeignKeyConstraintViolationException($message, $exception); + return new ForeignKeyConstraintViolationException($message, $exception); } break; case '23502': - return new Exception\NotNullConstraintViolationException($message, $exception); + return new NotNullConstraintViolationException($message, $exception); case '23503': - return new Exception\ForeignKeyConstraintViolationException($message, $exception); + return new ForeignKeyConstraintViolationException($message, $exception); case '23505': - return new Exception\UniqueConstraintViolationException($message, $exception); + return new UniqueConstraintViolationException($message, $exception); case '42601': - return new Exception\SyntaxErrorException($message, $exception); + return new SyntaxErrorException($message, $exception); case '42702': - return new Exception\NonUniqueFieldNameException($message, $exception); + return new NonUniqueFieldNameException($message, $exception); case '42703': - return new Exception\InvalidFieldNameException($message, $exception); + return new InvalidFieldNameException($message, $exception); case '42P01': - return new Exception\TableNotFoundException($message, $exception); + return new TableNotFoundException($message, $exception); case '42P07': - return new Exception\TableExistsException($message, $exception); + return new TableExistsException($message, $exception); case '08006': return new Exception\ConnectionException($message, $exception); @@ -80,13 +93,13 @@ public function convertException($message, DriverException $exception) // The exception code would be always set to 7 here. // We have to match against the SQLSTATE in the error message in these cases. if (strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) { - return new Exception\ConnectionException($message, $exception); + return new ConnectionException($message, $exception); } break; } - return new Exception\DriverException($message, $exception); + return new DriverException($message, $exception); } /** @@ -95,7 +108,7 @@ public function convertException($message, DriverException $exception) public function createDatabasePlatformForVersion($version) { if (! preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts)) { - throw DBALException::invalidPlatformVersionSpecified( + throw Exception::invalidPlatformVersionSpecified( $version, '..' ); @@ -122,6 +135,8 @@ public function createDatabasePlatformForVersion($version) /** * {@inheritdoc} + * + * @deprecated Use Connection::getDatabase() instead. */ public function getDatabase(Connection $conn) { diff --git a/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php b/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php index 5637f6bb98..7fc71870d5 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php @@ -3,9 +3,21 @@ namespace Doctrine\DBAL\Driver; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Exception\ConnectionException; +use Doctrine\DBAL\Exception\DeadlockException; +use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; +use Doctrine\DBAL\Exception\InvalidFieldNameException; +use Doctrine\DBAL\Exception\LockWaitTimeoutException; +use Doctrine\DBAL\Exception\NonUniqueFieldNameException; +use Doctrine\DBAL\Exception\NotNullConstraintViolationException; +use Doctrine\DBAL\Exception\SyntaxErrorException; +use Doctrine\DBAL\Exception\TableExistsException; +use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Platforms\SQLAnywhere11Platform; use Doctrine\DBAL\Platforms\SQLAnywhere12Platform; use Doctrine\DBAL\Platforms\SQLAnywhere16Platform; @@ -25,56 +37,58 @@ abstract class AbstractSQLAnywhereDriver implements Driver, ExceptionConverterDr /** * {@inheritdoc} * + * @deprecated + * * @link http://dcx.sybase.com/index.html#sa160/en/saerrors/sqlerror.html */ - public function convertException($message, DriverException $exception) + public function convertException($message, DeprecatedDriverException $exception) { switch ($exception->getErrorCode()) { case '-306': case '-307': case '-684': - return new Exception\DeadlockException($message, $exception); + return new DeadlockException($message, $exception); case '-210': case '-1175': case '-1281': - return new Exception\LockWaitTimeoutException($message, $exception); + return new LockWaitTimeoutException($message, $exception); case '-100': case '-103': case '-832': - return new Exception\ConnectionException($message, $exception); + return new ConnectionException($message, $exception); case '-143': - return new Exception\InvalidFieldNameException($message, $exception); + return new InvalidFieldNameException($message, $exception); case '-193': case '-196': - return new Exception\UniqueConstraintViolationException($message, $exception); + return new UniqueConstraintViolationException($message, $exception); case '-194': case '-198': - return new Exception\ForeignKeyConstraintViolationException($message, $exception); + return new ForeignKeyConstraintViolationException($message, $exception); case '-144': - return new Exception\NonUniqueFieldNameException($message, $exception); + return new NonUniqueFieldNameException($message, $exception); case '-184': case '-195': - return new Exception\NotNullConstraintViolationException($message, $exception); + return new NotNullConstraintViolationException($message, $exception); case '-131': - return new Exception\SyntaxErrorException($message, $exception); + return new SyntaxErrorException($message, $exception); case '-110': - return new Exception\TableExistsException($message, $exception); + return new TableExistsException($message, $exception); case '-141': case '-1041': - return new Exception\TableNotFoundException($message, $exception); + return new TableNotFoundException($message, $exception); } - return new Exception\DriverException($message, $exception); + return new DriverException($message, $exception); } /** @@ -89,7 +103,7 @@ public function createDatabasePlatformForVersion($version) $versionParts ) ) { - throw DBALException::invalidPlatformVersionSpecified( + throw Exception::invalidPlatformVersionSpecified( $version, '...' ); @@ -118,6 +132,8 @@ public function createDatabasePlatformForVersion($version) /** * {@inheritdoc} + * + * @deprecated Use Connection::getDatabase() instead. */ public function getDatabase(Connection $conn) { diff --git a/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php b/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php index ef8c4a563e..2550ec5a87 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php @@ -3,8 +3,10 @@ namespace Doctrine\DBAL\Driver; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\DriverException as TheDriverException; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Platforms\SQLServer2005Platform; use Doctrine\DBAL\Platforms\SQLServer2008Platform; use Doctrine\DBAL\Platforms\SQLServer2012Platform; @@ -33,7 +35,7 @@ public function createDatabasePlatformForVersion($version) $versionParts ) ) { - throw DBALException::invalidPlatformVersionSpecified( + throw Exception::invalidPlatformVersionSpecified( $version, '...' ); @@ -59,6 +61,8 @@ public function createDatabasePlatformForVersion($version) /** * {@inheritdoc} + * + * @deprecated Use Connection::getDatabase() instead. */ public function getDatabase(Connection $conn) { @@ -90,4 +94,14 @@ public function getSchemaManager(Connection $conn) { return new SQLServerSchemaManager($conn); } + + /** + * @param string $message + * + * @return DriverException + */ + public function convertException($message, TheDriverException $exception) + { + return new DriverException($message, $exception); + } } diff --git a/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver/PortWithoutHost.php b/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver/Exception/PortWithoutHost.php similarity index 82% rename from lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver/PortWithoutHost.php rename to lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver/Exception/PortWithoutHost.php index e968421fe7..c6dbf34bbd 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver/PortWithoutHost.php +++ b/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver/Exception/PortWithoutHost.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Doctrine\DBAL\Driver\AbstractSQLServerDriver; +namespace Doctrine\DBAL\Driver\AbstractSQLServerDriver\Exception; use Doctrine\DBAL\Driver\AbstractDriverException; diff --git a/lib/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php b/lib/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php index 0d2c72e09f..e14a2a76c9 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php @@ -4,7 +4,18 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver; -use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException; +use Doctrine\DBAL\Exception\ConnectionException; +use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Exception\InvalidFieldNameException; +use Doctrine\DBAL\Exception\LockWaitTimeoutException; +use Doctrine\DBAL\Exception\NonUniqueFieldNameException; +use Doctrine\DBAL\Exception\NotNullConstraintViolationException; +use Doctrine\DBAL\Exception\ReadOnlyException; +use Doctrine\DBAL\Exception\SyntaxErrorException; +use Doctrine\DBAL\Exception\TableExistsException; +use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\SqliteSchemaManager; @@ -18,12 +29,14 @@ abstract class AbstractSQLiteDriver implements Driver, ExceptionConverterDriver /** * {@inheritdoc} * + * @deprecated + * * @link http://www.sqlite.org/c3ref/c_abort.html */ - public function convertException($message, DriverException $exception) + public function convertException($message, DeprecatedDriverException $exception) { if (strpos($exception->getMessage(), 'database is locked') !== false) { - return new Exception\LockWaitTimeoutException($message, $exception); + return new LockWaitTimeoutException($message, $exception); } if ( @@ -32,49 +45,51 @@ public function convertException($message, DriverException $exception) strpos($exception->getMessage(), 'are not unique') !== false || strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false ) { - return new Exception\UniqueConstraintViolationException($message, $exception); + return new UniqueConstraintViolationException($message, $exception); } if ( strpos($exception->getMessage(), 'may not be NULL') !== false || strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false ) { - return new Exception\NotNullConstraintViolationException($message, $exception); + return new NotNullConstraintViolationException($message, $exception); } if (strpos($exception->getMessage(), 'no such table:') !== false) { - return new Exception\TableNotFoundException($message, $exception); + return new TableNotFoundException($message, $exception); } if (strpos($exception->getMessage(), 'already exists') !== false) { - return new Exception\TableExistsException($message, $exception); + return new TableExistsException($message, $exception); } if (strpos($exception->getMessage(), 'has no column named') !== false) { - return new Exception\InvalidFieldNameException($message, $exception); + return new InvalidFieldNameException($message, $exception); } if (strpos($exception->getMessage(), 'ambiguous column name') !== false) { - return new Exception\NonUniqueFieldNameException($message, $exception); + return new NonUniqueFieldNameException($message, $exception); } if (strpos($exception->getMessage(), 'syntax error') !== false) { - return new Exception\SyntaxErrorException($message, $exception); + return new SyntaxErrorException($message, $exception); } if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) { - return new Exception\ReadOnlyException($message, $exception); + return new ReadOnlyException($message, $exception); } if (strpos($exception->getMessage(), 'unable to open database file') !== false) { - return new Exception\ConnectionException($message, $exception); + return new ConnectionException($message, $exception); } - return new Exception\DriverException($message, $exception); + return new DriverException($message, $exception); } /** * {@inheritdoc} + * + * @deprecated Use Connection::getDatabase() instead. */ public function getDatabase(Connection $conn) { diff --git a/lib/Doctrine/DBAL/Driver/Connection.php b/lib/Doctrine/DBAL/Driver/Connection.php index cb4444dccd..8bd9f379f8 100644 --- a/lib/Doctrine/DBAL/Driver/Connection.php +++ b/lib/Doctrine/DBAL/Driver/Connection.php @@ -80,6 +80,8 @@ public function rollBack(); /** * Returns the error code associated with the last operation on the database handle. * + * @deprecated The error information is available via exceptions. + * * @return string|null The error code, or null if no operation has been run on the database handle. */ public function errorCode(); @@ -87,6 +89,8 @@ public function errorCode(); /** * Returns extended error information associated with the last operation on the database handle. * + * @deprecated The error information is available via exceptions. + * * @return mixed[] */ public function errorInfo(); diff --git a/lib/Doctrine/DBAL/Driver/DriverException.php b/lib/Doctrine/DBAL/Driver/DriverException.php index a7f4008e1b..2e83e82bee 100644 --- a/lib/Doctrine/DBAL/Driver/DriverException.php +++ b/lib/Doctrine/DBAL/Driver/DriverException.php @@ -2,34 +2,11 @@ namespace Doctrine\DBAL\Driver; -use Throwable; - /** - * Contract for a driver exception. - * - * Driver exceptions provide the SQLSTATE of the driver - * and the driver specific error code at the time the error occurred. + * @deprecated Use {@link Exception} instead * * @psalm-immutable */ -interface DriverException extends Throwable +interface DriverException extends Exception { - /** - * Returns the driver specific error code if available. - * - * Returns null if no driver specific error code is available - * for the error raised by the driver. - * - * @return int|string|null - */ - public function getErrorCode(); - - /** - * Returns the SQLSTATE the driver was in at the time the error occurred. - * - * Returns null if the driver does not provide a SQLSTATE for the error occurred. - * - * @return string|null - */ - public function getSQLState(); } diff --git a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php index 4089ab26e1..058bd19e2d 100644 --- a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php +++ b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php @@ -5,6 +5,9 @@ use Doctrine\DBAL\Driver\PDOConnection; use Doctrine\DBAL\ParameterType; +/** + * @deprecated + */ class Connection extends PDOConnection { /** diff --git a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php index c1b2110eb4..4c2c314c74 100644 --- a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php +++ b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php @@ -7,6 +7,8 @@ /** * Drizzle driver using PDO MySql. + * + * @deprecated */ class Driver extends \Doctrine\DBAL\Driver\PDOMySql\Driver { diff --git a/lib/Doctrine/DBAL/Driver/Exception.php b/lib/Doctrine/DBAL/Driver/Exception.php new file mode 100644 index 0000000000..a16db4f8ad --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/Exception.php @@ -0,0 +1,34 @@ +fetchNumeric(); + + if ($row === false) { + return false; + } + + return $row[0]; + } + + /** + * @return array> + * + * @throws Exception + */ + public static function fetchAllNumeric(Result $result): array + { + $rows = []; + + while (($row = $result->fetchNumeric()) !== false) { + $rows[] = $row; + } + + return $rows; + } + + /** + * @return array> + * + * @throws Exception + */ + public static function fetchAllAssociative(Result $result): array + { + $rows = []; + + while (($row = $result->fetchAssociative()) !== false) { + $rows[] = $row; + } + + return $rows; + } + + /** + * @return array + * + * @throws Exception + */ + public static function fetchFirstColumn(Result $result): array + { + $rows = []; + + while (($row = $result->fetchOne()) !== false) { + $rows[] = $row; + } + + return $rows; + } +} diff --git a/lib/Doctrine/DBAL/Driver/IBMDB2/Connection.php b/lib/Doctrine/DBAL/Driver/IBMDB2/Connection.php new file mode 100644 index 0000000000..b05475780f --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/Connection.php @@ -0,0 +1,7 @@ +conn = $conn; @@ -83,11 +91,12 @@ public function requiresQueryForServerVersion() public function prepare($sql) { $stmt = @db2_prepare($this->conn, $sql); - if (! $stmt) { - throw new DB2Exception(db2_stmt_errormsg()); + + if ($stmt === false) { + throw PrepareFailed::new(error_get_last()['message']); } - return new DB2Statement($stmt); + return new Statement($stmt); } /** @@ -125,7 +134,7 @@ public function exec($sql) $stmt = @db2_exec($this->conn, $sql); if ($stmt === false) { - throw new DB2Exception(db2_stmt_errormsg()); + throw ConnectionError::new($this->conn); } return db2_num_rows($stmt); @@ -156,7 +165,7 @@ public function beginTransaction() public function commit() { if (! db2_commit($this->conn)) { - throw new DB2Exception(db2_conn_errormsg($this->conn)); + throw ConnectionError::new($this->conn); } $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON); @@ -171,7 +180,7 @@ public function commit() public function rollBack() { if (! db2_rollback($this->conn)) { - throw new DB2Exception(db2_conn_errormsg($this->conn)); + throw ConnectionError::new($this->conn); } $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON); @@ -182,6 +191,8 @@ public function rollBack() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorCode() { @@ -190,6 +201,8 @@ public function errorCode() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorInfo() { diff --git a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php index 650d75ceac..056dbacd98 100644 --- a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php @@ -6,6 +6,8 @@ /** * IBM DB2 Driver. + * + * @deprecated Use {@link Driver} instead */ class DB2Driver extends AbstractDB2Driver { @@ -18,7 +20,7 @@ public function connect(array $params, $username = null, $password = null, array $params['password'] = $password; $params['dbname'] = DataSourceName::fromConnectionParameters($params)->toString(); - return new DB2Connection( + return new Connection( $params, (string) $username, (string) $password, diff --git a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php index 662d8533a8..aa9ee29c05 100644 --- a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php @@ -2,11 +2,13 @@ namespace Doctrine\DBAL\Driver\IBMDB2; -use Exception; +use Doctrine\DBAL\Driver\AbstractDriverException; /** + * @deprecated Use {@link Exception} instead + * * @psalm-immutable */ -class DB2Exception extends Exception +class DB2Exception extends AbstractDriverException { } diff --git a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php index ac9212e115..02cc652690 100644 --- a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php @@ -2,7 +2,13 @@ namespace Doctrine\DBAL\Driver\IBMDB2; -use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Driver\FetchUtils; +use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCopyStreamToStream; +use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCreateTemporaryFile; +use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotWriteToTemporaryFile; +use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError; +use Doctrine\DBAL\Driver\Result; +use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\Driver\StatementIterator; use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; @@ -50,7 +56,10 @@ use const DB2_PARAM_FILE; use const DB2_PARAM_IN; -class DB2Statement implements IteratorAggregate, Statement +/** + * @deprecated Use {@link Statement} instead + */ +class DB2Statement implements IteratorAggregate, StatementInterface, Result { /** @var resource */ private $stmt; @@ -83,6 +92,8 @@ class DB2Statement implements IteratorAggregate, Statement private $result = false; /** + * @internal The statement can be only instantiated by its driver connection. + * * @param resource $stmt */ public function __construct($stmt) @@ -145,12 +156,14 @@ private function bind($position, &$variable, int $parameterType, int $dataType): $this->bindParam[$position] =& $variable; if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) { - throw new DB2Exception(db2_stmt_errormsg()); + throw StatementError::new($this->stmt); } } /** * {@inheritdoc} + * + * @deprecated Use free() instead. */ public function closeCursor() { @@ -175,6 +188,8 @@ public function columnCount() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorCode() { @@ -183,6 +198,8 @@ public function errorCode() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorInfo() { @@ -226,7 +243,7 @@ public function execute($params = null) $this->lobs = []; if ($retval === false) { - throw new DB2Exception(db2_stmt_errormsg()); + throw StatementError::new($this->stmt); } $this->result = true; @@ -236,6 +253,8 @@ public function execute($params = null) /** * {@inheritdoc} + * + * @deprecated Use one of the fetch- or iterate-related methods. */ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) { @@ -248,6 +267,8 @@ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) /** * {@inheritdoc} + * + * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. */ public function getIterator() { @@ -256,6 +277,8 @@ public function getIterator() /** * {@inheritdoc} + * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. */ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) { @@ -307,6 +330,8 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} + * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { @@ -338,6 +363,8 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n /** * {@inheritdoc} + * + * @deprecated Use fetchOne() instead. */ public function fetchColumn($columnIndex = 0) { @@ -350,6 +377,64 @@ public function fetchColumn($columnIndex = 0) return $row[$columnIndex] ?? null; } + /** + * {@inheritDoc} + */ + public function fetchNumeric() + { + if (! $this->result) { + return false; + } + + return db2_fetch_array($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function fetchAssociative() + { + // do not try fetching from the statement if it's not expected to contain the result + // in order to prevent exceptional situation + if (! $this->result) { + return false; + } + + return db2_fetch_assoc($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + return FetchUtils::fetchAllNumeric($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + return FetchUtils::fetchAllAssociative($this); + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + /** * {@inheritdoc} */ @@ -358,6 +443,15 @@ public function rowCount() return @db2_num_rows($this->stmt) ? : 0; } + public function free(): void + { + $this->bindParam = []; + + db2_free_result($this->stmt); + + $this->result = false; + } + /** * Casts a stdClass object to the given class name mapping its' properties. * @@ -433,7 +527,7 @@ private function createTemporaryFile() $handle = @tmpfile(); if ($handle === false) { - throw new DB2Exception('Could not create temporary file: ' . error_get_last()['message']); + throw CannotCreateTemporaryFile::new(error_get_last()['message']); } return $handle; @@ -448,7 +542,7 @@ private function createTemporaryFile() private function copyStreamToStream($source, $target): void { if (@stream_copy_to_stream($source, $target) === false) { - throw new DB2Exception('Could not copy source stream to temporary file: ' . error_get_last()['message']); + throw CannotCopyStreamToStream::new(error_get_last()['message']); } } @@ -460,7 +554,7 @@ private function copyStreamToStream($source, $target): void private function writeStringToStream(string $string, $target): void { if (@fwrite($target, $string) === false) { - throw new DB2Exception('Could not write string to temporary file: ' . error_get_last()['message']); + throw CannotWriteToTemporaryFile::new(error_get_last()['message']); } } } diff --git a/lib/Doctrine/DBAL/Driver/IBMDB2/Driver.php b/lib/Doctrine/DBAL/Driver/IBMDB2/Driver.php new file mode 100644 index 0000000000..dcc84b32d0 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/Driver.php @@ -0,0 +1,9 @@ +error, $connection->sqlstate, $connection->errno); + } +} diff --git a/lib/Doctrine/DBAL/Driver/Mysqli/Exception/ConnectionFailed.php b/lib/Doctrine/DBAL/Driver/Mysqli/Exception/ConnectionFailed.php new file mode 100644 index 0000000000..f73e07c32f --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/Mysqli/Exception/ConnectionFailed.php @@ -0,0 +1,21 @@ +connect_error, 'HY000', $connection->connect_errno); + } +} diff --git a/lib/Doctrine/DBAL/Driver/Mysqli/Exception/FailedReadingStreamOffset.php b/lib/Doctrine/DBAL/Driver/Mysqli/Exception/FailedReadingStreamOffset.php new file mode 100644 index 0000000000..d3aaf8fbed --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/Mysqli/Exception/FailedReadingStreamOffset.php @@ -0,0 +1,22 @@ +error, $statement->sqlstate, $statement->errno); + } +} diff --git a/lib/Doctrine/DBAL/Driver/Mysqli/Exception/UnknownType.php b/lib/Doctrine/DBAL/Driver/Mysqli/Exception/UnknownType.php new file mode 100644 index 0000000000..0282b4d1b3 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/Mysqli/Exception/UnknownType.php @@ -0,0 +1,25 @@ +conn->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags)) { - throw new MysqliException( - $this->conn->connect_error, - $this->conn->sqlstate ?? 'HY000', - $this->conn->connect_errno - ); + throw ConnectionFailed::new($this->conn); } } finally { restore_error_handler(); @@ -134,7 +138,7 @@ public function requiresQueryForServerVersion() */ public function prepare($sql) { - return new MysqliStatement($this->conn, $sql); + return new Statement($this->conn, $sql); } /** @@ -164,7 +168,7 @@ public function quote($value, $type = ParameterType::STRING) public function exec($sql) { if ($this->conn->query($sql) === false) { - throw new MysqliException($this->conn->error, $this->conn->sqlstate, $this->conn->errno); + throw ConnectionError::new($this->conn); } return $this->conn->affected_rows; @@ -207,6 +211,8 @@ public function rollBack() /** * {@inheritdoc} * + * @deprecated The error information is available via exceptions. + * * @return int */ public function errorCode() @@ -217,6 +223,8 @@ public function errorCode() /** * {@inheritdoc} * + * @deprecated The error information is available via exceptions. + * * @return string */ public function errorInfo() @@ -237,15 +245,13 @@ private function setDriverOptions(array $driverOptions = []): void $supportedDriverOptions = [ MYSQLI_OPT_CONNECT_TIMEOUT, MYSQLI_OPT_LOCAL_INFILE, + MYSQLI_OPT_READ_TIMEOUT, MYSQLI_INIT_COMMAND, MYSQLI_READ_DEFAULT_FILE, MYSQLI_READ_DEFAULT_GROUP, + MYSQLI_SERVER_PUBLIC_KEY, ]; - if (defined('MYSQLI_SERVER_PUBLIC_KEY')) { - $supportedDriverOptions[] = MYSQLI_SERVER_PUBLIC_KEY; - } - $exceptionMsg = "%s option '%s' with value '%s'"; foreach ($driverOptions as $option => $value) { @@ -254,9 +260,7 @@ private function setDriverOptions(array $driverOptions = []): void } if (! in_array($option, $supportedDriverOptions, true)) { - throw new MysqliException( - sprintf($exceptionMsg, 'Unsupported', $option, $value) - ); + throw InvalidOption::fromOption($option, $value); } if (@mysqli_options($this->conn, $option, $value)) { @@ -277,6 +281,8 @@ private function setDriverOptions(array $driverOptions = []): void /** * Pings the server and re-connects when `mysqli.reconnect = 1` * + * @deprecated + * * @return bool */ public function ping() diff --git a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php index 8e8e697c4a..6fbff4291d 100644 --- a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php +++ b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php @@ -5,7 +5,7 @@ use Doctrine\DBAL\Driver\AbstractDriverException; /** - * Exception thrown in case the mysqli driver errors. + * @deprecated Use {@link Exception} instead * * @psalm-immutable */ diff --git a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php index 65da940333..c97f879cf8 100644 --- a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php +++ b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php @@ -2,7 +2,13 @@ namespace Doctrine\DBAL\Driver\Mysqli; -use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Driver\FetchUtils; +use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError; +use Doctrine\DBAL\Driver\Mysqli\Exception\FailedReadingStreamOffset; +use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError; +use Doctrine\DBAL\Driver\Mysqli\Exception\UnknownType; +use Doctrine\DBAL\Driver\Result; +use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\Driver\StatementIterator; use Doctrine\DBAL\Exception\InvalidArgumentException; use Doctrine\DBAL\FetchMode; @@ -25,10 +31,14 @@ use function sprintf; use function str_repeat; -class MysqliStatement implements IteratorAggregate, Statement +/** + * @deprecated Use {@link Statement} instead + */ +class MysqliStatement implements IteratorAggregate, StatementInterface, Result { /** @var string[] */ protected static $_paramTypeMap = [ + ParameterType::ASCII => 's', ParameterType::STRING => 's', ParameterType::BINARY => 's', ParameterType::BOOLEAN => 'i', @@ -73,6 +83,8 @@ class MysqliStatement implements IteratorAggregate, Statement private $result = false; /** + * @internal The statement can be only instantiated by its driver connection. + * * @param string $prepareString * * @throws MysqliException @@ -84,7 +96,7 @@ public function __construct(mysqli $conn, $prepareString) $stmt = $conn->prepare($prepareString); if ($stmt === false) { - throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno); + throw ConnectionError::new($this->_conn); } $this->_stmt = $stmt; @@ -106,7 +118,7 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le assert(is_int($param)); if (! isset(self::$_paramTypeMap[$type])) { - throw new MysqliException(sprintf("Unknown type: '%s'", $type)); + throw UnknownType::new($type); } $this->_bindedValues[$param] =& $variable; @@ -123,7 +135,7 @@ public function bindValue($param, $value, $type = ParameterType::STRING) assert(is_int($param)); if (! isset(self::$_paramTypeMap[$type])) { - throw new MysqliException(sprintf("Unknown type: '%s'", $type)); + throw UnknownType::new($type); } $this->_values[$param] = $value; @@ -141,7 +153,7 @@ public function execute($params = null) if ($this->_bindedValues !== null) { if ($params !== null) { if (! $this->bindUntypedValues($params)) { - throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + throw StatementError::new($this->_stmt); } } else { $this->bindTypedParameters(); @@ -149,7 +161,7 @@ public function execute($params = null) } if (! $this->_stmt->execute()) { - throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + throw StatementError::new($this->_stmt); } if ($this->_columnNames === null) { @@ -196,7 +208,7 @@ public function execute($params = null) } if (! $this->_stmt->bind_result(...$refs)) { - throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + throw StatementError::new($this->_stmt); } } @@ -240,7 +252,7 @@ private function bindTypedParameters(): void } if (! $this->_stmt->bind_param($types, ...$values)) { - throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + throw StatementError::new($this->_stmt); } $this->sendLongData($streams); @@ -260,11 +272,11 @@ private function sendLongData(array $streams): void $chunk = fread($stream, 8192); if ($chunk === false) { - throw new MysqliException("Failed reading the stream resource for parameter offset ${paramNr}."); + throw FailedReadingStreamOffset::new($paramNr); } if (! $this->_stmt->send_long_data($paramNr - 1, $chunk)) { - throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + throw StatementError::new($this->_stmt); } } } @@ -310,6 +322,8 @@ private function _fetch() /** * {@inheritdoc} + * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. */ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) { @@ -332,7 +346,7 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX } if ($values === false) { - throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + throw StatementError::new($this->_stmt); } if ($fetchMode === FetchMode::NUMERIC) { @@ -360,6 +374,8 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} + * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { @@ -382,6 +398,8 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n /** * {@inheritdoc} + * + * @deprecated Use fetchOne() instead. */ public function fetchColumn($columnIndex = 0) { @@ -394,6 +412,82 @@ public function fetchColumn($columnIndex = 0) return $row[$columnIndex] ?? null; } + /** + * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. + */ + public function fetchNumeric() + { + // do not try fetching from the statement if it's not expected to contain the result + // in order to prevent exceptional situation + if (! $this->result) { + return false; + } + + $values = $this->_fetch(); + + if ($values === null) { + return false; + } + + if ($values === false) { + throw StatementError::new($this->_stmt); + } + + return $values; + } + + /** + * {@inheritDoc} + */ + public function fetchAssociative() + { + $values = $this->fetchNumeric(); + + if ($values === false) { + return false; + } + + assert(is_array($this->_columnNames)); + $row = array_combine($this->_columnNames, $values); + assert(is_array($row)); + + return $row; + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + return FetchUtils::fetchAllNumeric($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + return FetchUtils::fetchAllAssociative($this); + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + /** * {@inheritdoc} */ @@ -405,6 +499,8 @@ public function errorCode() /** * {@inheritdoc} * + * @deprecated The error information is available via exceptions. + * * @return string */ public function errorInfo() @@ -414,11 +510,12 @@ public function errorInfo() /** * {@inheritdoc} + * + * @deprecated Use free() instead. */ public function closeCursor() { - $this->_stmt->free_result(); - $this->result = false; + $this->free(); return true; } @@ -443,8 +540,16 @@ public function columnCount() return $this->_stmt->field_count; } + public function free(): void + { + $this->_stmt->free_result(); + $this->result = false; + } + /** * {@inheritdoc} + * + * @deprecated Use one of the fetch- or iterate-related methods. */ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) { @@ -455,6 +560,8 @@ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) /** * {@inheritdoc} + * + * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. */ public function getIterator() { diff --git a/lib/Doctrine/DBAL/Driver/Mysqli/Statement.php b/lib/Doctrine/DBAL/Driver/Mysqli/Statement.php new file mode 100644 index 0000000000..bfd6ae9003 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/Mysqli/Statement.php @@ -0,0 +1,7 @@ +_constructDsn($params), @@ -32,7 +32,7 @@ public function connect(array $params, $username = null, $password = null, array $params['persistent'] ?? false ); } catch (OCI8Exception $e) { - throw DBALException::driverException($this, $e); + throw Exception::driverException($this, $e); } } diff --git a/lib/Doctrine/DBAL/Driver/OCI8/Exception/NonTerminatedStringLiteral.php b/lib/Doctrine/DBAL/Driver/OCI8/Exception/NonTerminatedStringLiteral.php new file mode 100644 index 0000000000..870e413697 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/OCI8/Exception/NonTerminatedStringLiteral.php @@ -0,0 +1,27 @@ +dbh, $sql, $this); + return new Statement($this->dbh, $sql, $this); } /** @@ -164,7 +169,7 @@ public function lastInsertId($name = null) $result = $stmt->fetchColumn(); if ($result === false) { - throw new OCI8Exception('lastInsertId failed: Query was executed but no result was returned.'); + throw SequenceDoesNotExist::new(); } return (int) $result; @@ -173,6 +178,8 @@ public function lastInsertId($name = null) /** * Returns the current execution mode. * + * @internal + * * @return int */ public function getExecuteMode() @@ -220,6 +227,8 @@ public function rollBack() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorCode() { @@ -234,6 +243,8 @@ public function errorCode() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorInfo() { diff --git a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php index 83b078097a..ab1449f3b5 100644 --- a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php +++ b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php @@ -5,6 +5,8 @@ use Doctrine\DBAL\Driver\AbstractDriverException; /** + * @deprecated Use {@link Exception} instead + * * @psalm-immutable */ class OCI8Exception extends AbstractDriverException diff --git a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php index 7237e763eb..3c34a8767c 100644 --- a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php +++ b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php @@ -2,7 +2,11 @@ namespace Doctrine\DBAL\Driver\OCI8; -use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Driver\FetchUtils; +use Doctrine\DBAL\Driver\OCI8\Exception\NonTerminatedStringLiteral; +use Doctrine\DBAL\Driver\OCI8\Exception\UnknownParameterIndex; +use Doctrine\DBAL\Driver\Result; +use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\Driver\StatementIterator; use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; @@ -29,7 +33,6 @@ use function oci_parse; use function preg_match; use function preg_quote; -use function sprintf; use function substr; use const OCI_ASSOC; @@ -48,8 +51,10 @@ /** * The OCI8 implementation of the Statement interface. + * + * @deprecated Use {@link Statement} instead */ -class OCI8Statement implements IteratorAggregate, Statement +class OCI8Statement implements IteratorAggregate, StatementInterface, Result { /** @var resource */ protected $_dbh; @@ -100,6 +105,8 @@ class OCI8Statement implements IteratorAggregate, Statement /** * Creates a new OCI8Statement that uses the given connection handle and SQL statement. * + * @internal The statement can be only instantiated by its driver connection. + * * @param resource $dbh The connection handle. * @param string $query The SQL query. */ @@ -128,6 +135,8 @@ public function __construct($dbh, $query, OCI8Connection $conn) * Question marks inside literal strings are therefore handled correctly by this method. * This comes at a cost, the whole sql statement has to be looped over. * + * @internal + * * @param string $statement The SQL statement to convert. * * @return mixed[] [0] => the statement value (string), [1] => the paramMap value (array). @@ -159,10 +168,7 @@ public static function convertPositionalToNamedPlaceholders($statement) } while ($result); if ($currentLiteralDelimiter) { - throw new OCI8Exception(sprintf( - 'The statement contains non-terminated string literal starting at offset %d', - $tokenOffset - 1 - )); + throw NonTerminatedStringLiteral::new($tokenOffset - 1); } $fragments[] = substr($statement, $fragmentOffset); @@ -284,9 +290,7 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le { if (is_int($param)) { if (! isset($this->_paramMap[$param])) { - throw new OCI8Exception( - sprintf('Could not find variable mapping with index %d, in the SQL statement', $param) - ); + throw UnknownParameterIndex::new($param); } $param = $this->_paramMap[$param]; @@ -333,17 +337,12 @@ private function convertParameterType(int $type): int /** * {@inheritdoc} + * + * @deprecated Use free() instead. */ public function closeCursor() { - // not having the result means there's nothing to close - if (! $this->result) { - return true; - } - - oci_cancel($this->_sth); - - $this->result = false; + $this->free(); return true; } @@ -358,6 +357,8 @@ public function columnCount() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorCode() { @@ -371,6 +372,8 @@ public function errorCode() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorInfo() { @@ -412,6 +415,8 @@ public function execute($params = null) /** * {@inheritdoc} + * + * @deprecated Use one of the fetch- or iterate-related methods. */ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) { @@ -422,6 +427,8 @@ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) /** * {@inheritdoc} + * + * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. */ public function getIterator() { @@ -430,6 +437,8 @@ public function getIterator() /** * {@inheritdoc} + * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. */ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) { @@ -461,6 +470,8 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} + * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { @@ -515,6 +526,8 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n /** * {@inheritdoc} + * + * @deprecated Use fetchOne() instead. */ public function fetchColumn($columnIndex = 0) { @@ -540,4 +553,103 @@ public function rowCount() { return oci_num_rows($this->_sth) ?: 0; } + + /** + * {@inheritdoc} + */ + public function fetchNumeric() + { + return $this->doFetch(OCI_NUM); + } + + /** + * {@inheritdoc} + */ + public function fetchAssociative() + { + return $this->doFetch(OCI_ASSOC); + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + return $this->doFetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_ROW); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + return $this->doFetchAll(OCI_ASSOC, OCI_FETCHSTATEMENT_BY_ROW); + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + return $this->doFetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_COLUMN)[0]; + } + + public function free(): void + { + // not having the result means there's nothing to close + if (! $this->result) { + return; + } + + oci_cancel($this->_sth); + + $this->result = false; + } + + /** + * @return mixed|false + */ + private function doFetch(int $mode) + { + // do not try fetching from the statement if it's not expected to contain the result + // in order to prevent exceptional situation + if (! $this->result) { + return false; + } + + return oci_fetch_array( + $this->_sth, + $mode | OCI_RETURN_NULLS | OCI_RETURN_LOBS + ); + } + + /** + * @return array + */ + private function doFetchAll(int $mode, int $fetchStructure): array + { + // do not try fetching from the statement if it's not expected to contain the result + // in order to prevent exceptional situation + if (! $this->result) { + return []; + } + + oci_fetch_all( + $this->_sth, + $result, + 0, + -1, + $mode | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS + ); + + return $result; + } } diff --git a/lib/Doctrine/DBAL/Driver/OCI8/Statement.php b/lib/Doctrine/DBAL/Driver/OCI8/Statement.php new file mode 100644 index 0000000000..2cab1e59c8 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/OCI8/Statement.php @@ -0,0 +1,7 @@ +setAttribute(PDO::ATTR_STATEMENT_CLASS, [PDOStatement::class, []]); + $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, [Statement::class, []]); $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } @@ -42,8 +51,8 @@ public function exec($sql) assert($result !== false); return $result; - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } @@ -59,24 +68,24 @@ public function getServerVersion() * @param string $sql * @param array $driverOptions * - * @return \PDOStatement + * @return PDOStatement */ public function prepare($sql, $driverOptions = []) { try { $statement = parent::prepare($sql, $driverOptions); - assert($statement instanceof \PDOStatement); + assert($statement instanceof PDOStatement); return $statement; - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } /** * {@inheritdoc} * - * @return \PDOStatement + * @return PDOStatement */ public function query() { @@ -84,11 +93,11 @@ public function query() try { $stmt = parent::query(...$args); - assert($stmt instanceof \PDOStatement); + assert($stmt instanceof PDOStatement); return $stmt; - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } @@ -103,8 +112,8 @@ public function lastInsertId($name = null) } return parent::lastInsertId($name); - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } diff --git a/lib/Doctrine/DBAL/Driver/PDOException.php b/lib/Doctrine/DBAL/Driver/PDOException.php index c2571032b8..b2c01eb443 100644 --- a/lib/Doctrine/DBAL/Driver/PDOException.php +++ b/lib/Doctrine/DBAL/Driver/PDOException.php @@ -2,8 +2,10 @@ namespace Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\PDO\Exception; + /** - * Tiny wrapper for PDOException instances to implement the {@link DriverException} interface. + * @deprecated Use {@link Exception} instead * * @psalm-immutable */ diff --git a/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php b/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php index 7ea26d4d4c..0543d8786e 100644 --- a/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php +++ b/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php @@ -3,10 +3,12 @@ namespace Doctrine\DBAL\Driver\PDOIbm; use Doctrine\DBAL\Driver\AbstractDB2Driver; -use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\Driver\PDO\Connection; /** * Driver for the PDO IBM extension. + * + * @deprecated Use the driver based on the ibm_db2 extension instead. */ class Driver extends AbstractDB2Driver { @@ -15,7 +17,7 @@ class Driver extends AbstractDB2Driver */ public function connect(array $params, $username = null, $password = null, array $driverOptions = []) { - return new PDOConnection( + return new Connection( $this->_constructPdoDsn($params), $username, $password, diff --git a/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php b/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php index 3058febd05..25eb2fbdb6 100644 --- a/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php +++ b/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php @@ -2,13 +2,15 @@ namespace Doctrine\DBAL\Driver\PDOMySql; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver\AbstractMySQLDriver; -use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\Driver\PDO; +use Doctrine\DBAL\Exception; use PDOException; /** * PDO MySql driver. + * + * @deprecated Use {@link PDO\MySQL\Driver} instead. */ class Driver extends AbstractMySQLDriver { @@ -18,14 +20,14 @@ class Driver extends AbstractMySQLDriver public function connect(array $params, $username = null, $password = null, array $driverOptions = []) { try { - $conn = new PDOConnection( + $conn = new PDO\Connection( $this->constructPdoDsn($params), $username, $password, $driverOptions ); } catch (PDOException $e) { - throw DBALException::driverException($this, $e); + throw Exception::driverException($this, $e); } return $conn; diff --git a/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php b/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php index f1239eafbd..b6792996c7 100644 --- a/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php +++ b/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php @@ -2,9 +2,9 @@ namespace Doctrine\DBAL\Driver\PDOOracle; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver\AbstractOracleDriver; -use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\Driver\PDO; +use Doctrine\DBAL\Exception; use PDOException; /** @@ -14,6 +14,8 @@ * stuff. PDO Oracle is not maintained by Oracle or anyone in the PHP community, * which leads us to the recommendation to use the "oci8" driver to connect * to Oracle instead. + * + * @deprecated Use {@link PDO\OCI\Driver} instead. */ class Driver extends AbstractOracleDriver { @@ -23,14 +25,14 @@ class Driver extends AbstractOracleDriver public function connect(array $params, $username = null, $password = null, array $driverOptions = []) { try { - return new PDOConnection( + return new PDO\Connection( $this->constructPdoDsn($params), $username, $password, $driverOptions ); } catch (PDOException $e) { - throw DBALException::driverException($this, $e); + throw Exception::driverException($this, $e); } } diff --git a/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php b/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php index 43f06cb7b7..83076aa8c0 100644 --- a/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php +++ b/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php @@ -2,16 +2,17 @@ namespace Doctrine\DBAL\Driver\PDOPgSql; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver; -use Doctrine\DBAL\Driver\PDOConnection; -use PDO; +use Doctrine\DBAL\Driver\PDO; +use Doctrine\DBAL\Exception; use PDOException; use function defined; /** * Driver that connects through pdo_pgsql. + * + * @deprecated Use {@link PDO\PgSQL\Driver} instead. */ class Driver extends AbstractPostgreSQLDriver { @@ -21,7 +22,7 @@ class Driver extends AbstractPostgreSQLDriver public function connect(array $params, $username = null, $password = null, array $driverOptions = []) { try { - $pdo = new PDOConnection( + $pdo = new PDO\Connection( $this->_constructPdoDsn($params), $username, $password, @@ -30,11 +31,11 @@ public function connect(array $params, $username = null, $password = null, array if ( defined('PDO::PGSQL_ATTR_DISABLE_PREPARES') - && (! isset($driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES]) - || $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] === true + && (! isset($driverOptions[\PDO::PGSQL_ATTR_DISABLE_PREPARES]) + || $driverOptions[\PDO::PGSQL_ATTR_DISABLE_PREPARES] === true ) ) { - $pdo->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true); + $pdo->setAttribute(\PDO::PGSQL_ATTR_DISABLE_PREPARES, true); } /* defining client_encoding via SET NAMES to avoid inconsistent DSN support @@ -47,7 +48,7 @@ public function connect(array $params, $username = null, $password = null, array return $pdo; } catch (PDOException $e) { - throw DBALException::driverException($this, $e); + throw Exception::driverException($this, $e); } } diff --git a/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php b/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php index 4566f64957..fc2b881ffc 100644 --- a/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php +++ b/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php @@ -2,9 +2,9 @@ namespace Doctrine\DBAL\Driver\PDOSqlite; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver\AbstractSQLiteDriver; -use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\Driver\PDO; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\SqlitePlatform; use PDOException; @@ -12,6 +12,8 @@ /** * The PDO Sqlite driver. + * + * @deprecated Use {@link PDO\SQLite\Driver} instead. */ class Driver extends AbstractSQLiteDriver { @@ -36,14 +38,14 @@ public function connect(array $params, $username = null, $password = null, array } try { - $pdo = new PDOConnection( + $pdo = new PDO\Connection( $this->_constructPdoDsn($params), $username, $password, $driverOptions ); } catch (PDOException $ex) { - throw DBALException::driverException($this, $ex); + throw Exception::driverException($this, $ex); } foreach ($this->_userDefinedFunctions as $fn => $data) { diff --git a/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php b/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php index 4790db626d..238b8155be 100644 --- a/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php +++ b/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php @@ -2,21 +2,25 @@ namespace Doctrine\DBAL\Driver\PDOSqlsrv; -use Doctrine\DBAL\Driver\PDOConnection; -use PDO; +use Doctrine\DBAL\Driver\PDO; +use Doctrine\DBAL\Driver\Result; /** * Sqlsrv Connection implementation. + * + * @deprecated Use {@link PDO\SQLSrv\Connection} instead. */ -class Connection extends PDOConnection +class Connection extends PDO\Connection { /** + * @internal The connection can be only instantiated by its driver. + * * {@inheritdoc} */ public function __construct($dsn, $user = null, $password = null, ?array $options = null) { parent::__construct($dsn, $user, $password, $options); - $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, [Statement::class, []]); + $this->setAttribute(\PDO::ATTR_STATEMENT_CLASS, [PDO\SQLSrv\Statement::class, []]); } /** @@ -31,6 +35,10 @@ public function lastInsertId($name = null) $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?'); $stmt->execute([$name]); + if ($stmt instanceof Result) { + return $stmt->fetchOne(); + } + return $stmt->fetchColumn(); } } diff --git a/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php b/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php index 880f1263da..d8f2250556 100644 --- a/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php +++ b/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php @@ -3,13 +3,16 @@ namespace Doctrine\DBAL\Driver\PDOSqlsrv; use Doctrine\DBAL\Driver\AbstractSQLServerDriver; -use Doctrine\DBAL\Driver\AbstractSQLServerDriver\PortWithoutHost; +use Doctrine\DBAL\Driver\AbstractSQLServerDriver\Exception\PortWithoutHost; +use Doctrine\DBAL\Driver\PDO; use function is_int; use function sprintf; /** * The PDO-based Sqlsrv driver. + * + * @deprecated Use {@link PDO\SQLSrv\Driver} instead. */ class Driver extends AbstractSQLServerDriver { @@ -28,7 +31,7 @@ public function connect(array $params, $username = null, $password = null, array } } - return new Connection( + return new PDO\SQLSrv\Connection( $this->_constructPdoDsn($params, $dsnOptions), $username, $password, diff --git a/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php b/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php index fe58c75295..5669ccc270 100644 --- a/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php +++ b/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php @@ -2,25 +2,35 @@ namespace Doctrine\DBAL\Driver\PDOSqlsrv; -use Doctrine\DBAL\Driver\PDOStatement; +use Doctrine\DBAL\Driver\PDO; use Doctrine\DBAL\ParameterType; -use PDO; /** * PDO SQL Server Statement + * + * @deprecated Use {@link PDO\SQLSrv\Statement} instead. */ -class Statement extends PDOStatement +class Statement extends PDO\Statement { /** * {@inheritdoc} */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null) { - if ( - ($type === ParameterType::LARGE_OBJECT || $type === ParameterType::BINARY) - && $driverOptions === null - ) { - $driverOptions = PDO::SQLSRV_ENCODING_BINARY; + switch ($type) { + case ParameterType::LARGE_OBJECT: + case ParameterType::BINARY: + if ($driverOptions === null) { + $driverOptions = \PDO::SQLSRV_ENCODING_BINARY; + } + + break; + + case ParameterType::ASCII: + $type = ParameterType::STRING; + $length = 0; + $driverOptions = \PDO::SQLSRV_ENCODING_SYSTEM; + break; } return parent::bindParam($param, $variable, $type, $length, $driverOptions); diff --git a/lib/Doctrine/DBAL/Driver/PDOStatement.php b/lib/Doctrine/DBAL/Driver/PDOStatement.php index 158f7c923a..4a244ab461 100644 --- a/lib/Doctrine/DBAL/Driver/PDOStatement.php +++ b/lib/Doctrine/DBAL/Driver/PDOStatement.php @@ -2,9 +2,12 @@ namespace Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\PDO\Exception; +use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; use PDO; +use PDOException; use function array_slice; use function assert; @@ -18,13 +21,16 @@ /** * The PDO implementation of the Statement interface. * Used by all PDO-based drivers. + * + * @deprecated Use {@link Statement} instead */ -class PDOStatement extends \PDOStatement implements Statement +class PDOStatement extends \PDOStatement implements StatementInterface, Result { private const PARAM_TYPE_MAP = [ ParameterType::NULL => PDO::PARAM_NULL, ParameterType::INTEGER => PDO::PARAM_INT, ParameterType::STRING => PDO::PARAM_STR, + ParameterType::ASCII => PDO::PARAM_STR, ParameterType::BINARY => PDO::PARAM_LOB, ParameterType::LARGE_OBJECT => PDO::PARAM_LOB, ParameterType::BOOLEAN => PDO::PARAM_BOOL, @@ -41,6 +47,8 @@ class PDOStatement extends \PDOStatement implements Statement /** * Protected constructor. + * + * @internal The statement can be only instantiated by its driver connection. */ protected function __construct() { @@ -48,6 +56,8 @@ protected function __construct() /** * {@inheritdoc} + * + * @deprecated Use one of the fetch- or iterate-related methods. */ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) { @@ -67,8 +77,8 @@ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) } return parent::setFetchMode($fetchMode, $arg2, $arg3); - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } @@ -81,8 +91,8 @@ public function bindValue($param, $value, $type = ParameterType::STRING) try { return parent::bindValue($param, $value, $type); - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } @@ -101,19 +111,21 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le try { return parent::bindParam($param, $variable, $type, ...array_slice(func_get_args(), 3)); - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } /** * {@inheritdoc} + * + * @deprecated Use free() instead. */ public function closeCursor() { try { return parent::closeCursor(); - } catch (\PDOException $exception) { + } catch (PDOException $exception) { // Exceptions not allowed by the interface. // In case driver implementations do not adhere to the interface, silence exceptions here. return true; @@ -127,13 +139,15 @@ public function execute($params = null) { try { return parent::execute($params); - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } /** * {@inheritdoc} + * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. */ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) { @@ -145,13 +159,15 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX try { return parent::fetch(...$args); - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } /** * {@inheritdoc} + * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { @@ -176,23 +192,78 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n assert(is_array($data)); return $data; - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } /** * {@inheritdoc} + * + * @deprecated Use fetchOne() instead. */ public function fetchColumn($columnIndex = 0) { try { return parent::fetchColumn($columnIndex); - } catch (\PDOException $exception) { - throw new PDOException($exception); + } catch (PDOException $exception) { + throw Exception::new($exception); } } + /** + * {@inheritdoc} + */ + public function fetchNumeric() + { + return $this->fetch(PDO::FETCH_NUM); + } + + /** + * {@inheritdoc} + */ + public function fetchAssociative() + { + return $this->fetch(PDO::FETCH_ASSOC); + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + return $this->fetch(PDO::FETCH_COLUMN); + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + return $this->fetchAll(PDO::FETCH_NUM); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + return $this->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + return $this->fetchAll(PDO::FETCH_COLUMN); + } + + public function free(): void + { + parent::closeCursor(); + } + /** * Converts DBAL parameter type to PDO parameter type * diff --git a/lib/Doctrine/DBAL/Driver/PingableConnection.php b/lib/Doctrine/DBAL/Driver/PingableConnection.php index 06bfb9a7f2..42202e7ab8 100644 --- a/lib/Doctrine/DBAL/Driver/PingableConnection.php +++ b/lib/Doctrine/DBAL/Driver/PingableConnection.php @@ -4,6 +4,8 @@ /** * An interface for connections which support a "native" ping method. + * + * @deprecated */ interface PingableConnection { diff --git a/lib/Doctrine/DBAL/Driver/Result.php b/lib/Doctrine/DBAL/Driver/Result.php new file mode 100644 index 0000000000..a52f0563e4 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/Result.php @@ -0,0 +1,89 @@ +|false + * + * @throws Exception + */ + public function fetchNumeric(); + + /** + * Returns the next row of the result as an associative array or FALSE if there are no more rows. + * + * @return array|false + * + * @throws Exception + */ + public function fetchAssociative(); + + /** + * Returns the first value of the next row of the result or FALSE if there are no more rows. + * + * @return mixed|false + * + * @throws Exception + */ + public function fetchOne(); + + /** + * Returns an array containing all of the result rows represented as numeric arrays. + * + * @return array> + * + * @throws Exception + */ + public function fetchAllNumeric(): array; + + /** + * Returns an array containing all of the result rows represented as associative arrays. + * + * @return array> + * + * @throws Exception + */ + public function fetchAllAssociative(): array; + + /** + * Returns an array containing the values of the first column of the result. + * + * @return array + * + * @throws Exception + */ + public function fetchFirstColumn(): array; + + /** + * Returns the number of rows affected by the DELETE, INSERT, or UPDATE statement that produced the result. + * + * If the statement executed a SELECT query or a similar platform-specific SQL (e.g. DESCRIBE, SHOW, etc.), + * some database drivers may return the number of rows returned by that query. However, this behaviour + * is not guaranteed for all drivers and should not be relied on in portable applications. + * + * @return int The number of rows. + */ + public function rowCount(); + + /** + * Returns the number of columns in the result + * + * @return int The number of columns in the result. If the columns cannot be counted, + * this method must return 0. + */ + public function columnCount(); + + /** + * Discards the non-fetched portion of the result, enabling the originating statement to be executed again. + */ + public function free(): void; +} diff --git a/lib/Doctrine/DBAL/Driver/ResultStatement.php b/lib/Doctrine/DBAL/Driver/ResultStatement.php index 68c7e2783b..f31f960f5e 100644 --- a/lib/Doctrine/DBAL/Driver/ResultStatement.php +++ b/lib/Doctrine/DBAL/Driver/ResultStatement.php @@ -13,6 +13,8 @@ interface ResultStatement extends Traversable /** * Closes the cursor, enabling the statement to be executed again. * + * @deprecated Use Result::free() instead. + * * @return bool TRUE on success or FALSE on failure. */ public function closeCursor(); @@ -29,6 +31,8 @@ public function columnCount(); /** * Sets the fetch mode to use while iterating this statement. * + * @deprecated Use one of the fetch- or iterate-related methods. + * * @param int $fetchMode The fetch mode must be one of the {@link FetchMode} constants. * @param mixed $arg2 * @param mixed $arg3 @@ -40,6 +44,8 @@ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null); /** * Returns the next row of a result set. * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. + * * @param int|null $fetchMode Controls how the next row will be returned to the caller. * The value must be one of the {@link FetchMode} constants, * defaulting to {@link FetchMode::MIXED}. @@ -67,6 +73,8 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * Returns an array containing all of the result set rows. * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. + * * @param int|null $fetchMode Controls how the next row will be returned to the caller. * The value must be one of the {@link FetchMode} constants, * defaulting to {@link FetchMode::MIXED}. @@ -91,6 +99,8 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n /** * Returns a single column from the next row of a result set or FALSE if there are no more rows. * + * @deprecated Use fetchOne() instead. + * * @param int $columnIndex 0-indexed number of the column you wish to retrieve from the row. * If no value is supplied, fetches the first column. * diff --git a/lib/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php b/lib/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php index bc29466e1a..95b59274e9 100644 --- a/lib/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php +++ b/lib/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php @@ -2,8 +2,8 @@ namespace Doctrine\DBAL\Driver\SQLAnywhere; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver\AbstractSQLAnywhereDriver; +use Doctrine\DBAL\Exception; use function array_keys; use function array_map; @@ -17,7 +17,7 @@ class Driver extends AbstractSQLAnywhereDriver /** * {@inheritdoc} * - * @throws DBALException If there was a problem establishing the connection. + * @throws Exception If there was a problem establishing the connection. */ public function connect(array $params, $username = null, $password = null, array $driverOptions = []) { @@ -35,7 +35,7 @@ public function connect(array $params, $username = null, $password = null, array $params['persistent'] ?? false ); } catch (SQLAnywhereException $e) { - throw DBALException::driverException($this, $e); + throw Exception::driverException($this, $e); } } diff --git a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php index cd464b01c6..ae5b13f4f5 100644 --- a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php +++ b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL\Driver\SQLAnywhere; use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\Result; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\ParameterType; @@ -35,6 +36,8 @@ class SQLAnywhereConnection implements Connection, ServerInfoAwareConnection /** * Connects to database with given connection string. * + * @internal The connection can be only instantiated by its driver. + * * @param string $dsn The connection string. * @param bool $persistent Whether or not to establish a persistent connection. * @@ -91,6 +94,8 @@ public function commit() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorCode() { @@ -99,6 +104,8 @@ public function errorCode() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorInfo() { @@ -122,7 +129,13 @@ public function exec($sql) */ public function getServerVersion() { - $version = $this->query("SELECT PROPERTY('ProductVersion')")->fetchColumn(); + $stmt = $this->query("SELECT PROPERTY('ProductVersion')"); + + if ($stmt instanceof Result) { + $version = $stmt->fetchOne(); + } else { + $version = $stmt->fetchColumn(); + } assert(is_string($version)); @@ -138,7 +151,13 @@ public function lastInsertId($name = null) return sasql_insert_id($this->connection); } - return $this->query('SELECT ' . $name . '.CURRVAL')->fetchColumn(); + $stmt = $this->query('SELECT ' . $name . '.CURRVAL'); + + if ($stmt instanceof Result) { + return $stmt->fetchOne(); + } + + return $stmt->fetchColumn(); } /** diff --git a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php index fe96fe2592..1542276dba 100644 --- a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php +++ b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php @@ -2,6 +2,9 @@ namespace Doctrine\DBAL\Driver\SQLAnywhere; +use Doctrine\DBAL\Driver\Exception; +use Doctrine\DBAL\Driver\FetchUtils; +use Doctrine\DBAL\Driver\Result; use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\Driver\StatementIterator; use Doctrine\DBAL\FetchMode; @@ -42,7 +45,7 @@ /** * SAP SQL Anywhere implementation of the Statement interface. */ -class SQLAnywhereStatement implements IteratorAggregate, Statement +class SQLAnywhereStatement implements IteratorAggregate, Statement, Result { /** @var resource The connection resource. */ private $conn; @@ -68,6 +71,8 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement /** * Prepares given statement for given connection. * + * @internal The statement can be only instantiated by its driver connection. + * * @param resource $conn The connection resource to use. * @param string $sql The SQL statement to prepare. * @@ -138,6 +143,8 @@ public function bindValue($param, $value, $type = ParameterType::STRING) /** * {@inheritdoc} * + * @deprecated Use free() instead. + * * @throws SQLAnywhereException */ public function closeCursor() @@ -159,6 +166,8 @@ public function columnCount() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorCode() { @@ -167,6 +176,8 @@ public function errorCode() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorInfo() { @@ -204,6 +215,8 @@ public function execute($params = null) /** * {@inheritdoc} * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. + * * @throws SQLAnywhereException */ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) @@ -255,6 +268,8 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} + * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { @@ -286,6 +301,8 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n /** * {@inheritdoc} + * + * @deprecated Use fetchOne() instead. */ public function fetchColumn($columnIndex = 0) { @@ -300,12 +317,78 @@ public function fetchColumn($columnIndex = 0) /** * {@inheritdoc} + * + * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. */ public function getIterator() { return new StatementIterator($this); } + /** + * {@inheritDoc} + */ + public function fetchNumeric() + { + if (! is_resource($this->result)) { + return false; + } + + return sasql_fetch_row($this->result); + } + + /** + * {@inheritdoc} + */ + public function fetchAssociative() + { + if (! is_resource($this->result)) { + return false; + } + + return sasql_fetch_assoc($this->result); + } + + /** + * {@inheritdoc} + * + * @throws Exception + */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** + * @return array> + * + * @throws Exception + */ + public function fetchAllNumeric(): array + { + return FetchUtils::fetchAllNumeric($this); + } + + /** + * @return array> + * + * @throws Exception + */ + public function fetchAllAssociative(): array + { + return FetchUtils::fetchAllAssociative($this); + } + + /** + * @return array + * + * @throws Exception + */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + /** * {@inheritdoc} */ @@ -314,8 +397,15 @@ public function rowCount() return sasql_stmt_affected_rows($this->stmt); } + public function free(): void + { + sasql_stmt_reset($this->stmt); + } + /** * {@inheritdoc} + * + * @deprecated Use one of the fetch- or iterate-related methods. */ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) { diff --git a/lib/Doctrine/DBAL/Driver/SQLSrv/Connection.php b/lib/Doctrine/DBAL/Driver/SQLSrv/Connection.php new file mode 100644 index 0000000000..6e009b5db3 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/SQLSrv/Connection.php @@ -0,0 +1,7 @@ +conn = $conn; @@ -79,7 +85,7 @@ public function requiresQueryForServerVersion() */ public function prepare($sql) { - return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId); + return new Statement($this->conn, $sql, $this->lastInsertId); } /** @@ -119,13 +125,13 @@ public function exec($sql) $stmt = sqlsrv_query($this->conn, $sql); if ($stmt === false) { - throw SQLSrvException::fromSqlSrvErrors(); + throw Error::new(); } $rowsAffected = sqlsrv_rows_affected($stmt); if ($rowsAffected === false) { - throw SQLSrvException::fromSqlSrvErrors(); + throw Error::new(); } return $rowsAffected; @@ -143,6 +149,10 @@ public function lastInsertId($name = null) $stmt = $this->query('SELECT @@IDENTITY'); } + if ($stmt instanceof Result) { + return $stmt->fetchOne(); + } + return $stmt->fetchColumn(); } @@ -152,7 +162,7 @@ public function lastInsertId($name = null) public function beginTransaction() { if (! sqlsrv_begin_transaction($this->conn)) { - throw SQLSrvException::fromSqlSrvErrors(); + throw Error::new(); } return true; @@ -164,7 +174,7 @@ public function beginTransaction() public function commit() { if (! sqlsrv_commit($this->conn)) { - throw SQLSrvException::fromSqlSrvErrors(); + throw Error::new(); } return true; @@ -176,7 +186,7 @@ public function commit() public function rollBack() { if (! sqlsrv_rollback($this->conn)) { - throw SQLSrvException::fromSqlSrvErrors(); + throw Error::new(); } return true; @@ -184,6 +194,8 @@ public function rollBack() /** * {@inheritDoc} + * + * @deprecated The error information is available via exceptions. */ public function errorCode() { @@ -197,6 +209,8 @@ public function errorCode() /** * {@inheritDoc} + * + * @deprecated The error information is available via exceptions. */ public function errorInfo() { diff --git a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php index af70c6852f..03300800e8 100644 --- a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php +++ b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php @@ -3,13 +3,11 @@ namespace Doctrine\DBAL\Driver\SQLSrv; use Doctrine\DBAL\Driver\AbstractDriverException; - -use function rtrim; -use function sqlsrv_errors; - -use const SQLSRV_ERR_ERRORS; +use Doctrine\DBAL\Driver\SQLSrv\Exception\Error; /** + * @deprecated Use {@link Exception} instead + * * @psalm-immutable */ class SQLSrvException extends AbstractDriverException @@ -21,28 +19,6 @@ class SQLSrvException extends AbstractDriverException */ public static function fromSqlSrvErrors() { - $message = ''; - $sqlState = null; - $errorCode = null; - - foreach ((array) sqlsrv_errors(SQLSRV_ERR_ERRORS) as $error) { - $message .= 'SQLSTATE [' . $error['SQLSTATE'] . ', ' . $error['code'] . ']: ' . $error['message'] . "\n"; - - if ($sqlState === null) { - $sqlState = $error['SQLSTATE']; - } - - if ($errorCode !== null) { - continue; - } - - $errorCode = $error['code']; - } - - if (! $message) { - $message = 'SQL Server error occurred but no error message was retrieved from driver.'; - } - - return new self(rtrim($message), $sqlState, $errorCode); + return Error::new(); } } diff --git a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php index ed30f9adc5..af98476e1a 100644 --- a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php +++ b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php @@ -2,7 +2,10 @@ namespace Doctrine\DBAL\Driver\SQLSrv; -use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Driver\FetchUtils; +use Doctrine\DBAL\Driver\Result; +use Doctrine\DBAL\Driver\SQLSrv\Exception\Error; +use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\Driver\StatementIterator; use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; @@ -31,6 +34,7 @@ use function stripos; use const SQLSRV_ENC_BINARY; +use const SQLSRV_ENC_CHAR; use const SQLSRV_ERR_ERRORS; use const SQLSRV_FETCH_ASSOC; use const SQLSRV_FETCH_BOTH; @@ -39,8 +43,10 @@ /** * SQL Server Statement. + * + * @deprecated Use {@link Statement} instead */ -class SQLSrvStatement implements IteratorAggregate, Statement +class SQLSrvStatement implements IteratorAggregate, StatementInterface, Result { /** * The SQLSRV Resource. @@ -131,6 +137,8 @@ class SQLSrvStatement implements IteratorAggregate, Statement public const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;'; /** + * @internal The statement can be only instantiated by its driver connection. + * * @param resource $conn * @param string $sql */ @@ -186,22 +194,12 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le /** * {@inheritdoc} + * + * @deprecated Use free() instead. */ public function closeCursor() { - // not having the result means there's nothing to close - if ($this->stmt === null || ! $this->result) { - return true; - } - - // emulate it by fetching and discarding rows, similarly to what PDO does in this case - // @link http://php.net/manual/en/pdostatement.closecursor.php - // @link https://github.com/php/php-src/blob/php-7.0.11/ext/pdo/pdo_stmt.c#L2075 - // deliberately do not consider multiple result sets, since doctrine/dbal doesn't support them - while (sqlsrv_fetch($this->stmt)) { - } - - $this->result = false; + $this->free(); return true; } @@ -220,6 +218,8 @@ public function columnCount() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorCode() { @@ -233,6 +233,8 @@ public function errorCode() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorInfo() { @@ -261,7 +263,7 @@ public function execute($params = null) } if (! sqlsrv_execute($this->stmt)) { - throw SQLSrvException::fromSqlSrvErrors(); + throw Error::new(); } if ($this->lastInsertId) { @@ -305,6 +307,14 @@ private function prepare() ]; break; + case ParameterType::ASCII: + $params[$column - 1] = [ + &$variable, + SQLSRV_PARAM_IN, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), + ]; + break; + default: $params[$column - 1] =& $variable; break; @@ -314,7 +324,7 @@ private function prepare() $stmt = sqlsrv_prepare($this->conn, $this->sql, $params); if (! $stmt) { - throw SQLSrvException::fromSqlSrvErrors(); + throw Error::new(); } return $stmt; @@ -322,6 +332,8 @@ private function prepare() /** * {@inheritdoc} + * + * @deprecated Use one of the fetch- or iterate-related methods. */ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) { @@ -334,6 +346,8 @@ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) /** * {@inheritdoc} + * + * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. */ public function getIterator() { @@ -343,6 +357,8 @@ public function getIterator() /** * {@inheritdoc} * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. + * * @throws SQLSrvException */ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) @@ -381,6 +397,8 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} + * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { @@ -412,6 +430,8 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n /** * {@inheritdoc} + * + * @deprecated Use fetchOne() instead. */ public function fetchColumn($columnIndex = 0) { @@ -424,6 +444,54 @@ public function fetchColumn($columnIndex = 0) return $row[$columnIndex] ?? null; } + /** + * {@inheritdoc} + */ + public function fetchNumeric() + { + return $this->doFetch(SQLSRV_FETCH_NUMERIC); + } + + /** + * {@inheritdoc} + */ + public function fetchAssociative() + { + return $this->doFetch(SQLSRV_FETCH_ASSOC); + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + return FetchUtils::fetchAllNumeric($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + return FetchUtils::fetchAllAssociative($this); + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + /** * {@inheritdoc} */ @@ -435,4 +503,35 @@ public function rowCount() return sqlsrv_rows_affected($this->stmt) ?: 0; } + + public function free(): void + { + // not having the result means there's nothing to close + if ($this->stmt === null || ! $this->result) { + return; + } + + // emulate it by fetching and discarding rows, similarly to what PDO does in this case + // @link http://php.net/manual/en/pdostatement.closecursor.php + // @link https://github.com/php/php-src/blob/php-7.0.11/ext/pdo/pdo_stmt.c#L2075 + // deliberately do not consider multiple result sets, since doctrine/dbal doesn't support them + while (sqlsrv_fetch($this->stmt)) { + } + + $this->result = false; + } + + /** + * @return mixed|false + */ + private function doFetch(int $fetchType) + { + // do not try fetching from the statement if it's not expected to contain the result + // in order to prevent exceptional situation + if ($this->stmt === null || ! $this->result) { + return false; + } + + return sqlsrv_fetch_array($this->stmt, $fetchType) ?? false; + } } diff --git a/lib/Doctrine/DBAL/Driver/SQLSrv/Statement.php b/lib/Doctrine/DBAL/Driver/SQLSrv/Statement.php new file mode 100644 index 0000000000..6a96b87c93 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/SQLSrv/Statement.php @@ -0,0 +1,7 @@ + PDOMySQLDriver::class, - 'pdo_sqlite' => PDOSQLiteDriver::class, - 'pdo_pgsql' => PDOPgSQLDriver::class, - 'pdo_oci' => PDOOCIDriver::class, - 'oci8' => OCI8Driver::class, - 'ibm_db2' => DB2Driver::class, - 'pdo_sqlsrv' => PDOSQLSrvDriver::class, - 'mysqli' => MySQLiDriver::class, - 'drizzle_pdo_mysql' => DrizzlePDOMySQLDriver::class, - 'sqlanywhere' => SQLAnywhereDriver::class, - 'sqlsrv' => SQLSrvDriver::class, + 'pdo_mysql' => PDO\MySQL\Driver::class, + 'pdo_sqlite' => PDO\SQLite\Driver::class, + 'pdo_pgsql' => PDO\PgSQL\Driver::class, + 'pdo_oci' => PDO\OCI\Driver::class, + 'oci8' => OCI8\Driver::class, + 'ibm_db2' => IBMDB2\Driver::class, + 'pdo_sqlsrv' => PDO\SQLSrv\Driver::class, + 'mysqli' => Mysqli\Driver::class, + 'drizzle_pdo_mysql' => DrizzlePDOMySql\Driver::class, + 'sqlanywhere' => SQLAnywhere\Driver::class, + 'sqlsrv' => SQLSrv\Driver::class, ]; /** @@ -122,7 +117,7 @@ private function __construct() * @param Configuration|null $config The configuration to use. * @param EventManager|null $eventManager The event manager to use. * - * @throws DBALException + * @throws Exception * * @phpstan-param mixed[] $params * @psalm-return ($params is array{wrapperClass:mixed} ? T : Connection) @@ -144,17 +139,29 @@ public static function getConnection( $params = self::parseDatabaseUrl($params); - // URL support for MasterSlaveConnection + // @todo: deprecated, notice thrown by connection constructor if (isset($params['master'])) { $params['master'] = self::parseDatabaseUrl($params['master']); } + // @todo: deprecated, notice thrown by connection constructor if (isset($params['slaves'])) { foreach ($params['slaves'] as $key => $slaveParams) { $params['slaves'][$key] = self::parseDatabaseUrl($slaveParams); } } + // URL support for PrimaryReplicaConnection + if (isset($params['primary'])) { + $params['primary'] = self::parseDatabaseUrl($params['primary']); + } + + if (isset($params['replica'])) { + foreach ($params['replica'] as $key => $replicaParams) { + $params['replica'][$key] = self::parseDatabaseUrl($replicaParams); + } + } + // URL support for PoolingShardConnection if (isset($params['global'])) { $params['global'] = self::parseDatabaseUrl($params['global']); @@ -167,13 +174,13 @@ public static function getConnection( } // check for existing pdo object - if (isset($params['pdo']) && ! $params['pdo'] instanceof PDO) { - throw DBALException::invalidPdoInstance(); + if (isset($params['pdo']) && ! $params['pdo'] instanceof \PDO) { + throw Exception::invalidPdoInstance(); } if (isset($params['pdo'])) { - $params['pdo']->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $params['driver'] = 'pdo_' . $params['pdo']->getAttribute(PDO::ATTR_DRIVER_NAME); + $params['pdo']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $params['driver'] = 'pdo_' . $params['pdo']->getAttribute(\PDO::ATTR_DRIVER_NAME); } else { self::_checkParams($params); } @@ -185,7 +192,7 @@ public static function getConnection( $wrapperClass = Connection::class; if (isset($params['wrapperClass'])) { if (! is_subclass_of($params['wrapperClass'], $wrapperClass)) { - throw DBALException::invalidWrapperClass($params['wrapperClass']); + throw Exception::invalidWrapperClass($params['wrapperClass']); } $wrapperClass = $params['wrapperClass']; @@ -209,7 +216,7 @@ public static function getAvailableDrivers(): array * * @param mixed[] $params The list of parameters. * - * @throws DBALException + * @throws Exception */ private static function _checkParams(array $params): void { @@ -217,21 +224,21 @@ private static function _checkParams(array $params): void // driver if (! isset($params['driver']) && ! isset($params['driverClass'])) { - throw DBALException::driverRequired(); + throw Exception::driverRequired(); } // check validity of parameters // driver if (isset($params['driver']) && ! isset(self::$_driverMap[$params['driver']])) { - throw DBALException::unknownDriver($params['driver'], array_keys(self::$_driverMap)); + throw Exception::unknownDriver($params['driver'], array_keys(self::$_driverMap)); } if ( isset($params['driverClass']) && ! in_array(Driver::class, class_implements($params['driverClass'], true)) ) { - throw DBALException::invalidDriverClass($params['driverClass']); + throw Exception::invalidDriverClass($params['driverClass']); } } @@ -255,7 +262,7 @@ private static function normalizeDatabaseUrlPath(string $urlPath): string * @return mixed[] A modified list of parameters with info from a database * URL extracted into indidivual parameter parts. * - * @throws DBALException + * @throws Exception */ private static function parseDatabaseUrl(array $params): array { @@ -270,7 +277,7 @@ private static function parseDatabaseUrl(array $params): array $url = parse_url($url); if ($url === false) { - throw new DBALException('Malformed parameter "url".'); + throw new Exception('Malformed parameter "url".'); } $url = array_map('rawurldecode', $url); @@ -410,7 +417,7 @@ private static function parseSqliteDatabaseUrlPath(array $url, array $params): a * * @return mixed[] The resolved connection parameters. * - * @throws DBALException If parsing failed or resolution is not possible. + * @throws Exception If parsing failed or resolution is not possible. */ private static function parseDatabaseUrlScheme(array $url, array $params): array { @@ -435,7 +442,7 @@ private static function parseDatabaseUrlScheme(array $url, array $params): array // If a schemeless connection URL is given, we require a default driver or default custom driver // as connection parameter. if (! isset($params['driverClass']) && ! isset($params['driver'])) { - throw DBALException::driverRequired($params['url']); + throw Exception::driverRequired($params['url']); } return $params; diff --git a/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php b/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php index 9e72290405..8748dd1747 100644 --- a/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php +++ b/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php @@ -45,7 +45,7 @@ public function __construct($charset = 'utf8', $collation = false) public function postConnect(ConnectionEventArgs $args) { $collation = $this->collation ? ' COLLATE ' . $this->collation : ''; - $args->getConnection()->executeUpdate('SET NAMES ' . $this->charset . $collation); + $args->getConnection()->executeStatement('SET NAMES ' . $this->charset . $collation); } /** diff --git a/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php b/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php index 3e382f72c6..a907716f8e 100644 --- a/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php +++ b/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php @@ -61,7 +61,7 @@ public function postConnect(ConnectionEventArgs $args) } $sql = 'ALTER SESSION SET ' . implode(' ', $vars); - $args->getConnection()->executeUpdate($sql); + $args->getConnection()->executeStatement($sql); } /** diff --git a/lib/Doctrine/DBAL/Exception.php b/lib/Doctrine/DBAL/Exception.php new file mode 100644 index 0000000000..34442da6a8 --- /dev/null +++ b/lib/Doctrine/DBAL/Exception.php @@ -0,0 +1,10 @@ +driverException = $driverException; } diff --git a/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php b/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php index 5d20332af0..0b06140740 100644 --- a/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php +++ b/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php @@ -2,14 +2,14 @@ namespace Doctrine\DBAL\Exception; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; /** * Exception to be thrown when invalid arguments are passed to any DBAL API * * @psalm-immutable */ -class InvalidArgumentException extends DBALException +class InvalidArgumentException extends Exception { /** * @return self diff --git a/lib/Doctrine/DBAL/FetchMode.php b/lib/Doctrine/DBAL/FetchMode.php index 4c20e05bc2..bef473f5e7 100644 --- a/lib/Doctrine/DBAL/FetchMode.php +++ b/lib/Doctrine/DBAL/FetchMode.php @@ -6,6 +6,8 @@ /** * Contains statement fetch modes. + * + * @deprecated Use one of the fetch- or iterate-related methods on the Statement. */ final class FetchMode { diff --git a/lib/Doctrine/DBAL/Id/TableGenerator.php b/lib/Doctrine/DBAL/Id/TableGenerator.php index 01a131bdf0..c17caadae2 100644 --- a/lib/Doctrine/DBAL/Id/TableGenerator.php +++ b/lib/Doctrine/DBAL/Id/TableGenerator.php @@ -3,9 +3,9 @@ namespace Doctrine\DBAL\Id; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; use Doctrine\DBAL\DriverManager; -use Doctrine\DBAL\FetchMode; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\LockMode; use Throwable; @@ -66,16 +66,19 @@ class TableGenerator /** * @param string $generatorTableName * - * @throws DBALException + * @throws Exception */ public function __construct(Connection $conn, $generatorTableName = 'sequences') { - $params = $conn->getParams(); - if ($params['driver'] === 'pdo_sqlite') { - throw new DBALException('Cannot use TableGenerator with SQLite.'); + if ($conn->getDriver() instanceof Driver\PDOSqlite\Driver) { + throw new Exception('Cannot use TableGenerator with SQLite.'); } - $this->conn = DriverManager::getConnection($params, $conn->getConfiguration(), $conn->getEventManager()); + $this->conn = DriverManager::getConnection( + $conn->getParams(), + $conn->getConfiguration(), + $conn->getEventManager() + ); $this->generatorTableName = $generatorTableName; } @@ -87,7 +90,7 @@ public function __construct(Connection $conn, $generatorTableName = 'sequences') * * @return int * - * @throws DBALException + * @throws Exception */ public function nextValue($sequence) { @@ -108,8 +111,7 @@ public function nextValue($sequence) $sql = 'SELECT sequence_value, sequence_increment_by' . ' FROM ' . $platform->appendLockHint($this->generatorTableName, LockMode::PESSIMISTIC_WRITE) . ' WHERE sequence_name = ? ' . $platform->getWriteLockSQL(); - $stmt = $this->conn->executeQuery($sql, [$sequence]); - $row = $stmt->fetch(FetchMode::ASSOCIATIVE); + $row = $this->conn->fetchAssociative($sql, [$sequence]); if ($row !== false) { $row = array_change_key_case($row, CASE_LOWER); @@ -129,10 +131,10 @@ public function nextValue($sequence) $sql = 'UPDATE ' . $this->generatorTableName . ' ' . 'SET sequence_value = sequence_value + sequence_increment_by ' . 'WHERE sequence_name = ? AND sequence_value = ?'; - $rows = $this->conn->executeUpdate($sql, [$sequence, $row['sequence_value']]); + $rows = $this->conn->executeStatement($sql, [$sequence, $row['sequence_value']]); if ($rows !== 1) { - throw new DBALException('Race-condition detected while updating sequence. Aborting generation'); + throw new Exception('Race-condition detected while updating sequence. Aborting generation'); } } else { $this->conn->insert( @@ -146,7 +148,7 @@ public function nextValue($sequence) } catch (Throwable $e) { $this->conn->rollBack(); - throw new DBALException( + throw new Exception( 'Error occurred while generating ID with TableGenerator, aborted generation: ' . $e->getMessage(), 0, $e diff --git a/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php b/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php index a3228d3df9..9499d2ce64 100644 --- a/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php +++ b/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php @@ -8,6 +8,8 @@ /** * A SQL logger that logs to the standard output using echo/var_dump. + * + * @deprecated */ class EchoSQLLogger implements SQLLogger { diff --git a/lib/Doctrine/DBAL/ParameterType.php b/lib/Doctrine/DBAL/ParameterType.php index 564adfce34..2c4c3ad18c 100644 --- a/lib/Doctrine/DBAL/ParameterType.php +++ b/lib/Doctrine/DBAL/ParameterType.php @@ -49,6 +49,11 @@ final class ParameterType */ public const BINARY = 16; + /** + * Represents an ASCII string data type + */ + public const ASCII = 17; + /** * This class cannot be instantiated. * diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index 728e4d8d52..ba55157465 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -3,7 +3,6 @@ namespace Doctrine\DBAL\Platforms; use Doctrine\Common\EventManager; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs; use Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs; use Doctrine\DBAL\Event\SchemaAlterTableEventArgs; @@ -13,6 +12,7 @@ use Doctrine\DBAL\Event\SchemaCreateTableEventArgs; use Doctrine\DBAL\Event\SchemaDropTableEventArgs; use Doctrine\DBAL\Events; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\Keywords\KeywordList; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\ColumnDiff; @@ -245,6 +245,17 @@ private function initializeAllDoctrineTypeMappings() } } + /** + * Returns the SQL snippet used to declare a column that can + * store characters in the ASCII character set + * + * @param mixed[] $column + */ + public function getAsciiStringTypeDeclarationSQL(array $column): string + { + return $this->getVarcharTypeDeclarationSQL($column); + } + /** * Returns the SQL snippet used to declare a VARCHAR column type. * @@ -343,11 +354,11 @@ public function getJsonTypeDeclarationSQL(array $column) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) { - throw DBALException::notSupported('VARCHARs not supported by Platform.'); + throw Exception::notSupported('VARCHARs not supported by Platform.'); } /** @@ -358,11 +369,11 @@ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) { - throw DBALException::notSupported('BINARY/VARBINARY column types are not supported by this platform.'); + throw Exception::notSupported('BINARY/VARBINARY column types are not supported by this platform.'); } /** @@ -398,7 +409,7 @@ abstract public function getName(); * * @return void * - * @throws DBALException If the type is not found. + * @throws Exception If the type is not found. */ public function registerDoctrineTypeMapping($dbType, $doctrineType) { @@ -407,7 +418,7 @@ public function registerDoctrineTypeMapping($dbType, $doctrineType) } if (! Types\Type::hasType($doctrineType)) { - throw DBALException::typeNotFound($doctrineType); + throw Exception::typeNotFound($doctrineType); } $dbType = strtolower($dbType); @@ -429,7 +440,7 @@ public function registerDoctrineTypeMapping($dbType, $doctrineType) * * @return string * - * @throws DBALException + * @throws Exception */ public function getDoctrineTypeMapping($dbType) { @@ -440,7 +451,7 @@ public function getDoctrineTypeMapping($dbType) $dbType = strtolower($dbType); if (! isset($this->doctrineTypeMapping[$dbType])) { - throw new DBALException( + throw new Exception( 'Unknown database type ' . $dbType . ' requested, ' . static::class . ' may not support it.' ); } @@ -639,11 +650,11 @@ public function getWildcards() * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getRegexpExpression() { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -653,11 +664,11 @@ public function getRegexpExpression() * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getGuidExpression() { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -885,11 +896,11 @@ public function getLowerExpression($str) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getLocateExpression($str, $substr, $startPos = false) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -1056,11 +1067,11 @@ public function getCosExpression($value) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateDiffExpression($date1, $date2) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -1071,7 +1082,7 @@ public function getDateDiffExpression($date1, $date2) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateAddSecondsExpression($date, $seconds) { @@ -1086,7 +1097,7 @@ public function getDateAddSecondsExpression($date, $seconds) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateSubSecondsExpression($date, $seconds) { @@ -1101,7 +1112,7 @@ public function getDateSubSecondsExpression($date, $seconds) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateAddMinutesExpression($date, $minutes) { @@ -1116,7 +1127,7 @@ public function getDateAddMinutesExpression($date, $minutes) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateSubMinutesExpression($date, $minutes) { @@ -1131,7 +1142,7 @@ public function getDateSubMinutesExpression($date, $minutes) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateAddHourExpression($date, $hours) { @@ -1146,7 +1157,7 @@ public function getDateAddHourExpression($date, $hours) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateSubHourExpression($date, $hours) { @@ -1161,7 +1172,7 @@ public function getDateSubHourExpression($date, $hours) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateAddDaysExpression($date, $days) { @@ -1176,7 +1187,7 @@ public function getDateAddDaysExpression($date, $days) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateSubDaysExpression($date, $days) { @@ -1191,7 +1202,7 @@ public function getDateSubDaysExpression($date, $days) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateAddWeeksExpression($date, $weeks) { @@ -1206,7 +1217,7 @@ public function getDateAddWeeksExpression($date, $weeks) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateSubWeeksExpression($date, $weeks) { @@ -1221,7 +1232,7 @@ public function getDateSubWeeksExpression($date, $weeks) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateAddMonthExpression($date, $months) { @@ -1236,7 +1247,7 @@ public function getDateAddMonthExpression($date, $months) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateSubMonthExpression($date, $months) { @@ -1251,7 +1262,7 @@ public function getDateSubMonthExpression($date, $months) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateAddQuartersExpression($date, $quarters) { @@ -1266,7 +1277,7 @@ public function getDateAddQuartersExpression($date, $quarters) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateSubQuartersExpression($date, $quarters) { @@ -1281,7 +1292,7 @@ public function getDateSubQuartersExpression($date, $quarters) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateAddYearsExpression($date, $years) { @@ -1296,7 +1307,7 @@ public function getDateAddYearsExpression($date, $years) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateSubYearsExpression($date, $years) { @@ -1314,11 +1325,11 @@ public function getDateSubYearsExpression($date, $years) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -1541,7 +1552,7 @@ public function getDropForeignKeySQL($foreignKey, $table) * * @return string[] The sequence of SQL statements. * - * @throws DBALException + * @throws Exception * @throws InvalidArgumentException */ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES) @@ -1553,7 +1564,7 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE } if (count($table->getColumns()) === 0) { - throw DBALException::noColumnsSpecifiedForTable($table->getName()); + throw Exception::noColumnsSpecifiedForTable($table->getName()); } $tableName = $table->getQuotedName($this); @@ -1682,12 +1693,12 @@ public function getCommentOnColumnSQL($tableName, $columnName, $comment) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getInlineColumnCommentSQL($comment) { if (! $this->supportsInlineColumnComments()) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } return 'COMMENT ' . $this->quoteStringLiteral($comment); @@ -1755,11 +1766,11 @@ public function getCreateTemporaryTableSnippetSQL() * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getCreateSequenceSQL(Sequence $sequence) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -1767,11 +1778,11 @@ public function getCreateSequenceSQL(Sequence $sequence) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getAlterSequenceSQL(Sequence $sequence) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -1895,11 +1906,11 @@ public function getCreatePrimaryKeySQL(Index $index, $table) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getCreateSchemaSQL($schemaName) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -1964,11 +1975,11 @@ public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) * * @return string[] * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getAlterTableSQL(TableDiff $diff) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2651,6 +2662,8 @@ public function getColumnCollationDeclarationSQL($collation) * Whether the platform prefers sequences for ID generation. * Subclasses should override this method to return TRUE if they prefer sequences. * + * @deprecated + * * @return bool */ public function prefersSequences() @@ -2791,11 +2804,11 @@ protected function _getTransactionIsolationLevelSQL($level) /** * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getListDatabasesSQL() { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2803,11 +2816,11 @@ public function getListDatabasesSQL() * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getListNamespacesSQL() { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2815,11 +2828,11 @@ public function getListNamespacesSQL() * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getListSequencesSQL($database) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2827,11 +2840,11 @@ public function getListSequencesSQL($database) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getListTableConstraintsSQL($table) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2840,31 +2853,31 @@ public function getListTableConstraintsSQL($table) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getListTableColumnsSQL($table, $database = null) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getListTablesSQL() { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getListUsersSQL() { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2874,11 +2887,11 @@ public function getListUsersSQL() * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getListViewsSQL($database) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2896,11 +2909,11 @@ public function getListViewsSQL($database) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getListTableIndexesSQL($table, $database = null) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2908,11 +2921,11 @@ public function getListTableIndexesSQL($table, $database = null) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getListTableForeignKeysSQL($table) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2921,11 +2934,11 @@ public function getListTableForeignKeysSQL($table) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getCreateViewSQL($name, $sql) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2933,11 +2946,11 @@ public function getCreateViewSQL($name, $sql) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDropViewSQL($name) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2947,11 +2960,11 @@ public function getDropViewSQL($name) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDropSequenceSQL($sequence) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2959,11 +2972,11 @@ public function getDropSequenceSQL($sequence) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getSequenceNextValSQL($sequence) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2973,11 +2986,11 @@ public function getSequenceNextValSQL($sequence) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getCreateDatabaseSQL($database) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -2987,11 +3000,11 @@ public function getCreateDatabaseSQL($database) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getSetTransactionIsolationSQL($level) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -3002,11 +3015,11 @@ public function getSetTransactionIsolationSQL($level) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateTimeTypeDeclarationSQL(array $column) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -3029,11 +3042,11 @@ public function getDateTimeTzTypeDeclarationSQL(array $column) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDateTypeDeclarationSQL(array $column) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -3044,11 +3057,11 @@ public function getDateTypeDeclarationSQL(array $column) * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getTimeTypeDeclarationSQL(array $column) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -3122,11 +3135,11 @@ public function usesSequenceEmulatedIdentityColumns() * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getIdentitySequenceName($tableName, $columnName) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -3220,6 +3233,8 @@ public function supportsForeignKeyConstraints() /** * Whether this platform supports onUpdate in foreign key constraints. * + * @deprecated + * * @return bool */ public function supportsForeignKeyOnUpdate() @@ -3255,11 +3270,11 @@ public function canEmulateSchemas() * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ public function getDefaultSchemaName() { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -3409,7 +3424,7 @@ public function getTimeFormatString() * * @return string * - * @throws DBALException + * @throws Exception */ final public function modifyLimitQuery($query, $limit, $offset = null) { @@ -3420,14 +3435,14 @@ final public function modifyLimitQuery($query, $limit, $offset = null) $offset = (int) $offset; if ($offset < 0) { - throw new DBALException(sprintf( + throw new Exception(sprintf( 'Offset must be a positive integer or zero, %d given', $offset )); } if ($offset > 0 && ! $this->supportsLimitOffset()) { - throw new DBALException(sprintf( + throw new Exception(sprintf( 'Platform %s does not support offset values in limit queries.', $this->getName() )); @@ -3471,6 +3486,8 @@ public function supportsLimitOffset() /** * Gets the character casing of a column in an SQL result set of this platform. * + * @deprecated + * * @param string $column The column name for which to get the correct character casing. * * @return string The column name in the character casing used in SQL result sets. @@ -3484,6 +3501,8 @@ public function getSQLResultCasing($column) * Makes any fixes to a name of a schema element (table, sequence, ...) that are required * by restrictions of the platform, like a maximum length. * + * @deprecated + * * @param string $schemaElementName * * @return string @@ -3587,7 +3606,7 @@ public function rollbackSavePoint($savepoint) * * @return KeywordList * - * @throws DBALException If no keyword list is specified. + * @throws Exception If no keyword list is specified. */ final public function getReservedKeywordsList() { @@ -3599,7 +3618,7 @@ final public function getReservedKeywordsList() $class = $this->getReservedKeywordsClass(); $keywords = new $class(); if (! $keywords instanceof KeywordList) { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } // Store the instance so it doesn't need to be generated on every request. @@ -3613,11 +3632,11 @@ final public function getReservedKeywordsList() * * @return string * - * @throws DBALException If not supported on this platform. + * @throws Exception If not supported on this platform. */ protected function getReservedKeywordsClass() { - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** diff --git a/lib/Doctrine/DBAL/Platforms/DB2Platform.php b/lib/Doctrine/DBAL/Platforms/DB2Platform.php index 877273bc64..3d75866e19 100644 --- a/lib/Doctrine/DBAL/Platforms/DB2Platform.php +++ b/lib/Doctrine/DBAL/Platforms/DB2Platform.php @@ -2,7 +2,7 @@ namespace Doctrine\DBAL\Platforms; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\ColumnDiff; use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; @@ -481,7 +481,7 @@ public function getCurrentTimestampSQL() public function getIndexDeclarationSQL($name, Index $index) { // Index declaration in statements like CREATE TABLE is not supported. - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** @@ -864,6 +864,8 @@ public function prefersIdentityColumns() * {@inheritDoc} * * DB2 returns all column names in SQL result sets in uppercase. + * + * @deprecated */ public function getSQLResultCasing($column) { diff --git a/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php b/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php index 199a14a232..6417698870 100644 --- a/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php @@ -24,6 +24,8 @@ /** * Drizzle platform + * + * @deprecated */ class DrizzlePlatform extends AbstractPlatform { diff --git a/lib/Doctrine/DBAL/Platforms/OraclePlatform.php b/lib/Doctrine/DBAL/Platforms/OraclePlatform.php index 2a0ceb9e90..9d2e44cbb8 100644 --- a/lib/Doctrine/DBAL/Platforms/OraclePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/OraclePlatform.php @@ -2,7 +2,7 @@ namespace Doctrine\DBAL\Platforms; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; @@ -40,12 +40,12 @@ class OraclePlatform extends AbstractPlatform * * @return void * - * @throws DBALException + * @throws Exception */ public static function assertValidIdentifier($identifier) { if (! preg_match('(^(([a-zA-Z]{1}[a-zA-Z0-9_$#]{0,})|("[^"]+"))$)', $identifier)) { - throw new DBALException('Invalid Oracle identifier'); + throw new Exception('Invalid Oracle identifier'); } } @@ -968,6 +968,8 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) /** * {@inheritDoc} + * + * @deprecated */ public function prefersSequences() { @@ -1055,6 +1057,8 @@ protected function doModifyLimitQuery($query, $limit, $offset = null) * {@inheritDoc} * * Oracle returns all column names in SQL result sets in uppercase. + * + * @deprecated */ public function getSQLResultCasing($column) { @@ -1095,6 +1099,8 @@ public function getTimeFormatString() /** * {@inheritDoc} + * + * @deprecated */ public function fixSchemaElementName($schemaElementName) { @@ -1124,6 +1130,8 @@ public function supportsSequences() /** * {@inheritDoc} + * + * @deprecated */ public function supportsForeignKeyOnUpdate() { diff --git a/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php b/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php index f1e299f78d..494a69c254 100644 --- a/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php +++ b/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php @@ -6,6 +6,8 @@ /** * Provides the behavior, features and SQL dialect of the PostgreSQL 9.1 database platform. + * + * @deprecated Use PostgreSQL 9.4 or newer */ class PostgreSQL91Platform extends PostgreSqlPlatform { diff --git a/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php b/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php index e886c9387d..9a1784a552 100644 --- a/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php +++ b/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php @@ -8,6 +8,8 @@ /** * Provides the behavior, features and SQL dialect of the PostgreSQL 9.2 database platform. + * + * @deprecated Use PostgreSQL 9.4 or newer */ class PostgreSQL92Platform extends PostgreSQL91Platform { diff --git a/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php b/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php index 0473d14867..6014c5962a 100644 --- a/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php @@ -36,6 +36,8 @@ /** * PostgreSqlPlatform. * + * @deprecated Use PostgreSQL 9.4 or newer + * * @todo Rename: PostgreSQLPlatform */ class PostgreSqlPlatform extends AbstractPlatform @@ -208,6 +210,8 @@ public function supportsCommentOnStatement() /** * {@inheritDoc} + * + * @deprecated */ public function prefersSequences() { @@ -440,6 +444,8 @@ public function getCreateDatabaseSQL($name) * * This is useful to force DROP DATABASE operations which could fail because of active connections. * + * @deprecated + * * @param string $database The name of the database to disallow new connections for. * * @return string @@ -454,6 +460,8 @@ public function getDisallowDatabaseConnectionsSQL($database) * * This is useful to force DROP DATABASE operations which could fail because of active connections. * + * @deprecated + * * @param string $database The name of the database to close currently active connections for. * * @return string @@ -1088,6 +1096,8 @@ public function getName() * {@inheritDoc} * * PostgreSQL returns all column names in SQL result sets in lowercase. + * + * @deprecated */ public function getSQLResultCasing($column) { diff --git a/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php b/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php index a46ae9352c..71922b2276 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php @@ -5,6 +5,8 @@ /** * The SQLAnywhere11Platform provides the behavior, features and SQL dialect of the * SAP Sybase SQL Anywhere 11 database platform. + * + * @deprecated Use SQLAnywhere 16 or newer */ class SQLAnywhere11Platform extends SQLAnywherePlatform { diff --git a/lib/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php b/lib/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php index 63563582c0..45bbef476e 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php @@ -8,6 +8,8 @@ /** * The SQLAnywhere12Platform provides the behavior, features and SQL dialect of the * SAP Sybase SQL Anywhere 12 database platform. + * + * @deprecated Use SQLAnywhere 16 or newer */ class SQLAnywhere12Platform extends SQLAnywhere11Platform { diff --git a/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php b/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php index ee6b2865b0..f7921cbde4 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php @@ -2,7 +2,7 @@ namespace Doctrine\DBAL\Platforms; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\LockMode; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\ColumnDiff; @@ -35,6 +35,8 @@ /** * The SQLAnywherePlatform provides the behavior, features and SQL dialect of the * SAP Sybase SQL Anywhere 10 database platform. + * + * @deprecated Use SQLAnywhere 16 or newer */ class SQLAnywherePlatform extends AbstractPlatform { @@ -689,7 +691,7 @@ public function getGuidTypeDeclarationSQL(array $column) public function getIndexDeclarationSQL($name, Index $index) { // Index declaration in statements like CREATE TABLE is not supported. - throw DBALException::notSupported(__METHOD__); + throw Exception::notSupported(__METHOD__); } /** diff --git a/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php b/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php index a104848f84..f281f4fc4e 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php @@ -10,6 +10,8 @@ * On top of SQL Server 2008 the following functionality is added: * * - Create tables with the FEDERATED ON syntax. + * + * @deprecated */ class SQLAzurePlatform extends SQLServer2008Platform { diff --git a/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php b/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php index 4b2c11bc33..6f02bb8ff5 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php @@ -15,6 +15,8 @@ * NVARCHAR(max) replace the old TEXT, NTEXT and IMAGE types. See * {@link http://www.sql-server-helper.com/faq/sql-server-2005-varchar-max-p01.aspx} * for more information. + * + * @deprecated Use SQL Server 2012 or newer */ class SQLServer2005Platform extends SQLServerPlatform { diff --git a/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php b/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php index d5275b92da..8dcdc1aa2b 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php @@ -7,6 +7,8 @@ * * Differences to SQL Server 2005 and before are that a new DATETIME2 type was * introduced that has a higher precision. + * + * @deprecated Use SQL Server 2012 or newer */ class SQLServer2008Platform extends SQLServer2005Platform { diff --git a/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php b/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php index cf48dc7e98..c1da7ab8e8 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php @@ -39,6 +39,8 @@ /** * The SQLServerPlatform provides the behavior, features and SQL dialect of the * Microsoft SQL Server database platform. + * + * @deprecated Use SQL Server 2012 or newer */ class SQLServerPlatform extends AbstractPlatform { @@ -1189,6 +1191,20 @@ public function getGuidTypeDeclarationSQL(array $column) return 'UNIQUEIDENTIFIER'; } + /** + * {@inheritDoc} + */ + public function getAsciiStringTypeDeclarationSQL(array $column): string + { + $length = $column['length'] ?? null; + + if (! isset($column['fixed'])) { + return sprintf('VARCHAR(%d)', $length); + } + + return sprintf('CHAR(%d)', $length); + } + /** * {@inheritDoc} */ diff --git a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php index e5e0fb16e6..c33970fc14 100644 --- a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -2,7 +2,7 @@ namespace Doctrine\DBAL\Platforms; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\Constraint; use Doctrine\DBAL\Schema\ForeignKeyConstraint; @@ -697,7 +697,7 @@ protected function getReservedKeywordsClass() protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) { if (! $diff->fromTable instanceof Table) { - throw new DBALException( + throw new Exception( 'Sqlite platform requires for alter table the table diff with reference to original table schema' ); } @@ -720,7 +720,7 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) { if (! $diff->fromTable instanceof Table) { - throw new DBALException( + throw new Exception( 'Sqlite platform requires for alter table the table diff with reference to original table schema' ); } @@ -800,7 +800,7 @@ public function supportsForeignKeyConstraints() */ public function getCreatePrimaryKeySQL(Index $index, $table) { - throw new DBALException('Sqlite platform does not support alter primary key.'); + throw new Exception('Sqlite platform does not support alter primary key.'); } /** @@ -808,7 +808,7 @@ public function getCreatePrimaryKeySQL(Index $index, $table) */ public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) { - throw new DBALException('Sqlite platform does not support alter foreign key.'); + throw new Exception('Sqlite platform does not support alter foreign key.'); } /** @@ -816,7 +816,7 @@ public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) */ public function getDropForeignKeySQL($foreignKey, $table) { - throw new DBALException('Sqlite platform does not support alter foreign key.'); + throw new Exception('Sqlite platform does not support alter foreign key.'); } /** @@ -824,7 +824,7 @@ public function getDropForeignKeySQL($foreignKey, $table) */ public function getCreateConstraintSQL(Constraint $constraint, $table) { - throw new DBALException('Sqlite platform does not support alter constraint.'); + throw new Exception('Sqlite platform does not support alter constraint.'); } /** @@ -864,7 +864,7 @@ public function getAlterTableSQL(TableDiff $diff) $fromTable = $diff->fromTable; if (! $fromTable instanceof Table) { - throw new DBALException( + throw new Exception( 'Sqlite platform requires for alter table the table diff with reference to original table schema' ); } diff --git a/lib/Doctrine/DBAL/Portability/Connection.php b/lib/Doctrine/DBAL/Portability/Connection.php index 96f8190fb7..ba47292628 100644 --- a/lib/Doctrine/DBAL/Portability/Connection.php +++ b/lib/Doctrine/DBAL/Portability/Connection.php @@ -4,7 +4,8 @@ use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\ColumnCase; -use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\Connection as BaseConnection; +use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection; use PDO; use function func_get_args; @@ -15,7 +16,7 @@ /** * Portability wrapper for a Connection. */ -class Connection extends \Doctrine\DBAL\Connection +class Connection extends BaseConnection { public const PORTABILITY_ALL = 255; public const PORTABILITY_NONE = 0; @@ -23,6 +24,10 @@ class Connection extends \Doctrine\DBAL\Connection public const PORTABILITY_EMPTY_TO_NULL = 4; public const PORTABILITY_FIX_CASE = 8; + /**#@+ + * + * @deprecated Will be removed as internal implementation details. + */ public const PORTABILITY_DB2 = 13; public const PORTABILITY_ORACLE = 9; public const PORTABILITY_POSTGRESQL = 13; @@ -31,6 +36,7 @@ class Connection extends \Doctrine\DBAL\Connection public const PORTABILITY_DRIZZLE = 13; public const PORTABILITY_SQLANYWHERE = 13; public const PORTABILITY_SQLSRV = 13; + /**#@-*/ /** @var int */ private $portability = self::PORTABILITY_NONE; @@ -47,25 +53,10 @@ public function connect() if ($ret) { $params = $this->getParams(); if (isset($params['portability'])) { - if ($this->getDatabasePlatform()->getName() === 'oracle') { - $params['portability'] &= self::PORTABILITY_ORACLE; - } elseif ($this->getDatabasePlatform()->getName() === 'postgresql') { - $params['portability'] &= self::PORTABILITY_POSTGRESQL; - } elseif ($this->getDatabasePlatform()->getName() === 'sqlite') { - $params['portability'] &= self::PORTABILITY_SQLITE; - } elseif ($this->getDatabasePlatform()->getName() === 'drizzle') { - $params['portability'] &= self::PORTABILITY_DRIZZLE; - } elseif ($this->getDatabasePlatform()->getName() === 'sqlanywhere') { - $params['portability'] &= self::PORTABILITY_SQLANYWHERE; - } elseif ($this->getDatabasePlatform()->getName() === 'db2') { - $params['portability'] &= self::PORTABILITY_DB2; - } elseif ($this->getDatabasePlatform()->getName() === 'mssql') { - $params['portability'] &= self::PORTABILITY_SQLSRV; - } else { - $params['portability'] &= self::PORTABILITY_OTHERVENDORS; - } - - $this->portability = $params['portability']; + $this->portability = $params['portability'] = (new OptimizeFlags())( + $this->getDatabasePlatform(), + $params['portability'] + ); } if (isset($params['fetch_case']) && $this->portability & self::PORTABILITY_FIX_CASE) { diff --git a/lib/Doctrine/DBAL/Portability/OptimizeFlags.php b/lib/Doctrine/DBAL/Portability/OptimizeFlags.php new file mode 100644 index 0000000000..7d8e55df45 --- /dev/null +++ b/lib/Doctrine/DBAL/Portability/OptimizeFlags.php @@ -0,0 +1,44 @@ + + */ + private static $platforms = [ + DB2Platform::class => 0, + OraclePlatform::class => Connection::PORTABILITY_EMPTY_TO_NULL, + PostgreSQL94Platform::class => 0, + SQLAnywhere16Platform::class => 0, + SqlitePlatform::class => 0, + SQLServer2012Platform::class => 0, + ]; + + public function __invoke(AbstractPlatform $platform, int $flags): int + { + foreach (self::$platforms as $class => $mask) { + if ($platform instanceof $class) { + $flags &= ~$mask; + + break; + } + } + + return $flags; + } +} diff --git a/lib/Doctrine/DBAL/Portability/Statement.php b/lib/Doctrine/DBAL/Portability/Statement.php index fe2fb0bd0c..9a476c3381 100644 --- a/lib/Doctrine/DBAL/Portability/Statement.php +++ b/lib/Doctrine/DBAL/Portability/Statement.php @@ -2,6 +2,7 @@ namespace Doctrine\DBAL\Portability; +use Doctrine\DBAL\Driver\Result; use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\Driver\Statement as DriverStatement; use Doctrine\DBAL\Driver\StatementIterator; @@ -18,7 +19,7 @@ /** * Portability wrapper for a Statement. */ -class Statement implements IteratorAggregate, DriverStatement +class Statement implements IteratorAggregate, DriverStatement, Result { /** @var int */ private $portability; @@ -66,6 +67,8 @@ public function bindValue($param, $value, $type = ParameterType::STRING) /** * {@inheritdoc} + * + * @deprecated Use free() instead. */ public function closeCursor() { @@ -82,6 +85,8 @@ public function columnCount() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorCode() { @@ -92,6 +97,8 @@ public function errorCode() /** * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. */ public function errorInfo() { @@ -112,6 +119,8 @@ public function execute($params = null) /** * {@inheritdoc} + * + * @deprecated Use one of the fetch- or iterate-related methods. */ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) { @@ -122,6 +131,8 @@ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) /** * {@inheritdoc} + * + * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. */ public function getIterator() { @@ -130,6 +141,8 @@ public function getIterator() /** * {@inheritdoc} + * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. */ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) { @@ -137,8 +150,11 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX $row = $this->stmt->fetch($fetchMode); - $iterateRow = $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL | Connection::PORTABILITY_RTRIM); - $fixCase = $this->case !== null + $iterateRow = ( + $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL | Connection::PORTABILITY_RTRIM) + ) !== 0; + + $fixCase = $this->case !== null && ($fetchMode === FetchMode::ASSOCIATIVE || $fetchMode === FetchMode::MIXED) && ($this->portability & Connection::PORTABILITY_FIX_CASE); @@ -149,6 +165,8 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} + * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { @@ -160,37 +178,169 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n $rows = $this->stmt->fetchAll($fetchMode); } - $iterateRow = $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL | Connection::PORTABILITY_RTRIM); - $fixCase = $this->case !== null + $fixCase = $this->case !== null && ($fetchMode === FetchMode::ASSOCIATIVE || $fetchMode === FetchMode::MIXED) && ($this->portability & Connection::PORTABILITY_FIX_CASE); + return $this->fixResultSet($rows, $fixCase, $fetchMode !== FetchMode::COLUMN); + } + + /** + * {@inheritdoc} + */ + public function fetchNumeric() + { + if ($this->stmt instanceof Result) { + $row = $this->stmt->fetchNumeric(); + } else { + $row = $this->stmt->fetch(FetchMode::NUMERIC); + } + + return $this->fixResult($row, false); + } + + /** + * {@inheritdoc} + */ + public function fetchAssociative() + { + if ($this->stmt instanceof Result) { + $row = $this->stmt->fetchAssociative(); + } else { + $row = $this->stmt->fetch(FetchMode::ASSOCIATIVE); + } + + return $this->fixResult($row, true); + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + if ($this->stmt instanceof Result) { + $value = $this->stmt->fetchOne(); + } else { + $value = $this->stmt->fetch(FetchMode::COLUMN); + } + + if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) !== 0 && $value === '') { + $value = null; + } elseif (($this->portability & Connection::PORTABILITY_RTRIM) !== 0 && is_string($value)) { + $value = rtrim($value); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + if ($this->stmt instanceof Result) { + $data = $this->stmt->fetchAllNumeric(); + } else { + $data = $this->stmt->fetchAll(FetchMode::NUMERIC); + } + + return $this->fixResultSet($data, false, true); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + if ($this->stmt instanceof Result) { + $data = $this->stmt->fetchAllAssociative(); + } else { + $data = $this->stmt->fetchAll(FetchMode::ASSOCIATIVE); + } + + return $this->fixResultSet($data, true, true); + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + if ($this->stmt instanceof Result) { + $data = $this->stmt->fetchFirstColumn(); + } else { + $data = $this->stmt->fetchAll(FetchMode::COLUMN); + } + + return $this->fixResultSet($data, true, false); + } + + public function free(): void + { + if ($this->stmt instanceof Result) { + $this->stmt->free(); + + return; + } + + $this->stmt->closeCursor(); + } + + /** + * @param mixed $result + * + * @return mixed + */ + private function fixResult($result, bool $fixCase) + { + $iterateRow = ( + $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL | Connection::PORTABILITY_RTRIM) + ) !== 0; + + $fixCase = $fixCase && $this->case !== null && ($this->portability & Connection::PORTABILITY_FIX_CASE) !== 0; + + return $this->fixRow($result, $iterateRow, $fixCase); + } + + /** + * @param array $resultSet + * + * @return array + */ + private function fixResultSet(array $resultSet, bool $fixCase, bool $isArray): array + { + $iterateRow = ( + $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL | Connection::PORTABILITY_RTRIM) + ) !== 0; + + $fixCase = $fixCase && $this->case !== null && ($this->portability & Connection::PORTABILITY_FIX_CASE) !== 0; + if (! $iterateRow && ! $fixCase) { - return $rows; + return $resultSet; } - if ($fetchMode === FetchMode::COLUMN) { - foreach ($rows as $num => $row) { - $rows[$num] = [$row]; + if (! $isArray) { + foreach ($resultSet as $num => $value) { + $resultSet[$num] = [$value]; } } - foreach ($rows as $num => $row) { - $rows[$num] = $this->fixRow($row, $iterateRow, $fixCase); + foreach ($resultSet as $num => $row) { + $resultSet[$num] = $this->fixRow($row, $iterateRow, $fixCase); } - if ($fetchMode === FetchMode::COLUMN) { - foreach ($rows as $num => $row) { - $rows[$num] = $row[0]; + if (! $isArray) { + foreach ($resultSet as $num => $row) { + $resultSet[$num] = $row[0]; } } - return $rows; + return $resultSet; } /** * @param mixed $row - * @param int $iterateRow + * @param bool $iterateRow * @param bool $fixCase * * @return mixed @@ -220,6 +370,8 @@ protected function fixRow($row, $iterateRow, $fixCase) /** * {@inheritdoc} + * + * @deprecated Use fetchOne() instead. */ public function fetchColumn($columnIndex = 0) { diff --git a/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php b/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php index 234b61b61d..596e9fb3af 100644 --- a/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php +++ b/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php @@ -4,6 +4,7 @@ use Countable; +use function array_merge; use function count; use function implode; @@ -37,6 +38,8 @@ class CompositeExpression implements Countable private $parts = []; /** + * @internal Use the and() / or() factory methods. + * * @param string $type Instance type of composite expression. * @param self[]|string[] $parts Composition of expressions to be joined on composite expression. */ @@ -47,9 +50,29 @@ public function __construct($type, array $parts = []) $this->addMultiple($parts); } + /** + * @param self|string $part + * @param self|string ...$parts + */ + public static function and($part, ...$parts): self + { + return new self(self::TYPE_AND, array_merge([$part], $parts)); + } + + /** + * @param self|string $part + * @param self|string ...$parts + */ + public static function or($part, ...$parts): self + { + return new self(self::TYPE_OR, array_merge([$part], $parts)); + } + /** * Adds multiple parts to composite expression. * + * @deprecated This class will be made immutable. Use with() instead. + * * @param self[]|string[] $parts * * @return CompositeExpression @@ -66,6 +89,8 @@ public function addMultiple(array $parts = []) /** * Adds an expression to composite expression. * + * @deprecated This class will be made immutable. Use with() instead. + * * @param mixed $part * * @return CompositeExpression @@ -85,6 +110,25 @@ public function add($part) return $this; } + /** + * Returns a new CompositeExpression with the given parts added. + * + * @param self|string $part + * @param self|string ...$parts + */ + public function with($part, ...$parts): self + { + $that = clone $this; + + $that->parts[] = $part; + + foreach ($parts as $part) { + $that->parts[] = $part; + } + + return $that; + } + /** * Retrieves the amount of expressions on composite expression. * diff --git a/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php b/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php index 1b0c1df9f4..f12ef0fa5f 100644 --- a/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php +++ b/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php @@ -40,13 +40,29 @@ public function __construct(Connection $connection) } /** - * Creates a conjunction of the given boolean expressions. + * Creates a conjunction of the given expressions. * - * Example: + * @param string|CompositeExpression $expression + * @param string|CompositeExpression ...$expressions + */ + public function and($expression, ...$expressions): CompositeExpression + { + return CompositeExpression::and($expression, ...$expressions); + } + + /** + * Creates a disjunction of the given expressions. * - * [php] - * // (u.type = ?) AND (u.role = ?) - * $expr->andX('u.type = ?', 'u.role = ?')); + * @param string|CompositeExpression $expression + * @param string|CompositeExpression ...$expressions + */ + public function or($expression, ...$expressions): CompositeExpression + { + return CompositeExpression::or($expression, ...$expressions); + } + + /** + * @deprecated Use `and()` instead. * * @param mixed $x Optional clause. Defaults = null, but requires * at least one defined when converting to string. @@ -59,13 +75,7 @@ public function andX($x = null) } /** - * Creates a disjunction of the given boolean expressions. - * - * Example: - * - * [php] - * // (u.type = ?) OR (u.role = ?) - * $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?')); + * @deprecated Use `or()` instead. * * @param mixed $x Optional clause. Defaults = null, but requires * at least one defined when converting to string. diff --git a/lib/Doctrine/DBAL/Query/QueryBuilder.php b/lib/Doctrine/DBAL/Query/QueryBuilder.php index 8c1c8ad3fc..6c4aa6ef02 100644 --- a/lib/Doctrine/DBAL/Query/QueryBuilder.php +++ b/lib/Doctrine/DBAL/Query/QueryBuilder.php @@ -196,9 +196,6 @@ public function getState() /** * Executes this query using the bound parameters and their types. * - * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} - * for insert, update and delete statements. - * * @return ResultStatement|int */ public function execute() @@ -207,7 +204,7 @@ public function execute() return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); } - return $this->connection->executeUpdate($this->getSQL(), $this->params, $this->paramTypes); + return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); } /** @@ -457,6 +454,8 @@ public function add($sqlPartName, $sqlPart, $append = false) * Specifies an item that is to be returned in the query result. * Replaces any previously specified selections, if any. * + * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. + * * * $qb = $conn->createQueryBuilder() * ->select('u.id', 'p.id') @@ -464,11 +463,12 @@ public function add($sqlPartName, $sqlPart, $append = false) * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); * * - * @param mixed $select The selection expressions. + * @param string|string[]|null $select The selection expression. USING AN ARRAY OR NULL IS DEPRECATED. + * Pass each value as an individual argument. * * @return $this This QueryBuilder instance. */ - public function select($select = null) + public function select($select = null/*, string ...$selects*/) { $this->type = self::SELECT; @@ -503,6 +503,8 @@ public function distinct(): self /** * Adds an item that is to be returned in the query result. * + * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. + * * * $qb = $conn->createQueryBuilder() * ->select('u.id') @@ -511,11 +513,12 @@ public function distinct(): self * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); * * - * @param mixed $select The selection expression. + * @param string|string[]|null $select The selection expression. USING AN ARRAY OR NULL IS DEPRECATED. + * Pass each value as an individual argument. * * @return $this This QueryBuilder instance. */ - public function addSelect($select = null) + public function addSelect($select = null/*, string ...$selects*/) { $this->type = self::SELECT; @@ -799,7 +802,7 @@ public function set($key, $value) public function where($predicates) { if (! (func_num_args() === 1 && $predicates instanceof CompositeExpression)) { - $predicates = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + $predicates = CompositeExpression::and(...func_get_args()); } return $this->add('where', $predicates); @@ -829,10 +832,10 @@ public function andWhere($where) $where = $this->getQueryPart('where'); if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) { - $where->addMultiple($args); + $where = $where->with(...$args); } else { array_unshift($args, $where); - $where = new CompositeExpression(CompositeExpression::TYPE_AND, $args); + $where = CompositeExpression::and(...$args); } return $this->add('where', $where, true); @@ -862,10 +865,10 @@ public function orWhere($where) $where = $this->getQueryPart('where'); if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) { - $where->addMultiple($args); + $where = $where->with(...$args); } else { array_unshift($args, $where); - $where = new CompositeExpression(CompositeExpression::TYPE_OR, $args); + $where = CompositeExpression::or(...$args); } return $this->add('where', $where, true); @@ -875,6 +878,8 @@ public function orWhere($where) * Specifies a grouping over the results of the query. * Replaces any previously specified groupings, if any. * + * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. + * * * $qb = $conn->createQueryBuilder() * ->select('u.name') @@ -882,11 +887,12 @@ public function orWhere($where) * ->groupBy('u.id'); * * - * @param mixed $groupBy The grouping expression. + * @param string|string[] $groupBy The grouping expression. USING AN ARRAY IS DEPRECATED. + * Pass each value as an individual argument. * * @return $this This QueryBuilder instance. */ - public function groupBy($groupBy) + public function groupBy($groupBy/*, string ...$groupBys*/) { if (empty($groupBy)) { return $this; @@ -900,6 +906,8 @@ public function groupBy($groupBy) /** * Adds a grouping expression to the query. * + * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. + * * * $qb = $conn->createQueryBuilder() * ->select('u.name') @@ -908,11 +916,12 @@ public function groupBy($groupBy) * ->addGroupBy('u.createdAt'); * * - * @param mixed $groupBy The grouping expression. + * @param string|string[] $groupBy The grouping expression. USING AN ARRAY IS DEPRECATED. + * Pass each value as an individual argument. * * @return $this This QueryBuilder instance. */ - public function addGroupBy($groupBy) + public function addGroupBy($groupBy/*, string ...$groupBys*/) { if (empty($groupBy)) { return $this; @@ -984,7 +993,7 @@ public function values(array $values) public function having($having) { if (! (func_num_args() === 1 && $having instanceof CompositeExpression)) { - $having = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + $having = CompositeExpression::and(...func_get_args()); } return $this->add('having', $having); @@ -1004,10 +1013,10 @@ public function andHaving($having) $having = $this->getQueryPart('having'); if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) { - $having->addMultiple($args); + $having = $having->with(...$args); } else { array_unshift($args, $having); - $having = new CompositeExpression(CompositeExpression::TYPE_AND, $args); + $having = CompositeExpression::and(...$args); } return $this->add('having', $having); @@ -1027,10 +1036,10 @@ public function orHaving($having) $having = $this->getQueryPart('having'); if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) { - $having->addMultiple($args); + $having = $having->with(...$args); } else { array_unshift($args, $having); - $having = new CompositeExpression(CompositeExpression::TYPE_OR, $args); + $having = CompositeExpression::or(...$args); } return $this->add('having', $having); diff --git a/lib/Doctrine/DBAL/Query/QueryException.php b/lib/Doctrine/DBAL/Query/QueryException.php index 76bb9ba755..58e941e984 100644 --- a/lib/Doctrine/DBAL/Query/QueryException.php +++ b/lib/Doctrine/DBAL/Query/QueryException.php @@ -2,14 +2,14 @@ namespace Doctrine\DBAL\Query; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use function implode; /** * @psalm-immutable */ -class QueryException extends DBALException +class QueryException extends Exception { /** * @param string $alias diff --git a/lib/Doctrine/DBAL/SQLParserUtilsException.php b/lib/Doctrine/DBAL/SQLParserUtilsException.php index 99cdb8d4a6..297b0761b8 100644 --- a/lib/Doctrine/DBAL/SQLParserUtilsException.php +++ b/lib/Doctrine/DBAL/SQLParserUtilsException.php @@ -9,7 +9,7 @@ * * @psalm-immutable */ -class SQLParserUtilsException extends DBALException +class SQLParserUtilsException extends Exception { /** * @param string $paramName diff --git a/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php b/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php index 352ce58f3e..35f2c61278 100644 --- a/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php @@ -4,10 +4,10 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\ConnectionException; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs; use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; use Doctrine\DBAL\Events; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; use Throwable; @@ -102,7 +102,7 @@ public function listDatabases() { $sql = $this->_platform->getListDatabasesSQL(); - $databases = $this->_conn->fetchAll($sql); + $databases = $this->_conn->fetchAllAssociative($sql); return $this->_getPortableDatabasesList($databases); } @@ -116,7 +116,7 @@ public function listNamespaceNames() { $sql = $this->_platform->getListNamespacesSQL(); - $namespaces = $this->_conn->fetchAll($sql); + $namespaces = $this->_conn->fetchAllAssociative($sql); return $this->getPortableNamespacesList($namespaces); } @@ -136,7 +136,7 @@ public function listSequences($database = null) $sql = $this->_platform->getListSequencesSQL($database); - $sequences = $this->_conn->fetchAll($sql); + $sequences = $this->_conn->fetchAllAssociative($sql); return $this->filterAssetNames($this->_getPortableSequencesList($sequences)); } @@ -164,7 +164,7 @@ public function listTableColumns($table, $database = null) $sql = $this->_platform->getListTableColumnsSQL($table, $database); - $tableColumns = $this->_conn->fetchAll($sql); + $tableColumns = $this->_conn->fetchAllAssociative($sql); return $this->_getPortableTableColumnList($table, $database, $tableColumns); } @@ -182,7 +182,7 @@ public function listTableIndexes($table) { $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); - $tableIndexes = $this->_conn->fetchAll($sql); + $tableIndexes = $this->_conn->fetchAllAssociative($sql); return $this->_getPortableTableIndexesList($tableIndexes, $table); } @@ -212,7 +212,7 @@ public function listTableNames() { $sql = $this->_platform->getListTablesSQL(); - $tables = $this->_conn->fetchAll($sql); + $tables = $this->_conn->fetchAllAssociative($sql); $tableNames = $this->_getPortableTablesList($tables); return $this->filterAssetNames($tableNames); @@ -290,7 +290,7 @@ public function listViews() { $database = $this->_conn->getDatabase(); $sql = $this->_platform->getListViewsSQL($database); - $views = $this->_conn->fetchAll($sql); + $views = $this->_conn->fetchAllAssociative($sql); return $this->_getPortableViewsList($views); } @@ -310,7 +310,7 @@ public function listTableForeignKeys($table, $database = null) } $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); - $tableForeignKeys = $this->_conn->fetchAll($sql); + $tableForeignKeys = $this->_conn->fetchAllAssociative($sql); return $this->_getPortableTableForeignKeysList($tableForeignKeys); } @@ -776,11 +776,11 @@ protected function _getPortableSequencesList($sequences) * * @return Sequence * - * @throws DBALException + * @throws Exception */ protected function _getPortableSequenceDefinition($sequence) { - throw DBALException::notSupported('Sequences'); + throw Exception::notSupported('Sequences'); } /** @@ -1041,7 +1041,7 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) protected function _execSql($sql) { foreach ((array) $sql as $query) { - $this->_conn->executeUpdate($query); + $this->_conn->executeStatement($query); } } diff --git a/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php b/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php index d270b58568..25f0282c53 100644 --- a/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php @@ -32,7 +32,7 @@ public function listTableNames() $sql = $this->_platform->getListTablesSQL(); $sql .= ' AND CREATOR = UPPER(' . $this->_conn->quote($this->_conn->getUsername()) . ')'; - $tables = $this->_conn->fetchAll($sql); + $tables = $this->_conn->fetchAllAssociative($sql); return $this->filterAssetNames($this->_getPortableTablesList($tables)); } @@ -230,7 +230,7 @@ public function listTableDetails($name): Table assert($platform instanceof DB2Platform); $sql = $platform->getListTableCommentsSQL($name); - $tableOptions = $this->_conn->fetchAssoc($sql); + $tableOptions = $this->_conn->fetchAssociative($sql); if ($tableOptions !== false) { $table->addOption('comment', $tableOptions['REMARKS']); diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index edcba52c3a..e5532f802a 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -328,7 +328,7 @@ public function listTableDetails($name) assert($platform instanceof MySqlPlatform); $sql = $platform->getListTableMetadataSQL($name); - $tableOptions = $this->_conn->fetchAssoc($sql); + $tableOptions = $this->_conn->fetchAssociative($sql); if ($tableOptions === false) { return $table; diff --git a/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php b/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php index 9e5b64fdc8..188f59e3f9 100644 --- a/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php @@ -3,7 +3,7 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\Driver\DriverException; +use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Types\Type; use Throwable; @@ -37,7 +37,7 @@ public function dropDatabase($database) $exception = $exception->getPrevious(); assert($exception instanceof Throwable); - if (! $exception instanceof DriverException) { + if (! $exception instanceof Exception) { throw $exception; } @@ -307,10 +307,10 @@ public function createDatabase($database = null) $password = $params['password']; $query = 'CREATE USER ' . $username . ' IDENTIFIED BY ' . $password; - $this->_conn->executeUpdate($query); + $this->_conn->executeStatement($query); $query = 'GRANT DBA TO ' . $username; - $this->_conn->executeUpdate($query); + $this->_conn->executeStatement($query); } /** @@ -324,7 +324,7 @@ public function dropAutoincrement($table) $sql = $this->_platform->getDropAutoincrementSql($table); foreach ($sql as $query) { - $this->_conn->executeUpdate($query); + $this->_conn->executeStatement($query); } return true; @@ -382,7 +382,7 @@ protected function killUserSessions($user) AND p.addr(+) = s.paddr SQL; - $activeUserSessions = $this->_conn->fetchAll($sql, [strtoupper($user)]); + $activeUserSessions = $this->_conn->fetchAllAssociative($sql, [strtoupper($user)]); foreach ($activeUserSessions as $activeUserSession) { $activeUserSession = array_change_key_case($activeUserSession, CASE_LOWER); @@ -408,7 +408,7 @@ public function listTableDetails($name): Table assert($platform instanceof OraclePlatform); $sql = $platform->getListTableCommentsSQL($name); - $tableOptions = $this->_conn->fetchAssoc($sql); + $tableOptions = $this->_conn->fetchAssociative($sql); if ($tableOptions !== false) { $table->addOption('comment', $tableOptions['COMMENTS']); diff --git a/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php index 4894fd7096..d9fabec166 100644 --- a/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php @@ -225,8 +225,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null implode(' ,', $colNumbers) ); - $stmt = $this->_conn->executeQuery($columnNameSql); - $indexColumns = $stmt->fetchAll(); + $indexColumns = $this->_conn->fetchAllAssociative($columnNameSql); // required for getting the order of the columns right. foreach ($colNumbers as $colNum) { diff --git a/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php b/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php index d06f1b1d78..63dc9b04f8 100644 --- a/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php @@ -3,7 +3,7 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\Driver\DriverException; +use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\DBAL\Types\Type; use PDOException; @@ -35,7 +35,7 @@ public function dropDatabase($database) $exception = $exception->getPrevious(); assert($exception instanceof Throwable); - if (! $exception instanceof DriverException) { + if (! $exception instanceof Exception) { throw $exception; } @@ -251,7 +251,7 @@ public function listTableIndexes($table) $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); try { - $tableIndexes = $this->_conn->fetchAll($sql); + $tableIndexes = $this->_conn->fetchAllAssociative($sql); } catch (PDOException $e) { if ($e->getCode() === 'IMSSP') { return []; @@ -277,7 +277,7 @@ public function alterTable(TableDiff $tableDiff) if (count($tableDiff->removedColumns) > 0) { foreach ($tableDiff->removedColumns as $col) { $columnConstraintSql = $this->getColumnConstraintSQL($tableDiff->name, $col->getName()); - foreach ($this->_conn->fetchAll($columnConstraintSql) as $constraint) { + foreach ($this->_conn->fetchAllAssociative($columnConstraintSql) as $constraint) { $this->_conn->exec( sprintf( 'ALTER TABLE %s DROP CONSTRAINT %s', @@ -343,7 +343,7 @@ public function listTableDetails($name): Table assert($platform instanceof SQLServerPlatform); $sql = $platform->getListTableMetadataSQL($name); - $tableOptions = $this->_conn->fetchAssoc($sql); + $tableOptions = $this->_conn->fetchAssociative($sql); if ($tableOptions !== false) { $table->addOption('comment', $tableOptions['table_comment']); diff --git a/lib/Doctrine/DBAL/Schema/SchemaException.php b/lib/Doctrine/DBAL/Schema/SchemaException.php index 6317679de0..64f4b250a8 100644 --- a/lib/Doctrine/DBAL/Schema/SchemaException.php +++ b/lib/Doctrine/DBAL/Schema/SchemaException.php @@ -2,7 +2,7 @@ namespace Doctrine\DBAL\Schema; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use function implode; use function sprintf; @@ -10,7 +10,7 @@ /** * @psalm-immutable */ -class SchemaException extends DBALException +class SchemaException extends Exception { public const TABLE_DOESNT_EXIST = 10; public const TABLE_ALREADY_EXISTS = 20; diff --git a/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php b/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php index 17cb5efac3..de7b137503 100644 --- a/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php @@ -2,9 +2,8 @@ namespace Doctrine\DBAL\Schema; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DriverManager; -use Doctrine\DBAL\FetchMode; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Types\StringType; use Doctrine\DBAL\Types\TextType; use Doctrine\DBAL\Types\Type; @@ -118,7 +117,7 @@ public function listTableForeignKeys($table, $database = null) } $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); - $tableForeignKeys = $this->_conn->fetchAll($sql); + $tableForeignKeys = $this->_conn->fetchAllAssociative($sql); if (! empty($tableForeignKeys)) { $createSql = $this->getCreateTableSQL($table); @@ -177,11 +176,10 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null $indexBuffer = []; // fetch primary - $stmt = $this->_conn->executeQuery(sprintf( + $indexArray = $this->_conn->fetchAllAssociative(sprintf( 'PRAGMA TABLE_INFO (%s)', $this->_conn->quote($tableName) )); - $indexArray = $stmt->fetchAll(FetchMode::ASSOCIATIVE); usort($indexArray, static function ($a, $b) { if ($a['pk'] === $b['pk']) { @@ -216,11 +214,10 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null $idx['primary'] = false; $idx['non_unique'] = ! $tableIndex['unique']; - $stmt = $this->_conn->executeQuery(sprintf( - 'PRAGMA INDEX_INFO (%s)', - $this->_conn->quote($keyName) - )); - $indexArray = $stmt->fetchAll(FetchMode::ASSOCIATIVE); + $indexArray = $this->_conn->fetchAllAssociative(sprintf( + 'PRAGMA INDEX_INFO (%s)', + $this->_conn->quote($keyName) + )); foreach ($indexArray as $indexColumnRow) { $idx['column_name'] = $indexColumnRow['name']; @@ -458,7 +455,7 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) * * @return TableDiff * - * @throws DBALException + * @throws Exception */ private function getTableDiffForAlterForeignKey($table) { @@ -466,7 +463,7 @@ private function getTableDiffForAlterForeignKey($table) $tableDetails = $this->tryMethod('listTableDetails', $table); if ($tableDetails === false) { - throw new DBALException( + throw new Exception( sprintf('Sqlite schema manager requires to modify foreign keys table definition "%s".', $table) ); } diff --git a/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php b/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php index 9b1974747e..7cfd1f2c00 100644 --- a/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php +++ b/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php @@ -7,6 +7,8 @@ /** * Abstract schema synchronizer with methods for executing batches of SQL. + * + * @deprecated */ abstract class AbstractSchemaSynchronizer implements SchemaSynchronizer { diff --git a/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php b/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php index 3e7beea750..a10d3b7f32 100644 --- a/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php +++ b/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php @@ -7,6 +7,8 @@ /** * The synchronizer knows how to synchronize a schema with the configured * database. + * + * @deprecated */ interface SchemaSynchronizer { diff --git a/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php b/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php index 43c0fc6a54..62a5857778 100644 --- a/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php +++ b/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php @@ -12,6 +12,8 @@ /** * Schema Synchronizer for Default DBAL Connection. + * + * @deprecated */ class SingleDatabaseSynchronizer extends AbstractSchemaSynchronizer { diff --git a/lib/Doctrine/DBAL/Schema/Table.php b/lib/Doctrine/DBAL/Schema/Table.php index 4d8b362a0d..bd0462151a 100644 --- a/lib/Doctrine/DBAL/Schema/Table.php +++ b/lib/Doctrine/DBAL/Schema/Table.php @@ -2,7 +2,7 @@ namespace Doctrine\DBAL\Schema; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Visitor\Visitor; use Doctrine\DBAL\Types\Type; @@ -51,7 +51,7 @@ class Table extends AbstractAsset * @param int $idGeneratorType * @param mixed[] $options * - * @throws DBALException + * @throws Exception */ public function __construct( $name, @@ -62,7 +62,7 @@ public function __construct( array $options = [] ) { if (strlen($name) === 0) { - throw DBALException::invalidTableName($name); + throw Exception::invalidTableName($name); } $this->_setName($name); @@ -316,11 +316,11 @@ public function addColumn($name, $typeName, array $options = []) * * @return void * - * @throws DBALException + * @throws Exception */ public function renameColumn($oldName, $name) { - throw new DBALException('Table#renameColumn() was removed, because it drops and recreates ' . + throw new Exception('Table#renameColumn() was removed, because it drops and recreates ' . 'the column instead. There is no fix available, because a schema diff cannot reliably detect if a ' . 'column was renamed or one column was created and another one dropped.'); } @@ -554,11 +554,13 @@ protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) $this->_fkConstraints[$name] = $constraint; - // Add an explicit index on the foreign key columns. - // If there is already an index that fulfils this requirements drop the request. - // In the case of __construct calling this method during hydration from schema-details - // all the explicitly added indexes lead to duplicates. This creates computation overhead in this case, - // however no duplicate indexes are ever added (based on columns). + /* Add an implicit index (defined by the DBAL) on the foreign key + columns. If there is already a user-defined index that fulfills these + requirements drop the request. In the case of __construct() calling + this method during hydration from schema-details, all the explicitly + added indexes lead to duplicates. This creates computation overhead in + this case, however no duplicate indexes are ever added (based on + columns). */ $indexName = $this->_generateIdentifierName( array_merge([$this->getName()], $constraint->getColumns()), 'idx', @@ -727,14 +729,14 @@ public function getPrimaryKey() * * @return string[] * - * @throws DBALException + * @throws Exception */ public function getPrimaryKeyColumns() { $primaryKey = $this->getPrimaryKey(); if ($primaryKey === null) { - throw new DBALException('Table ' . $this->getName() . ' has no primary key.'); + throw new Exception('Table ' . $this->getName() . ' has no primary key.'); } return $primaryKey->getColumns(); diff --git a/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php b/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php index e16b8d46f9..70f63c76d0 100644 --- a/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php +++ b/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php @@ -33,6 +33,8 @@ * * Instantiation through the DriverManager looks like: * + * @deprecated + * * @example * * $conn = DriverManager::getConnection(array( @@ -63,6 +65,8 @@ class PoolingShardConnection extends Connection /** * {@inheritDoc} * + * @internal The connection can be only instantiated by the driver manager. + * * @throws InvalidArgumentException */ public function __construct( diff --git a/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php b/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php index 5edc56b877..81a197ee11 100644 --- a/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php +++ b/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php @@ -7,6 +7,8 @@ /** * Shard Manager for the Connection Pooling Shard Strategy + * + * @deprecated */ class PoolingShardManager implements ShardManager { @@ -85,7 +87,7 @@ public function queryAll($sql, array $params, array $types) foreach ($shards as $shard) { $this->conn->connect($shard['id']); - foreach ($this->conn->fetchAll($sql, $params, $types) as $row) { + foreach ($this->conn->fetchAllAssociative($sql, $params, $types) as $row) { $result[] = $row; } } diff --git a/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php b/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php index b204ea68e5..7ea9ce1fad 100644 --- a/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php +++ b/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php @@ -21,6 +21,8 @@ * by partitioning the passed schema into subschemas for the federation and the * global database and then applying the operations step by step using the * {@see \Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer}. + * + * @deprecated */ class SQLAzureFederationsSynchronizer extends AbstractSchemaSynchronizer { diff --git a/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php b/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php index ce092c6bcb..d511553c15 100644 --- a/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php +++ b/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php @@ -12,6 +12,8 @@ /** * Sharding using the SQL Azure Federations support. + * + * @deprecated */ class SQLAzureShardManager implements ShardManager { @@ -158,7 +160,7 @@ public function getShards() INNER JOIN sys.federations f ON f.federation_id = d.federation_id WHERE f.name = ' . $this->conn->quote($this->federationName); - return $this->conn->fetchAll($sql); + return $this->conn->fetchAllAssociative($sql); } /** @@ -176,7 +178,7 @@ public function queryAll($sql, array $params = [], array $types = []) foreach ($shards as $shard) { $this->selectShard($shard['rangeLow']); - foreach ($this->conn->fetchAll($sql, $params, $types) as $row) { + foreach ($this->conn->fetchAllAssociative($sql, $params, $types) as $row) { $result[] = $row; } } diff --git a/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php b/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php index 658927eb43..d9ef9e8964 100644 --- a/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php +++ b/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php @@ -31,6 +31,8 @@ * (otherwise they will affect the same-id rows from other tenants as well). * SQLAzure throws errors when you try to create IDENTIY columns on federated * tables. + * + * @deprecated */ class MultiTenantVisitor implements Visitor { diff --git a/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php b/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php index 584e8155ae..f44c3af432 100644 --- a/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php +++ b/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php @@ -7,6 +7,8 @@ /** * The MultiTenant Shard choser assumes that the distribution value directly * maps to the shard id. + * + * @deprecated */ class MultiTenantShardChoser implements ShardChoser { diff --git a/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php b/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php index 6f8a9d4770..92510d704b 100644 --- a/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php +++ b/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php @@ -7,6 +7,8 @@ /** * Given a distribution value this shard-choser strategy will pick the shard to * connect to for retrieving rows with the distribution value. + * + * @deprecated */ interface ShardChoser { diff --git a/lib/Doctrine/DBAL/Sharding/ShardManager.php b/lib/Doctrine/DBAL/Sharding/ShardManager.php index e988fdcf95..79e276be0c 100644 --- a/lib/Doctrine/DBAL/Sharding/ShardManager.php +++ b/lib/Doctrine/DBAL/Sharding/ShardManager.php @@ -17,6 +17,8 @@ * executed against the last shard that was selected. If a query is created for * a shard Y but then a shard X is selected when its actually executed you * will hit the wrong shard. + * + * @deprecated */ interface ShardManager { diff --git a/lib/Doctrine/DBAL/Sharding/ShardingException.php b/lib/Doctrine/DBAL/Sharding/ShardingException.php index f760dc7baa..d3363a126c 100644 --- a/lib/Doctrine/DBAL/Sharding/ShardingException.php +++ b/lib/Doctrine/DBAL/Sharding/ShardingException.php @@ -2,14 +2,16 @@ namespace Doctrine\DBAL\Sharding; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; /** * Sharding related Exceptions * + * @deprecated + * * @psalm-immutable */ -class ShardingException extends DBALException +class ShardingException extends Exception { /** * @return ShardingException diff --git a/lib/Doctrine/DBAL/Statement.php b/lib/Doctrine/DBAL/Statement.php index 2a1069bc34..0e47e09303 100644 --- a/lib/Doctrine/DBAL/Statement.php +++ b/lib/Doctrine/DBAL/Statement.php @@ -2,12 +2,15 @@ namespace Doctrine\DBAL; +use Doctrine\DBAL\Abstraction\Result; +use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Driver\Statement as DriverStatement; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; use IteratorAggregate; use PDO; use Throwable; +use Traversable; use function is_array; use function is_string; @@ -16,7 +19,7 @@ * A thin wrapper around a Doctrine\DBAL\Driver\Statement that adds support * for logging, DBAL mapping types, etc. */ -class Statement implements IteratorAggregate, DriverStatement +class Statement implements IteratorAggregate, DriverStatement, Result { /** * The SQL statement. @@ -63,6 +66,8 @@ class Statement implements IteratorAggregate, DriverStatement /** * Creates a new Statement for the given SQL and Connection. * + * @internal The statement can be only instantiated by {@link Connection}. + * * @param string $sql The SQL of the statement. * @param Connection $conn The connection on which the statement should be executed. */ @@ -138,7 +143,7 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le * * @return bool TRUE on success, FALSE on failure. * - * @throws DBALException + * @throws Exception */ public function execute($params = null) { @@ -158,12 +163,7 @@ public function execute($params = null) $logger->stopQuery(); } - throw DBALException::driverExceptionDuringQuery( - $this->conn->getDriver(), - $ex, - $this->sql, - $this->conn->resolveParams($this->params, $this->types) - ); + $this->conn->handleExceptionDuringQuery($ex, $this->sql, $this->params, $this->types); } if ($logger) { @@ -176,6 +176,8 @@ public function execute($params = null) /** * Closes the cursor, freeing the database resources used by this statement. * + * @deprecated Use Result::free() instead. + * * @return bool TRUE on success, FALSE on failure. */ public function closeCursor() @@ -196,6 +198,8 @@ public function columnCount() /** * Fetches the SQLSTATE associated with the last operation on the statement. * + * @deprecated The error information is available via exceptions. + * * @return string|int|bool */ public function errorCode() @@ -205,6 +209,8 @@ public function errorCode() /** * {@inheritDoc} + * + * @deprecated The error information is available via exceptions. */ public function errorInfo() { @@ -213,6 +219,8 @@ public function errorInfo() /** * {@inheritdoc} + * + * @deprecated Use one of the fetch- or iterate-related methods. */ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) { @@ -230,6 +238,8 @@ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) /** * Required by interface IteratorAggregate. * + * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. + * * {@inheritdoc} */ public function getIterator() @@ -239,6 +249,8 @@ public function getIterator() /** * {@inheritdoc} + * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. */ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) { @@ -247,6 +259,8 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} + * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { @@ -255,12 +269,194 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n /** * {@inheritDoc} + * + * @deprecated Use fetchOne() instead. */ public function fetchColumn($columnIndex = 0) { return $this->stmt->fetchColumn($columnIndex); } + /** + * {@inheritdoc} + * + * @throws Exception + */ + public function fetchNumeric() + { + try { + if ($this->stmt instanceof Result) { + return $this->stmt->fetchNumeric(); + } + + return $this->stmt->fetch(FetchMode::NUMERIC); + } catch (Exception $e) { + $this->conn->handleDriverException($e); + } + } + + /** + * {@inheritdoc} + * + * @throws Exception + */ + public function fetchAssociative() + { + try { + if ($this->stmt instanceof Result) { + return $this->stmt->fetchAssociative(); + } + + return $this->stmt->fetch(FetchMode::ASSOCIATIVE); + } catch (Exception $e) { + $this->conn->handleDriverException($e); + } + } + + /** + * {@inheritDoc} + * + * @throws Exception + */ + public function fetchOne() + { + try { + if ($this->stmt instanceof Result) { + return $this->stmt->fetchOne(); + } + + return $this->stmt->fetch(FetchMode::COLUMN); + } catch (Exception $e) { + $this->conn->handleDriverException($e); + } + } + + /** + * {@inheritdoc} + * + * @throws Exception + */ + public function fetchAllNumeric(): array + { + try { + if ($this->stmt instanceof Result) { + return $this->stmt->fetchAllNumeric(); + } + + return $this->stmt->fetchAll(FetchMode::NUMERIC); + } catch (Exception $e) { + $this->conn->handleDriverException($e); + } + } + + /** + * {@inheritdoc} + * + * @throws Exception + */ + public function fetchAllAssociative(): array + { + try { + if ($this->stmt instanceof Result) { + return $this->stmt->fetchAllAssociative(); + } + + return $this->stmt->fetchAll(FetchMode::ASSOCIATIVE); + } catch (Exception $e) { + $this->conn->handleDriverException($e); + } + } + + /** + * {@inheritdoc} + * + * @throws Exception + */ + public function fetchFirstColumn(): array + { + try { + if ($this->stmt instanceof Result) { + return $this->stmt->fetchFirstColumn(); + } + + return $this->stmt->fetchAll(FetchMode::COLUMN); + } catch (Exception $e) { + $this->conn->handleDriverException($e); + } + } + + /** + * {@inheritDoc} + * + * @return Traversable> + * + * @throws Exception + */ + public function iterateNumeric(): Traversable + { + try { + if ($this->stmt instanceof Result) { + while (($row = $this->stmt->fetchNumeric()) !== false) { + yield $row; + } + } else { + while (($row = $this->stmt->fetch(FetchMode::NUMERIC)) !== false) { + yield $row; + } + } + } catch (Exception $e) { + $this->conn->handleDriverException($e); + } + } + + /** + * {@inheritDoc} + * + * @return Traversable> + * + * @throws Exception + */ + public function iterateAssociative(): Traversable + { + try { + if ($this->stmt instanceof Result) { + while (($row = $this->stmt->fetchAssociative()) !== false) { + yield $row; + } + } else { + while (($row = $this->stmt->fetch(FetchMode::ASSOCIATIVE)) !== false) { + yield $row; + } + } + } catch (Exception $e) { + $this->conn->handleDriverException($e); + } + } + + /** + * {@inheritDoc} + * + * @return Traversable + * + * @throws Exception + */ + public function iterateColumn(): Traversable + { + try { + if ($this->stmt instanceof Result) { + while (($value = $this->stmt->fetchOne()) !== false) { + yield $value; + } + } else { + while (($value = $this->stmt->fetch(FetchMode::COLUMN)) !== false) { + yield $value; + } + } + } catch (Exception $e) { + $this->conn->handleDriverException($e); + } + } + /** * Returns the number of rows affected by the last execution of this statement. * @@ -271,6 +467,17 @@ public function rowCount() return $this->stmt->rowCount(); } + public function free(): void + { + if ($this->stmt instanceof Result) { + $this->stmt->free(); + + return; + } + + $this->stmt->closeCursor(); + } + /** * Gets the wrapped driver statement. * diff --git a/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php b/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php index c53c9ccf17..3d53f34296 100644 --- a/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php +++ b/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php @@ -2,8 +2,8 @@ namespace Doctrine\DBAL\Tools\Console\Command; -use Doctrine\DBAL\Driver\PDOConnection; -use Doctrine\DBAL\Driver\PDOStatement; +use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection; +use Doctrine\DBAL\Driver\PDO\Statement as PDOStatement; use InvalidArgumentException; use PDOException; use RuntimeException; diff --git a/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php b/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php index d454c97476..50e6a59343 100644 --- a/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php +++ b/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php @@ -21,6 +21,8 @@ use Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords; use Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords; use Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords; +use Doctrine\DBAL\Tools\Console\ConnectionProvider; +use Exception; use InvalidArgumentException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -31,6 +33,10 @@ use function assert; use function count; use function implode; +use function is_string; +use function trigger_error; + +use const E_USER_DEPRECATED; class ReservedWordsCommand extends Command { @@ -55,6 +61,23 @@ class ReservedWordsCommand extends Command 'sqlanywhere16' => SQLAnywhere16Keywords::class, ]; + /** @var ConnectionProvider|null */ + private $connectionProvider; + + public function __construct(?ConnectionProvider $connectionProvider = null) + { + parent::__construct(); + $this->connectionProvider = $connectionProvider; + if ($connectionProvider !== null) { + return; + } + + @trigger_error( + 'Not passing a connection provider as the first constructor argument is deprecated', + E_USER_DEPRECATED + ); + } + /** * If you want to add or replace a keywords list use this command. * @@ -74,12 +97,14 @@ protected function configure() $this ->setName('dbal:reserved-words') ->setDescription('Checks if the current database contains identifiers that are reserved.') - ->setDefinition([new InputOption( - 'list', - 'l', - InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'Keyword-List name.' - ), + ->setDefinition([ + new InputOption('connection', null, InputOption::VALUE_REQUIRED, 'The named database connection'), + new InputOption( + 'list', + 'l', + InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + 'Keyword-List name.' + ), ]) ->setHelp(<<getHelper('db')->getConnection(); - assert($conn instanceof Connection); + $conn = $this->getConnection($input); $keywordLists = (array) $input->getOption('list'); if (! $keywordLists) { @@ -187,4 +211,24 @@ protected function execute(InputInterface $input, OutputInterface $output) return 0; } + + private function getConnection(InputInterface $input): Connection + { + $connectionName = $input->getOption('connection'); + assert(is_string($connectionName) || $connectionName === null); + + if ($this->connectionProvider === null) { + if ($connectionName !== null) { + throw new Exception('Specifying a connection is only supported when a ConnectionProvider is used.'); + } + + return $this->getHelper('db')->getConnection(); + } + + if ($connectionName !== null) { + return $this->connectionProvider->getConnection($connectionName); + } + + return $this->connectionProvider->getDefaultConnection(); + } } diff --git a/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php b/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php index d17db5ae4c..f725386640 100644 --- a/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php +++ b/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php @@ -2,7 +2,10 @@ namespace Doctrine\DBAL\Tools\Console\Command; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Tools\Console\ConnectionProvider; use Doctrine\DBAL\Tools\Dumper; +use Exception; use LogicException; use RuntimeException; use Symfony\Component\Console\Command\Command; @@ -15,6 +18,9 @@ use function is_numeric; use function is_string; use function stripos; +use function trigger_error; + +use const E_USER_DEPRECATED; /** * Task for executing arbitrary SQL that can come from a file or directly from @@ -22,6 +28,23 @@ */ class RunSqlCommand extends Command { + /** @var ConnectionProvider|null */ + private $connectionProvider; + + public function __construct(?ConnectionProvider $connectionProvider = null) + { + parent::__construct(); + $this->connectionProvider = $connectionProvider; + if ($connectionProvider !== null) { + return; + } + + @trigger_error( + 'Not passing a connection provider as the first constructor argument is deprecated', + E_USER_DEPRECATED + ); + } + /** @return void */ protected function configure() { @@ -29,12 +52,16 @@ protected function configure() ->setName('dbal:run-sql') ->setDescription('Executes arbitrary SQL directly from the command line.') ->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('force-fetch', null, InputOption::VALUE_NONE, 'Forces fetching the result.'), ]) ->setHelp(<<%command.name% command executes the given SQL query and +outputs the results: + +php %command.full_name% "SELECT * FROM users" EOT ); } @@ -44,7 +71,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $conn = $this->getHelper('db')->getConnection(); + $conn = $this->getConnection($input); $sql = $input->getArgument('sql'); @@ -61,13 +88,33 @@ protected function execute(InputInterface $input, OutputInterface $output) } if (stripos($sql, 'select') === 0 || $input->getOption('force-fetch')) { - $resultSet = $conn->fetchAll($sql); + $resultSet = $conn->fetchAllAssociative($sql); } else { - $resultSet = $conn->executeUpdate($sql); + $resultSet = $conn->executeStatement($sql); } $output->write(Dumper::dump($resultSet, (int) $depth)); return 0; } + + private function getConnection(InputInterface $input): Connection + { + $connectionName = $input->getOption('connection'); + assert(is_string($connectionName) || $connectionName === null); + + if ($this->connectionProvider === null) { + if ($connectionName !== null) { + throw new Exception('Specifying a connection is only supported when a ConnectionProvider is used.'); + } + + return $this->getHelper('db')->getConnection(); + } + + if ($connectionName !== null) { + return $this->connectionProvider->getConnection($connectionName); + } + + return $this->connectionProvider->getDefaultConnection(); + } } diff --git a/lib/Doctrine/DBAL/Tools/Console/ConnectionNotFound.php b/lib/Doctrine/DBAL/Tools/Console/ConnectionNotFound.php new file mode 100644 index 0000000000..81ca4182a0 --- /dev/null +++ b/lib/Doctrine/DBAL/Tools/Console/ConnectionNotFound.php @@ -0,0 +1,9 @@ +connection = $connection; + $this->defaultConnectionName = $defaultConnectionName; + } + + public function getDefaultConnection(): Connection + { + return $this->connection; + } + + public function getConnection(string $name): Connection + { + if ($name !== $this->defaultConnectionName) { + throw new ConnectionNotFound(sprintf('Connection with name "%s" does not exist.', $name)); + } + + return $this->connection; + } +} diff --git a/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php b/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php index e028c807c0..3d73388c73 100644 --- a/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php +++ b/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php @@ -11,6 +11,12 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\HelperSet; +use TypeError; + +use function sprintf; +use function trigger_error; + +use const E_USER_DEPRECATED; /** * Handles running the Console Tools inside Symfony Console context. @@ -20,6 +26,8 @@ class ConsoleRunner /** * Create a Symfony Console HelperSet * + * @deprecated use a ConnectionProvider instead. + * * @return HelperSet */ public static function createHelperSet(Connection $connection) @@ -30,20 +38,39 @@ public static function createHelperSet(Connection $connection) } /** - * Runs console with the given helperset. + * Runs console with the given connection provider or helperset (deprecated). * - * @param Command[] $commands + * @param ConnectionProvider|HelperSet $helperSetOrConnectionProvider + * @param Command[] $commands * * @return void */ - public static function run(HelperSet $helperSet, $commands = []) + public static function run($helperSetOrConnectionProvider, $commands = []) { $cli = new Application('Doctrine Command Line Interface', Version::VERSION); $cli->setCatchExceptions(true); - $cli->setHelperSet($helperSet); - self::addCommands($cli); + $connectionProvider = null; + if ($helperSetOrConnectionProvider instanceof HelperSet) { + @trigger_error(sprintf( + 'Passing an instance of "%s" as the first argument is deprecated. Pass an instance of "%s" instead.', + HelperSet::class, + ConnectionProvider::class + ), E_USER_DEPRECATED); + $connectionProvider = null; + $cli->setHelperSet($helperSetOrConnectionProvider); + } elseif ($helperSetOrConnectionProvider instanceof ConnectionProvider) { + $connectionProvider = $helperSetOrConnectionProvider; + } else { + throw new TypeError(sprintf( + 'First argument must be an instance of "%s" or "%s"', + HelperSet::class, + ConnectionProvider::class + )); + } + + self::addCommands($cli, $connectionProvider); $cli->addCommands($commands); $cli->run(); @@ -52,12 +79,12 @@ public static function run(HelperSet $helperSet, $commands = []) /** * @return void */ - public static function addCommands(Application $cli) + public static function addCommands(Application $cli, ?ConnectionProvider $connectionProvider = null) { $cli->addCommands([ - new RunSqlCommand(), + new RunSqlCommand($connectionProvider), new ImportCommand(), - new ReservedWordsCommand(), + new ReservedWordsCommand($connectionProvider), ]); } @@ -74,14 +101,17 @@ public static function printCliConfigTemplate() following sample as a template: getAsciiStringTypeDeclarationSQL($column); + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return ParameterType::ASCII; + } + + public function getName(): string + { + return Types::ASCII_STRING; + } +} diff --git a/lib/Doctrine/DBAL/Types/ConversionException.php b/lib/Doctrine/DBAL/Types/ConversionException.php index 55dfedee75..5be4743a5e 100644 --- a/lib/Doctrine/DBAL/Types/ConversionException.php +++ b/lib/Doctrine/DBAL/Types/ConversionException.php @@ -2,7 +2,7 @@ namespace Doctrine\DBAL\Types; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Throwable; use function get_class; @@ -19,7 +19,7 @@ * * @psalm-immutable */ -class ConversionException extends DBALException +class ConversionException extends Exception { /** * Thrown when a Database to Doctrine Type Conversion fails. diff --git a/lib/Doctrine/DBAL/Types/Type.php b/lib/Doctrine/DBAL/Types/Type.php index 0e56a5658f..5ff51c4c9b 100644 --- a/lib/Doctrine/DBAL/Types/Type.php +++ b/lib/Doctrine/DBAL/Types/Type.php @@ -2,7 +2,7 @@ namespace Doctrine\DBAL\Types; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\AbstractPlatform; @@ -99,6 +99,7 @@ abstract class Type */ private const BUILTIN_TYPES_MAP = [ Types::ARRAY => ArrayType::class, + Types::ASCII_STRING => AsciiStringType::class, Types::BIGINT => BigIntType::class, Types::BINARY => BinaryType::class, Types::BLOB => BlobType::class, @@ -208,13 +209,13 @@ final public static function getTypeRegistry(): TypeRegistry private static function createTypeRegistry(): TypeRegistry { - $registry = new TypeRegistry(); + $instances = []; foreach (self::BUILTIN_TYPES_MAP as $name => $class) { - $registry->register($name, new $class()); + $instances[$name] = new $class(); } - return $registry; + return new TypeRegistry($instances); } /** @@ -225,7 +226,7 @@ private static function createTypeRegistry(): TypeRegistry * * @return Type * - * @throws DBALException + * @throws Exception */ public static function getType($name) { @@ -240,7 +241,7 @@ public static function getType($name) * * @return void * - * @throws DBALException + * @throws Exception */ public static function addType($name, $className) { @@ -267,7 +268,7 @@ public static function hasType($name) * * @return void * - * @throws DBALException + * @throws Exception */ public static function overrideType($name, $className) { diff --git a/lib/Doctrine/DBAL/Types/TypeRegistry.php b/lib/Doctrine/DBAL/Types/TypeRegistry.php index 362a5ccc9a..fdae6d603c 100644 --- a/lib/Doctrine/DBAL/Types/TypeRegistry.php +++ b/lib/Doctrine/DBAL/Types/TypeRegistry.php @@ -4,7 +4,7 @@ namespace Doctrine\DBAL\Types; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use function array_search; use function in_array; @@ -18,17 +18,25 @@ final class TypeRegistry { /** @var array Map of type names and their corresponding flyweight objects. */ - private $instances = []; + private $instances; + + /** + * @param array $instances + */ + public function __construct(array $instances = []) + { + $this->instances = $instances; + } /** * Finds a type by the given name. * - * @throws DBALException + * @throws Exception */ public function get(string $name): Type { if (! isset($this->instances[$name])) { - throw DBALException::unknownColumnType($name); + throw Exception::unknownColumnType($name); } return $this->instances[$name]; @@ -37,14 +45,14 @@ public function get(string $name): Type /** * Finds a name for the given type. * - * @throws DBALException + * @throws Exception */ public function lookupName(Type $type): string { $name = $this->findTypeName($type); if ($name === null) { - throw DBALException::typeNotRegistered($type); + throw Exception::typeNotRegistered($type); } return $name; @@ -61,16 +69,16 @@ public function has(string $name): bool /** * Registers a custom type to the type map. * - * @throws DBALException + * @throws Exception */ public function register(string $name, Type $type): void { if (isset($this->instances[$name])) { - throw DBALException::typeExists($name); + throw Exception::typeExists($name); } if ($this->findTypeName($type) !== null) { - throw DBALException::typeAlreadyRegistered($type); + throw Exception::typeAlreadyRegistered($type); } $this->instances[$name] = $type; @@ -79,16 +87,16 @@ public function register(string $name, Type $type): void /** * Overrides an already defined type to use a different implementation. * - * @throws DBALException + * @throws Exception */ public function override(string $name, Type $type): void { if (! isset($this->instances[$name])) { - throw DBALException::typeNotFound($name); + throw Exception::typeNotFound($name); } if (! in_array($this->findTypeName($type), [$name, null], true)) { - throw DBALException::typeAlreadyRegistered($type); + throw Exception::typeAlreadyRegistered($type); } $this->instances[$name] = $type; diff --git a/lib/Doctrine/DBAL/Types/Types.php b/lib/Doctrine/DBAL/Types/Types.php index 37a3d444bc..3ca677942c 100644 --- a/lib/Doctrine/DBAL/Types/Types.php +++ b/lib/Doctrine/DBAL/Types/Types.php @@ -10,6 +10,7 @@ final class Types { public const ARRAY = 'array'; + public const ASCII_STRING = 'ascii_string'; public const BIGINT = 'bigint'; public const BINARY = 'binary'; public const BLOB = 'blob'; diff --git a/lib/Doctrine/DBAL/Version.php b/lib/Doctrine/DBAL/Version.php index 845ecf3f39..cc4069ee9d 100644 --- a/lib/Doctrine/DBAL/Version.php +++ b/lib/Doctrine/DBAL/Version.php @@ -8,13 +8,16 @@ /** * Class to store and retrieve the version of Doctrine. + * + * @internal + * @deprecated Refrain from checking the DBAL version at runtime. */ class Version { /** * Current Doctrine Version. */ - public const VERSION = '2.10.4'; + public const VERSION = '2.11.0'; /** * Compares a Doctrine version with the current one. diff --git a/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php b/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php index a4864d0b28..8bdae92644 100644 --- a/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php +++ b/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php @@ -22,7 +22,7 @@ interface VersionAwarePlatformDriver * * @return AbstractPlatform * - * @throws DBALException If the given version string could not be evaluated. + * @throws Exception If the given version string could not be evaluated. */ public function createDatabasePlatformForVersion($version); } diff --git a/phpcs.xml.dist b/phpcs.xml.dist index b5ebec7a62..c82ca439ec 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -97,6 +97,7 @@ https://github.com/squizlabs/PHP_CodeSniffer/issues/2950 --> lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php + lib/Doctrine/DBAL/Driver/Mysqli/Exception/ConnectionFailed.php lib/Doctrine/DBAL/SQLParserUtils.php lib/Doctrine/DBAL/Tools/Dumper.php @@ -109,6 +110,12 @@ lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php + + + lib/Doctrine/DBAL/Driver/ExceptionConverterDriver.php + lib/Doctrine/DBAL/Driver/PDOException.php + + lib/Doctrine/DBAL/Schema/Comparator.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index ba1a1b9558..38c7bdace8 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -5,6 +5,10 @@ parameters: reportUnmatchedIgnoredErrors: false checkMissingIterableValueType: false checkGenericClassInNonGenericObjectType: false + earlyTerminatingMethodCalls: + Doctrine\DBAL\Connection: + - handleDriverException + - handleExceptionDuringQuery ignoreErrors: # extension not available - '~^(Used )?(Function|Constant) sasql_\S+ not found\.\z~i' @@ -17,11 +21,6 @@ parameters: - '~^Property Doctrine\\DBAL\\Schema\\Schema::\$_schemaConfig \(Doctrine\\DBAL\\Schema\\SchemaConfig\) does not accept default value of type false\.\z~' - '~^Return type \(int\|false\) of method Doctrine\\DBAL\\Driver\\OCI8\\OCI8Connection\:\:lastInsertId\(\) should be compatible with return type \(string\) of method Doctrine\\DBAL\\Driver\\Connection::lastInsertId\(\)~' - # https://github.com/phpstan/phpstan/issues/3527 - - - message: '~^Call to private method sqliteCreateFunction\(\) of parent class PDO\.$~' - path: %currentWorkingDirectory%/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php - # legacy variadic-like signature # TODO: remove in 3.0.0 - '~^Method Doctrine\\DBAL(\\.*)?Connection::query\(\) invoked with \d+ parameters?, 0 required\.\z~' diff --git a/phpunit.xml.dist b/phpunit.xml.dist index efdf1ce659..6d693d9c92 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,7 +12,7 @@ Example: phpunit -c mysqlconf.xml --> - ./tests/Doctrine/Tests/DBAL - ./tests/Doctrine/Tests/DBAL/Performance - - - ./tests/Doctrine/Tests/DBAL/Performance + tests/Doctrine/Tests/DBAL - - - lib/Doctrine - - - - - - - - - - performance - - + + + lib/Doctrine + + diff --git a/psalm.xml b/psalm.xml index f2fbc3cdbc..c813b8ce45 100644 --- a/psalm.xml +++ b/psalm.xml @@ -37,6 +37,7 @@ + diff --git a/tests/Doctrine/Tests/DBAL/Connection/LoggingTest.php b/tests/Doctrine/Tests/DBAL/Connection/LoggingTest.php index 7ffc32b6e0..290624767a 100644 --- a/tests/Doctrine/Tests/DBAL/Connection/LoggingTest.php +++ b/tests/Doctrine/Tests/DBAL/Connection/LoggingTest.php @@ -22,13 +22,13 @@ public function testLogExecuteQuery(): void ->executeQuery('SELECT * FROM table'); } - public function testLogExecuteUpdate(): void + public function testLogExecuteStatement(): void { $this->createConnection( $this->createStub(DriverConnection::class), 'UPDATE table SET foo = ?' ) - ->executeUpdate('UPDATE table SET foo = ?'); + ->executeStatement('UPDATE table SET foo = ?'); } public function testLogPrepareExecute(): void @@ -54,7 +54,7 @@ private function createConnection(DriverConnection $driverConnection, string $ex $logger->expects($this->once()) ->method('startQuery') ->with($this->equalTo($expectedSQL), $this->equalTo([])); - $logger->expects($this->at(1)) + $logger->expects($this->once()) ->method('stopQuery'); $connection = new Connection([], $driver); diff --git a/tests/Doctrine/Tests/DBAL/ConnectionTest.php b/tests/Doctrine/Tests/DBAL/ConnectionTest.php index 528dd019c9..8bcf1b817f 100644 --- a/tests/Doctrine/Tests/DBAL/ConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/ConnectionTest.php @@ -9,13 +9,13 @@ use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; use Doctrine\DBAL\ConnectionException; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\Connection as DriverConnection; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Events; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\InvalidArgumentException; use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\Logging\DebugStack; @@ -23,7 +23,6 @@ use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\Tests\DbalTestCase; -use Exception; use PHPUnit\Framework\MockObject\MockObject; use stdClass; @@ -54,7 +53,7 @@ protected function setUp(): void /** * @return Connection|MockObject */ - private function getExecuteUpdateMockConnection() + private function getExecuteStatementMockConnection() { $driverMock = $this->createMock(Driver::class); @@ -67,7 +66,7 @@ private function getExecuteUpdateMockConnection() $platform = $this->getMockForAbstractClass(AbstractPlatform::class); return $this->getMockBuilder(Connection::class) - ->onlyMethods(['executeUpdate']) + ->onlyMethods(['executeStatement']) ->setConstructorArgs([['platform' => $platform], $driverMock]) ->getMock(); } @@ -135,7 +134,7 @@ public function testGetPassword(): void public function testGetDriver(): void { - self::assertInstanceOf(\Doctrine\DBAL\Driver\PDOMySql\Driver::class, $this->connection->getDriver()); + self::assertInstanceOf(Driver\PDO\MySQL\Driver::class, $this->connection->getDriver()); } public function testGetEventManager(): void @@ -152,7 +151,7 @@ public function testConnectDispatchEvent(): void $eventManager->addEventListener([Events::postConnect], $listenerMock); $driverMock = $this->createMock(Driver::class); - $driverMock->expects($this->at(0)) + $driverMock->expects($this->once()) ->method('connect'); $conn = new Connection([], $driverMock, new Configuration(), $eventManager); @@ -183,7 +182,7 @@ public function testEventManagerPassedToPlatform(): void */ public function testDriverExceptionIsWrapped(string $method): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->expectExceptionMessage(<<getExecuteUpdateMockConnection(); + $conn = $this->getExecuteStatementMockConnection(); $conn->expects($this->once()) - ->method('executeUpdate') + ->method('executeStatement') ->with('INSERT INTO footable () VALUES ()'); $conn->insert('footable', []); @@ -366,10 +365,10 @@ public function testEmptyInsert(): void public function testUpdateWithDifferentColumnsInDataAndIdentifiers(): void { - $conn = $this->getExecuteUpdateMockConnection(); + $conn = $this->getExecuteStatementMockConnection(); $conn->expects($this->once()) - ->method('executeUpdate') + ->method('executeStatement') ->with( 'UPDATE TestTable SET text = ?, is_edited = ? WHERE id = ? AND name = ?', [ @@ -407,10 +406,10 @@ public function testUpdateWithDifferentColumnsInDataAndIdentifiers(): void public function testUpdateWithSameColumnInDataAndIdentifiers(): void { - $conn = $this->getExecuteUpdateMockConnection(); + $conn = $this->getExecuteStatementMockConnection(); $conn->expects($this->once()) - ->method('executeUpdate') + ->method('executeStatement') ->with( 'UPDATE TestTable SET text = ?, is_edited = ? WHERE id = ? AND is_edited = ?', [ @@ -447,10 +446,10 @@ public function testUpdateWithSameColumnInDataAndIdentifiers(): void public function testUpdateWithIsNull(): void { - $conn = $this->getExecuteUpdateMockConnection(); + $conn = $this->getExecuteStatementMockConnection(); $conn->expects($this->once()) - ->method('executeUpdate') + ->method('executeStatement') ->with( 'UPDATE TestTable SET text = ?, is_edited = ? WHERE id IS NULL AND name = ?', [ @@ -486,10 +485,10 @@ public function testUpdateWithIsNull(): void public function testDeleteWithIsNull(): void { - $conn = $this->getExecuteUpdateMockConnection(); + $conn = $this->getExecuteStatementMockConnection(); $conn->expects($this->once()) - ->method('executeUpdate') + ->method('executeStatement') ->with( 'DELETE FROM TestTable WHERE id IS NULL AND name = ?', ['foo'], @@ -696,7 +695,7 @@ public static function dataCallConnectOnce(): iterable ['insert', ['tbl', ['data' => 'foo']]], ['update', ['tbl', ['data' => 'bar'], ['id' => 12345]]], ['prepare', ['select * from dual']], - ['executeUpdate', ['insert into tbl (id) values (?)'], [123]], + ['executeStatement', ['insert into tbl (id) values (?)'], [123]], ]; } @@ -833,7 +832,7 @@ public function testThrowsExceptionWhenInValidPlatformSpecified(): void $driver = $this->createMock(Driver::class); - $this->expectException(DBALException::class); + $this->expectException(Exception::class); new Connection($connectionParams, $driver); } @@ -843,16 +842,14 @@ public function testRethrowsOriginalExceptionOnDeterminingPlatformWhenConnecting $driverMock = $this->createMock(FutureVersionAwarePlatformDriver::class); $connection = new Connection(['dbname' => 'foo'], $driverMock); - $originalException = new Exception('Original exception'); - $fallbackException = new Exception('Fallback exception'); - - $driverMock->expects($this->at(0)) - ->method('connect') - ->willThrowException($originalException); + $originalException = new \Exception('Original exception'); + $fallbackException = new \Exception('Fallback exception'); - $driverMock->expects($this->at(1)) - ->method('connect') - ->willThrowException($fallbackException); + $driverMock->method('connect') + ->will(self::onConsecutiveCalls( + self::throwException($originalException), + self::throwException($fallbackException) + )); $this->expectExceptionMessage($originalException->getMessage()); diff --git a/tests/Doctrine/Tests/DBAL/DBALExceptionTest.php b/tests/Doctrine/Tests/DBAL/DBALExceptionTest.php index 8fa510e8d0..5f7f137225 100644 --- a/tests/Doctrine/Tests/DBAL/DBALExceptionTest.php +++ b/tests/Doctrine/Tests/DBAL/DBALExceptionTest.php @@ -2,12 +2,11 @@ namespace Doctrine\Tests\DBAL; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\DriverException as InnerDriverException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\DriverException; use Doctrine\Tests\DbalTestCase; -use Exception; use stdClass; use function chr; @@ -19,7 +18,7 @@ class DBALExceptionTest extends DbalTestCase public function testDriverExceptionDuringQueryAcceptsBinaryData(): void { $driver = $this->createMock(Driver::class); - $e = DBALException::driverExceptionDuringQuery($driver, new Exception(), '', ['ABC', chr(128)]); + $e = Exception::driverExceptionDuringQuery($driver, new \Exception(), '', ['ABC', chr(128)]); self::assertStringContainsString('with params ["ABC", "\x80"]', $e->getMessage()); } @@ -27,9 +26,9 @@ public function testDriverExceptionDuringQueryAcceptsResource(): void { $driver = $this->createMock(Driver::class); - $e = DBALException::driverExceptionDuringQuery( + $e = Exception::driverExceptionDuringQuery( $driver, - new Exception(), + new \Exception(), 'INSERT INTO file (`content`) VALUES (?)', [1 => fopen(__FILE__, 'r')] ); @@ -44,16 +43,16 @@ public function testAvoidOverWrappingOnDriverException(): void $inner = $this->createMock(InnerDriverException::class); $ex = new DriverException('', $inner); - $e = DBALException::driverExceptionDuringQuery($driver, $ex, ''); + $e = Exception::driverExceptionDuringQuery($driver, $ex, ''); self::assertSame($ex, $e); } public function testDriverRequiredWithUrl(): void { $url = 'mysql://localhost'; - $exception = DBALException::driverRequired($url); + $exception = Exception::driverRequired($url); - self::assertInstanceOf(DBALException::class, $exception); + self::assertInstanceOf(Exception::class, $exception); self::assertSame( sprintf( "The options 'driver' or 'driverClass' are mandatory if a connection URL without scheme " . @@ -66,7 +65,7 @@ public function testDriverRequiredWithUrl(): void public function testInvalidPlatformTypeObject(): void { - $exception = DBALException::invalidPlatformType(new stdClass()); + $exception = Exception::invalidPlatformType(new stdClass()); self::assertSame( "Option 'platform' must be a subtype of 'Doctrine\DBAL\Platforms\AbstractPlatform', " @@ -77,7 +76,7 @@ public function testInvalidPlatformTypeObject(): void public function testInvalidPlatformTypeScalar(): void { - $exception = DBALException::invalidPlatformType('some string'); + $exception = Exception::invalidPlatformType('some string'); self::assertSame( "Option 'platform' must be an object and subtype of 'Doctrine\DBAL\Platforms\AbstractPlatform'. " diff --git a/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php index 29a8441f64..078875a408 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php @@ -3,10 +3,10 @@ namespace Doctrine\Tests\DBAL\Driver; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface; use Doctrine\DBAL\Driver\ExceptionConverterDriver; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\ConnectionException; use Doctrine\DBAL\Exception\ConstraintViolationException; use Doctrine\DBAL\Exception\DatabaseObjectExistsException; @@ -138,7 +138,7 @@ public function testThrowsExceptionOnCreatingDatabasePlatformsForInvalidVersion( $this->markTestSkipped('This test is only intended for version aware platform drivers.'); } - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->driver->createDatabasePlatformForVersion('foo'); } diff --git a/tests/Doctrine/Tests/DBAL/Driver/AbstractSQLServerDriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/AbstractSQLServerDriverTest.php index bd9b8b6b11..c55fba56fa 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/AbstractSQLServerDriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/AbstractSQLServerDriverTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Driver; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Driver\AbstractSQLServerDriver\PortWithoutHost; +use Doctrine\DBAL\Driver\AbstractSQLServerDriver\Exception\PortWithoutHost; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SQLServer2005Platform; use Doctrine\DBAL\Platforms\SQLServer2008Platform; diff --git a/tests/Doctrine/Tests/DBAL/Driver/IBMDB2/DB2DriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/IBMDB2/DB2DriverTest.php index e48d3c40b6..33e76c5e53 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/IBMDB2/DB2DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/IBMDB2/DB2DriverTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Driver\IBMDB2; use Doctrine\DBAL\Driver as DriverInterface; -use Doctrine\DBAL\Driver\IBMDB2\DB2Driver; +use Doctrine\DBAL\Driver\IBMDB2\Driver; use Doctrine\Tests\DBAL\Driver\AbstractDB2DriverTest; class DB2DriverTest extends AbstractDB2DriverTest @@ -15,6 +15,6 @@ public function testReturnsName(): void protected function createDriver(): DriverInterface { - return new DB2Driver(); + return new Driver(); } } diff --git a/tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php b/tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php index ce1ce87a5a..e79869cf82 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php @@ -33,23 +33,12 @@ public function testExecute(array $params): void ->disableOriginalConstructor() ->getMock(); - $statement->expects($this->at(0)) + $statement->expects($this->exactly(3)) ->method('bindValue') - ->with( - $this->equalTo(1), - $this->equalTo($params[0]) - ); - $statement->expects($this->at(1)) - ->method('bindValue') - ->with( - $this->equalTo(2), - $this->equalTo($params[1]) - ); - $statement->expects($this->at(2)) - ->method('bindValue') - ->with( - $this->equalTo(3), - $this->equalTo($params[2]) + ->withConsecutive( + [1, $params[0]], + [2, $params[1]], + [3, $params[2]], ); // the return value is irrelevant to the test diff --git a/tests/Doctrine/Tests/DBAL/Driver/PDOExceptionTest.php b/tests/Doctrine/Tests/DBAL/Driver/PDO/ExceptionTest.php similarity index 78% rename from tests/Doctrine/Tests/DBAL/Driver/PDOExceptionTest.php rename to tests/Doctrine/Tests/DBAL/Driver/PDO/ExceptionTest.php index 98189453df..4ba493ab2e 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/PDOExceptionTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/PDO/ExceptionTest.php @@ -1,15 +1,16 @@ wrappedException = new \PDOException(self::MESSAGE, self::SQLSTATE); + $this->wrappedException = new PDOException(self::MESSAGE, self::SQLSTATE); $this->wrappedException->errorInfo = [self::SQLSTATE, self::ERROR_CODE]; - $this->exception = new PDOException($this->wrappedException); + $this->exception = new Exception($this->wrappedException); } public function testReturnsCode(): void diff --git a/tests/Doctrine/Tests/DBAL/Driver/PDOMySql/DriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/PDOMySql/DriverTest.php index e67a067340..9eef41ed4f 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/PDOMySql/DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/PDOMySql/DriverTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Driver\PDOMySql; use Doctrine\DBAL\Driver as DriverInterface; -use Doctrine\DBAL\Driver\PDOMySql\Driver; +use Doctrine\DBAL\Driver\PDO\MySQL\Driver; use Doctrine\Tests\DBAL\Driver\AbstractMySQLDriverTest; class DriverTest extends AbstractMySQLDriverTest diff --git a/tests/Doctrine/Tests/DBAL/Driver/PDOOracle/DriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/PDOOracle/DriverTest.php index ae647cbe0c..3042d6633e 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/PDOOracle/DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/PDOOracle/DriverTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Driver\PDOOracle; use Doctrine\DBAL\Driver as DriverInterface; -use Doctrine\DBAL\Driver\PDOOracle\Driver; +use Doctrine\DBAL\Driver\PDO\OCI\Driver; use Doctrine\Tests\DBAL\Driver\AbstractOracleDriverTest; class DriverTest extends AbstractOracleDriverTest diff --git a/tests/Doctrine/Tests/DBAL/Driver/PDOPgSql/DriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/PDOPgSql/DriverTest.php index dcc3855529..4a7044594f 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/PDOPgSql/DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/PDOPgSql/DriverTest.php @@ -3,8 +3,8 @@ namespace Doctrine\Tests\DBAL\Driver\PDOPgSql; use Doctrine\DBAL\Driver as DriverInterface; -use Doctrine\DBAL\Driver\PDOConnection; -use Doctrine\DBAL\Driver\PDOPgSql\Driver; +use Doctrine\DBAL\Driver\PDO\Connection; +use Doctrine\DBAL\Driver\PDO\PgSQL\Driver; use Doctrine\Tests\DBAL\Driver\AbstractPostgreSQLDriverTest; use Doctrine\Tests\TestUtil; use PDO; @@ -80,7 +80,7 @@ private function skipWhenNotUsingPdoPgsql(): void /** * @param array $driverOptions */ - private function connect(array $driverOptions): PDOConnection + private function connect(array $driverOptions): Connection { $params = TestUtil::getConnectionParams(); diff --git a/tests/Doctrine/Tests/DBAL/Driver/PDOSqlite/DriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/PDOSqlite/DriverTest.php index b360a46cba..004bb00f50 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/PDOSqlite/DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/PDOSqlite/DriverTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Driver\PDOSqlite; use Doctrine\DBAL\Driver as DriverInterface; -use Doctrine\DBAL\Driver\PDOSqlite\Driver; +use Doctrine\DBAL\Driver\PDO\SQLite\Driver; use Doctrine\Tests\DBAL\Driver\AbstractSQLiteDriverTest; class DriverTest extends AbstractSQLiteDriverTest diff --git a/tests/Doctrine/Tests/DBAL/Driver/PDOSqlsrv/DriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/PDOSqlsrv/DriverTest.php index e3ee50ff29..c83dd1cfb1 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/PDOSqlsrv/DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/PDOSqlsrv/DriverTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Driver\PDOSqlsrv; use Doctrine\DBAL\Driver as DriverInterface; -use Doctrine\DBAL\Driver\PDOSqlsrv\Driver; +use Doctrine\DBAL\Driver\PDO\SQLSrv\Driver; use Doctrine\Tests\DBAL\Driver\AbstractSQLServerDriverTest; class DriverTest extends AbstractSQLServerDriverTest diff --git a/tests/Doctrine/Tests/DBAL/DriverManagerTest.php b/tests/Doctrine/Tests/DBAL/DriverManagerTest.php index 1b4c36efc1..e6544de56f 100644 --- a/tests/Doctrine/Tests/DBAL/DriverManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/DriverManagerTest.php @@ -3,14 +3,14 @@ namespace Doctrine\Tests\DBAL; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Connections\MasterSlaveConnection; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver as DrizzlePDOMySqlDriver; -use Doctrine\DBAL\Driver\PDOMySql\Driver as PDOMySQLDriver; -use Doctrine\DBAL\Driver\PDOSqlite\Driver as PDOSqliteDriver; +use Doctrine\DBAL\Driver\PDO\MySQL\Driver as PDOMySQLDriver; +use Doctrine\DBAL\Driver\PDO\SQLite\Driver as PDOSQLiteDriver; use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver; use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Sharding\PoolingShardConnection; use Doctrine\DBAL\Sharding\ShardChoser\MultiTenantShardChoser; @@ -30,7 +30,7 @@ class DriverManagerTest extends DbalTestCase */ public function testInvalidPdoInstance(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); DriverManager::getConnection(['pdo' => 'test']); } @@ -61,14 +61,14 @@ public function testPdoInstanceSetErrorMode(): void public function testCheckParams(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); DriverManager::getConnection([]); } public function testInvalidDriver(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); DriverManager::getConnection(['driver' => 'invalid_driver']); } @@ -111,7 +111,7 @@ public function testCustomWrapper(): void */ public function testInvalidWrapperClass(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $options = [ 'pdo' => new PDO('sqlite::memory:'), @@ -123,7 +123,7 @@ public function testInvalidWrapperClass(): void public function testInvalidDriverClass(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $options = ['driverClass' => stdClass::class]; @@ -138,15 +138,15 @@ public function testValidDriverClass(): void self::assertInstanceOf(PDOMySQLDriver::class, $conn->getDriver()); } - public function testDatabaseUrlMasterSlave(): void + public function testDatabaseUrlPrimaryReplica(): void { $options = [ 'driver' => 'pdo_mysql', - 'master' => ['url' => 'mysql://foo:bar@localhost:11211/baz'], - 'slaves' => [ - 'slave1' => ['url' => 'mysql://foo:bar@localhost:11211/baz_slave'], + 'primary' => ['url' => 'mysql://foo:bar@localhost:11211/baz'], + 'replica' => [ + 'replica1' => ['url' => 'mysql://foo:bar@localhost:11211/baz_replica'], ], - 'wrapperClass' => MasterSlaveConnection::class, + 'wrapperClass' => PrimaryReadReplicaConnection::class, ]; $conn = DriverManager::getConnection($options); @@ -162,12 +162,12 @@ public function testDatabaseUrlMasterSlave(): void ]; foreach ($expected as $key => $value) { - self::assertEquals($value, $params['master'][$key]); - self::assertEquals($value, $params['slaves']['slave1'][$key]); + self::assertEquals($value, $params['primary'][$key]); + self::assertEquals($value, $params['replica']['replica1'][$key]); } - self::assertEquals('baz', $params['master']['dbname']); - self::assertEquals('baz_slave', $params['slaves']['slave1']['dbname']); + self::assertEquals('baz', $params['primary']['dbname']); + self::assertEquals('baz_replica', $params['replica']['replica1']['dbname']); } public function testDatabaseUrlShard(): void @@ -227,7 +227,7 @@ public function testDatabaseUrl($url, $expected): void $options = is_array($url) ? $url : ['url' => $url]; if ($expected === false) { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); } $conn = DriverManager::getConnection($options); @@ -276,42 +276,42 @@ public function databaseUrls(): iterable 'sqlite://localhost/foo/dbname.sqlite', [ 'path' => 'foo/dbname.sqlite', - 'driver' => PDOSqliteDriver::class, + 'driver' => PDOSQLiteDriver::class, ], ], 'sqlite absolute URL with host' => [ 'sqlite://localhost//tmp/dbname.sqlite', [ 'path' => '/tmp/dbname.sqlite', - 'driver' => PDOSqliteDriver::class, + 'driver' => PDOSQLiteDriver::class, ], ], 'sqlite relative URL without host' => [ 'sqlite:///foo/dbname.sqlite', [ 'path' => 'foo/dbname.sqlite', - 'driver' => PDOSqliteDriver::class, + 'driver' => PDOSQLiteDriver::class, ], ], 'sqlite absolute URL without host' => [ 'sqlite:////tmp/dbname.sqlite', [ 'path' => '/tmp/dbname.sqlite', - 'driver' => PDOSqliteDriver::class, + 'driver' => PDOSQLiteDriver::class, ], ], 'sqlite memory' => [ 'sqlite:///:memory:', [ 'memory' => true, - 'driver' => PDOSqliteDriver::class, + 'driver' => PDOSQLiteDriver::class, ], ], 'sqlite memory with host' => [ 'sqlite://localhost/:memory:', [ 'memory' => true, - 'driver' => PDOSqliteDriver::class, + 'driver' => PDOSQLiteDriver::class, ], ], 'params parsed from URL override individual params' => [ diff --git a/tests/Doctrine/Tests/DBAL/Events/MysqlSessionInitTest.php b/tests/Doctrine/Tests/DBAL/Events/MysqlSessionInitTest.php index 73df349a83..e2641e6010 100644 --- a/tests/Doctrine/Tests/DBAL/Events/MysqlSessionInitTest.php +++ b/tests/Doctrine/Tests/DBAL/Events/MysqlSessionInitTest.php @@ -14,7 +14,7 @@ public function testPostConnect(): void { $connectionMock = $this->createMock(Connection::class); $connectionMock->expects($this->once()) - ->method('executeUpdate') + ->method('executeStatement') ->with($this->equalTo('SET NAMES foo COLLATE bar')); $eventArgs = new ConnectionEventArgs($connectionMock); diff --git a/tests/Doctrine/Tests/DBAL/Events/OracleSessionInitTest.php b/tests/Doctrine/Tests/DBAL/Events/OracleSessionInitTest.php index 721b3cdeab..d5ee7990dc 100644 --- a/tests/Doctrine/Tests/DBAL/Events/OracleSessionInitTest.php +++ b/tests/Doctrine/Tests/DBAL/Events/OracleSessionInitTest.php @@ -16,7 +16,7 @@ public function testPostConnect(): void { $connectionMock = $this->createMock(Connection::class); $connectionMock->expects($this->once()) - ->method('executeUpdate') + ->method('executeStatement') ->with($this->isType('string')); $eventArgs = new ConnectionEventArgs($connectionMock); @@ -34,7 +34,7 @@ public function testPostConnectQuotesSessionParameterValues(string $name, string ->disableOriginalConstructor() ->getMock(); $connectionMock->expects($this->once()) - ->method('executeUpdate') + ->method('executeStatement') ->with($this->stringContains(sprintf('%s = %s', $name, $value))); $eventArgs = new ConnectionEventArgs($connectionMock); diff --git a/tests/Doctrine/Tests/DBAL/Functional/BlobTest.php b/tests/Doctrine/Tests/DBAL/Functional/BlobTest.php index 74a93ee6bc..f8393ede19 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/BlobTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/BlobTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Functional; use Doctrine\DBAL\Driver\OCI8\Driver as OCI8Driver; -use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver; +use Doctrine\DBAL\Driver\PDO; use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Schema\Table; @@ -20,7 +20,7 @@ protected function setUp(): void { parent::setUp(); - if ($this->connection->getDriver() instanceof PDOOracleDriver) { + if ($this->connection->getDriver() instanceof PDO\OCI\Driver) { // inserting BLOBs as streams on Oracle requires Oracle-specific SQL syntax which is currently not supported // see http://php.net/manual/en/pdo.lobs.php#example-1035 $this->markTestSkipped('DBAL doesn\'t support storing LOBs represented as streams using PDO_OCI'); diff --git a/tests/Doctrine/Tests/DBAL/Functional/Connection/BackwardCompatibility/Connection.php b/tests/Doctrine/Tests/DBAL/Functional/Connection/BackwardCompatibility/Connection.php new file mode 100644 index 0000000000..b864a79423 --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Connection/BackwardCompatibility/Connection.php @@ -0,0 +1,38 @@ +connection = DriverManager::getConnection( + array_merge($this->connection->getParams(), [ + 'wrapperClass' => Connection::class, + ]), + $this->connection->getConfiguration(), + $this->connection->getEventManager() + ); + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/Connection/BackwardCompatibility/Statement.php b/tests/Doctrine/Tests/DBAL/Functional/Connection/BackwardCompatibility/Statement.php new file mode 100644 index 0000000000..97fa03fed0 --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Connection/BackwardCompatibility/Statement.php @@ -0,0 +1,159 @@ +stmt = $stmt; + } + + /** + * {@inheritdoc} + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + { + assert($this->stmt instanceof DriverStatement); + + return $this->stmt->bindParam($param, $variable, $type, $length); + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = ParameterType::STRING) + { + assert($this->stmt instanceof DriverStatement); + + return $this->stmt->bindValue($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + return $this->stmt->closeCursor(); + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->stmt->columnCount(); + } + + /** + * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. + */ + public function errorCode() + { + assert($this->stmt instanceof DriverStatement); + + return $this->stmt->errorCode(); + } + + /** + * {@inheritdoc} + * + * @deprecated The error information is available via exceptions. + */ + public function errorInfo() + { + assert($this->stmt instanceof DriverStatement); + + return $this->stmt->errorInfo(); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + assert($this->stmt instanceof DriverStatement); + + return $this->stmt->execute($params); + } + + /** + * {@inheritdoc} + * + * @deprecated Use one of the fetch- or iterate-related methods. + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + return $this->stmt->setFetchMode($fetchMode, $arg2, $arg3); + } + + /** + * {@inheritdoc} + * + * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. + */ + public function getIterator() + { + return new StatementIterator($this); + } + + /** + * {@inheritdoc} + * + * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. + */ + public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + return $this->stmt->fetch($fetchMode); + } + + /** + * {@inheritdoc} + * + * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchColumn() instead. + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + return $this->stmt->fetchAll($fetchMode, $fetchArgument); + } + + /** + * {@inheritdoc} + * + * @deprecated Use fetchOne() instead. + */ + public function fetchColumn($columnIndex = 0) + { + return $this->stmt->fetchColumn($columnIndex); + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + assert($this->stmt instanceof DriverStatement); + + return $this->stmt->rowCount(); + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/Connection/ConnectionLostTest.php b/tests/Doctrine/Tests/DBAL/Functional/Connection/ConnectionLostTest.php new file mode 100644 index 0000000000..1c8e385a87 --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Connection/ConnectionLostTest.php @@ -0,0 +1,52 @@ +connection->getDatabasePlatform() instanceof MySqlPlatform) { + return; + } + + $this->markTestSkipped('Currently only supported with MySQL'); + } + + protected function tearDown(): void + { + $this->resetSharedConn(); + + parent::tearDown(); + } + + public function testConnectionLost(): void + { + $this->connection->query('SET SESSION wait_timeout=1'); + + sleep(2); + + $query = $this->connection->getDatabasePlatform() + ->getDummySelectSQL(); + + try { + // in addition to the error, PHP 7.3 will generate a warning that needs to be + // suppressed in order to not let PHPUnit handle it before the actual error + @$this->connection->executeQuery($query); + } catch (ConnectionLost $e) { + self::assertEquals(1, $this->connection->fetchOne($query)); + + return; + } + + self::fail('The connection should have lost'); + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/Connection/FetchTest.php b/tests/Doctrine/Tests/DBAL/Functional/Connection/FetchTest.php new file mode 100644 index 0000000000..d33fc1708e --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Connection/FetchTest.php @@ -0,0 +1,124 @@ +query = TestUtil::generateResultSetQuery([ + [ + 'a' => 'foo', + 'b' => 1, + ], + [ + 'a' => 'bar', + 'b' => 2, + ], + [ + 'a' => 'baz', + 'b' => 3, + ], + ], $this->connection->getDatabasePlatform()); + } + + public function testFetchNumeric(): void + { + self::assertEquals(['foo', 1], $this->connection->fetchNumeric($this->query)); + } + + public function testFetchOne(): void + { + self::assertEquals('foo', $this->connection->fetchOne($this->query)); + } + + public function testFetchAssociative(): void + { + self::assertEquals([ + 'a' => 'foo', + 'b' => 1, + ], $this->connection->fetchAssociative($this->query)); + } + + public function testFetchAllNumeric(): void + { + self::assertEquals([ + ['foo', 1], + ['bar', 2], + ['baz', 3], + ], $this->connection->fetchAllNumeric($this->query)); + } + + public function testFetchAllAssociative(): void + { + self::assertEquals([ + [ + 'a' => 'foo', + 'b' => 1, + ], + [ + 'a' => 'bar', + 'b' => 2, + ], + [ + 'a' => 'baz', + 'b' => 3, + ], + ], $this->connection->fetchAllAssociative($this->query)); + } + + public function testFetchFirstColumn(): void + { + self::assertEquals([ + 'foo', + 'bar', + 'baz', + ], $this->connection->fetchFirstColumn($this->query)); + } + + public function testIterateNumeric(): void + { + self::assertEquals([ + ['foo', 1], + ['bar', 2], + ['baz', 3], + ], iterator_to_array($this->connection->iterateNumeric($this->query))); + } + + public function testIterateAssociative(): void + { + self::assertEquals([ + [ + 'a' => 'foo', + 'b' => 1, + ], + [ + 'a' => 'bar', + 'b' => 2, + ], + [ + 'a' => 'baz', + 'b' => 3, + ], + ], iterator_to_array($this->connection->iterateAssociative($this->query))); + } + + public function testIterateColumn(): void + { + self::assertEquals([ + 'foo', + 'bar', + 'baz', + ], iterator_to_array($this->connection->iterateColumn($this->query))); + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/DataAccessTest.php b/tests/Doctrine/Tests/DBAL/Functional/DataAccessTest.php index 6be75df75f..ef68b5be62 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/DataAccessTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/DataAccessTest.php @@ -4,12 +4,13 @@ use DateTime; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\IBMDB2\Driver as IBMDB2Driver; use Doctrine\DBAL\Driver\Mysqli\Driver as MySQLiDriver; use Doctrine\DBAL\Driver\OCI8\Driver as Oci8Driver; -use Doctrine\DBAL\Driver\PDOConnection; -use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver; +use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection; +use Doctrine\DBAL\Driver\PDO\OCI\Driver as PDOOCIDriver; use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\SqlitePlatform; @@ -242,7 +243,10 @@ public function testFetchAllWithTypes(): void self::assertStringStartsWith($datetimeString, $row['test_datetime']); } - public function testFetchAllWithMissingTypes(): void + /** + * @dataProvider fetchProvider + */ + public function testFetchAllWithMissingTypes(callable $fetch): void { if ( $this->connection->getDriver() instanceof MySQLiDriver || @@ -251,13 +255,51 @@ public function testFetchAllWithMissingTypes(): void $this->markTestSkipped('mysqli and sqlsrv actually supports this'); } + if ( + $this->connection->getDriver() instanceof IBMDB2Driver + ) { + $this->markTestSkipped( + 'ibm_ibm2 may or may not report the error depending on the PHP version and the connection state' + ); + } + $datetimeString = '2010-01-01 10:10:10'; $datetime = new DateTime($datetimeString); $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?'; - $this->expectException(DBALException::class); + $this->expectException(Exception::class); + + $fetch($this->connection, $sql, [1, $datetime]); + } + + /** + * @return iterable + */ + public static function fetchProvider(): iterable + { + yield 'fetch-all-associative' => [ + static function (Connection $connection, string $query, array $params): void { + $connection->fetchAll($query, $params); + }, + ]; + + yield 'fetch-numeric' => [ + static function (Connection $connection, string $query, array $params): void { + $connection->fetchArray($query, $params); + }, + ]; + + yield 'fetch-associative' => [ + static function (Connection $connection, string $query, array $params): void { + $connection->fetchAssoc($query, $params); + }, + ]; - $this->connection->fetchAll($sql, [1, $datetime]); + yield 'fetch-one' => [ + static function (Connection $connection, string $query, array $params): void { + $connection->fetchColumn($query, $params); + }, + ]; } public function testFetchBoth(): void @@ -315,24 +357,6 @@ public function testFetchAssocWithTypes(): void self::assertStringStartsWith($datetimeString, $row['test_datetime']); } - public function testFetchAssocWithMissingTypes(): void - { - if ( - $this->connection->getDriver() instanceof MySQLiDriver || - $this->connection->getDriver() instanceof SQLSrvDriver - ) { - $this->markTestSkipped('mysqli and sqlsrv actually supports this'); - } - - $datetimeString = '2010-01-01 10:10:10'; - $datetime = new DateTime($datetimeString); - $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?'; - - $this->expectException(DBALException::class); - - $this->connection->fetchAssoc($sql, [1, $datetime]); - } - public function testFetchArray(): void { $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?'; @@ -362,24 +386,6 @@ public function testFetchArrayWithTypes(): void self::assertStringStartsWith($datetimeString, $row[1]); } - public function testFetchArrayWithMissingTypes(): void - { - if ( - $this->connection->getDriver() instanceof MySQLiDriver || - $this->connection->getDriver() instanceof SQLSrvDriver - ) { - $this->markTestSkipped('mysqli and sqlsrv actually supports this'); - } - - $datetimeString = '2010-01-01 10:10:10'; - $datetime = new DateTime($datetimeString); - $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?'; - - $this->expectException(DBALException::class); - - $this->connection->fetchArray($sql, [1, $datetime]); - } - public function testFetchColumn(): void { $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?'; @@ -411,24 +417,6 @@ public function testFetchColumnWithTypes(): void self::assertStringStartsWith($datetimeString, $column); } - public function testFetchColumnWithMissingTypes(): void - { - if ( - $this->connection->getDriver() instanceof MySQLiDriver || - $this->connection->getDriver() instanceof SQLSrvDriver - ) { - $this->markTestSkipped('mysqli and sqlsrv actually supports this'); - } - - $datetimeString = '2010-01-01 10:10:10'; - $datetime = new DateTime($datetimeString); - $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?'; - - $this->expectException(DBALException::class); - - $this->connection->fetchColumn($sql, [1, $datetime], 1); - } - public function testExecuteQueryBindDateTimeType(): void { $sql = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?'; @@ -441,12 +429,12 @@ public function testExecuteQueryBindDateTimeType(): void self::assertEquals(1, $stmt->fetchColumn()); } - public function testExecuteUpdateBindDateTimeType(): void + public function testExecuteStatementBindDateTimeType(): void { $datetime = new DateTime('2010-02-02 20:20:20'); $sql = 'INSERT INTO fetch_table (test_int, test_string, test_datetime) VALUES (?, ?, ?)'; - $affectedRows = $this->connection->executeUpdate($sql, [ + $affectedRows = $this->connection->executeStatement($sql, [ 1 => 50, 2 => 'foo', 3 => $datetime, @@ -787,7 +775,7 @@ public function testFetchAllSupportFetchClass(): void public function testFetchAllStyleColumn(): void { $sql = 'DELETE FROM fetch_table'; - $this->connection->executeUpdate($sql); + $this->connection->executeStatement($sql); $this->connection->insert('fetch_table', ['test_int' => 1, 'test_string' => 'foo']); $this->connection->insert('fetch_table', ['test_int' => 10, 'test_string' => 'foo']); @@ -872,7 +860,7 @@ public function testEmptyParameters(): void public function testFetchColumnNullValue(): void { - $this->connection->executeUpdate( + $this->connection->executeStatement( 'INSERT INTO fetch_table (test_int, test_string) VALUES (?, ?)', [2, 'foo'] ); @@ -911,7 +899,7 @@ private function beforeFetchClassTest(): void $this->markTestSkipped('Mysqli driver dont support this feature.'); } - if (! $driver instanceof PDOOracleDriver) { + if (! $driver instanceof PDOOCIDriver) { return; } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/ConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/ConnectionTest.php new file mode 100644 index 0000000000..d042ec5502 --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/ConnectionTest.php @@ -0,0 +1,56 @@ +markTestSkipped('ibm_db2 is not installed.'); + } + + parent::setUp(); + + if ($this->connection->getDriver() instanceof Driver) { + return; + } + + $this->markTestSkipped('ibm_db2 only test.'); + } + + protected function tearDown(): void + { + $this->resetSharedConn(); + } + + public function testConnectionFailure(): void + { + $this->expectException(ConnectionFailed::class); + new Connection(['dbname' => 'garbage'], '', ''); + } + + public function testPrepareFailure(): void + { + $driverConnection = $this->connection->getWrappedConnection(); + + $re = new ReflectionProperty(get_parent_class($driverConnection), 'conn'); + $re->setAccessible(true); + $conn = $re->getValue($driverConnection); + db2_close($conn); + + $this->expectException(PrepareFailed::class); + $driverConnection->prepare('SELECT 1'); + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/DB2StatementTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/DB2StatementTest.php deleted file mode 100644 index 9ffe5d7c06..0000000000 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/DB2StatementTest.php +++ /dev/null @@ -1,37 +0,0 @@ -connection->getDriver() instanceof DB2Driver) { - return; - } - - $this->markTestSkipped('ibm_db2 only test.'); - } - - public function testExecutionErrorsAreNotSuppressed(): void - { - $stmt = $this->connection->prepare('SELECT * FROM SYSIBM.SYSDUMMY1 WHERE \'foo\' = ?'); - - // unwrap the statement to prevent the wrapper from handling the PHPUnit-originated exception - $wrappedStmt = $stmt->getWrappedStatement(); - - $this->expectException(Notice::class); - $wrappedStmt->execute([[]]); - } -} diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/DB2DriverTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/DriverTest.php similarity index 71% rename from tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/DB2DriverTest.php rename to tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/DriverTest.php index 81606b2d58..49a6cd3f0b 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/DB2DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/DriverTest.php @@ -2,20 +2,20 @@ namespace Doctrine\Tests\DBAL\Functional\Driver\IBMDB2; -use Doctrine\DBAL\Driver; -use Doctrine\DBAL\Driver\IBMDB2\DB2Driver; +use Doctrine\DBAL\Driver as DriverInterface; +use Doctrine\DBAL\Driver\IBMDB2\Driver; use Doctrine\Tests\DBAL\Functional\Driver\AbstractDriverTest; /** * @requires extension ibm_db2 */ -class DB2DriverTest extends AbstractDriverTest +class DriverTest extends AbstractDriverTest { protected function setUp(): void { parent::setUp(); - if ($this->connection->getDriver() instanceof DB2Driver) { + if ($this->connection->getDriver() instanceof Driver) { return; } @@ -32,8 +32,8 @@ public function testReturnsDatabaseNameWithoutDatabaseNameParameter(): void $this->markTestSkipped('IBM DB2 does not support connecting without database name.'); } - protected function createDriver(): Driver + protected function createDriver(): DriverInterface { - return new DB2Driver(); + return new Driver(); } } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/StatementTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/StatementTest.php new file mode 100644 index 0000000000..eb23518500 --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/StatementTest.php @@ -0,0 +1,46 @@ +markTestSkipped('ibm_db2 is not installed.'); + } + + parent::setUp(); + + if ($this->connection->getDriver() instanceof Driver) { + return; + } + + $this->markTestSkipped('ibm_db2 only test.'); + } + + public function testExecutionErrorsAreNotSuppressed(): void + { + $driverConnection = $this->connection->getWrappedConnection(); + + $stmt = $driverConnection->prepare('SELECT * FROM SYSIBM.SYSDUMMY1 WHERE \'foo\' = ?'); + + // prevent the PHPUnit error handler from handling the errors that db2_execute() may trigger + $this->iniSet('error_reporting', (string) (E_ALL & ~E_WARNING & ~E_NOTICE)); + + $this->expectException(StatementError::class); + $stmt->execute([[]]); + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/Mysqli/ConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/Mysqli/ConnectionTest.php index 08bf20a742..a7cb0686eb 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/Mysqli/ConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/Mysqli/ConnectionTest.php @@ -2,8 +2,8 @@ namespace Doctrine\Tests\DBAL\Functional\Driver\Mysqli; +use Doctrine\DBAL\Driver\Mysqli\Connection; use Doctrine\DBAL\Driver\Mysqli\Driver; -use Doctrine\DBAL\Driver\Mysqli\MysqliConnection; use Doctrine\DBAL\Driver\Mysqli\MysqliException; use Doctrine\Tests\DbalFunctionalTestCase; use Doctrine\Tests\TestUtil; @@ -33,7 +33,7 @@ public function testDriverOptions(): void $driverOptions = [MYSQLI_OPT_CONNECT_TIMEOUT => 1]; $connection = $this->getConnection($driverOptions); - self::assertInstanceOf(MysqliConnection::class, $connection); + self::assertInstanceOf(Connection::class, $connection); } public function testUnsupportedDriverOption(): void @@ -52,7 +52,7 @@ public function testPing(): void /** * @param mixed[] $driverOptions */ - private function getConnection(array $driverOptions): MysqliConnection + private function getConnection(array $driverOptions): Connection { $params = TestUtil::getConnectionParams(); @@ -60,7 +60,7 @@ private function getConnection(array $driverOptions): MysqliConnection $driverOptions = array_merge($params['driverOptions'], $driverOptions); } - return new MysqliConnection( + return new Connection( $params, $params['user'] ?? '', $params['password'] ?? '', diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/OCI8/OCI8ConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/OCI8/ConnectionTest.php similarity index 84% rename from tests/Doctrine/Tests/DBAL/Functional/Driver/OCI8/OCI8ConnectionTest.php rename to tests/Doctrine/Tests/DBAL/Functional/Driver/OCI8/ConnectionTest.php index a38e099d57..f4947212ec 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/OCI8/OCI8ConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/OCI8/ConnectionTest.php @@ -2,17 +2,17 @@ namespace Doctrine\Tests\DBAL\Functional\Driver\OCI8; +use Doctrine\DBAL\Driver\OCI8\Connection; use Doctrine\DBAL\Driver\OCI8\Driver; -use Doctrine\DBAL\Driver\OCI8\OCI8Connection; use Doctrine\DBAL\Schema\Table; use Doctrine\Tests\DbalFunctionalTestCase; /** * @requires extension oci8 */ -class OCI8ConnectionTest extends DbalFunctionalTestCase +class ConnectionTest extends DbalFunctionalTestCase { - /** @var OCI8Connection */ + /** @var Connection */ protected $driverConnection; protected function setUp(): void @@ -37,7 +37,7 @@ public function testLastInsertIdAcceptsFqn(): void $schemaManager->dropAndCreateTable($table); - $this->connection->executeUpdate('INSERT INTO DBAL2595 (foo) VALUES (1)'); + $this->connection->executeStatement('INSERT INTO DBAL2595 (foo) VALUES (1)'); $schema = $this->connection->getDatabase(); $sequence = $platform->getIdentitySequenceName($schema . '.DBAL2595', 'id'); diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDO/ConnectionTest.php similarity index 72% rename from tests/Doctrine/Tests/DBAL/Functional/Driver/PDOConnectionTest.php rename to tests/Doctrine/Tests/DBAL/Functional/Driver/PDO/ConnectionTest.php index 7b302f6905..f54cb07b06 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDO/ConnectionTest.php @@ -1,12 +1,12 @@ driverConnection = $this->connection->getWrappedConnection(); - if ($this->driverConnection instanceof PDOConnection) { + if ($this->driverConnection instanceof Connection) { return; } @@ -52,14 +52,14 @@ public function testDoesNotRequireQueryForServerVersion(): void public function testThrowsWrappedExceptionOnConstruct(): void { - $this->expectException(PDOException::class); + $this->expectException(Exception::class); - new PDOConnection('foo'); + new Connection('foo'); } public function testThrowsWrappedExceptionOnExec(): void { - $this->expectException(PDOException::class); + $this->expectException(Exception::class); $this->driverConnection->exec('foo'); } @@ -68,7 +68,7 @@ public function testThrowsWrappedExceptionOnPrepare(): void { $driver = $this->connection->getDriver(); - if ($driver instanceof PDOSQLSRVDriver) { + if ($driver instanceof PDOSQLSrvDriver) { $this->markTestSkipped('pdo_sqlsrv does not allow setting PDO::ATTR_EMULATE_PREPARES at connection level.'); } @@ -76,7 +76,7 @@ public function testThrowsWrappedExceptionOnPrepare(): void // even though emulated prepared statements are disabled, // so an exception is thrown only eventually. if ( - $driver instanceof PDOOracleDriver + $driver instanceof PDOOCIDriver || $driver instanceof PDOPgSQLDriver ) { self::markTestSkipped(sprintf( @@ -89,14 +89,14 @@ public function testThrowsWrappedExceptionOnPrepare(): void // so that PDO actually communicates with the database server to check the query. $this->driverConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $this->expectException(PDOException::class); + $this->expectException(Exception::class); $this->driverConnection->prepare('foo'); } public function testThrowsWrappedExceptionOnQuery(): void { - $this->expectException(PDOException::class); + $this->expectException(Exception::class); $this->driverConnection->query('foo'); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOMySql/DriverTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOMySql/DriverTest.php index 597cc0918f..247b14b2e6 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOMySql/DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOMySql/DriverTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Functional\Driver\PDOMySql; use Doctrine\DBAL\Driver as DriverInterface; -use Doctrine\DBAL\Driver\PDOMySql\Driver; +use Doctrine\DBAL\Driver\PDO\MySQL\Driver; use Doctrine\Tests\DBAL\Functional\Driver\AbstractDriverTest; /** diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOOracle/DriverTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOOracle/DriverTest.php index 546307bb5b..f77d22ab67 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOOracle/DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOOracle/DriverTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Functional\Driver\PDOOracle; use Doctrine\DBAL\Driver as DriverInterface; -use Doctrine\DBAL\Driver\PDOOracle\Driver; +use Doctrine\DBAL\Driver\PDO\OCI\Driver; use Doctrine\Tests\DBAL\Functional\Driver\AbstractDriverTest; /** diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOPgsqlConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOPgSql/ConnectionTest.php similarity index 91% rename from tests/Doctrine/Tests/DBAL/Functional/Driver/PDOPgsqlConnectionTest.php rename to tests/Doctrine/Tests/DBAL/Functional/Driver/PDOPgSql/ConnectionTest.php index c11c56cd3c..c39f4c5765 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOPgsqlConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOPgSql/ConnectionTest.php @@ -1,6 +1,6 @@ expectException(Exception\ForeignKeyConstraintViolationException::class); try { - $this->connection->executeUpdate($platform->getTruncateTableSQL('constraint_error_table')); + $this->connection->executeStatement($platform->getTruncateTableSQL('constraint_error_table')); } catch (Exception\ForeignKeyConstraintViolationException $exception) { $this->tearDownForeignKeyConstraintViolationExceptionTest(); diff --git a/tests/Doctrine/Tests/DBAL/Functional/MasterSlaveConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/MasterSlaveConnectionTest.php index e975033577..13aaea3c6a 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/MasterSlaveConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/MasterSlaveConnectionTest.php @@ -40,7 +40,7 @@ protected function setUp(): void } catch (Throwable $e) { } - $this->connection->executeUpdate('DELETE FROM master_slave_table'); + $this->connection->executeStatement('DELETE FROM master_slave_table'); $this->connection->insert('master_slave_table', ['test_int' => 1]); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/PDOStatementTest.php b/tests/Doctrine/Tests/DBAL/Functional/PDOStatementTest.php index 9e27cc348b..ba23bb2039 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/PDOStatementTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/PDOStatementTest.php @@ -2,7 +2,7 @@ namespace Doctrine\Tests\DBAL\Functional; -use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\Driver\PDO\Connection; use Doctrine\DBAL\Schema\Table; use Doctrine\Tests\DbalFunctionalTestCase; use PDO; @@ -16,7 +16,7 @@ protected function setUp(): void { parent::setUp(); - if (! $this->connection->getWrappedConnection() instanceof PDOConnection) { + if (! $this->connection->getWrappedConnection() instanceof Connection) { $this->markTestSkipped('PDO-only test'); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/ParameterTypes/AsciiTest.php b/tests/Doctrine/Tests/DBAL/Functional/ParameterTypes/AsciiTest.php new file mode 100644 index 0000000000..940152352d --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/ParameterTypes/AsciiTest.php @@ -0,0 +1,28 @@ +connection->getDriver() instanceof AbstractSQLServerDriver) { + self::markTestSkipped('Driver does not support ascii string binding'); + } + + $statement = $this->connection->prepare('SELECT sql_variant_property(?, \'BaseType\')'); + + $statement->bindValue(1, 'test', ParameterType::ASCII); + $statement->execute(); + + $results = $statement->fetchOne(); + + self::assertEquals('varchar', $results); + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/PortabilityTest.php b/tests/Doctrine/Tests/DBAL/Functional/PortabilityTest.php index 3ee08d0e35..ee3bb01d4b 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/PortabilityTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/PortabilityTest.php @@ -139,7 +139,11 @@ private function assertFetchResultRows(array $rows): void */ public function assertFetchResultRow(array $row): void { - self::assertContains($row['test_int'], [1, 2], 'Primary key test_int should either be 1 or 2.'); + self::assertThat($row['test_int'], self::logicalOr( + self::equalTo(1), + self::equalTo(2) + )); + self::assertArrayHasKey('test_string', $row, 'Case should be lowered.'); self::assertEquals(3, strlen($row['test_string'])); self::assertNull($row['test_null']); diff --git a/tests/Doctrine/Tests/DBAL/Functional/PrimaryReadReplicaConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/PrimaryReadReplicaConnectionTest.php new file mode 100644 index 0000000000..7dd14c355e --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/PrimaryReadReplicaConnectionTest.php @@ -0,0 +1,243 @@ +connection->getDatabasePlatform()->getName(); + + // This is a MySQL specific test, skip other vendors. + if ($platformName !== 'mysql') { + $this->markTestSkipped(sprintf('Test does not work on %s.', $platformName)); + } + + try { + $table = new Table('primary_replica_table'); + $table->addColumn('test_int', 'integer'); + $table->setPrimaryKey(['test_int']); + + $sm = $this->connection->getSchemaManager(); + $sm->createTable($table); + } catch (Throwable $e) { + } + + $this->connection->executeStatement('DELETE FROM primary_replica_table'); + $this->connection->insert('primary_replica_table', ['test_int' => 1]); + } + + private function createPrimaryReadReplicaConnection(bool $keepReplica = false): PrimaryReadReplicaConnection + { + return DriverManager::getConnection($this->createPrimaryReadReplicaConnectionParams($keepReplica)); + } + + /** + * @return mixed[] + */ + private function createPrimaryReadReplicaConnectionParams(bool $keepReplica = false): array + { + $params = $this->connection->getParams(); + $params['primary'] = $params; + $params['replica'] = [$params, $params]; + $params['keepReplica'] = $keepReplica; + $params['wrapperClass'] = PrimaryReadReplicaConnection::class; + + return $params; + } + + public function testInheritCharsetFromPrimary(): void + { + $charsets = [ + 'utf8', + 'latin1', + ]; + + foreach ($charsets as $charset) { + $params = $this->createPrimaryReadReplicaConnectionParams(); + $params['primary']['charset'] = $charset; + + foreach ($params['replica'] as $index => $replicaParams) { + if (! isset($replicaParams['charset'])) { + continue; + } + + unset($params['replica'][$index]['charset']); + } + + $conn = DriverManager::getConnection($params); + self::assertInstanceOf(PrimaryReadReplicaConnection::class, $conn); + $conn->ensureConnectedToReplica(); + + self::assertFalse($conn->isConnectedToPrimary()); + + $clientCharset = $conn->fetchColumn('select @@character_set_client as c'); + + self::assertSame( + $charset, + substr(strtolower($clientCharset), 0, strlen($charset)) + ); + } + } + + public function testPrimaryOnConnect(): void + { + $conn = $this->createPrimaryReadReplicaConnection(); + + self::assertFalse($conn->isConnectedToPrimary()); + $conn->ensureConnectedToReplica(); + self::assertFalse($conn->isConnectedToPrimary()); + $conn->ensureConnectedToPrimary(); + self::assertTrue($conn->isConnectedToPrimary()); + } + + public function testNoPrimaryrOnExecuteQuery(): void + { + $conn = $this->createPrimaryReadReplicaConnection(); + + $sql = 'SELECT count(*) as num FROM primary_replica_table'; + $data = $conn->fetchAll($sql); + $data[0] = array_change_key_case($data[0], CASE_LOWER); + + self::assertEquals(1, $data[0]['num']); + self::assertFalse($conn->isConnectedToPrimary()); + } + + public function testPrimaryOnWriteOperation(): void + { + $conn = $this->createPrimaryReadReplicaConnection(); + $conn->insert('primary_replica_table', ['test_int' => 30]); + + self::assertTrue($conn->isConnectedToPrimary()); + + $sql = 'SELECT count(*) as num FROM primary_replica_table'; + $data = $conn->fetchAll($sql); + $data[0] = array_change_key_case($data[0], CASE_LOWER); + + self::assertEquals(2, $data[0]['num']); + self::assertTrue($conn->isConnectedToPrimary()); + } + + /** + * @group DBAL-335 + */ + public function testKeepReplicaBeginTransactionStaysOnPrimary(): void + { + $conn = $this->createPrimaryReadReplicaConnection($keepReplica = true); + $conn->ensureConnectedToReplica(); + + $conn->beginTransaction(); + $conn->insert('primary_replica_table', ['test_int' => 30]); + $conn->commit(); + + self::assertTrue($conn->isConnectedToPrimary()); + + $conn->connect(); + self::assertTrue($conn->isConnectedToPrimary()); + + $conn->ensureConnectedToReplica(); + self::assertFalse($conn->isConnectedToPrimary()); + } + + /** + * @group DBAL-335 + */ + public function testKeepReplicaInsertStaysOnPrimary(): void + { + $conn = $this->createPrimaryReadReplicaConnection($keepReplica = true); + $conn->ensureConnectedToReplica(); + + $conn->insert('primary_replica_table', ['test_int' => 30]); + + self::assertTrue($conn->isConnectedToPrimary()); + + $conn->connect(); + self::assertTrue($conn->isConnectedToPrimary()); + + $conn->ensureConnectedToReplica(); + self::assertFalse($conn->isConnectedToPrimary()); + } + + public function testPrimaryReadReplicaConnectionCloseAndReconnect(): void + { + $conn = $this->createPrimaryReadReplicaConnection(); + $conn->ensureConnectedToPrimary(); + self::assertTrue($conn->isConnectedToPrimary()); + + $conn->close(); + self::assertFalse($conn->isConnectedToPrimary()); + + $conn->ensureConnectedToPrimary(); + self::assertTrue($conn->isConnectedToPrimary()); + } + + public function testQueryOnPrimary(): void + { + $conn = $this->createPrimaryReadReplicaConnection(); + + $query = 'SELECT count(*) as num FROM primary_replica_table'; + + $statement = $conn->query($query); + + self::assertInstanceOf(Statement::class, $statement); + + //Query must be executed only on Primary + self::assertTrue($conn->isConnectedToPrimary()); + + $data = $statement->fetchAll(); + + //Default fetchmode is FetchMode::ASSOCIATIVE + self::assertArrayHasKey(0, $data); + self::assertArrayHasKey('num', $data[0]); + + //Could be set in other fetchmodes + self::assertArrayNotHasKey(0, $data[0]); + self::assertEquals(1, $data[0]['num']); + } + + public function testQueryOnReplica(): void + { + $conn = $this->createPrimaryReadReplicaConnection(); + $conn->ensureConnectedToReplica(); + + $query = 'SELECT count(*) as num FROM primary_replica_table'; + + $statement = $conn->query($query); + + self::assertInstanceOf(Statement::class, $statement); + + //Query must be executed only on Primary, even when we connect to the replica + self::assertTrue($conn->isConnectedToPrimary()); + + $data = $statement->fetchAll(); + + //Default fetchmode is FetchMode::ASSOCIATIVE + self::assertArrayHasKey(0, $data); + self::assertArrayHasKey('num', $data[0]); + + //Could be set in other fetchmodes + self::assertArrayNotHasKey(0, $data[0]); + + self::assertEquals(1, $data[0]['num']); + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php index f065ad1833..b1a88e8a26 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php @@ -109,7 +109,7 @@ public function testMixingFetch(): void 'SELECT * FROM caching ORDER BY test_int ASC', [], [], - new QueryCacheProfile(10, 'testcachekey') + new QueryCacheProfile(0, 'testcachekey') ); $data = $this->hydrateStmt($stmt, FetchMode::ASSOCIATIVE); @@ -120,7 +120,7 @@ public function testMixingFetch(): void 'SELECT * FROM caching ORDER BY test_int ASC', [], [], - new QueryCacheProfile(10, 'testcachekey') + new QueryCacheProfile(0, 'testcachekey') ); $data = $this->hydrateStmt($stmt, FetchMode::NUMERIC); @@ -141,7 +141,7 @@ private function assertStandardAndIteratorFetchAreEqual(int $fetchMode): void 'SELECT * FROM caching ORDER BY test_int ASC', [], [], - new QueryCacheProfile(10, 'testcachekey') + new QueryCacheProfile(0, 'testcachekey') ); $data = $this->hydrateStmt($stmt, $fetchMode); @@ -150,7 +150,7 @@ private function assertStandardAndIteratorFetchAreEqual(int $fetchMode): void 'SELECT * FROM caching ORDER BY test_int ASC', [], [], - new QueryCacheProfile(10, 'testcachekey') + new QueryCacheProfile(0, 'testcachekey') ); $dataIterator = $this->hydrateStmtIterator($stmt, $fetchMode); @@ -158,13 +158,13 @@ private function assertStandardAndIteratorFetchAreEqual(int $fetchMode): void self::assertEquals($data, $dataIterator); } - public function testDontCloseNoCache(): void + public function testFetchAndFinishSavesCache(): void { $stmt = $this->connection->executeQuery( 'SELECT * FROM caching ORDER BY test_int ASC', [], [], - new QueryCacheProfile(10, 'testcachekey') + new QueryCacheProfile(0, 'testcachekey') ); $data = []; @@ -177,7 +177,7 @@ public function testDontCloseNoCache(): void 'SELECT * FROM caching ORDER BY test_int ASC', [], [], - new QueryCacheProfile(10, 'testcachekey') + new QueryCacheProfile(0, 'testcachekey') ); $data = []; @@ -186,7 +186,7 @@ public function testDontCloseNoCache(): void $data[] = $row; } - self::assertCount(2, $this->sqlLogger->queries); + self::assertCount(1, $this->sqlLogger->queries); } public function testDontFinishNoCache(): void @@ -195,17 +195,16 @@ public function testDontFinishNoCache(): void 'SELECT * FROM caching ORDER BY test_int ASC', [], [], - new QueryCacheProfile(10, 'testcachekey') + new QueryCacheProfile(0, 'testcachekey') ); $stmt->fetch(FetchMode::ASSOCIATIVE); - $stmt->closeCursor(); $stmt = $this->connection->executeQuery( 'SELECT * FROM caching ORDER BY test_int ASC', [], [], - new QueryCacheProfile(10, 'testcachekey') + new QueryCacheProfile(0, 'testcachekey') ); $this->hydrateStmt($stmt, FetchMode::NUMERIC); @@ -213,7 +212,7 @@ public function testDontFinishNoCache(): void self::assertCount(2, $this->sqlLogger->queries); } - public function testFetchAllAndFinishSavesCache(): void + public function testFetchAllSavesCache(): void { $layerCache = new ArrayCache(); @@ -221,11 +220,9 @@ public function testFetchAllAndFinishSavesCache(): void 'SELECT * FROM caching WHERE test_int > 500', [], [], - new QueryCacheProfile(10, 'testcachekey', $layerCache) + new QueryCacheProfile(0, 'testcachekey', $layerCache) ); - $stmt->fetchAll(); - $stmt->closeCursor(); self::assertCount(1, $layerCache->fetch('testcachekey')); } @@ -239,7 +236,6 @@ public function testFetchAllColumn(): void $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp); $stmt->fetchAll(FetchMode::COLUMN); - $stmt->closeCursor(); $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp); @@ -255,7 +251,7 @@ private function assertCacheNonCacheSelectSameFetchModeAreEqual(array $expectedR 'SELECT * FROM caching ORDER BY test_int ASC', [], [], - new QueryCacheProfile(10, 'testcachekey') + new QueryCacheProfile(0, 'testcachekey') ); self::assertEquals(2, $stmt->columnCount()); @@ -266,7 +262,7 @@ private function assertCacheNonCacheSelectSameFetchModeAreEqual(array $expectedR 'SELECT * FROM caching ORDER BY test_int ASC', [], [], - new QueryCacheProfile(10, 'testcachekey') + new QueryCacheProfile(0, 'testcachekey') ); self::assertEquals(2, $stmt->columnCount()); @@ -330,12 +326,11 @@ public function testChangeCacheImpl(): void private function hydrateStmt(ResultStatement $stmt, int $fetchMode = FetchMode::ASSOCIATIVE): array { $data = []; - while ($row = $stmt->fetch($fetchMode)) { + + foreach ($stmt->fetchAll($fetchMode) as $row) { $data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row; } - $stmt->closeCursor(); - return $data; } @@ -350,8 +345,6 @@ private function hydrateStmtIterator(ResultStatement $stmt, int $fetchMode = Fet $data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row; } - $stmt->closeCursor(); - return $data; } } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index e62ebfdd98..90a78487f1 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -444,7 +444,7 @@ public function testColumnDefaultsAreValid(): void $this->schemaManager->dropAndCreateTable($table); - $this->connection->executeUpdate( + $this->connection->executeStatement( 'INSERT INTO test_column_defaults_are_valid () VALUES()' ); diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLAnywhereSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLAnywhereSchemaManagerTest.php index 5a2bb2f42a..dbce7bba89 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLAnywhereSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLAnywhereSchemaManagerTest.php @@ -24,7 +24,10 @@ public function testCreateAndListViews(): void self::assertCount(1, $views, 'Database has to have one view.'); self::assertInstanceOf(View::class, $views[$name]); self::assertEquals($name, $views[$name]->getName()); - self::assertRegExp('/^SELECT \* from "?DBA"?\."?view_test_table"?$/', $views[$name]->getSql()); + self::assertMatchesRegularExpression( + '/^SELECT \* from "?DBA"?\."?view_test_table"?$/', + $views[$name]->getSql() + ); } public function testDropAndCreateAdvancedIndex(): void diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php index d4e5c3ed2d..e34ef79ec1 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php @@ -221,7 +221,7 @@ public function testListNamespaceNames(): void $namespaces = array_map('strtolower', $namespaces); if (! in_array('test_create_schema', $namespaces)) { - $this->connection->executeUpdate( + $this->connection->executeStatement( $this->schemaManager->getDatabasePlatform()->getCreateSchemaSQL('test_create_schema') ); diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SqliteSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SqliteSchemaManagerTest.php index 6764b1ed17..d94a8f8dab 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SqliteSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SqliteSchemaManagerTest.php @@ -2,8 +2,8 @@ namespace Doctrine\Tests\DBAL\Functional\Schema; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\BlobType; @@ -19,7 +19,7 @@ class SqliteSchemaManagerTest extends SchemaManagerFunctionalTestCase */ public function testListDatabases(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->schemaManager->listDatabases(); } @@ -31,7 +31,7 @@ public function testCreateAndDropDatabase(): void $this->schemaManager->createDatabase($path); self::assertFileExists($path); $this->schemaManager->dropDatabase($path); - self::assertFileNotExists($path); + self::assertFileDoesNotExist($path); } public function testDropsDatabaseWithActiveConnections(): void @@ -53,7 +53,7 @@ public function testDropsDatabaseWithActiveConnections(): void $this->schemaManager->dropDatabase('test_drop_database'); - self::assertFileNotExists('test_drop_database'); + self::assertFileDoesNotExist('test_drop_database'); unset($connection); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/StatementTest.php b/tests/Doctrine/Tests/DBAL/Functional/StatementTest.php index ed78cdcc3a..3782296eb3 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/StatementTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/StatementTest.php @@ -2,7 +2,7 @@ namespace Doctrine\Tests\DBAL\Functional; -use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver; +use Doctrine\DBAL\Driver\PDO\OCI\Driver as PDOOCIDriver; use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; @@ -27,7 +27,7 @@ protected function setUp(): void public function testStatementIsReusableAfterClosingCursor(): void { - if ($this->connection->getDriver() instanceof PDOOracleDriver) { + if ($this->connection->getDriver() instanceof PDOOCIDriver) { $this->markTestIncomplete('See https://bugs.php.net/bug.php?id=77181'); } @@ -52,7 +52,7 @@ public function testStatementIsReusableAfterClosingCursor(): void public function testReuseStatementWithLongerResults(): void { - if ($this->connection->getDriver() instanceof PDOOracleDriver) { + if ($this->connection->getDriver() instanceof PDOOCIDriver) { $this->markTestIncomplete('PDO_OCI doesn\'t support fetching blobs via PDOStatement::fetchAll()'); } @@ -89,7 +89,7 @@ public function testReuseStatementWithLongerResults(): void public function testFetchLongBlob(): void { - if ($this->connection->getDriver() instanceof PDOOracleDriver) { + if ($this->connection->getDriver() instanceof PDOOCIDriver) { // inserting BLOBs as streams on Oracle requires Oracle-specific SQL syntax which is currently not supported // see http://php.net/manual/en/pdo.lobs.php#example-1035 $this->markTestSkipped('DBAL doesn\'t support storing LOBs represented as streams using PDO_OCI'); @@ -154,7 +154,7 @@ public function testIncompletelyFetchedStatementDoesNotBlockConnection(): void public function testReuseStatementAfterClosingCursor(): void { - if ($this->connection->getDriver() instanceof PDOOracleDriver) { + if ($this->connection->getDriver() instanceof PDOOCIDriver) { $this->markTestIncomplete('See https://bugs.php.net/bug.php?id=77181'); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/TemporaryTableTest.php b/tests/Doctrine/Tests/DBAL/Functional/TemporaryTableTest.php index c169618256..74382f0cf0 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/TemporaryTableTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/TemporaryTableTest.php @@ -46,7 +46,7 @@ public function testDropTemporaryTableNotAutoCommitTransaction(): void $createTempTableSQL = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; - $this->connection->executeUpdate($createTempTableSQL); + $this->connection->executeStatement($createTempTableSQL); $table = new Table('nontemporary'); $table->addColumn('id', 'integer'); diff --git a/tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL630Test.php b/tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL630Test.php index 7f43a6fd8d..77cd108b59 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL630Test.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL630Test.php @@ -2,8 +2,8 @@ namespace Doctrine\Tests\DBAL\Functional\Ticket; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\Driver\PDO\Connection; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\ParameterType; use Doctrine\Tests\DbalFunctionalTestCase; use PDO; @@ -28,7 +28,7 @@ protected function setUp(): void try { $this->connection->exec('CREATE TABLE dbal630 (id SERIAL, bool_col BOOLEAN NOT NULL);'); $this->connection->exec('CREATE TABLE dbal630_allow_nulls (id SERIAL, bool_col BOOLEAN);'); - } catch (DBALException $e) { + } catch (Exception $e) { } $this->running = true; @@ -45,7 +45,7 @@ protected function tearDown(): void public function testBooleanConversionSqlLiteral(): void { - $this->connection->executeUpdate('INSERT INTO dbal630 (bool_col) VALUES(false)'); + $this->connection->executeStatement('INSERT INTO dbal630 (bool_col) VALUES(false)'); $id = $this->connection->lastInsertId('dbal630_id_seq'); self::assertNotEmpty($id); @@ -56,7 +56,7 @@ public function testBooleanConversionSqlLiteral(): void public function testBooleanConversionBoolParamRealPrepares(): void { - $this->connection->executeUpdate( + $this->connection->executeStatement( 'INSERT INTO dbal630 (bool_col) VALUES(?)', ['false'], [ParameterType::BOOLEAN] @@ -170,10 +170,10 @@ public static function booleanTypeConversionWithoutPdoTypeProvider(): iterable ]; } - private function getWrappedConnection(): PDOConnection + private function getWrappedConnection(): Connection { $connection = $this->connection->getWrappedConnection(); - self::assertInstanceOf(PDOConnection::class, $connection); + self::assertInstanceOf(Connection::class, $connection); return $connection; } diff --git a/tests/Doctrine/Tests/DBAL/Functional/TypeConversionTest.php b/tests/Doctrine/Tests/DBAL/Functional/TypeConversionTest.php index 427df9d82f..42a5668a43 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/TypeConversionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/TypeConversionTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Functional; use DateTime; -use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver; +use Doctrine\DBAL\Driver\PDO\OCI\Driver as PDOOCIDriver; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Type; use Doctrine\Tests\DbalFunctionalTestCase; @@ -120,7 +120,7 @@ public static function floatProvider(): iterable */ public function testIdempotentConversionToString(string $type, $originalValue): void { - if ($type === 'text' && $this->connection->getDriver() instanceof PDOOracleDriver) { + if ($type === 'text' && $this->connection->getDriver() instanceof PDOOCIDriver) { // inserting BLOBs as streams on Oracle requires Oracle-specific SQL syntax which is currently not supported // see http://php.net/manual/en/pdo.lobs.php#example-1035 $this->markTestSkipped('DBAL doesn\'t support storing LOBs represented as streams using PDO_OCI'); diff --git a/tests/Doctrine/Tests/DBAL/Functional/Types/AsciiStringTest.php b/tests/Doctrine/Tests/DBAL/Functional/Types/AsciiStringTest.php new file mode 100644 index 0000000000..d0559c7027 --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Types/AsciiStringTest.php @@ -0,0 +1,70 @@ +addColumn('id', 'ascii_string', [ + 'length' => 3, + 'fixed' => true, + ]); + + $table->addColumn('val', 'ascii_string', ['length' => 4]); + $table->setPrimaryKey(['id']); + + $sm = $this->connection->getSchemaManager(); + $sm->dropAndCreateTable($table); + } + + public function testInsertAndSelect(): void + { + $id1 = 'id1'; + $id2 = 'id2'; + + $value1 = 'val1'; + $value2 = 'val2'; + + $this->insert($id1, $value1); + $this->insert($id2, $value2); + + self::assertSame($value1, $this->select($id1)); + self::assertSame($value2, $this->select($id2)); + } + + private function insert(string $id, string $value): void + { + $result = $this->connection->insert('ascii_table', [ + 'id' => $id, + 'val' => $value, + ], [ + ParameterType::ASCII, + ParameterType::ASCII, + ]); + + self::assertSame(1, $result); + } + + private function select(string $id): string + { + $value = $this->connection->fetchOne( + 'SELECT val FROM ascii_table WHERE id = ?', + [$id], + [ParameterType::ASCII] + ); + + self::assertIsString($value); + + return $value; + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/Types/BinaryTest.php b/tests/Doctrine/Tests/DBAL/Functional/Types/BinaryTest.php index ff8a503b5a..2c84cce461 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Types/BinaryTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Types/BinaryTest.php @@ -4,8 +4,8 @@ namespace Doctrine\Tests\DBAL\Functional\Types; -use Doctrine\DBAL\Driver\IBMDB2\DB2Driver; -use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver; +use Doctrine\DBAL\Driver\IBMDB2\Driver; +use Doctrine\DBAL\Driver\PDO\OCI\Driver as PDOOCIDriver; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Schema\Table; use Doctrine\Tests\DbalFunctionalTestCase; @@ -21,7 +21,7 @@ protected function setUp(): void { parent::setUp(); - if ($this->connection->getDriver() instanceof PDOOracleDriver) { + if ($this->connection->getDriver() instanceof PDOOCIDriver) { $this->markTestSkipped('PDO_OCI doesn\'t support binding binary values'); } @@ -46,7 +46,7 @@ public function testInsertAndSelect(): void $value2 = random_bytes(64); /** @see https://bugs.php.net/bug.php?id=76322 */ - if ($this->connection->getDriver() instanceof DB2Driver) { + if ($this->connection->getDriver() instanceof Driver) { $value1 = str_replace("\x00", "\xFF", $value1); $value2 = str_replace("\x00", "\xFF", $value2); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php b/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php index 77129de00f..d5640e3b7a 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Functional; use DateTime; -use Doctrine\DBAL\Driver\DriverException; +use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; @@ -32,36 +32,36 @@ protected function setUp(): void } catch (Throwable $e) { } - $this->connection->executeUpdate('DELETE FROM write_table'); + $this->connection->executeStatement('DELETE FROM write_table'); } - public function testExecuteUpdateFirstTypeIsNull(): void + public function testExecuteStatementFirstTypeIsNull(): void { $sql = 'INSERT INTO write_table (test_string, test_int) VALUES (?, ?)'; - $this->connection->executeUpdate($sql, ['text', 1111], [null, ParameterType::INTEGER]); + $this->connection->executeStatement($sql, ['text', 1111], [null, ParameterType::INTEGER]); $sql = 'SELECT * FROM write_table WHERE test_string = ? AND test_int = ?'; self::assertTrue((bool) $this->connection->fetchColumn($sql, ['text', 1111])); } - public function testExecuteUpdate(): void + public function testExecuteStatement(): void { $sql = 'INSERT INTO write_table (test_int) VALUES ( ' . $this->connection->quote(1) . ')'; - $affected = $this->connection->executeUpdate($sql); + $affected = $this->connection->executeStatement($sql); - self::assertEquals(1, $affected, 'executeUpdate() should return the number of affected rows!'); + self::assertEquals(1, $affected, 'executeStatement() should return the number of affected rows!'); } - public function testExecuteUpdateWithTypes(): void + public function testExecuteStatementWithTypes(): void { $sql = 'INSERT INTO write_table (test_int, test_string) VALUES (?, ?)'; - $affected = $this->connection->executeUpdate( + $affected = $this->connection->executeStatement( $sql, [1, 'foo'], [ParameterType::INTEGER, ParameterType::STRING] ); - self::assertEquals(1, $affected, 'executeUpdate() should return the number of affected rows!'); + self::assertEquals(1, $affected, 'executeStatement() should return the number of affected rows!'); } public function testPrepareRowCountReturnsAffectedRows(): void @@ -369,13 +369,13 @@ public function testDeleteWhereIsNull(): void * * @return string|false * - * @throws DriverException + * @throws Exception */ private function lastInsertId(?string $name = null) { try { return $this->connection->lastInsertId($name); - } catch (DriverException $e) { + } catch (Exception $e) { if ($e->getCode() === 'IM001') { $this->markTestSkipped($e->getMessage()); } diff --git a/tests/Doctrine/Tests/DBAL/Performance/TypeConversionPerformanceTest.php b/tests/Doctrine/Tests/DBAL/Performance/TypeConversionPerformanceTest.php deleted file mode 100644 index f9367ffe9e..0000000000 --- a/tests/Doctrine/Tests/DBAL/Performance/TypeConversionPerformanceTest.php +++ /dev/null @@ -1,45 +0,0 @@ -connection->getDatabasePlatform(); - $this->startTiming(); - for ($i = 0; $i < $count; $i++) { - $type->convertToDatabaseValue($value, $platform); - } - - $this->stopTiming(); - } - - /** - * @return mixed[][] - */ - public static function itemCountProvider(): iterable - { - return [ - '100 items' => [100], - '1000 items' => [1000], - '10000 items' => [10000], - '100000 items' => [100000], - ]; - } -} diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php index eef443b8ef..aa51cd1e83 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php @@ -3,8 +3,8 @@ namespace Doctrine\Tests\DBAL\Platforms; use Doctrine\Common\EventManager; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Events; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\Keywords\KeywordList; use Doctrine\DBAL\Schema\Column; @@ -90,7 +90,7 @@ public function testGetInvalidForeignKeyReferentialActionSQL(): void public function testGetUnknownDoctrineMappingType(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->platform->getDoctrineTypeMapping('foobar'); } @@ -102,7 +102,7 @@ public function testRegisterDoctrineMappingType(): void public function testRegisterUnknownDoctrineMappingType(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->platform->registerDoctrineTypeMapping('foo', 'bar'); } @@ -151,7 +151,7 @@ public function testCreateWithNoColumns(): void { $table = new Table('test'); - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $sql = $this->platform->getCreateTableSQL($table); } @@ -264,7 +264,7 @@ public function testGeneratesForeignKeySqlOnlyWhenSupportingForeignKeys(): void if ($this->platform->supportsForeignKeyConstraints()) { self::assertIsString($this->platform->getCreateForeignKeySQL($fk, 'test')); } else { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->platform->getCreateForeignKeySQL($fk, 'test'); } } @@ -732,7 +732,7 @@ public function testQuotesReservedKeywordInIndexDeclarationSQL(): void $index = new Index('select', ['foo']); if (! $this->supportsInlineIndexDeclaration()) { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); } self::assertSame( @@ -760,7 +760,7 @@ protected function supportsCommentOnStatement(): bool public function testGetCreateSchemaSQL(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->platform->getCreateSchemaSQL('schema'); } @@ -791,7 +791,7 @@ public function testUsesSequenceEmulatedIdentityColumns(): void public function testReturnsIdentitySequenceName(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->platform->getIdentitySequenceName('mytable', 'mycolumn'); } @@ -818,7 +818,7 @@ protected function getBinaryMaxLength(): int public function testReturnsBinaryTypeDeclarationSQL(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->platform->getBinaryTypeDeclarationSQL([]); } @@ -1226,7 +1226,7 @@ public function testThrowsExceptionOnGeneratingInlineColumnCommentSQLIfUnsupport $this->markTestSkipped(sprintf('%s supports inline column comments.', get_class($this->platform))); } - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->expectExceptionMessage( "Operation '" . AbstractPlatform::class . "::getInlineColumnCommentSQL' is not supported by platform." ); @@ -1255,7 +1255,7 @@ public function testQuoteStringLiteral(): void public function testReturnsGuidTypeDeclarationSQL(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->platform->getGuidTypeDeclarationSQL([]); } @@ -1451,6 +1451,28 @@ public function testZeroOffsetWithoutLimitIsIgnored(): void $this->platform->modifyLimitQuery($query, null, 0) ); } + + /** + * @param array $column + * + * @dataProvider asciiStringSqlDeclarationDataProvider + */ + public function testAsciiSQLDeclaration(string $expectedSql, array $column): void + { + $declarationSql = $this->platform->getAsciiStringTypeDeclarationSQL($column); + self::assertEquals($expectedSql, $declarationSql); + } + + /** + * @return array}> + */ + public function asciiStringSqlDeclarationDataProvider(): array + { + return [ + ['VARCHAR(12)', ['length' => 12]], + ['CHAR(12)', ['length' => 12, 'fixed' => true]], + ]; + } } interface GetCreateTableSqlDispatchEventListener diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php index 6c166ac2df..ddb7df84a0 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php @@ -2,7 +2,7 @@ namespace Doctrine\Tests\DBAL\Platforms; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\ColumnDiff; @@ -61,7 +61,7 @@ public function getGenerateAlterTableSql(): array public function testDoesNotSupportRegexp(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->platform->getRegexpExpression(); } @@ -469,9 +469,6 @@ public function testModifyLimitQueryWithAggregateFunctionInOrderByClause(): void $this->expectCteWithMaxRowNum($alteredSql, 1, $sql); } - /** - * @throws DBALException - */ public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromBaseTable(): void { $querySql = 'SELECT DISTINCT id_0, name_1 ' @@ -493,9 +490,6 @@ public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromBas $this->expectCteWithMaxRowNum($alteredSql, 5, $sql); } - /** - * @throws DBALException - */ public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromJoinTable(): void { $querySql = 'SELECT DISTINCT id_0, name_1 ' @@ -517,9 +511,6 @@ public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromJoi $this->expectCteWithMaxRowNum($alteredSql, 5, $sql); } - /** - * @throws DBALException - */ public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnsFromBothTables(): void { $querySql = 'SELECT DISTINCT id_0, name_1, foo_2 ' diff --git a/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php index 22e02ee405..092c073551 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php @@ -2,7 +2,7 @@ namespace Doctrine\Tests\DBAL\Platforms; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Schema\Column; @@ -74,7 +74,7 @@ public static function dataInvalidIdentifiers(): iterable */ public function testInvalidIdentifiers(string $identifier): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $platform = $this->createPlatform(); $platform->assertValidIdentifier($identifier); @@ -120,7 +120,7 @@ public function getGenerateAlterTableSql(): array public function testRLike(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); self::assertEquals('RLIKE', $this->platform->getRegexpExpression()); } @@ -156,7 +156,7 @@ public function testGeneratesTransactionsCommands(): void public function testCreateDatabaseThrowsException(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); self::assertEquals('CREATE DATABASE foobar', $this->platform->getCreateDatabaseSQL('foobar')); } @@ -976,4 +976,15 @@ public function testQuotesDatabaseNameInListTableColumnsSQL(): void $this->platform->getListTableColumnsSQL('foo_table', "Foo'Bar\\") ); } + + /** + * @return array}> + */ + public function asciiStringSqlDeclarationDataProvider(): array + { + return [ + ['VARCHAR2(12)', ['length' => 12]], + ['CHAR(12)', ['length' => 12, 'fixed' => true]], + ]; + } } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php index 6e0ae42306..5f3bd50e2a 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php @@ -2,7 +2,7 @@ namespace Doctrine\Tests\DBAL\Platforms; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\LockMode; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SQLAnywherePlatform; @@ -522,7 +522,7 @@ public function testGeneratesCreateIndexWithAdvancedPlatformOptionsSQL(): void public function testDoesNotSupportIndexDeclarationInCreateAlterTableStatements(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->platform->getIndexDeclarationSQL('index', new Index('index', [])); } @@ -712,7 +712,7 @@ public function testGeneratesSQLSnippets(): void public function testDoesNotSupportRegexp(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->platform->getRegexpExpression(); } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/SQLServer2012PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/SQLServer2012PlatformTest.php index 47fe7ec69b..1c21098b67 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/SQLServer2012PlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/SQLServer2012PlatformTest.php @@ -2,7 +2,6 @@ namespace Doctrine\Tests\DBAL\Platforms; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SQLServer2012Platform; use Doctrine\DBAL\Schema\Sequence; @@ -370,9 +369,6 @@ public function testModifyLimitQueryWithComplexOrderByExpression(): void self::assertEquals($sql, $expected); } - /** - * @throws DBALException - */ public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromBaseTable(): void { $querySql = 'SELECT DISTINCT id_0, name_1 ' @@ -393,9 +389,6 @@ public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromBas self::assertEquals($alteredSql, $sql); } - /** - * @throws DBALException - */ public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromJoinTable(): void { $querySql = 'SELECT DISTINCT id_0, name_1 ' @@ -416,9 +409,6 @@ public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromJoi self::assertEquals($alteredSql, $sql); } - /** - * @throws DBALException - */ public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnsFromBothTables(): void { $querySql = 'SELECT DISTINCT id_0, name_1, foo_2 ' diff --git a/tests/Doctrine/Tests/DBAL/Platforms/SqlitePlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/SqlitePlatformTest.php index 83e25717ed..1bf0fd63ef 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/SqlitePlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/SqlitePlatformTest.php @@ -2,7 +2,7 @@ namespace Doctrine\Tests\DBAL\Platforms; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\Column; @@ -256,14 +256,14 @@ public function getGenerateUniqueIndexSql(): string public function testGeneratesForeignKeyCreationSql(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); parent::testGeneratesForeignKeyCreationSql(); } public function testGeneratesConstraintCreationSql(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); parent::testGeneratesConstraintCreationSql(); } @@ -345,7 +345,7 @@ public function testAlterTableAddColumns(): void */ public function testAlterTableAddComplexColumns(TableDiff $diff): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->platform->getAlterTableSQL($diff); } diff --git a/tests/Doctrine/Tests/DBAL/Portability/OptimizeFlagsTest.php b/tests/Doctrine/Tests/DBAL/Portability/OptimizeFlagsTest.php new file mode 100644 index 0000000000..3d776de6e7 --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Portability/OptimizeFlagsTest.php @@ -0,0 +1,36 @@ +optimizeFlags = new OptimizeFlags(); + } + + public function testOracle(): void + { + $flags = ($this->optimizeFlags)(new OraclePlatform(), Connection::PORTABILITY_ALL); + + self::assertSame(0, $flags & Connection::PORTABILITY_EMPTY_TO_NULL); + } + + public function testAnotherPlatform(): void + { + $flags = ($this->optimizeFlags)(new SqlitePlatform(), Connection::PORTABILITY_ALL); + + self::assertSame(Connection::PORTABILITY_ALL, $flags); + } +} diff --git a/tests/Doctrine/Tests/DBAL/Query/Expression/CompositeExpressionTest.php b/tests/Doctrine/Tests/DBAL/Query/Expression/CompositeExpressionTest.php index fa76b6ef5a..c1daefa4d3 100644 --- a/tests/Doctrine/Tests/DBAL/Query/Expression/CompositeExpressionTest.php +++ b/tests/Doctrine/Tests/DBAL/Query/Expression/CompositeExpressionTest.php @@ -9,18 +9,18 @@ class CompositeExpressionTest extends DbalTestCase { public function testCount(): void { - $expr = new CompositeExpression(CompositeExpression::TYPE_OR, ['u.group_id = 1']); + $expr = CompositeExpression::or('u.group_id = 1'); self::assertCount(1, $expr); - $expr->add('u.group_id = 2'); + $expr = $expr->with('u.group_id = 2'); self::assertCount(2, $expr); } public function testAdd(): void { - $expr = new CompositeExpression(CompositeExpression::TYPE_OR, ['u.group_id = 1']); + $expr = CompositeExpression::or('u.group_id = 1'); self::assertCount(1, $expr); @@ -28,7 +28,7 @@ public function testAdd(): void self::assertCount(1, $expr); - $expr->add(new CompositeExpression(CompositeExpression::TYPE_OR, ['u.user_id = 1'])); + $expr->add(CompositeExpression::or('u.user_id = 1')); self::assertCount(2, $expr); @@ -41,6 +41,26 @@ public function testAdd(): void self::assertCount(3, $expr); } + public function testWith(): void + { + $expr = CompositeExpression::or('u.group_id = 1'); + + self::assertCount(1, $expr); + + // test immutability + $expr->with(CompositeExpression::or('u.user_id = 1')); + + self::assertCount(1, $expr); + + $expr = $expr->with(CompositeExpression::or('u.user_id = 1')); + + self::assertCount(2, $expr); + + $expr = $expr->with('u.user_id = 1'); + + self::assertCount(3, $expr); + } + /** * @param string[]|CompositeExpression[] $parts * @@ -83,9 +103,9 @@ public static function provideDataForConvertToString(): iterable CompositeExpression::TYPE_AND, [ 'u.user = 1', - new CompositeExpression( - CompositeExpression::TYPE_OR, - ['u.group_id = 1', 'u.group_id = 2'] + CompositeExpression::or( + 'u.group_id = 1', + 'u.group_id = 2' ), ], '(u.user = 1) AND ((u.group_id = 1) OR (u.group_id = 2))', @@ -94,9 +114,9 @@ public static function provideDataForConvertToString(): iterable CompositeExpression::TYPE_OR, [ 'u.group_id = 1', - new CompositeExpression( - CompositeExpression::TYPE_AND, - ['u.user = 1', 'u.group_id = 2'] + CompositeExpression::and( + 'u.user = 1', + 'u.group_id = 2' ), ], '(u.group_id = 1) OR ((u.user = 1) AND (u.group_id = 2))', diff --git a/tests/Doctrine/Tests/DBAL/Query/Expression/ExpressionBuilderTest.php b/tests/Doctrine/Tests/DBAL/Query/Expression/ExpressionBuilderTest.php index 4428b6a92c..4525edb447 100644 --- a/tests/Doctrine/Tests/DBAL/Query/Expression/ExpressionBuilderTest.php +++ b/tests/Doctrine/Tests/DBAL/Query/Expression/ExpressionBuilderTest.php @@ -26,7 +26,19 @@ protected function setUp(): void /** * @param string[]|CompositeExpression[] $parts * - * @dataProvider provideDataForAndX + * @dataProvider provideDataForAnd + */ + public function testAnd(array $parts, string $expected): void + { + $composite = $this->expr->and(...$parts); + + self::assertEquals($expected, (string) $composite); + } + + /** + * @param string[]|CompositeExpression[] $parts + * + * @dataProvider provideDataForAnd */ public function testAndX(array $parts, string $expected): void { @@ -42,7 +54,7 @@ public function testAndX(array $parts, string $expected): void /** * @return mixed[][] */ - public static function provideDataForAndX(): iterable + public static function provideDataForAnd(): iterable { return [ [ @@ -64,9 +76,9 @@ public static function provideDataForAndX(): iterable [ [ 'u.user = 1', - new CompositeExpression( - CompositeExpression::TYPE_OR, - ['u.group_id = 1', 'u.group_id = 2'] + CompositeExpression::or( + 'u.group_id = 1', + 'u.group_id = 2' ), ], '(u.user = 1) AND ((u.group_id = 1) OR (u.group_id = 2))', @@ -74,9 +86,9 @@ public static function provideDataForAndX(): iterable [ [ 'u.group_id = 1', - new CompositeExpression( - CompositeExpression::TYPE_AND, - ['u.user = 1', 'u.group_id = 2'] + CompositeExpression::and( + 'u.user = 1', + 'u.group_id = 2' ), ], '(u.group_id = 1) AND ((u.user = 1) AND (u.group_id = 2))', @@ -87,7 +99,19 @@ public static function provideDataForAndX(): iterable /** * @param string[]|CompositeExpression[] $parts * - * @dataProvider provideDataForOrX + * @dataProvider provideDataForOr + */ + public function testOr(array $parts, string $expected): void + { + $composite = $this->expr->or(...$parts); + + self::assertEquals($expected, (string) $composite); + } + + /** + * @param string[]|CompositeExpression[] $parts + * + * @dataProvider provideDataForOr */ public function testOrX(array $parts, string $expected): void { @@ -103,7 +127,7 @@ public function testOrX(array $parts, string $expected): void /** * @return mixed[][] */ - public static function provideDataForOrX(): iterable + public static function provideDataForOr(): iterable { return [ [ @@ -125,9 +149,9 @@ public static function provideDataForOrX(): iterable [ [ 'u.user = 1', - new CompositeExpression( - CompositeExpression::TYPE_OR, - ['u.group_id = 1', 'u.group_id = 2'] + CompositeExpression::or( + 'u.group_id = 1', + 'u.group_id = 2' ), ], '(u.user = 1) OR ((u.group_id = 1) OR (u.group_id = 2))', @@ -135,9 +159,9 @@ public static function provideDataForOrX(): iterable [ [ 'u.group_id = 1', - new CompositeExpression( - CompositeExpression::TYPE_AND, - ['u.user = 1', 'u.group_id = 2'] + CompositeExpression::and( + 'u.user = 1', + 'u.group_id = 2' ), ], '(u.group_id = 1) OR ((u.user = 1) AND (u.group_id = 2))', diff --git a/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php b/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php index 1d7bf190ee..bc67178787 100644 --- a/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php +++ b/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php @@ -62,7 +62,7 @@ public function testSelectWithSimpleWhere(): void $qb->select('u.id') ->from('users', 'u') - ->where($expr->andX($expr->eq('u.nickname', '?'))); + ->where($expr->and($expr->eq('u.nickname', '?'))); self::assertEquals('SELECT u.id FROM users u WHERE u.nickname = ?', (string) $qb); } diff --git a/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php b/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php index f1138ca907..f2a8ec2fd1 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php @@ -1174,29 +1174,17 @@ public function testComparesNamespaces(): void ->method('getNamespaces') ->will($this->returnValue(['foo', 'bar'])); - $fromSchema->expects($this->at(0)) - ->method('hasNamespace') - ->with('bar') - ->will($this->returnValue(true)); - - $fromSchema->expects($this->at(1)) - ->method('hasNamespace') - ->with('baz') - ->will($this->returnValue(false)); + $fromSchema->method('hasNamespace') + ->withConsecutive(['bar'], ['baz']) + ->willReturnOnConsecutiveCalls(true, false); $toSchema->expects($this->once()) ->method('getNamespaces') ->will($this->returnValue(['bar', 'baz'])); - $toSchema->expects($this->at(1)) - ->method('hasNamespace') - ->with('foo') - ->will($this->returnValue(false)); - - $toSchema->expects($this->at(2)) - ->method('hasNamespace') - ->with('bar') - ->will($this->returnValue(true)); + $toSchema->method('hasNamespace') + ->withConsecutive(['foo'], ['bar']) + ->willReturnOnConsecutiveCalls(false, true); $expected = new SchemaDiff(); $expected->fromSchema = $fromSchema; diff --git a/tests/Doctrine/Tests/DBAL/Schema/DB2SchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Schema/DB2SchemaManagerTest.php index c2bc4a4bac..faa784a613 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/DB2SchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/DB2SchemaManagerTest.php @@ -31,7 +31,7 @@ protected function setUp(): void $platform = $this->createMock(DB2Platform::class); $this->conn = $this ->getMockBuilder(Connection::class) - ->onlyMethods(['fetchAll', 'quote']) + ->onlyMethods(['fetchAllAssociative', 'quote']) ->setConstructorArgs([['platform' => $platform], $driverMock, new Configuration(), $eventManager]) ->getMock(); $this->manager = new DB2SchemaManager($this->conn); @@ -43,7 +43,7 @@ protected function setUp(): void public function testListTableNamesFiltersAssetNamesCorrectly(): void { $this->conn->getConfiguration()->setFilterSchemaAssetsExpression('/^(?!T_)/'); - $this->conn->expects($this->once())->method('fetchAll')->will($this->returnValue([ + $this->conn->expects($this->once())->method('fetchAllAssociative')->will($this->returnValue([ ['name' => 'FOO'], ['name' => 'T_FOO'], ['name' => 'BAR'], @@ -63,7 +63,7 @@ public function testAssetFilteringSetsACallable(): void { $filterExpression = '/^(?!T_)/'; $this->conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); - $this->conn->expects($this->once())->method('fetchAll')->will($this->returnValue([ + $this->conn->expects($this->once())->method('fetchAllAssociative')->will($this->returnValue([ ['name' => 'FOO'], ['name' => 'T_FOO'], ['name' => 'BAR'], @@ -92,7 +92,7 @@ public function testListTableNamesFiltersAssetNamesCorrectlyWithCallable(): void return in_array($assetName, $accepted); }); $this->conn->expects($this->any())->method('quote'); - $this->conn->expects($this->once())->method('fetchAll')->will($this->returnValue([ + $this->conn->expects($this->once())->method('fetchAllAssociative')->will($this->returnValue([ ['name' => 'FOO'], ['name' => 'T_FOO'], ['name' => 'BAR'], @@ -117,7 +117,7 @@ public function testSettingNullExpressionWillResetCallable(): void return in_array($assetName, $accepted); }); $this->conn->expects($this->any())->method('quote'); - $this->conn->expects($this->atLeastOnce())->method('fetchAll')->will($this->returnValue([ + $this->conn->expects($this->atLeastOnce())->method('fetchAllAssociative')->will($this->returnValue([ ['name' => 'FOO'], ['name' => 'T_FOO'], ['name' => 'BAR'], @@ -152,7 +152,7 @@ public function testSettingNullAsCallableClearsExpression(): void $filterExpression = '/^(?!T_)/'; $this->conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); - $this->conn->expects($this->exactly(2))->method('fetchAll')->will($this->returnValue([ + $this->conn->expects($this->exactly(2))->method('fetchAllAssociative')->will($this->returnValue([ ['name' => 'FOO'], ['name' => 'T_FOO'], ['name' => 'BAR'], diff --git a/tests/Doctrine/Tests/DBAL/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Schema/MySqlSchemaManagerTest.php index c91a433d07..64f2166629 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/MySqlSchemaManagerTest.php @@ -25,11 +25,15 @@ class MySqlSchemaManagerTest extends TestCase protected function setUp(): void { - $eventManager = new EventManager(); - $driverMock = $this->createMock(Driver::class); - $platform = $this->createMock(MySqlPlatform::class); + $eventManager = new EventManager(); + $driverMock = $this->createMock(Driver::class); + + $platform = $this->createMock(MySqlPlatform::class); + $platform->method('getListTableForeignKeysSQL') + ->willReturn(''); + $this->conn = $this->getMockBuilder(Connection::class) - ->onlyMethods(['fetchAll']) + ->onlyMethods(['fetchAllAssociative']) ->setConstructorArgs([['platform' => $platform], $driverMock, new Configuration(), $eventManager]) ->getMock(); $this->manager = new MySqlSchemaManager($this->conn); @@ -37,7 +41,10 @@ protected function setUp(): void public function testCompositeForeignKeys(): void { - $this->conn->expects($this->once())->method('fetchAll')->will($this->returnValue($this->getFKDefinition())); + $this->conn->expects($this->once()) + ->method('fetchAllAssociative') + ->willReturn($this->getFKDefinition()); + $fkeys = $this->manager->listTableForeignKeys('dummy'); self::assertCount(1, $fkeys, 'Table has to have one foreign key.'); diff --git a/tests/Doctrine/Tests/DBAL/Schema/SchemaTest.php b/tests/Doctrine/Tests/DBAL/Schema/SchemaTest.php index 1eee8abd2c..35d884a95a 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/SchemaTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/SchemaTest.php @@ -357,29 +357,21 @@ public function testVisitsVisitor(): void ->method('acceptSchema') ->with($schema); - $visitor->expects($this->at(1)) + $visitor->expects(self::exactly(2)) ->method('acceptTable') - ->with($schema->getTable('baz')); + ->withConsecutive( + [$schema->getTable('baz')], + [$schema->getTable('bla.bloo')] + ); - $visitor->expects($this->at(2)) - ->method('acceptTable') - ->with($schema->getTable('bla.bloo')); - - $visitor->expects($this->exactly(2)) - ->method('acceptTable'); - - $visitor->expects($this->at(3)) - ->method('acceptSequence') - ->with($schema->getSequence('moo')); - - $visitor->expects($this->at(4)) + $visitor->expects(self::exactly(2)) ->method('acceptSequence') - ->with($schema->getSequence('war')); + ->withConsecutive( + [$schema->getSequence('moo')], + [$schema->getSequence('war')] + ); - $visitor->expects($this->exactly(2)) - ->method('acceptSequence'); - - self::assertNull($schema->visit($visitor)); + $schema->visit($visitor); } public function testVisitsNamespaceVisitor(): void @@ -400,43 +392,24 @@ public function testVisitsNamespaceVisitor(): void ->method('acceptSchema') ->with($schema); - $visitor->expects($this->at(1)) - ->method('acceptNamespace') - ->with('foo'); - - $visitor->expects($this->at(2)) - ->method('acceptNamespace') - ->with('bar'); - - $visitor->expects($this->at(3)) - ->method('acceptNamespace') - ->with('bla'); - $visitor->expects($this->exactly(3)) - ->method('acceptNamespace'); - - $visitor->expects($this->at(4)) - ->method('acceptTable') - ->with($schema->getTable('baz')); + ->method('acceptNamespace') + ->withConsecutive(['foo'], ['bar'], ['bla']); - $visitor->expects($this->at(5)) + $visitor->expects($this->exactly(2)) ->method('acceptTable') - ->with($schema->getTable('bla.bloo')); + ->withConsecutive( + [$schema->getTable('baz')], + [$schema->getTable('bla.bloo')] + ); $visitor->expects($this->exactly(2)) - ->method('acceptTable'); - - $visitor->expects($this->at(6)) - ->method('acceptSequence') - ->with($schema->getSequence('moo')); - - $visitor->expects($this->at(7)) ->method('acceptSequence') - ->with($schema->getSequence('war')); - - $visitor->expects($this->exactly(2)) - ->method('acceptSequence'); + ->withConsecutive( + [$schema->getSequence('moo')], + [$schema->getSequence('war')] + ); - self::assertNull($schema->visit($visitor)); + $schema->visit($visitor); } } diff --git a/tests/Doctrine/Tests/DBAL/Schema/TableTest.php b/tests/Doctrine/Tests/DBAL/Schema/TableTest.php index 25a50ba999..c596fecfc9 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/TableTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/TableTest.php @@ -2,7 +2,7 @@ namespace Doctrine\Tests\DBAL\Schema; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\Column; @@ -20,7 +20,7 @@ class TableTest extends DbalTestCase { public function testCreateWithInvalidTableName(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); new Table(''); } diff --git a/tests/Doctrine/Tests/DBAL/Schema/Visitor/CreateSchemaSqlCollectorTest.php b/tests/Doctrine/Tests/DBAL/Schema/Visitor/CreateSchemaSqlCollectorTest.php index 2610f756e0..c4e1aca9e8 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/Visitor/CreateSchemaSqlCollectorTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/Visitor/CreateSchemaSqlCollectorTest.php @@ -45,19 +45,20 @@ protected function setUp(): void ->willReturn(['foo']); } - public function testAcceptsNamespace(): void + public function testAcceptsNamespaceDoesNotSupportSchemas(): void { - $this->platformMock->expects($this->at(0)) - ->method('supportsSchemas') - ->will($this->returnValue(false)); - - $this->platformMock->expects($this->at(1)) - ->method('supportsSchemas') - ->will($this->returnValue(true)); + $this->platformMock->method('supportsSchemas') + ->willReturn(false); $this->visitor->acceptNamespace('foo'); self::assertEmpty($this->visitor->getQueries()); + } + + public function testAcceptsNamespaceSupportsSchemas(): void + { + $this->platformMock->method('supportsSchemas') + ->willReturn(true); $this->visitor->acceptNamespace('foo'); @@ -73,15 +74,10 @@ public function testAcceptsTable(): void self::assertSame(['foo'], $this->visitor->getQueries()); } - public function testAcceptsForeignKey(): void + public function testAcceptsForeignKeyDoesNotSupportForeignKeyConstraints(): void { - $this->platformMock->expects($this->at(0)) - ->method('supportsForeignKeyConstraints') - ->will($this->returnValue(false)); - - $this->platformMock->expects($this->at(1)) - ->method('supportsForeignKeyConstraints') - ->will($this->returnValue(true)); + $this->platformMock->method('supportsForeignKeyConstraints') + ->willReturn(false); $table = $this->createTableMock(); $foreignKey = $this->createForeignKeyConstraintMock(); @@ -89,6 +85,15 @@ public function testAcceptsForeignKey(): void $this->visitor->acceptForeignKey($table, $foreignKey); self::assertEmpty($this->visitor->getQueries()); + } + + public function testAcceptsForeignKeySupportsForeignKeyConstraints(): void + { + $this->platformMock->method('supportsForeignKeyConstraints') + ->willReturn(true); + + $table = $this->createTableMock(); + $foreignKey = $this->createForeignKeyConstraintMock(); $this->visitor->acceptForeignKey($table, $foreignKey); diff --git a/tests/Doctrine/Tests/DBAL/Schema/Visitor/DropSchemaSqlCollectorTest.php b/tests/Doctrine/Tests/DBAL/Schema/Visitor/DropSchemaSqlCollectorTest.php index bd663dc93e..1adde34600 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/Visitor/DropSchemaSqlCollectorTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/Visitor/DropSchemaSqlCollectorTest.php @@ -28,16 +28,12 @@ public function testGetQueriesUsesAcceptedForeignKeys(): void $collector = new DropSchemaSqlCollector($platform); - $platform->expects($this->exactly(2)) - ->method('getDropForeignKeySQL'); - - $platform->expects($this->at(0)) - ->method('getDropForeignKeySQL') - ->with($keyConstraintOne, $tableOne); - - $platform->expects($this->at(1)) + $platform->expects(self::exactly(2)) ->method('getDropForeignKeySQL') - ->with($keyConstraintTwo, $tableTwo); + ->withConsecutive( + [$keyConstraintOne, $tableOne], + [$keyConstraintTwo, $tableTwo] + ); $collector->acceptForeignKey($tableOne, $keyConstraintOne); $collector->acceptForeignKey($tableTwo, $keyConstraintTwo); diff --git a/tests/Doctrine/Tests/DBAL/Sharding/PoolingShardManagerTest.php b/tests/Doctrine/Tests/DBAL/Sharding/PoolingShardManagerTest.php index 0611680278..d2ce7b7f94 100644 --- a/tests/Doctrine/Tests/DBAL/Sharding/PoolingShardManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Sharding/PoolingShardManagerTest.php @@ -16,7 +16,7 @@ class PoolingShardManagerTest extends TestCase private function createConnectionMock(): PoolingShardConnection { return $this->getMockBuilder(PoolingShardConnection::class) - ->onlyMethods(['connect', 'getParams', 'fetchAll']) + ->onlyMethods(['connect', 'getParams', 'fetchAllAssociative']) ->disableOriginalConstructor() ->getMock(); } @@ -63,13 +63,11 @@ public function testSelectShard(): void $shardId = 10; $conn = $this->createConnectionMock(); - $conn->expects($this->at(0)) - ->method('getParams') + $conn->method('getParams') ->willReturn(['shardChoser' => $this->createPassthroughShardChoser()]); - $conn->expects($this->at(1)) - ->method('connect') - ->with($this->equalTo($shardId)); + $conn->method('connect') + ->with($shardId); $shardManager = new PoolingShardManager($conn); $shardManager->selectShard($shardId); @@ -99,22 +97,24 @@ public function testQueryAll(): void $types = [1]; $conn = $this->createConnectionMock(); - $conn->expects($this->at(0))->method('getParams')->will($this->returnValue( - ['shards' => [['id' => 1], ['id' => 2]], 'shardChoser' => $this->createPassthroughShardChoser()] - )); - $conn->expects($this->at(1))->method('getParams')->will($this->returnValue( - ['shards' => [['id' => 1], ['id' => 2]], 'shardChoser' => $this->createPassthroughShardChoser()] - )); - $conn->expects($this->at(2))->method('connect')->with($this->equalTo(1)); - $conn->expects($this->at(3)) - ->method('fetchAll') - ->with($this->equalTo($sql), $this->equalTo($params), $this->equalTo($types)) - ->will($this->returnValue([['id' => 1]])); - $conn->expects($this->at(4))->method('connect')->with($this->equalTo(2)); - $conn->expects($this->at(5)) - ->method('fetchAll') - ->with($this->equalTo($sql), $this->equalTo($params), $this->equalTo($types)) - ->will($this->returnValue([['id' => 2]])); + + $conn->method('getParams')->willReturn([ + 'shards' => [ + ['id' => 1], + ['id' => 2], + ], + 'shardChoser' => $this->createPassthroughShardChoser(), + ]); + + $conn->method('connect') + ->withConsecutive([1], [2]); + + $conn->method('fetchAllAssociative') + ->with($sql, $params, $types) + ->willReturnOnConsecutiveCalls( + [['id' => 1]], + [['id' => 2]], + ); $shardManager = new PoolingShardManager($conn); $result = $shardManager->queryAll($sql, $params, $types); @@ -129,22 +129,24 @@ public function testQueryAllWithStaticShardChoser(): void $types = [1]; $conn = $this->createConnectionMock(); - $conn->expects($this->at(0))->method('getParams')->will($this->returnValue( - ['shards' => [['id' => 1], ['id' => 2]], 'shardChoser' => $this->createStaticShardChooser()] - )); - $conn->expects($this->at(1))->method('getParams')->will($this->returnValue( - ['shards' => [['id' => 1], ['id' => 2]], 'shardChoser' => $this->createStaticShardChooser()] - )); - $conn->expects($this->at(2))->method('connect')->with($this->equalTo(1)); - $conn->expects($this->at(3)) - ->method('fetchAll') - ->with($this->equalTo($sql), $this->equalTo($params), $this->equalTo($types)) - ->will($this->returnValue([['id' => 1]])); - $conn->expects($this->at(4))->method('connect')->with($this->equalTo(2)); - $conn->expects($this->at(5)) - ->method('fetchAll') - ->with($this->equalTo($sql), $this->equalTo($params), $this->equalTo($types)) - ->will($this->returnValue([['id' => 2]])); + + $conn->method('getParams')->willReturn([ + 'shards' => [ + ['id' => 1], + ['id' => 2], + ], + 'shardChoser' => $this->createStaticShardChooser(), + ]); + + $conn->method('connect') + ->withConsecutive([1], [2]); + + $conn->method('fetchAllAssociative') + ->with($sql, $params, $types) + ->willReturnOnConsecutiveCalls( + [['id' => 1]], + [['id' => 2]], + ); $shardManager = new PoolingShardManager($conn); $result = $shardManager->queryAll($sql, $params, $types); diff --git a/tests/Doctrine/Tests/DBAL/Sharding/SQLAzure/SQLAzureShardManagerTest.php b/tests/Doctrine/Tests/DBAL/Sharding/SQLAzure/SQLAzureShardManagerTest.php index 55e54ac9b5..09a8f933a3 100644 --- a/tests/Doctrine/Tests/DBAL/Sharding/SQLAzure/SQLAzureShardManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Sharding/SQLAzure/SQLAzureShardManagerTest.php @@ -60,7 +60,8 @@ public function testSelectGlobalTransactionActive(): void ], ]); - $conn->expects($this->at(1))->method('isTransactionActive')->will($this->returnValue(true)); + $conn->method('isTransactionActive') + ->willReturn(true); $this->expectException(ShardingException::class); $this->expectExceptionMessage('Cannot switch shard during an active transaction.'); @@ -79,8 +80,12 @@ public function testSelectGlobal(): void ], ]); - $conn->expects($this->at(1))->method('isTransactionActive')->will($this->returnValue(false)); - $conn->expects($this->at(2))->method('exec')->with($this->equalTo('USE FEDERATION ROOT WITH RESET')); + $conn->method('isTransactionActive') + ->willReturn(false); + + $conn->expects($this->once()) + ->method('exec') + ->with('USE FEDERATION ROOT WITH RESET'); $sm = new SQLAzureShardManager($conn); $sm->selectGlobal(); @@ -96,7 +101,8 @@ public function testSelectShard(): void ], ]); - $conn->expects($this->at(1))->method('isTransactionActive')->will($this->returnValue(true)); + $conn->method('isTransactionActive') + ->willReturn(true); $this->expectException(ShardingException::class); $this->expectExceptionMessage('Cannot switch shard during an active transaction.'); @@ -118,7 +124,9 @@ private function createConnection(array $params): Connection ->onlyMethods(['getParams', 'exec', 'isTransactionActive']) ->disableOriginalConstructor() ->getMock(); - $conn->expects($this->at(0))->method('getParams')->will($this->returnValue($params)); + + $conn->method('getParams') + ->willReturn($params); return $conn; } diff --git a/tests/Doctrine/Tests/DBAL/StatementTest.php b/tests/Doctrine/Tests/DBAL/StatementTest.php index 69cae98514..e40c0d1537 100644 --- a/tests/Doctrine/Tests/DBAL/StatementTest.php +++ b/tests/Doctrine/Tests/DBAL/StatementTest.php @@ -4,15 +4,14 @@ use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\Connection as DriverConnection; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\Logging\SQLLogger; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Statement; use Doctrine\Tests\DbalTestCase; -use Exception; use PDOStatement; use PHPUnit\Framework\MockObject\MockObject; @@ -131,10 +130,9 @@ public function testExecuteCallsLoggerStopQueryOnException(): void ->method('getSQLLogger') ->will($this->returnValue($logger)); - // Needed to satisfy construction of DBALException $this->conn->expects($this->any()) - ->method('resolveParams') - ->will($this->returnValue([])); + ->method('handleExceptionDuringQuery') + ->will($this->throwException(new Exception())); $logger->expects($this->once()) ->method('startQuery'); @@ -144,11 +142,11 @@ public function testExecuteCallsLoggerStopQueryOnException(): void $this->pdoStatement->expects($this->once()) ->method('execute') - ->will($this->throwException(new Exception('Mock test exception'))); + ->will($this->throwException(new \Exception('Mock test exception'))); $statement = new Statement('', $this->conn); - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $statement->execute(); } diff --git a/tests/Doctrine/Tests/DBAL/Tools/Console/RunSqlCommandTest.php b/tests/Doctrine/Tests/DBAL/Tools/Console/RunSqlCommandTest.php index 38da446544..8a99dd366b 100644 --- a/tests/Doctrine/Tests/DBAL/Tools/Console/RunSqlCommandTest.php +++ b/tests/Doctrine/Tests/DBAL/Tools/Console/RunSqlCommandTest.php @@ -31,10 +31,6 @@ protected function setUp(): void $this->commandTester = new CommandTester($this->command); $this->connectionMock = $this->createMock(Connection::class); - $this->connectionMock->method('fetchAll') - ->willReturn([[1]]); - $this->connectionMock->method('executeUpdate') - ->willReturn(42); $helperSet = ConsoleRunner::createHelperSet($this->connectionMock); $this->command->setHelperSet($helperSet); @@ -69,7 +65,7 @@ public function testIncorrectDepthOption(): void public function testSelectStatementsPrintsResult(): void { - $this->expectConnectionFetchAll(); + $this->expectConnectionFetchAllAssociative(); $exitCode = $this->commandTester->execute([ 'command' => $this->command->getName(), @@ -77,46 +73,50 @@ public function testSelectStatementsPrintsResult(): void ]); $this->assertSame(0, $exitCode); - self::assertRegExp('@int.*1.*@', $this->commandTester->getDisplay()); - self::assertRegExp('@array.*1.*@', $this->commandTester->getDisplay()); + self::assertMatchesRegularExpression('@int.*1.*@', $this->commandTester->getDisplay()); + self::assertMatchesRegularExpression('@array.*1.*@', $this->commandTester->getDisplay()); } public function testUpdateStatementsPrintsAffectedLines(): void { - $this->expectConnectionExecuteUpdate(); + $this->expectConnectionExecuteStatement(); $this->commandTester->execute([ 'command' => $this->command->getName(), 'sql' => 'UPDATE foo SET bar = 42', ]); - self::assertRegExp('@int.*42.*@', $this->commandTester->getDisplay()); - self::assertNotRegExp('@array.*1.*@', $this->commandTester->getDisplay()); + self::assertMatchesRegularExpression('@int.*42.*@', $this->commandTester->getDisplay()); + self::assertDoesNotMatchRegularExpression('@array.*1.*@', $this->commandTester->getDisplay()); } - private function expectConnectionExecuteUpdate(): void + private function expectConnectionExecuteStatement(): void { $this->connectionMock - ->expects($this->exactly(1)) - ->method('executeUpdate'); + ->expects($this->once()) + ->method('executeStatement') + ->willReturn(42); + $this->connectionMock - ->expects($this->exactly(0)) - ->method('fetchAll'); + ->expects($this->never()) + ->method('fetchAllAssociative'); } - private function expectConnectionFetchAll(): void + private function expectConnectionFetchAllAssociative(): void { $this->connectionMock - ->expects($this->exactly(0)) - ->method('executeUpdate'); + ->expects($this->once()) + ->method('fetchAllAssociative') + ->willReturn([[1]]); + $this->connectionMock - ->expects($this->exactly(1)) - ->method('fetchAll'); + ->expects($this->never()) + ->method('executeStatement'); } public function testStatementsWithFetchResultPrintsResult(): void { - $this->expectConnectionFetchAll(); + $this->expectConnectionFetchAllAssociative(); $this->commandTester->execute([ 'command' => $this->command->getName(), @@ -124,7 +124,7 @@ public function testStatementsWithFetchResultPrintsResult(): void '--force-fetch' => true, ]); - self::assertRegExp('@int.*1.*@', $this->commandTester->getDisplay()); - self::assertRegExp('@array.*1.*@', $this->commandTester->getDisplay()); + self::assertMatchesRegularExpression('@int.*1.*@', $this->commandTester->getDisplay()); + self::assertMatchesRegularExpression('@array.*1.*@', $this->commandTester->getDisplay()); } } diff --git a/tests/Doctrine/Tests/DBAL/Types/AsciiStringTest.php b/tests/Doctrine/Tests/DBAL/Types/AsciiStringTest.php new file mode 100644 index 0000000000..0e4d5d299f --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Types/AsciiStringTest.php @@ -0,0 +1,43 @@ +type = new AsciiStringType(); + } + + public function testReturnCorrectBindingType(): void + { + self::assertEquals($this->type->getBindingType(), ParameterType::ASCII); + } + + public function testDelegateToPlatformForSqlDeclaration(): void + { + $columnDefinitions = [ + [['length' => 12, 'fixed' => true]], + [['length' => 14]], + ]; + + foreach ($columnDefinitions as $column) { + $platform = $this->createMock(AbstractPlatform::class); + $platform->expects(self::once()) + ->method('getAsciiStringTypeDeclarationSQL') + ->with($column); + + $this->type->getSQLDeclaration($column, $platform); + } + } +} diff --git a/tests/Doctrine/Tests/DBAL/Types/ConversionExceptionTest.php b/tests/Doctrine/Tests/DBAL/Types/ConversionExceptionTest.php index f2997ec5a9..226987b856 100644 --- a/tests/Doctrine/Tests/DBAL/Types/ConversionExceptionTest.php +++ b/tests/Doctrine/Tests/DBAL/Types/ConversionExceptionTest.php @@ -31,7 +31,7 @@ public function testConversionFailedInvalidTypeWithScalar($scalarValue): void $exception = ConversionException::conversionFailedInvalidType($scalarValue, 'foo', ['bar', 'baz']); self::assertInstanceOf(ConversionException::class, $exception); - self::assertRegExp( + self::assertMatchesRegularExpression( '/^Could not convert PHP value \'.*\' of type \'(string|boolean|float|double|integer)\' to type \'foo\'. ' . 'Expected one of the following types: bar, baz$/', $exception->getMessage() @@ -48,7 +48,7 @@ public function testConversionFailedInvalidTypeWithNonScalar($nonScalar): void $exception = ConversionException::conversionFailedInvalidType($nonScalar, 'foo', ['bar', 'baz']); self::assertInstanceOf(ConversionException::class, $exception); - self::assertRegExp( + self::assertMatchesRegularExpression( '/^Could not convert PHP value of type \'(.*)\' to type \'foo\'. ' . 'Expected one of the following types: bar, baz$/', $exception->getMessage() diff --git a/tests/Doctrine/Tests/DBAL/Types/TypeRegistryTest.php b/tests/Doctrine/Tests/DBAL/Types/TypeRegistryTest.php index be4ba0dddf..1cd7dca4fe 100644 --- a/tests/Doctrine/Tests/DBAL/Types/TypeRegistryTest.php +++ b/tests/Doctrine/Tests/DBAL/Types/TypeRegistryTest.php @@ -4,7 +4,7 @@ namespace Doctrine\Tests\DBAL\Types; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Types\BinaryType; use Doctrine\DBAL\Types\BlobType; use Doctrine\DBAL\Types\StringType; @@ -31,9 +31,10 @@ protected function setUp(): void $this->testType = new BlobType(); $this->otherTestType = new BinaryType(); - $this->registry = new TypeRegistry(); - $this->registry->register(self::TEST_TYPE_NAME, $this->testType); - $this->registry->register(self::OTHER_TEST_TYPE_NAME, $this->otherTestType); + $this->registry = new TypeRegistry([ + self::TEST_TYPE_NAME => $this->testType, + self::OTHER_TEST_TYPE_NAME => $this->otherTestType, + ]); } public function testGet(): void @@ -41,7 +42,7 @@ public function testGet(): void self::assertSame($this->testType, $this->registry->get(self::TEST_TYPE_NAME)); self::assertSame($this->otherTestType, $this->registry->get(self::OTHER_TEST_TYPE_NAME)); - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->registry->get('unknown'); } @@ -64,7 +65,7 @@ public function testLookupName(): void $this->registry->lookupName($this->otherTestType) ); - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->registry->lookupName(new TextType()); } @@ -89,7 +90,7 @@ public function testRegisterWithAlradyRegisteredName(): void { $this->registry->register('some', new TextType()); - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->registry->register('some', new TextType()); } @@ -99,7 +100,7 @@ public function testRegisterWithAlreadyRegisteredInstance(): void $this->registry->register('some', $newType); - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->registry->register('other', $newType); } @@ -131,13 +132,13 @@ public function testOverrideWithAlreadyRegisteredInstance(): void $this->registry->register('first', $newType); $this->registry->register('second', new StringType()); - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->registry->override('second', $newType); } public function testOverrideWithUnknownType(): void { - $this->expectException(DBALException::class); + $this->expectException(Exception::class); $this->registry->override('unknown', new TextType()); } diff --git a/tests/Doctrine/Tests/DBAL/UtilTest.php b/tests/Doctrine/Tests/DBAL/UtilTest.php index 15edbf97a5..da27013cf0 100644 --- a/tests/Doctrine/Tests/DBAL/UtilTest.php +++ b/tests/Doctrine/Tests/DBAL/UtilTest.php @@ -2,7 +2,7 @@ namespace Doctrine\Tests\DBAL; -use Doctrine\DBAL\Driver\OCI8\OCI8Statement; +use Doctrine\DBAL\Driver\OCI8\Statement; use Doctrine\Tests\DbalTestCase; class UtilTest extends DbalTestCase @@ -76,7 +76,7 @@ public function testConvertPositionalToNamedParameters( string $expectedOutputSQL, array $expectedOutputParamsMap ): void { - [$statement, $params] = OCI8Statement::convertPositionalToNamedPlaceholders($inputSQL); + [$statement, $params] = Statement::convertPositionalToNamedPlaceholders($inputSQL); self::assertEquals($expectedOutputSQL, $statement); self::assertEquals($expectedOutputParamsMap, $params); diff --git a/tests/Doctrine/Tests/DbalPerformanceTestCase.php b/tests/Doctrine/Tests/DbalPerformanceTestCase.php deleted file mode 100644 index 67ad9be5c7..0000000000 --- a/tests/Doctrine/Tests/DbalPerformanceTestCase.php +++ /dev/null @@ -1,60 +0,0 @@ -startTime, 'Test timing was started'); - self::assertNotNull($this->runTime, 'Test timing was stopped'); - } - - /** - * begin timing - */ - protected function startTiming(): void - { - $this->startTime = microtime(true); - } - - /** - * end timing - */ - protected function stopTiming(): void - { - $this->runTime = microtime(true) - $this->startTime; - } - - /** - * @return float elapsed test execution time - */ - public function getTime(): float - { - return $this->runTime; - } -} diff --git a/tests/Doctrine/Tests/DbalPerformanceTestListener.php b/tests/Doctrine/Tests/DbalPerformanceTestListener.php deleted file mode 100644 index 7e64849848..0000000000 --- a/tests/Doctrine/Tests/DbalPerformanceTestListener.php +++ /dev/null @@ -1,63 +0,0 @@ -> */ - private $timings = []; - - public function endTest(Test $test, float $time): void - { - // This listener only applies to performance tests. - if (! ($test instanceof DbalPerformanceTestCase)) { - return; - } - - // we identify perf tests by class, method, and dataset - $class = str_replace('\\Doctrine\\Tests\\DBAL\\Performance\\', '', get_class($test)); - - if (! isset($this->timings[$class])) { - $this->timings[$class] = []; - } - - // Store timing data for each test in the order they were run. - $this->timings[$class][$test->getName(true)] = $test->getTime(); - } - - /** - * Report performance test timings. - * - * Note: __destruct is used here because PHPUnit doesn't have a - * 'All tests over' hook. - */ - public function __destruct() - { - if (empty($this->timings)) { - return; - } - - // Report timings. - print "\nPerformance test results:\n\n"; - - foreach ($this->timings as $class => $tests) { - printf("%s:\n", $class); - foreach ($tests as $test => $time) { - printf("\t%s: %.3f seconds\n", $test, $time); - } - } - } -} diff --git a/tests/Doctrine/Tests/TestUtil.php b/tests/Doctrine/Tests/TestUtil.php index bab3dc9be0..f45ee6e232 100644 --- a/tests/Doctrine/Tests/TestUtil.php +++ b/tests/Doctrine/Tests/TestUtil.php @@ -4,10 +4,16 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Platforms\AbstractPlatform; use PHPUnit\Framework\Assert; +use function array_keys; +use function array_map; +use function array_values; use function explode; use function extension_loaded; +use function implode; +use function is_string; use function strlen; use function strpos; use function substr; @@ -208,4 +214,24 @@ public static function getPrivilegedConnection(): Connection { return DriverManager::getConnection(self::getPrivilegedConnectionParameters()); } + + /** + * Generates a query that will return the given rows without the need to create a temporary table. + * + * @param array> $rows + */ + public static function generateResultSetQuery(array $rows, AbstractPlatform $platform): string + { + return implode(' UNION ALL ', array_map(static function (array $row) use ($platform): string { + return $platform->getDummySelectSQL( + implode(', ', array_map(static function (string $column, $value) use ($platform): string { + if (is_string($value)) { + $value = $platform->quoteStringLiteral($value); + } + + return $value . ' ' . $platform->quoteIdentifier($column); + }, array_keys($row), array_values($row))) + ); + }, $rows)); + } } diff --git a/tests/appveyor/mssql.sql2008r2sp2.sqlsrv.appveyor.xml b/tests/appveyor/mssql.sql2008r2sp2.sqlsrv.appveyor.xml index cacadae2cb..f9e1c220ec 100644 --- a/tests/appveyor/mssql.sql2008r2sp2.sqlsrv.appveyor.xml +++ b/tests/appveyor/mssql.sql2008r2sp2.sqlsrv.appveyor.xml @@ -1,8 +1,7 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/appveyor/mssql.sql2012sp1.sqlsrv.appveyor.xml b/tests/appveyor/mssql.sql2012sp1.sqlsrv.appveyor.xml index 9fea8d8668..0b06001862 100644 --- a/tests/appveyor/mssql.sql2012sp1.sqlsrv.appveyor.xml +++ b/tests/appveyor/mssql.sql2012sp1.sqlsrv.appveyor.xml @@ -1,8 +1,7 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/appveyor/mssql.sql2017.pdo_sqlsrv.appveyor.xml b/tests/appveyor/mssql.sql2017.pdo_sqlsrv.appveyor.xml index 88a1ea41b4..dcf655bee0 100644 --- a/tests/appveyor/mssql.sql2017.pdo_sqlsrv.appveyor.xml +++ b/tests/appveyor/mssql.sql2017.pdo_sqlsrv.appveyor.xml @@ -1,8 +1,7 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/appveyor/mssql.sql2017.sqlsrv.appveyor.xml b/tests/appveyor/mssql.sql2017.sqlsrv.appveyor.xml index ac1cfd0834..bf908ed9b5 100644 --- a/tests/appveyor/mssql.sql2017.sqlsrv.appveyor.xml +++ b/tests/appveyor/mssql.sql2017.sqlsrv.appveyor.xml @@ -1,8 +1,7 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/travis/ibm_db2.travis.xml b/tests/travis/ibm_db2.travis.xml index 8e81125da6..3089c56c47 100644 --- a/tests/travis/ibm_db2.travis.xml +++ b/tests/travis/ibm_db2.travis.xml @@ -1,6 +1,6 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/travis/mariadb.docker.travis.xml b/tests/travis/mariadb.docker.travis.xml index 6805a5cce8..67f63b2fde 100644 --- a/tests/travis/mariadb.docker.travis.xml +++ b/tests/travis/mariadb.docker.travis.xml @@ -1,6 +1,6 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/travis/mariadb.mysqli.docker.travis.xml b/tests/travis/mariadb.mysqli.docker.travis.xml index bfb147b7fd..d93ca5ceda 100644 --- a/tests/travis/mariadb.mysqli.docker.travis.xml +++ b/tests/travis/mariadb.mysqli.docker.travis.xml @@ -1,6 +1,6 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/travis/mysql.docker.travis.xml b/tests/travis/mysql.docker.travis.xml index 6805a5cce8..67f63b2fde 100644 --- a/tests/travis/mysql.docker.travis.xml +++ b/tests/travis/mysql.docker.travis.xml @@ -1,6 +1,6 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/travis/mysqli-tls.docker.travis.xml b/tests/travis/mysqli-tls.docker.travis.xml index 95d6444522..f6f653c42b 100644 --- a/tests/travis/mysqli-tls.docker.travis.xml +++ b/tests/travis/mysqli-tls.docker.travis.xml @@ -1,6 +1,6 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/travis/mysqli.docker.travis.xml b/tests/travis/mysqli.docker.travis.xml index bfb147b7fd..d93ca5ceda 100644 --- a/tests/travis/mysqli.docker.travis.xml +++ b/tests/travis/mysqli.docker.travis.xml @@ -1,6 +1,6 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/travis/pdo_sqlsrv.travis.xml b/tests/travis/pdo_sqlsrv.travis.xml index 35ba2624cc..38802c599e 100644 --- a/tests/travis/pdo_sqlsrv.travis.xml +++ b/tests/travis/pdo_sqlsrv.travis.xml @@ -1,6 +1,6 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/travis/pgsql.travis.xml b/tests/travis/pgsql.travis.xml index 7fb828fc7f..0a1a5c1beb 100644 --- a/tests/travis/pgsql.travis.xml +++ b/tests/travis/pgsql.travis.xml @@ -1,6 +1,6 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/travis/sqlite.travis.xml b/tests/travis/sqlite.travis.xml index c6e3e291fc..ce3c1d0f91 100644 --- a/tests/travis/sqlite.travis.xml +++ b/tests/travis/sqlite.travis.xml @@ -1,6 +1,6 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + + diff --git a/tests/travis/sqlsrv.travis.xml b/tests/travis/sqlsrv.travis.xml index 890fb9d442..de56350ce9 100644 --- a/tests/travis/sqlsrv.travis.xml +++ b/tests/travis/sqlsrv.travis.xml @@ -1,6 +1,6 @@ - - + + ../../lib/Doctrine - - - - - - performance - locking_functional - - + +