Unverified Commit 3b6e69d4 authored by Grégoire Paris's avatar Grégoire Paris

Merge branch '3.0.x'

parents ea4232a7 430dce61
...@@ -23,11 +23,11 @@ jobs: ...@@ -23,11 +23,11 @@ jobs:
uses: "actions/cache@v1.0.3" uses: "actions/cache@v1.0.3"
with: with:
path: "~/.composer/cache" path: "~/.composer/cache"
key: "composer-${{ hashFiles('composer.json') }}" key: "composer-${{ hashFiles('composer.lock') }}"
restore-keys: "composer-" restore-keys: "composer-"
- name: "Install dependencies with composer" - name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest" run: "composer install --no-interaction --no-progress --no-suggest"
- name: Psalm - name: Psalm
run: "vendor/bin/psalm" run: "vendor/bin/psalm"
...@@ -1283,16 +1283,16 @@ ...@@ -1283,16 +1283,16 @@
}, },
{ {
"name": "phpstan/phpdoc-parser", "name": "phpstan/phpdoc-parser",
"version": "0.4.2", "version": "0.4.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git", "url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "a6d13524641bb780efc821d9e0a1e1bfb23cbd0e" "reference": "d8d9d4645379e677466d407034436bb155b11c65"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a6d13524641bb780efc821d9e0a1e1bfb23cbd0e", "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/d8d9d4645379e677466d407034436bb155b11c65",
"reference": "a6d13524641bb780efc821d9e0a1e1bfb23cbd0e", "reference": "d8d9d4645379e677466d407034436bb155b11c65",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
...@@ -1300,10 +1300,11 @@ ...@@ -1300,10 +1300,11 @@
}, },
"require-dev": { "require-dev": {
"consistence/coding-standard": "^3.5", "consistence/coding-standard": "^3.5",
"ergebnis/composer-normalize": "^2.0.2",
"jakub-onderka/php-parallel-lint": "^0.9.2", "jakub-onderka/php-parallel-lint": "^0.9.2",
"phing/phing": "^2.16.0", "phing/phing": "^2.16.0",
"phpstan/extension-installer": "^1.0", "phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12", "phpstan/phpstan": "^0.12.19",
"phpstan/phpstan-strict-rules": "^0.12", "phpstan/phpstan-strict-rules": "^0.12",
"phpunit/phpunit": "^6.3", "phpunit/phpunit": "^6.3",
"slevomat/coding-standard": "^4.7.2", "slevomat/coding-standard": "^4.7.2",
...@@ -1327,7 +1328,7 @@ ...@@ -1327,7 +1328,7 @@
"MIT" "MIT"
], ],
"description": "PHPDoc parser with support for nullable, intersection and generic types", "description": "PHPDoc parser with support for nullable, intersection and generic types",
"time": "2019-12-13T12:03:22+00:00" "time": "2020-04-13T16:28:46+00:00"
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
...@@ -2586,33 +2587,39 @@ ...@@ -2586,33 +2587,39 @@
}, },
{ {
"name": "slevomat/coding-standard", "name": "slevomat/coding-standard",
"version": "6.1.1", "version": "6.3.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/slevomat/coding-standard.git", "url": "https://github.com/slevomat/coding-standard.git",
"reference": "0a7934d7ecdfe402079027513daa3b7e881f315d" "reference": "b905a82255749de847fd4de607c7a4c8163f058d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/0a7934d7ecdfe402079027513daa3b7e881f315d", "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b905a82255749de847fd4de607c7a4c8163f058d",
"reference": "0a7934d7ecdfe402079027513daa3b7e881f315d", "reference": "b905a82255749de847fd4de607c7a4c8163f058d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1", "php": "^7.1",
"phpstan/phpdoc-parser": "0.3.5 - 0.4.2", "phpstan/phpdoc-parser": "0.4.0 - 0.4.4",
"squizlabs/php_codesniffer": "^3.5.3" "squizlabs/php_codesniffer": "^3.5.5"
}, },
"require-dev": { "require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "0.5.0", "dealerdirect/phpcodesniffer-composer-installer": "0.6.2",
"jakub-onderka/php-parallel-lint": "1.0.0", "phing/phing": "2.16.3",
"phing/phing": "2.16.2", "php-parallel-lint/php-parallel-lint": "1.2.0",
"phpstan/phpstan": "0.11.19|0.12.5", "phpstan/phpstan": "0.12.19",
"phpstan/phpstan-phpunit": "0.11.2|0.12.6", "phpstan/phpstan-deprecation-rules": "0.12.2",
"phpstan/phpstan-strict-rules": "0.11.1|0.12.1", "phpstan/phpstan-phpunit": "0.12.8",
"phpunit/phpunit": "7.5.18|8.5.2" "phpstan/phpstan-strict-rules": "0.12.2",
"phpunit/phpunit": "7.5.20|8.5.2|9.1.2"
}, },
"type": "phpcodesniffer-standard", "type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
}
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"SlevomatCodingStandard\\": "SlevomatCodingStandard" "SlevomatCodingStandard\\": "SlevomatCodingStandard"
...@@ -2623,20 +2630,30 @@ ...@@ -2623,20 +2630,30 @@
"MIT" "MIT"
], ],
"description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
"time": "2020-01-23T15:37:30+00:00" "funding": [
{
"url": "https://github.com/kukulich",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
"type": "tidelift"
}
],
"time": "2020-04-28T07:15:08+00:00"
}, },
{ {
"name": "squizlabs/php_codesniffer", "name": "squizlabs/php_codesniffer",
"version": "3.5.3", "version": "3.5.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb" "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/557a1fc7ac702c66b0bbfe16ab3d55839ef724cb", "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb", "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
...@@ -2674,7 +2691,7 @@ ...@@ -2674,7 +2691,7 @@
"phpcs", "phpcs",
"standards" "standards"
], ],
"time": "2019-12-04T04:46:47+00:00" "time": "2020-04-17T01:09:41+00:00"
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
<!-- https://github.com/slevomat/coding-standard/issues/867 --> <!-- https://github.com/slevomat/coding-standard/issues/867 -->
<exclude name="SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing.IncorrectLinesCountAfterLastControlStructure"/> <exclude name="SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing.IncorrectLinesCountAfterLastControlStructure"/>
<!-- See https://github.com/squizlabs/PHP_CodeSniffer/issues/2937 -->
<exclude name="Squiz.Arrays.ArrayDeclaration.ValueNoNewline"/>
<exclude name="Squiz.NamingConventions.ValidVariableName.PublicHasUnderscore"/> <exclude name="Squiz.NamingConventions.ValidVariableName.PublicHasUnderscore"/>
</rule> </rule>
...@@ -34,6 +36,18 @@ ...@@ -34,6 +36,18 @@
</properties> </properties>
</rule> </rule>
<rule ref="Squiz.NamingConventions.ValidVariableName.PublicHasUnderscore">
<exclude-pattern>*/src/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint">
<exclude-pattern>*/src/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint">
<exclude-pattern>*/src/*</exclude-pattern>
</rule>
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses"> <rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
<exclude-pattern>*/tests/*</exclude-pattern> <exclude-pattern>*/tests/*</exclude-pattern>
</rule> </rule>
...@@ -77,7 +91,13 @@ ...@@ -77,7 +91,13 @@
<!-- https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 --> <!-- https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 -->
<rule ref="Squiz.NamingConventions.ValidVariableName.NotCamelCaps"> <rule ref="Squiz.NamingConventions.ValidVariableName.NotCamelCaps">
<!--
This file uses the return value db2_server_info(), which does not follow conventions
phpcs wrongly complains about it, and that has been reported here:
https://github.com/squizlabs/PHP_CodeSniffer/issues/2950
-->
<exclude-pattern>src/Driver/IBMDB2/DB2Connection.php</exclude-pattern> <exclude-pattern>src/Driver/IBMDB2/DB2Connection.php</exclude-pattern>
<!-- See https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 -->
<exclude-pattern>src/SQLParserUtils.php</exclude-pattern> <exclude-pattern>src/SQLParserUtils.php</exclude-pattern>
<exclude-pattern>src/Tools/Dumper.php</exclude-pattern> <exclude-pattern>src/Tools/Dumper.php</exclude-pattern>
<exclude-pattern>tests/Driver/StatementIteratorTest.php</exclude-pattern> <exclude-pattern>tests/Driver/StatementIteratorTest.php</exclude-pattern>
......
...@@ -47,6 +47,19 @@ parameters: ...@@ -47,6 +47,19 @@ parameters:
message: '~^Cannot cast array<string>\|bool\|string\|null to int\.$~' message: '~^Cannot cast array<string>\|bool\|string\|null to int\.$~'
path: %currentWorkingDirectory%/src/Tools/Console/Command/RunSqlCommand.php path: %currentWorkingDirectory%/src/Tools/Console/Command/RunSqlCommand.php
# Requires a release of https://github.com/JetBrains/phpstorm-stubs/pull/732
-
message: '~^Access to undefined constant PDO::PGSQL_ATTR_DISABLE_PREPARES\.$~'
paths:
- %currentWorkingDirectory%/src/Driver/PDOPgSql/Driver.php
- %currentWorkingDirectory%/tests/Driver/PDOPgSql/DriverTest.php
# False Positive
- '~Strict comparison using === between 1 and 2 will always evaluate to false~'
# Needs Generics
- '~Method Doctrine\\DBAL\\Schema\\SchemaDiff::getNewTablesSortedByDependencies\(\) should return array<Doctrine\\DBAL\\Schema\\Table> but returns array<object>.~'
# https://github.com/phpstan/phpstan/issues/3134 # https://github.com/phpstan/phpstan/issues/3134
- -
message: '~^Call to static method PHPUnit\\Framework\\Assert::assertSame\(\) with Doctrine\\DBAL\\Types\\Type and Doctrine\\DBAL\\Types\\Type will always evaluate to true\.$~' message: '~^Call to static method PHPUnit\\Framework\\Assert::assertSame\(\) with Doctrine\\DBAL\\Types\\Type and Doctrine\\DBAL\\Types\\Type will always evaluate to true\.$~'
......
...@@ -7,7 +7,7 @@ namespace Doctrine\DBAL\Driver\OCI8; ...@@ -7,7 +7,7 @@ namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractOracleDriver; use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Connection;
use const OCI_DEFAULT; use const OCI_NO_AUTO_COMMIT;
/** /**
* A Doctrine DBAL driver for the Oracle OCI8 PHP extensions. * A Doctrine DBAL driver for the Oracle OCI8 PHP extensions.
...@@ -29,7 +29,7 @@ final class Driver extends AbstractOracleDriver ...@@ -29,7 +29,7 @@ final class Driver extends AbstractOracleDriver
$password, $password,
$this->constructDsn($params), $this->constructDsn($params),
$params['charset'] ?? '', $params['charset'] ?? '',
$params['sessionMode'] ?? OCI_DEFAULT, $params['sessionMode'] ?? OCI_NO_AUTO_COMMIT,
$params['persistent'] ?? false $params['persistent'] ?? false
); );
} catch (OCI8Exception $e) { } catch (OCI8Exception $e) {
......
...@@ -19,7 +19,7 @@ use function oci_server_version; ...@@ -19,7 +19,7 @@ use function oci_server_version;
use function preg_match; use function preg_match;
use function sprintf; use function sprintf;
use function str_replace; use function str_replace;
use const OCI_DEFAULT; use const OCI_NO_AUTO_COMMIT;
/** /**
* OCI8 implementation of the Connection interface. * OCI8 implementation of the Connection interface.
...@@ -42,7 +42,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection ...@@ -42,7 +42,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
string $password, string $password,
string $db, string $db,
string $charset = '', string $charset = '',
int $sessionMode = OCI_DEFAULT, int $sessionMode = OCI_NO_AUTO_COMMIT,
bool $persistent = false bool $persistent = false
) { ) {
$dbh = $persistent $dbh = $persistent
......
...@@ -32,7 +32,6 @@ use const OCI_COMMIT_ON_SUCCESS; ...@@ -32,7 +32,6 @@ use const OCI_COMMIT_ON_SUCCESS;
use const OCI_D_LOB; use const OCI_D_LOB;
use const OCI_FETCHSTATEMENT_BY_COLUMN; use const OCI_FETCHSTATEMENT_BY_COLUMN;
use const OCI_FETCHSTATEMENT_BY_ROW; use const OCI_FETCHSTATEMENT_BY_ROW;
use const OCI_NO_AUTO_COMMIT;
use const OCI_NUM; use const OCI_NUM;
use const OCI_RETURN_LOBS; use const OCI_RETURN_LOBS;
use const OCI_RETURN_NULLS; use const OCI_RETURN_NULLS;
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Internal;
use function array_reverse;
/**
* DependencyOrderCalculator implements topological sorting, which is an ordering
* algorithm for directed graphs (DG) and/or directed acyclic graphs (DAG) by
* using a depth-first searching (DFS) to traverse the graph built in memory.
* This algorithm have a linear running time based on nodes (V) and dependency
* between the nodes (E), resulting in a computational complexity of O(V + E).
*/
final class DependencyOrderCalculator
{
public const NOT_VISITED = 0;
public const IN_PROGRESS = 1;
public const VISITED = 2;
/**
* Matrix of nodes (aka. vertex).
* Keys are provided hashes and values are the node definition objects.
*
* @var array<string,DependencyOrderNode>
*/
private $nodeList = [];
/**
* Volatile variable holding calculated nodes during sorting process.
*
* @var array<object>
*/
private $sortedNodeList = [];
/**
* Checks for node (vertex) existence in graph.
*/
public function hasNode(string $hash) : bool
{
return isset($this->nodeList[$hash]);
}
/**
* Adds a new node (vertex) to the graph, assigning its hash and value.
*
* @param object $node
*/
public function addNode(string $hash, $node) : void
{
$vertex = new DependencyOrderNode();
$vertex->hash = $hash;
$vertex->state = self::NOT_VISITED;
$vertex->value = $node;
$this->nodeList[$hash] = $vertex;
}
/**
* Adds a new dependency (edge) to the graph using their hashes.
*/
public function addDependency(string $fromHash, string $toHash) : void
{
$vertex = $this->nodeList[$fromHash];
$edge = new DependencyOrderEdge();
$edge->from = $fromHash;
$edge->to = $toHash;
$vertex->dependencyList[$toHash] = $edge;
}
/**
* Return a valid order list of all current nodes.
* The desired topological sorting is the reverse post order of these searches.
*
* {@internal Highly performance-sensitive method.}
*
* @return array<object>
*/
public function sort() : array
{
foreach ($this->nodeList as $vertex) {
if ($vertex->state !== self::NOT_VISITED) {
continue;
}
$this->visit($vertex);
}
$sortedList = $this->sortedNodeList;
$this->nodeList = [];
$this->sortedNodeList = [];
return array_reverse($sortedList);
}
/**
* Visit a given node definition for reordering.
*
* {@internal Highly performance-sensitive method.}
*/
private function visit(DependencyOrderNode $vertex) : void
{
$vertex->state = self::IN_PROGRESS;
foreach ($vertex->dependencyList as $edge) {
$adjacentVertex = $this->nodeList[$edge->to];
switch ($adjacentVertex->state) {
case self::VISITED:
case self::IN_PROGRESS:
// Do nothing, since node was already visited or is
// currently visited
break;
case self::NOT_VISITED:
$this->visit($adjacentVertex);
}
}
if ($vertex->state === self::VISITED) {
return;
}
$vertex->state = self::VISITED;
$this->sortedNodeList[] = $vertex->value;
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Internal;
class DependencyOrderEdge
{
/** @var string */
public $from;
/** @var string */
public $to;
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Internal;
class DependencyOrderNode
{
/** @var string */
public $hash;
/** @var int */
public $state;
/** @var object */
public $value;
/** @var DependencyOrderEdge[] */
public $dependencyList = [];
}
...@@ -2807,6 +2807,16 @@ abstract class AbstractPlatform ...@@ -2807,6 +2807,16 @@ abstract class AbstractPlatform
return true; return true;
} }
/**
* Whether foreign key constraints can be dropped.
*
* If false, then getDropForeignKeySQL() throws exception.
*/
public function supportsCreateDropForeignKeyConstraints() : bool
{
return true;
}
/** /**
* Whether this platform supports onUpdate in foreign key constraints. * Whether this platform supports onUpdate in foreign key constraints.
*/ */
......
...@@ -591,7 +591,10 @@ class DB2Platform extends AbstractPlatform ...@@ -591,7 +591,10 @@ class DB2Platform extends AbstractPlatform
foreach ($diff->removedIndexes as $remKey => $remIndex) { foreach ($diff->removedIndexes as $remKey => $remIndex) {
foreach ($diff->addedIndexes as $addKey => $addIndex) { foreach ($diff->addedIndexes as $addKey => $addIndex) {
if ($remIndex->getColumns() === $addIndex->getColumns()) { if ($remIndex->getColumns() !== $addIndex->getColumns()) {
continue;
}
if ($remIndex->isPrimary()) { if ($remIndex->isPrimary()) {
$sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; $sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
} elseif ($remIndex->isUnique()) { } elseif ($remIndex->isUnique()) {
...@@ -607,7 +610,6 @@ class DB2Platform extends AbstractPlatform ...@@ -607,7 +610,6 @@ class DB2Platform extends AbstractPlatform
break; break;
} }
} }
}
$sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff)); $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
......
...@@ -547,7 +547,10 @@ SQL ...@@ -547,7 +547,10 @@ SQL
$sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $remIndex)); $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $remIndex));
foreach ($diff->addedIndexes as $addKey => $addIndex) { foreach ($diff->addedIndexes as $addKey => $addIndex) {
if ($remIndex->getColumns() === $addIndex->getColumns()) { if ($remIndex->getColumns() !== $addIndex->getColumns()) {
continue;
}
$indexClause = 'INDEX ' . $addIndex->getName(); $indexClause = 'INDEX ' . $addIndex->getName();
if ($addIndex->isPrimary()) { if ($addIndex->isPrimary()) {
...@@ -567,7 +570,6 @@ SQL ...@@ -567,7 +570,6 @@ SQL
break; break;
} }
} }
}
$engine = 'INNODB'; $engine = 'INNODB';
......
...@@ -659,6 +659,11 @@ class SqlitePlatform extends AbstractPlatform ...@@ -659,6 +659,11 @@ class SqlitePlatform extends AbstractPlatform
} }
public function supportsForeignKeyConstraints() : bool public function supportsForeignKeyConstraints() : bool
{
return true;
}
public function supportsCreateDropForeignKeyConstraints() : bool
{ {
return false; return false;
} }
...@@ -676,7 +681,7 @@ class SqlitePlatform extends AbstractPlatform ...@@ -676,7 +681,7 @@ class SqlitePlatform extends AbstractPlatform
*/ */
public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) : string public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) : string
{ {
throw new DBALException('Sqlite platform does not support alter foreign key.'); throw new DBALException('Sqlite platform does not support alter foreign key, the table must be fully recreated using getAlterTableSQL.');
} }
/** /**
...@@ -684,7 +689,7 @@ class SqlitePlatform extends AbstractPlatform ...@@ -684,7 +689,7 @@ class SqlitePlatform extends AbstractPlatform
*/ */
public function getDropForeignKeySQL($foreignKey, $table) : string public function getDropForeignKeySQL($foreignKey, $table) : string
{ {
throw new DBALException('Sqlite platform does not support alter foreign key.'); throw new DBALException('Sqlite platform does not support alter foreign key, the table must be fully recreated using getAlterTableSQL.');
} }
/** /**
......
...@@ -4,6 +4,7 @@ declare(strict_types=1); ...@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Schema; namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Internal\DependencyOrderCalculator;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use function array_merge; use function array_merge;
...@@ -137,13 +138,16 @@ class SchemaDiff ...@@ -137,13 +138,16 @@ class SchemaDiff
} }
$foreignKeySql = []; $foreignKeySql = [];
foreach ($this->newTables as $table) { $createFlags = AbstractPlatform::CREATE_INDEXES;
$sql = array_merge(
$sql, if (! $platform->supportsCreateDropForeignKeyConstraints()) {
$platform->getCreateTableSQL($table, AbstractPlatform::CREATE_INDEXES) $createFlags |= AbstractPlatform::CREATE_FOREIGNKEYS;
); }
if (! $platform->supportsForeignKeyConstraints()) { foreach ($this->getNewTablesSortedByDependencies() as $table) {
$sql = array_merge($sql, $platform->getCreateTableSQL($table, $createFlags));
if (! $platform->supportsCreateDropForeignKeyConstraints()) {
continue; continue;
} }
...@@ -166,4 +170,37 @@ class SchemaDiff ...@@ -166,4 +170,37 @@ class SchemaDiff
return $sql; return $sql;
} }
/**
* Sorts tables by dependencies so that they are created in the right order.
*
* This is necessary when one table depends on another while creating foreign key
* constraints directly during CREATE TABLE.
*
* @return array<Table>
*/
private function getNewTablesSortedByDependencies()
{
$calculator = new DependencyOrderCalculator();
$newTables = [];
foreach ($this->newTables as $table) {
$newTables[$table->getName()] = true;
$calculator->addNode($table->getName(), $table);
}
foreach ($this->newTables as $table) {
foreach ($table->getForeignKeys() as $foreignKey) {
$foreignTableName = $foreignKey->getForeignTableName();
if (! isset($newTables[$foreignTableName])) {
continue;
}
$calculator->addDependency($foreignTableName, $table->getName());
}
}
return $calculator->sort();
}
} }
...@@ -43,6 +43,10 @@ class DropSchemaSqlCollector extends AbstractVisitor ...@@ -43,6 +43,10 @@ class DropSchemaSqlCollector extends AbstractVisitor
public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) : void public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) : void
{ {
if (! $this->platform->supportsCreateDropForeignKeyConstraints()) {
return;
}
if (strlen($fkConstraint->getName()) === 0) { if (strlen($fkConstraint->getName()) === 0) {
throw NamedForeignKeyRequired::new($localTable, $fkConstraint); throw NamedForeignKeyRequired::new($localTable, $fkConstraint);
} }
......
...@@ -133,13 +133,11 @@ class ConnectionTest extends TestCase ...@@ -133,13 +133,11 @@ class ConnectionTest extends TestCase
{ {
$eventManager = new EventManager(); $eventManager = new EventManager();
/** @var AbstractPlatform|MockObject $platform */
$platform = $this->createMock(AbstractPlatform::class); $platform = $this->createMock(AbstractPlatform::class);
$platform->expects(self::once()) $platform->expects(self::once())
->method('setEventManager') ->method('setEventManager')
->with($eventManager); ->with($eventManager);
/** @var Driver|MockObject $driver */
$driver = $this->createMock(Driver::class); $driver = $this->createMock(Driver::class);
$driver->expects(self::any()) $driver->expects(self::any())
->method('getDatabasePlatform') ->method('getDatabasePlatform')
...@@ -518,7 +516,6 @@ class ConnectionTest extends TestCase ...@@ -518,7 +516,6 @@ class ConnectionTest extends TestCase
->with(FetchMode::ASSOCIATIVE) ->with(FetchMode::ASSOCIATIVE)
->will(self::returnValue($result)); ->will(self::returnValue($result));
/** @var Connection|MockObject $conn */
$conn = $this->getMockBuilder(Connection::class) $conn = $this->getMockBuilder(Connection::class)
->onlyMethods(['executeQuery']) ->onlyMethods(['executeQuery'])
->setConstructorArgs([[], $driverMock]) ->setConstructorArgs([[], $driverMock])
...@@ -554,7 +551,6 @@ class ConnectionTest extends TestCase ...@@ -554,7 +551,6 @@ class ConnectionTest extends TestCase
->with(FetchMode::NUMERIC) ->with(FetchMode::NUMERIC)
->will(self::returnValue($result)); ->will(self::returnValue($result));
/** @var Connection|MockObject $conn */
$conn = $this->getMockBuilder(Connection::class) $conn = $this->getMockBuilder(Connection::class)
->onlyMethods(['executeQuery']) ->onlyMethods(['executeQuery'])
->setConstructorArgs([[], $driverMock]) ->setConstructorArgs([[], $driverMock])
...@@ -589,7 +585,6 @@ class ConnectionTest extends TestCase ...@@ -589,7 +585,6 @@ class ConnectionTest extends TestCase
->method('fetchColumn') ->method('fetchColumn')
->will(self::returnValue($result)); ->will(self::returnValue($result));
/** @var Connection|MockObject $conn */
$conn = $this->getMockBuilder(Connection::class) $conn = $this->getMockBuilder(Connection::class)
->onlyMethods(['executeQuery']) ->onlyMethods(['executeQuery'])
->setConstructorArgs([[], $driverMock]) ->setConstructorArgs([[], $driverMock])
...@@ -624,7 +619,6 @@ class ConnectionTest extends TestCase ...@@ -624,7 +619,6 @@ class ConnectionTest extends TestCase
->method('fetchAll') ->method('fetchAll')
->will(self::returnValue($result)); ->will(self::returnValue($result));
/** @var Connection|MockObject $conn */
$conn = $this->getMockBuilder(Connection::class) $conn = $this->getMockBuilder(Connection::class)
->onlyMethods(['executeQuery']) ->onlyMethods(['executeQuery'])
->setConstructorArgs([[], $driverMock]) ->setConstructorArgs([[], $driverMock])
...@@ -640,7 +634,6 @@ class ConnectionTest extends TestCase ...@@ -640,7 +634,6 @@ class ConnectionTest extends TestCase
public function testCallingDeleteWithNoDeletionCriteriaResultsInInvalidArgumentException() : void public function testCallingDeleteWithNoDeletionCriteriaResultsInInvalidArgumentException() : void
{ {
/** @var Driver $driver */
$driver = $this->createMock(Driver::class); $driver = $this->createMock(Driver::class);
$conn = new Connection([], $driver); $conn = new Connection([], $driver);
...@@ -650,7 +643,6 @@ class ConnectionTest extends TestCase ...@@ -650,7 +643,6 @@ class ConnectionTest extends TestCase
public function testCallConnectOnce() : void public function testCallConnectOnce() : void
{ {
/** @var Driver|MockObject $driver */
$driver = $this->createMock(Driver::class); $driver = $this->createMock(Driver::class);
$driver->expects(self::once()) $driver->expects(self::once())
->method('connect'); ->method('connect');
...@@ -667,13 +659,10 @@ class ConnectionTest extends TestCase ...@@ -667,13 +659,10 @@ class ConnectionTest extends TestCase
*/ */
public function testPlatformDetectionIsTriggerOnlyOnceOnRetrievingPlatform() : void public function testPlatformDetectionIsTriggerOnlyOnceOnRetrievingPlatform() : void
{ {
/** @var VersionAwarePlatformDriver|MockObject $driverMock */
$driverMock = $this->createMock(VersionAwarePlatformDriver::class); $driverMock = $this->createMock(VersionAwarePlatformDriver::class);
/** @var DriverConnection|ServerInfoAwareConnection|MockObject $driverConnectionMock */
$driverConnectionMock = $this->createMock(ServerInfoAwareConnection::class); $driverConnectionMock = $this->createMock(ServerInfoAwareConnection::class);
/** @var AbstractPlatform|MockObject $platformMock */
$platformMock = $this->getMockForAbstractClass(AbstractPlatform::class); $platformMock = $this->getMockForAbstractClass(AbstractPlatform::class);
$connection = new Connection([], $driverMock); $connection = new Connection([], $driverMock);
...@@ -708,7 +697,6 @@ class ConnectionTest extends TestCase ...@@ -708,7 +697,6 @@ class ConnectionTest extends TestCase
$params = [666]; $params = [666];
$types = [ParameterType::INTEGER]; $types = [ParameterType::INTEGER];
/** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
$queryCacheProfileMock = $this->createMock(QueryCacheProfile::class); $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
$queryCacheProfileMock $queryCacheProfileMock
...@@ -723,7 +711,6 @@ class ConnectionTest extends TestCase ...@@ -723,7 +711,6 @@ class ConnectionTest extends TestCase
->with($query, $params, $types, $this->params) ->with($query, $params, $types, $this->params)
->will(self::returnValue(['cacheKey', 'realKey'])); ->will(self::returnValue(['cacheKey', 'realKey']));
/** @var Driver $driver */
$driver = $this->createMock(Driver::class); $driver = $this->createMock(Driver::class);
self::assertInstanceOf( self::assertInstanceOf(
...@@ -745,7 +732,6 @@ class ConnectionTest extends TestCase ...@@ -745,7 +732,6 @@ class ConnectionTest extends TestCase
->with('cacheKey') ->with('cacheKey')
->will(self::returnValue(['realKey' => []])); ->will(self::returnValue(['realKey' => []]));
/** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
$queryCacheProfileMock = $this->createMock(QueryCacheProfile::class); $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
$queryCacheProfileMock $queryCacheProfileMock
...@@ -765,7 +751,6 @@ class ConnectionTest extends TestCase ...@@ -765,7 +751,6 @@ class ConnectionTest extends TestCase
$connectionParams['platform'] = $this->createMock(AbstractPlatform::class); $connectionParams['platform'] = $this->createMock(AbstractPlatform::class);
/** @var Driver $driver */
$driver = $this->createMock(Driver::class); $driver = $this->createMock(Driver::class);
(new Connection($connectionParams, $driver))->executeCacheQuery($query, [], [], $queryCacheProfileMock); (new Connection($connectionParams, $driver))->executeCacheQuery($query, [], [], $queryCacheProfileMock);
...@@ -779,7 +764,6 @@ class ConnectionTest extends TestCase ...@@ -779,7 +764,6 @@ class ConnectionTest extends TestCase
$connectionParams = $this->params; $connectionParams = $this->params;
$connectionParams['platform'] = new stdClass(); $connectionParams['platform'] = new stdClass();
/** @var Driver $driver */
$driver = $this->createMock(Driver::class); $driver = $this->createMock(Driver::class);
$this->expectException(DBALException::class); $this->expectException(DBALException::class);
...@@ -792,7 +776,6 @@ class ConnectionTest extends TestCase ...@@ -792,7 +776,6 @@ class ConnectionTest extends TestCase
*/ */
public function testRethrowsOriginalExceptionOnDeterminingPlatformWhenConnectingToNonExistentDatabase() : void public function testRethrowsOriginalExceptionOnDeterminingPlatformWhenConnectingToNonExistentDatabase() : void
{ {
/** @var VersionAwarePlatformDriver|MockObject $driverMock */
$driverMock = $this->createMock(VersionAwarePlatformDriver::class); $driverMock = $this->createMock(VersionAwarePlatformDriver::class);
$connection = new Connection(['dbname' => 'foo'], $driverMock); $connection = new Connection(['dbname' => 'foo'], $driverMock);
...@@ -817,16 +800,12 @@ class ConnectionTest extends TestCase ...@@ -817,16 +800,12 @@ class ConnectionTest extends TestCase
*/ */
public function testExecuteCacheQueryStripsPlatformFromConnectionParamsBeforeGeneratingCacheKeys() : void public function testExecuteCacheQueryStripsPlatformFromConnectionParamsBeforeGeneratingCacheKeys() : void
{ {
/** @var Driver|MockObject $driver */
$driver = $this->createMock(Driver::class); $driver = $this->createMock(Driver::class);
/** @var AbstractPlatform|MockObject $platform */
$platform = $this->createMock(AbstractPlatform::class); $platform = $this->createMock(AbstractPlatform::class);
/** @var QueryCacheProfile|MockObject $queryCacheProfile */
$queryCacheProfile = $this->createMock(QueryCacheProfile::class); $queryCacheProfile = $this->createMock(QueryCacheProfile::class);
/** @var Cache|MockObject $resultCacheDriver */
$resultCacheDriver = $this->createMock(Cache::class); $resultCacheDriver = $this->createMock(Cache::class);
$queryCacheProfile $queryCacheProfile
......
...@@ -21,7 +21,6 @@ class DBALExceptionTest extends TestCase ...@@ -21,7 +21,6 @@ class DBALExceptionTest extends TestCase
{ {
public function testDriverExceptionDuringQueryAcceptsBinaryData() : void public function testDriverExceptionDuringQueryAcceptsBinaryData() : void
{ {
/** @var Driver $driver */
$driver = $this->createMock(Driver::class); $driver = $this->createMock(Driver::class);
$e = DBALException::driverExceptionDuringQuery($driver, new Exception(), '', ['ABC', chr(128)]); $e = DBALException::driverExceptionDuringQuery($driver, new Exception(), '', ['ABC', chr(128)]);
self::assertStringContainsString('with params ["ABC", "\x80"]', $e->getMessage()); self::assertStringContainsString('with params ["ABC", "\x80"]', $e->getMessage());
...@@ -29,7 +28,6 @@ class DBALExceptionTest extends TestCase ...@@ -29,7 +28,6 @@ class DBALExceptionTest extends TestCase
public function testDriverExceptionDuringQueryAcceptsResource() : void public function testDriverExceptionDuringQueryAcceptsResource() : void
{ {
/** @var Driver $driver */
$driver = $this->createMock(Driver::class); $driver = $this->createMock(Driver::class);
$e = DBALException::driverExceptionDuringQuery($driver, new Exception(), 'INSERT INTO file (`content`) VALUES (?)', [1 => fopen(__FILE__, 'r')]); $e = DBALException::driverExceptionDuringQuery($driver, new Exception(), 'INSERT INTO file (`content`) VALUES (?)', [1 => fopen(__FILE__, 'r')]);
self::assertStringContainsString('Resource', $e->getMessage()); self::assertStringContainsString('Resource', $e->getMessage());
...@@ -37,10 +35,8 @@ class DBALExceptionTest extends TestCase ...@@ -37,10 +35,8 @@ class DBALExceptionTest extends TestCase
public function testAvoidOverWrappingOnDriverException() : void public function testAvoidOverWrappingOnDriverException() : void
{ {
/** @var Driver $driver */
$driver = $this->createMock(Driver::class); $driver = $this->createMock(Driver::class);
/** @var InnerDriverException $inner */
$inner = $this->createMock(InnerDriverException::class); $inner = $this->createMock(InnerDriverException::class);
$ex = new DriverException('', $inner); $ex = new DriverException('', $inner);
......
...@@ -79,7 +79,6 @@ abstract class AbstractDriverTest extends TestCase ...@@ -79,7 +79,6 @@ abstract class AbstractDriverTest extends TestCase
self::markTestSkipped('This test is only intended for exception converter drivers.'); self::markTestSkipped('This test is only intended for exception converter drivers.');
} }
/** @var DriverExceptionInterface|MockObject $driverException */
$driverException = $this->getMockBuilder(DriverExceptionInterface::class) $driverException = $this->getMockBuilder(DriverExceptionInterface::class)
->setConstructorArgs([$message, $errorCode]) ->setConstructorArgs([$message, $errorCode])
->getMock(); ->getMock();
...@@ -182,7 +181,7 @@ abstract class AbstractDriverTest extends TestCase ...@@ -182,7 +181,7 @@ abstract class AbstractDriverTest extends TestCase
abstract protected function createSchemaManager(Connection $connection) : AbstractSchemaManager; abstract protected function createSchemaManager(Connection $connection) : AbstractSchemaManager;
/** /**
* @return Connection|MockObject * @return Connection&MockObject
*/ */
protected function getConnectionMock() : Connection protected function getConnectionMock() : Connection
{ {
......
...@@ -40,7 +40,7 @@ class StatementIteratorTest extends TestCase ...@@ -40,7 +40,7 @@ class StatementIteratorTest extends TestCase
} }
/** /**
* @param Traversable<mixed> $iterator * @param Traversable<int, mixed> $iterator
*/ */
private function assertIterationCallsFetchOncePerStep(Traversable $iterator, int &$calls) : void private function assertIterationCallsFetchOncePerStep(Traversable $iterator, int &$calls) : void
{ {
......
...@@ -89,8 +89,8 @@ class MasterSlaveConnectionTest extends FunctionalTestCase ...@@ -89,8 +89,8 @@ class MasterSlaveConnectionTest extends FunctionalTestCase
unset($params['slaves'][$index]['charset']); unset($params['slaves'][$index]['charset']);
} }
/** @var MasterSlaveConnection $conn */
$conn = DriverManager::getConnection($params); $conn = DriverManager::getConnection($params);
self::assertInstanceOf(MasterSlaveConnection::class, $conn);
$conn->connect('slave'); $conn->connect('slave');
self::assertFalse($conn->isConnectedToMaster()); self::assertFalse($conn->isConnectedToMaster());
......
...@@ -16,6 +16,7 @@ use Doctrine\DBAL\Schema\ColumnDiff; ...@@ -16,6 +16,7 @@ use Doctrine\DBAL\Schema\ColumnDiff;
use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaDiff; use Doctrine\DBAL\Schema\SchemaDiff;
use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\Table;
...@@ -40,6 +41,7 @@ use function count; ...@@ -40,6 +41,7 @@ use function count;
use function current; use function current;
use function end; use function end;
use function explode; use function explode;
use function implode;
use function in_array; use function in_array;
use function is_string; use function is_string;
use function sprintf; use function sprintf;
...@@ -1279,4 +1281,49 @@ abstract class SchemaManagerFunctionalTestCase extends FunctionalTestCase ...@@ -1279,4 +1281,49 @@ abstract class SchemaManagerFunctionalTestCase extends FunctionalTestCase
$table = $this->schemaManager->listTableDetails('table_with_comment'); $table = $this->schemaManager->listTableDetails('table_with_comment');
self::assertSame('Foo with control characters \'\\', $table->getComment()); self::assertSame('Foo with control characters \'\\', $table->getComment());
} }
public function testSchemaDiffForeignKeys() : void
{
$schemaManager = $this->connection->getSchemaManager();
$platform = $this->connection->getDatabasePlatform();
$table1 = new Table('child');
$table1->addColumn('id', 'integer', ['autoincrement' => true]);
$table1->addColumn('parent_id', 'integer');
$table1->setPrimaryKey(['id']);
$table1->addForeignKeyConstraint('parent', ['parent_id'], ['id']);
$table2 = new Table('parent');
$table2->addColumn('id', 'integer', ['autoincrement' => true]);
$table2->setPrimaryKey(['id']);
$diff = new SchemaDiff(['table1' => $table1, 'table2' => $table2]);
$sqls = $diff->toSql($platform);
foreach ($sqls as $sql) {
$this->connection->exec($sql);
}
$schema = new Schema([
$schemaManager->listTableDetails('child'),
$schemaManager->listTableDetails('parent'),
]);
self::assertCount(1, $schema->getTable('child')->getForeignKeys());
$offlineSchema = new Schema([$table1, $table2]);
$diff = Comparator::compareSchemas($offlineSchema, $schema);
foreach ($diff->changedTables as $table) {
if (count($table->changedForeignKeys) <= 0 && count($table->addedForeignKeys) <= 0 && count($table->removedForeignKeys) <= 0) {
continue;
}
self::fail(
'No changes on foreigh keys should be detected, but we have: ' .
implode(', ', $diff->toSql($platform))
);
}
}
} }
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Tests\Internal;
use Doctrine\DBAL\Internal\DependencyOrderCalculator;
use Doctrine\DBAL\Schema\Table;
use PHPUnit\Framework\TestCase;
/**
* Tests of the commit order calculation.
*
* IMPORTANT: When writing tests here consider that a lot of graph constellations
* can have many valid orderings, so you may want to build a graph that has only
* 1 valid order to simplify your tests.
*/
class DependencyOrderCalculatorTest extends TestCase
{
/** @var DependencyOrderCalculator */
private $calculator;
protected function setUp() : void
{
$this->calculator = new DependencyOrderCalculator();
}
public function testCommitOrdering1() : void
{
$table1 = new Table('table1');
$table2 = new Table('table2');
$table3 = new Table('table3');
$table4 = new Table('table4');
$table5 = new Table('table5');
self::assertFalse($this->calculator->hasNode($table1->getName()));
$this->calculator->addNode($table1->getName(), $table1);
$this->calculator->addNode($table2->getName(), $table2);
$this->calculator->addNode($table3->getName(), $table3);
$this->calculator->addNode($table4->getName(), $table4);
$this->calculator->addNode($table5->getName(), $table5);
self::assertTrue($this->calculator->hasNode($table1->getName()));
$this->calculator->addDependency($table1->getName(), $table2->getName());
$this->calculator->addDependency($table2->getName(), $table3->getName());
$this->calculator->addDependency($table3->getName(), $table4->getName());
$this->calculator->addDependency($table5->getName(), $table1->getName());
$sorted = $this->calculator->sort();
// There is only 1 valid ordering for this constellation
$correctOrder = [$table5, $table1, $table2, $table3, $table4];
self::assertSame($correctOrder, $sorted);
}
}
...@@ -250,6 +250,10 @@ abstract class AbstractPlatformTestCase extends TestCase ...@@ -250,6 +250,10 @@ abstract class AbstractPlatformTestCase extends TestCase
public function testGeneratesConstraintCreationSql() : void public function testGeneratesConstraintCreationSql() : void
{ {
if (! $this->platform->supportsCreateDropForeignKeyConstraints()) {
self::markTestSkipped('Platform does not support creating or dropping foreign key constraints.');
}
$idx = new Index('constraint_name', ['test'], true, false); $idx = new Index('constraint_name', ['test'], true, false);
$sql = $this->platform->getCreateConstraintSQL($idx, 'test'); $sql = $this->platform->getCreateConstraintSQL($idx, 'test');
self::assertEquals($this->getGenerateConstraintUniqueIndexSql(), $sql); self::assertEquals($this->getGenerateConstraintUniqueIndexSql(), $sql);
...@@ -1201,9 +1205,9 @@ abstract class AbstractPlatformTestCase extends TestCase ...@@ -1201,9 +1205,9 @@ abstract class AbstractPlatformTestCase extends TestCase
*/ */
public function testQuotesDropForeignKeySQL() : void public function testQuotesDropForeignKeySQL() : void
{ {
if (! $this->platform->supportsForeignKeyConstraints()) { if (! $this->platform->supportsCreateDropForeignKeyConstraints()) {
self::markTestSkipped( self::markTestSkipped(
sprintf('%s does not support foreign key constraints.', get_class($this->platform)) sprintf('%s does not support modifying foreign key constraints.', get_class($this->platform))
); );
} }
......
...@@ -44,12 +44,15 @@ class SchemaDiffTest extends TestCase ...@@ -44,12 +44,15 @@ class SchemaDiffTest extends TestCase
*/ */
private function createPlatform(bool $unsafe) private function createPlatform(bool $unsafe)
{ {
/** @var AbstractPlatform|MockObject $platform */
$platform = $this->createMock(AbstractPlatform::class); $platform = $this->createMock(AbstractPlatform::class);
$platform->expects(self::exactly(1)) $platform->expects(self::exactly(1))
->method('getCreateSchemaSQL') ->method('getCreateSchemaSQL')
->with('foo_ns') ->with('foo_ns')
->will(self::returnValue('create_schema')); ->will(self::returnValue('create_schema'));
$platform->method('supportsCreateDropForeignKeyConstraints')
->will(self::returnValue(true));
if ($unsafe) { if ($unsafe) {
$platform->expects(self::exactly(1)) $platform->expects(self::exactly(1))
->method('getDropSequenceSql') ->method('getDropSequenceSql')
...@@ -100,7 +103,7 @@ class SchemaDiffTest extends TestCase ...@@ -100,7 +103,7 @@ class SchemaDiffTest extends TestCase
$platform->expects(self::exactly(1)) $platform->expects(self::exactly(1))
->method('supportsSequences') ->method('supportsSequences')
->will(self::returnValue(true)); ->will(self::returnValue(true));
$platform->expects(self::exactly(2)) $platform->expects(self::any())
->method('supportsForeignKeyConstraints') ->method('supportsForeignKeyConstraints')
->will(self::returnValue(true)); ->will(self::returnValue(true));
......
...@@ -2,12 +2,9 @@ ...@@ -2,12 +2,9 @@
declare(strict_types=1); declare(strict_types=1);
(static function () : void { // PHPStan does not read global constants from the stubs yet, remove this when it does
foreach (['ibm_db2', 'mysqli', 'oci8', 'sqlsrv', 'pgsql'] as $extension) { if (defined('OCI_NO_AUTO_COMMIT')) {
if (extension_loaded($extension)) { return;
continue; }
}
require sprintf(__DIR__ . '/../vendor/jetbrains/phpstorm-stubs/%1$s/%1$s.php', $extension); define('OCI_NO_AUTO_COMMIT', 0);
}
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment