diff --git a/.doctrine-project.json b/.doctrine-project.json
index a79077189c3..70f63b8bd01 100644
--- a/.doctrine-project.json
+++ b/.doctrine-project.json
@@ -12,21 +12,27 @@
"upcoming": true
},
{
- "name": "2.12",
- "branchName": "2.12.x",
- "slug": "2.12",
+ "name": "2.13",
+ "branchName": "2.13.x",
+ "slug": "2.13",
"upcoming": true
},
{
- "name": "2.11",
- "branchName": "2.11.x",
- "slug": "2.11",
+ "name": "2.12",
+ "branchName": "2.12.x",
+ "slug": "2.12",
"current": true,
"aliases": [
"current",
"stable"
]
},
+ {
+ "name": "2.11",
+ "branchName": "2.11.x",
+ "slug": "2.11",
+ "maintained": false
+ },
{
"name": "2.10",
"branchName": "2.10.x",
diff --git a/README.md b/README.md
index 7a817e24d47..92b14b99ac0 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
-| [3.0.x][3.0] | [2.12.x][2.12] | [2.11.x][2.11] |
+| [3.0.x][3.0] | [2.13.x][2.13] | [2.12.x][2.12] |
|:----------------:|:----------------:|:----------:|
-| [![Build status][3.0 image]][3.0] | [![Build status][2.12 image]][2.12] | [![Build status][2.11 image]][2.11] |
-| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.12 coverage image]][2.12 coverage] | [![Coverage Status][2.11 coverage image]][2.11 coverage] |
+| [![Build status][3.0 image]][3.0] | [![Build status][2.13 image]][2.13] | [![Build status][2.12 image]][2.12] |
+| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.13 coverage image]][2.13 coverage] | [![Coverage Status][2.12 coverage image]][2.12 coverage] |
[
πΊπ¦ UKRAINE NEEDS YOUR HELP NOW!
](https://www.doctrine-project.org/stop-war.html)
@@ -22,11 +22,11 @@ without requiring unnecessary code duplication.
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
+ [2.13 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.13.x
+ [2.13]: https://github.com/doctrine/orm/tree/2.13.x
+ [2.13 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.13.x/graph/badge.svg
+ [2.13 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.13.x
[2.12 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.12.x
[2.12]: https://github.com/doctrine/orm/tree/2.12.x
[2.12 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.12.x/graph/badge.svg
[2.12 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.12.x
- [2.11 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.11.x
- [2.11]: https://github.com/doctrine/orm/tree/2.11.x
- [2.11 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.11.x/graph/badge.svg
- [2.11 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.11.x
diff --git a/UPGRADE.md b/UPGRADE.md
index 27dc3c7198d..8bf749997f8 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -460,6 +460,20 @@ Use `toIterable()` instead.
# Upgrade to 2.13
+## Deprecated `QueryBuilder` methods and constants.
+
+1. The `QueryBuilder::getState()` method has been deprecated as the builder state is an internal concern.
+2. Relying on the type of the query being built by using `QueryBuilder::getType()` has been deprecated.
+ If necessary, track the type of the query being built outside of the builder.
+
+The following `QueryBuilder` constants related to the above methods have been deprecated:
+
+1. `SELECT`,
+2. `DELETE`,
+3. `UPDATE`,
+4. `STATE_DIRTY`,
+5. `STATE_CLEAN`.
+
## Deprecated omitting only the alias argument for `QueryBuilder::update` and `QueryBuilder::delete`
When building an UPDATE or DELETE query and when passing a class/type to the function, the alias argument must not be omitted.
diff --git a/composer.json b/composer.json
index 183fae8d274..84dd72af332 100644
--- a/composer.json
+++ b/composer.json
@@ -39,12 +39,12 @@
"doctrine/annotations": "^1.13",
"doctrine/coding-standard": "^9.0",
"phpbench/phpbench": "^1.0",
- "phpstan/phpstan": "1.8.0",
+ "phpstan/phpstan": "1.8.2",
"phpunit/phpunit": "^9.5",
"psr/log": "^1 || ^2 || ^3",
"squizlabs/php_codesniffer": "3.7.1",
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
- "vimeo/psalm": "4.24.0"
+ "vimeo/psalm": "4.25.0"
},
"conflict": {
"doctrine/annotations": "<1.13 || >= 2.0"
diff --git a/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php b/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php
index 209ffd15915..15e11db93ee 100644
--- a/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php
+++ b/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php
@@ -12,6 +12,7 @@
use Doctrine\ORM\Utility\IdentifierFlattener;
use function array_merge;
+use function assert;
use function is_array;
use function is_object;
use function reset;
@@ -41,6 +42,7 @@ public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, ob
if ($metadata->requiresFetchAfterChange) {
if ($metadata->isVersioned) {
+ assert($metadata->versionField !== null);
$data[$metadata->versionField] = $metadata->getFieldValue($entity, $metadata->versionField);
}
diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php
index 122b8d92851..a47c8647783 100644
--- a/lib/Doctrine/ORM/EntityManager.php
+++ b/lib/Doctrine/ORM/EntityManager.php
@@ -59,8 +59,10 @@
* is not a valid extension point for the EntityManager. Instead you
* should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator}
* and wrap your entity manager in a decorator.
+ *
+ * @final
*/
-/* final */class EntityManager implements EntityManagerInterface
+class EntityManager implements EntityManagerInterface
{
/**
* The used Configuration.
@@ -121,11 +123,15 @@
* Creates a new EntityManager that operates on the given database connection
* and uses the given Configuration and EventManager implementations.
*/
- protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
+ public function __construct(Connection $conn, Configuration $config)
{
+ if (! $config->getMetadataDriverImpl()) {
+ throw MissingMappingDriverImplementation::create();
+ }
+
$this->conn = $conn;
$this->config = $config;
- $this->eventManager = $eventManager;
+ $this->eventManager = $conn->getEventManager();
$metadataFactoryClassName = $config->getClassMetadataFactoryName();
@@ -637,13 +643,9 @@ public function initializeObject(object $obj): void
*/
public static function create(array|Connection $connection, Configuration $config, ?EventManager $eventManager = null): EntityManager
{
- if (! $config->getMetadataDriverImpl()) {
- throw MissingMappingDriverImplementation::create();
- }
-
$connection = static::createConnection($connection, $config, $eventManager);
- return new EntityManager($connection, $config, $connection->getEventManager());
+ return new EntityManager($connection, $config);
}
/**
diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
index c46eacc60ed..5d22a9643c5 100644
--- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
+++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
@@ -695,7 +695,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
*
- * @var mixed
+ * @var string|null
*/
public $versionField;
@@ -3213,7 +3213,7 @@ public function setVersioned($bool)
* Sets the name of the field that is to be used for versioning if this class is
* versioned for optimistic locking.
*
- * @param string $versionField
+ * @param string|null $versionField
*
* @return void
*/
diff --git a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
index 312d85e6fad..296b3f63f47 100644
--- a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
+++ b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
@@ -450,7 +450,8 @@ final protected function updateTable(
}
if ($versioned) {
- $versionField = $this->class->versionField;
+ $versionField = $this->class->versionField;
+ assert($versionField !== null);
$versionFieldType = $this->class->fieldMappings[$versionField]['type'];
$versionColumn = $this->quoteStrategy->getColumnName($versionField, $this->class, $this->platform);
diff --git a/lib/Doctrine/ORM/QueryBuilder.php b/lib/Doctrine/ORM/QueryBuilder.php
index d757e79d0e4..8c0e23709fe 100644
--- a/lib/Doctrine/ORM/QueryBuilder.php
+++ b/lib/Doctrine/ORM/QueryBuilder.php
@@ -38,13 +38,19 @@
*/
class QueryBuilder
{
- /* The query types. */
+ /** @deprecated */
public const SELECT = 0;
+
+ /** @deprecated */
public const DELETE = 1;
+
+ /** @deprecated */
public const UPDATE = 2;
- /* The builder states. */
+ /** @deprecated */
public const STATE_DIRTY = 0;
+
+ /** @deprecated */
public const STATE_CLEAN = 1;
/**
@@ -237,10 +243,19 @@ public function setCacheMode(int $cacheMode): static
/**
* Gets the type of the currently built query.
*
+ * @deprecated If necessary, track the type of the query being built outside of the builder.
+ *
* @psalm-return self::SELECT|self::DELETE|self::UPDATE
*/
public function getType(): int
{
+ Deprecation::trigger(
+ 'doctrine/dbal',
+ 'https://github.com/doctrine/orm/pull/9945',
+ 'Relying on the type of the query being built is deprecated.'
+ . ' If necessary, track the type of the query being built outside of the builder.'
+ );
+
return $this->type;
}
@@ -255,11 +270,19 @@ public function getEntityManager(): EntityManagerInterface
/**
* Gets the state of this query builder instance.
*
+ * @deprecated The builder state is an internal concern.
+ *
* @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
* @psalm-return self::STATE_*
*/
public function getState(): int
{
+ Deprecation::trigger(
+ 'doctrine/dbal',
+ 'https://github.com/doctrine/orm/pull/9945',
+ 'Relying on the query builder state is deprecated as it is an internal concern.'
+ );
+
return $this->state;
}
diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php
index 8dcb54f165b..4ceddeb8406 100644
--- a/lib/Doctrine/ORM/UnitOfWork.php
+++ b/lib/Doctrine/ORM/UnitOfWork.php
@@ -55,6 +55,7 @@
use function array_pop;
use function array_sum;
use function array_values;
+use function assert;
use function count;
use function current;
use function get_debug_type;
@@ -1455,6 +1456,8 @@ public function getEntityState(object $entity, ?int $assume = null): int
case $class->isIdentifierNatural():
// Check for a version field, if available, to avoid a db lookup.
if ($class->isVersioned) {
+ assert($class->versionField !== null);
+
return $class->getFieldValue($entity, $class->versionField)
? self::STATE_DETACHED
: self::STATE_NEW;
@@ -2035,6 +2038,7 @@ public function lock(object $entity, LockMode|int $lockMode, $lockVersion = null
$entity->__load();
}
+ assert($class->versionField !== null);
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
// phpcs:ignore SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 0787c8953dd..1a94ae786d7 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -1,5 +1,5 @@
-
+
! $filteredParameters->isEmpty() ? $filteredParameters->first() : null
@@ -179,12 +179,9 @@
-
+
$className
$connection
- ltrim($className, '\\')
- ltrim($entityName, '\\')
- ltrim($entityName, '\\')
$entity
@@ -498,10 +495,9 @@
$sequenceGeneratorDefinition
$table
-
+
$this->associationMappings
$this->associationMappings
- $this->discriminatorMap
$this->entityListeners
$this->fieldMappings
$this->fullyQualifiedClassName($repositoryClassName)
@@ -743,6 +739,10 @@
+
+ $className
+ $entityName
+
$className
$className
@@ -1992,6 +1992,7 @@
+ $this->conn->quote((string) $newValue)
is_string($expression)
@@ -2128,7 +2129,6 @@
$entity
- $entity
$state === UnitOfWork::STATE_DETACHED
diff --git a/psalm.xml b/psalm.xml
index b6a1d02eaa7..5d876ea7b27 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -17,11 +17,6 @@
-
-
-
-
-
@@ -32,6 +27,12 @@
+
+
+
+
+
+
diff --git a/tests/Doctrine/Performance/EntityManagerFactory.php b/tests/Doctrine/Performance/EntityManagerFactory.php
index 74416b2f33d..3ea47ee3e9b 100644
--- a/tests/Doctrine/Performance/EntityManagerFactory.php
+++ b/tests/Doctrine/Performance/EntityManagerFactory.php
@@ -71,6 +71,6 @@ public function executeQuery(string $sql, array $params = [], $types = [], ?Quer
}
};
- return EntityManager::create($connection, $config);
+ return EntityManager::create($connection, $config, $connection->getEventManager());
}
}
diff --git a/tests/Doctrine/Tests/Mocks/EntityManagerMock.php b/tests/Doctrine/Tests/Mocks/EntityManagerMock.php
index f22da389293..d1c1493b799 100644
--- a/tests/Doctrine/Tests/Mocks/EntityManagerMock.php
+++ b/tests/Doctrine/Tests/Mocks/EntityManagerMock.php
@@ -58,8 +58,6 @@ public static function create($conn, ?Configuration $config = null, ?EventManage
$config->setMetadataDriverImpl(ORMSetup::createDefaultAnnotationDriver());
}
- $eventManager ??= new EventManager();
-
- return new EntityManagerMock($conn, $config, $eventManager);
+ return new EntityManagerMock($conn, $config);
}
}
diff --git a/tests/Doctrine/Tests/ORM/Functional/SQLFilterTest.php b/tests/Doctrine/Tests/ORM/Functional/SQLFilterTest.php
index 599e96b4d0b..73278d04794 100644
--- a/tests/Doctrine/Tests/ORM/Functional/SQLFilterTest.php
+++ b/tests/Doctrine/Tests/ORM/Functional/SQLFilterTest.php
@@ -4,6 +4,7 @@
namespace Doctrine\Tests\ORM\Functional;
+use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Configuration;
@@ -208,7 +209,11 @@ private function configureFilters(EntityManagerInterface $em): void
*/
private function getMockConnection(): Connection
{
- return $this->createMock(Connection::class);
+ $connection = $this->createMock(Connection::class);
+ $connection->method('getEventManager')
+ ->willReturn(new EventManager());
+
+ return $connection;
}
/**
diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7869Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7869Test.php
index 8a60355d7ff..faf3332b504 100644
--- a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7869Test.php
+++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7869Test.php
@@ -4,6 +4,7 @@
namespace Doctrine\Tests\ORM\Functional\Ticket;
+use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\ORM\Decorator\EntityManagerDecorator;
@@ -31,6 +32,8 @@ public function testDQLDeferredEagerLoad(): void
$connection = $this->createMock(Connection::class);
$connection->method('getDatabasePlatform')
->willReturn($platform);
+ $connection->method('getEventManager')
+ ->willReturn(new EventManager());
$em = new class (EntityManagerMock::create($connection)) extends EntityManagerDecorator {
/** @var int */
diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php
index e0101867488..a17b3c470a1 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php
+++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php
@@ -232,7 +232,8 @@ public function testAddDefaultDiscriminatorMap(): void
public function testGetAllMetadataWorksWithBadConnection(): void
{
// DDC-3551
- $conn = $this->createMock(Connection::class);
+ $conn = $this->createMock(Connection::class);
+ $conn->method('getEventManager')->willReturn($this->createMock(EventManager::class));
$mockDriver = new MetadataDriverMock();
$em = $this->createEntityManager($mockDriver, $conn);
@@ -269,7 +270,7 @@ protected function createEntityManager(MappingDriver $metadataDriver, $conn = nu
$config->setMetadataDriverImpl($metadataDriver);
- return EntityManagerMock::create($conn, $config, $eventManager);
+ return EntityManagerMock::create($conn, $config);
}
protected function createTestFactory(): ClassMetadataFactoryTestSubject
diff --git a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php
index f4d82b265e1..45733f148bd 100644
--- a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php
+++ b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php
@@ -5,6 +5,7 @@
namespace Doctrine\Tests\ORM;
use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Result;
@@ -41,6 +42,8 @@ protected function setUp(): void
$connection = $this->createMock(Connection::class);
$connection->method('getDatabasePlatform')
->willReturn($platform);
+ $connection->method('getEventManager')
+ ->willReturn(new EventManager());
$connection->method('executeQuery')
->willReturn($this->createMock(Result::class));
diff --git a/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php b/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php
index d6bc52397c4..ab21c480f41 100644
--- a/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php
+++ b/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php
@@ -5,6 +5,7 @@
namespace Doctrine\Tests\ORM\Proxy;
use Closure;
+use Doctrine\Common\EventManager;
use Doctrine\Common\Proxy\AbstractProxyFactory;
use Doctrine\Common\Proxy\Proxy;
use Doctrine\DBAL\Connection;
@@ -46,6 +47,8 @@ protected function setUp(): void
$connection = $this->createMock(Connection::class);
$connection->method('getDatabasePlatform')
->willReturn($platform);
+ $connection->method('getEventManager')
+ ->willReturn(new EventManager());
$this->emMock = EntityManagerMock::create($connection);
$this->uowMock = new UnitOfWorkMock($this->emMock);
diff --git a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php
index 0beb4d05add..0df86f09cb9 100644
--- a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php
+++ b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php
@@ -90,9 +90,9 @@ protected function setUp(): void
$driver->method('connect')
->willReturn($driverConnection);
- $this->connection = new Connection([], $driver);
$this->eventManager = $this->getMockBuilder(EventManager::class)->getMock();
- $this->_emMock = EntityManagerMock::create($this->connection, null, $this->eventManager);
+ $this->connection = new Connection([], $driver, null, $this->eventManager);
+ $this->_emMock = EntityManagerMock::create($this->connection);
// SUT
$this->_unitOfWork = new UnitOfWorkMock($this->_emMock);
$this->_emMock->setUnitOfWork($this->_unitOfWork);
@@ -611,7 +611,7 @@ public function testCommitThrowOptimisticLockExceptionWhenConnectionCommitFails(
->onlyMethods(['commit'])
->setConstructorArgs([[], $driver])
->getMock();
- $this->_emMock = EntityManagerMock::create($this->connection, null, $this->eventManager);
+ $this->_emMock = EntityManagerMock::create($this->connection);
$this->_unitOfWork = new UnitOfWorkMock($this->_emMock);
$this->_emMock->setUnitOfWork($this->_unitOfWork);