From 2fda625dba2a5e2e9ccc29936aa6fc8e6062f25e Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 8 Aug 2022 22:21:07 +0200 Subject: [PATCH 1/5] Add phpdoc for discriminatorColumn --- lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 4 ++-- lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php | 8 ++++---- lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php | 2 +- lib/Doctrine/ORM/Tools/EntityGenerator.php | 7 ++++--- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 5f8b3b74fb7..bbd4a12eccc 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -574,7 +574,7 @@ class ClassMetadataInfo implements ClassMetadata * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE * inheritance mappings. * - * @psalm-var array|null + * @psalm-var array{name: string, fieldName: string, type: string, length?: int, columnDefinition?: string|null}|null */ public $discriminatorColumn; @@ -3215,7 +3215,7 @@ public function addEntityListener($eventName, $class, $method) * @see getDiscriminatorColumn() * * @param mixed[]|null $columnDef - * @psalm-param array|null $columnDef + * @psalm-param array{name: string|null, fieldName?: string, type?: string, length?: int, columnDefinition?: string|null}|null $columnDef * * @return void * diff --git a/lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php index fa2ef7d1dad..a837d8a7847 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php @@ -254,10 +254,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad $metadata->setDiscriminatorColumn( [ - 'name' => $discrColumnAttribute->name, - 'type' => $discrColumnAttribute->type ?: 'string', - 'length' => $discrColumnAttribute->length ?: 255, - 'columnDefinition' => $discrColumnAttribute->columnDefinition, + 'name' => isset($discrColumnAttribute->name) ? (string) $discrColumnAttribute->name : null, + 'type' => isset($discrColumnAttribute->type) ? (string) $discrColumnAttribute->type : 'string', + 'length' => isset($discrColumnAttribute->length) ? (int) $discrColumnAttribute->length : 255, + 'columnDefinition' => isset($discrColumnAttribute->columnDefinition) ? (string) $discrColumnAttribute->columnDefinition : null, ] ); } else { diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index a6afb4106fc..0e05c841fc5 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -199,7 +199,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad [ 'name' => isset($discrColumn['name']) ? (string) $discrColumn['name'] : null, 'type' => isset($discrColumn['type']) ? (string) $discrColumn['type'] : 'string', - 'length' => isset($discrColumn['length']) ? (string) $discrColumn['length'] : 255, + 'length' => isset($discrColumn['length']) ? (int) $discrColumn['length'] : 255, 'columnDefinition' => isset($discrColumn['columnDefinition']) ? (string) $discrColumn['columnDefinition'] : null, ] ); diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 6b95cacb7d9..6c43f82c693 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -1145,9 +1145,10 @@ protected function generateDiscriminatorColumnAnnotation(ClassMetadataInfo $meta return ''; } - $columnDefinition = 'name="' . $discrColumn['name'] - . '", type="' . $discrColumn['type'] - . '", length=' . $discrColumn['length']; + $columnDefinition = sprintf('name="%s", type="%s"', $discrColumn['name'], $discrColumn['type']); + if (isset($discrColumn['length'])) { + $columnDefinition .= ', length=' . $discrColumn['length']; + } return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')'; } From 73e1e42ab5d8b387807a3801485d563632f66553 Mon Sep 17 00:00:00 2001 From: Carlos Buenosvinos Date: Sat, 3 Sep 2022 21:40:53 +0200 Subject: [PATCH 2/5] More strange break lines in inheritance-mapping.rst (#10028) --- docs/en/reference/inheritance-mapping.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/reference/inheritance-mapping.rst b/docs/en/reference/inheritance-mapping.rst index 1b025fbc2e6..f646eac3246 100644 --- a/docs/en/reference/inheritance-mapping.rst +++ b/docs/en/reference/inheritance-mapping.rst @@ -284,9 +284,9 @@ themselves on access of any subtype fields, so accessing fields of subtypes after such a query is not safe. There is a general performance consideration with Class Table -Inheritance: If the target-entity of a many-to-one or one-to-one -association is a CTI entity, it is preferable for performance reasons that it -be a leaf entity in the inheritance hierarchy, (ie. have no subclasses). +Inheritance: If the target-entity of a many-to-one or one-to-one +association is a CTI entity, it is preferable for performance reasons that it +be a leaf entity in the inheritance hierarchy, (ie. have no subclasses). Otherwise Doctrine *CANNOT* create proxy instances of this entity and will *ALWAYS* load the entity eagerly. From 498da2ff98ebe62bae54fb2d3e04997c229eca83 Mon Sep 17 00:00:00 2001 From: Carlos Buenosvinos Date: Sun, 4 Sep 2022 18:16:02 +0200 Subject: [PATCH 3/5] "Strange" return lines in documentation of inheritance-mapping.rst (#10027) --- docs/en/reference/inheritance-mapping.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/reference/inheritance-mapping.rst b/docs/en/reference/inheritance-mapping.rst index f646eac3246..8695b50d296 100644 --- a/docs/en/reference/inheritance-mapping.rst +++ b/docs/en/reference/inheritance-mapping.rst @@ -178,9 +178,9 @@ relationships involving types that employ this mapping strategy are very performing. There is a general performance consideration with Single Table -Inheritance: If the target-entity of a many-to-one or one-to-one -association is an STI entity, it is preferable for performance reasons that it -be a leaf entity in the inheritance hierarchy, (ie. have no subclasses). +Inheritance: If the target-entity of a many-to-one or one-to-one +association is an STI entity, it is preferable for performance reasons that it +be a leaf entity in the inheritance hierarchy, (ie. have no subclasses). Otherwise Doctrine *CANNOT* create proxy instances of this entity and will *ALWAYS* load the entity eagerly. From bb3ce7e802b3e16ba8d23f9ccb1bc1dbebda63de Mon Sep 17 00:00:00 2001 From: Ilya Shashilov Date: Wed, 14 Sep 2022 19:33:42 +0700 Subject: [PATCH 4/5] Fix EnumType not being hydrated with HYDRATE_ARRAY (#9995) Co-authored-by: Ilya Shashilov --- .../Internal/Hydration/AbstractHydrator.php | 2 +- .../ORM/Internal/Hydration/ArrayHydrator.php | 34 ++++++++ .../Tests/ORM/Hydration/ArrayHydratorTest.php | 77 +++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index 0c74f23a560..524f162a3c4 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -692,7 +692,7 @@ protected function registerManaged(ClassMetadata $class, $entity, array $data) * * @return BackedEnum|array */ - private function buildEnum($value, string $enumType) + protected function buildEnum($value, string $enumType) { if (is_array($value)) { return array_map(static function ($value) use ($enumType): BackedEnum { diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index 00748eaf5ce..f4f0915b3df 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -36,6 +36,13 @@ class ArrayHydrator extends AbstractHydrator /** @var int */ private $_resultCounter = 0; + /** + * The cache used during enum processing. + * + * @var array + */ + private $_enumCache = []; + /** * {@inheritdoc} */ @@ -76,6 +83,9 @@ protected function hydrateRowData(array $row, array &$result) // 2) Now hydrate the data found in the current row. foreach ($rowData['data'] as $dqlAlias => $data) { + // build enums if exists + $this->processEnums($dqlAlias, $data); + $index = false; if (isset($this->resultSetMapping()->parentAliasMap[$dqlAlias])) { @@ -277,4 +287,28 @@ private function updateResultPointer( end($coll); $this->_resultPointers[$dqlAlias] =& $coll[key($coll)]; } + + /** @param mixed[] $data */ + private function processEnums(string $dqlAlias, array &$data): void + { + // init cache + if (! isset($this->_enumCache[$dqlAlias])) { + $this->_enumCache[$dqlAlias] = []; + + $className = $this->resultSetMapping()->aliasMap[$dqlAlias]; + $classMetadata = $this->getClassMetadata($className); + + foreach ($classMetadata->fieldMappings as $key => $fieldMapping) { + if (isset($fieldMapping['enumType'])) { + $this->_enumCache[$dqlAlias][$key] = $fieldMapping['enumType']; + } + } + } + + foreach ($data as $key => $value) { + if (isset($this->_enumCache[$dqlAlias][$key]) && $value !== null) { + $data[$key] = $this->buildEnum($value, $this->_enumCache[$dqlAlias][$key]); + } + } + } } diff --git a/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php index 76d29672b18..42da7309f53 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php @@ -11,6 +11,8 @@ use Doctrine\Tests\Models\CMS\CmsComment; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; +use Doctrine\Tests\Models\Enums\Card; +use Doctrine\Tests\Models\Enums\Suit; use Doctrine\Tests\Models\Forum\ForumBoard; use Doctrine\Tests\Models\Forum\ForumCategory; @@ -1222,4 +1224,79 @@ public function testIndexByAndMixedResult($userEntityKey): void self::assertArrayHasKey(2, $result); self::assertEquals(2, $result[2][$userEntityKey]['id']); } + + /** + * @requires PHP 8.1 + */ + public function testArrayResultWithEnumField(): void + { + $rsm = new ResultSetMapping(); + + $rsm->addEntityResult(Card::class, 'c'); + $rsm->addFieldResult('c', 'c__id', 'id'); + $rsm->addFieldResult('c', 'c__suit', 'suit'); + $rsm->addIndexBy('c', 'id'); + + // Faked result set + $resultSet = [ + //row1 + [ + 'c__id' => '1', + 'c__suit' => 'H', + ], + [ + 'c__id' => '2', + 'c__suit' => 'D', + ], + ]; + + $stmt = ArrayResultFactory::createFromArray($resultSet); + $hydrator = new ArrayHydrator($this->entityManager); + $result = $hydrator->hydrateAll($stmt, $rsm); + + self::assertCount(2, $result); + + self::assertEquals(1, $result[1]['id']); + self::assertEquals(Suit::Hearts, $result[1]['suit']); + + self::assertEquals(2, $result[2]['id']); + self::assertEquals(Suit::Diamonds, $result[2]['suit']); + } + + /** + * @requires PHP 8.1 + */ + public function testScalarResultWithEnumField(): void + { + $rsm = new ResultSetMapping(); + + $rsm->addEntityResult(Card::class, 'c'); + $rsm->addScalarResult('c__suit', 'someAlias', 'string'); + $rsm->addEnumResult('c__suit', Suit::class); + + // Faked result set + $resultSet = [ + //row1 + [ + 'c__id' => '1', + 'c__suit' => 'C', + ], + [ + 'c__id' => '2', + 'c__suit' => 'S', + ], + ]; + + $stmt = ArrayResultFactory::createFromArray($resultSet); + $hydrator = new ArrayHydrator($this->entityManager); + $result = $hydrator->hydrateAll($stmt, $rsm); + + self::assertCount(2, $result); + + self::assertCount(1, $result[0]); //assert that in each result we have only one "someAlias" field + self::assertEquals(Suit::Clubs, $result[0]['someAlias']); + + self::assertCount(1, $result[1]); + self::assertEquals(Suit::Spades, $result[1]['someAlias']); + } } From 0d043059b966728892d6a64bf233dcfc0b6013f4 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sun, 18 Sep 2022 15:06:51 +0200 Subject: [PATCH 5/5] PHPStan 1.8.5, Psalm 4.27.0 (#10033) --- composer.json | 4 ++-- phpstan-baseline.neon | 17 +---------------- phpstan-dbal2.neon | 2 +- phpstan-persistence2.neon | 2 +- phpstan.neon | 2 +- psalm-baseline.xml | 2 +- 6 files changed, 7 insertions(+), 22 deletions(-) diff --git a/composer.json b/composer.json index db02205d9fe..cf7b249ae57 100644 --- a/composer.json +++ b/composer.json @@ -42,13 +42,13 @@ "doctrine/annotations": "^1.13", "doctrine/coding-standard": "^9.0.2 || ^10.0", "phpbench/phpbench": "^0.16.10 || ^1.0", - "phpstan/phpstan": "~1.4.10 || 1.8.2", + "phpstan/phpstan": "~1.4.10 || 1.8.5", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "psr/log": "^1 || ^2 || ^3", "squizlabs/php_codesniffer": "3.7.1", "symfony/cache": "^4.4 || ^5.4 || ^6.0", "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "vimeo/psalm": "4.26.0" + "vimeo/psalm": "4.27.0" }, "conflict": { "doctrine/annotations": "<1.13 || >= 2.0" diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index b686cc94dcd..f5997905199 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -506,7 +506,7 @@ parameters: path: lib/Doctrine/ORM/Query/Parser.php - - message: "#^Offset 'columns' on array\\{name\\: string, entities\\: array, columns\\: array\\} in isset\\(\\) always exists and is not nullable\\.$#" + message: "#^Offset 'columns' on array\\{name\\: string, entities\\: array\\{\\}, columns\\: array\\}\\|array\\{name\\: string, entities\\: non\\-empty\\-array, columns\\: array\\} in isset\\(\\) always exists and is not nullable\\.$#" count: 1 path: lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php @@ -660,26 +660,11 @@ parameters: count: 1 path: lib/Doctrine/ORM/Tools/EntityGenerator.php - - - message: "#^Method Doctrine\\\\ORM\\\\Tools\\\\Export\\\\Driver\\\\AbstractExporter\\:\\:_getChangeTrackingPolicyString\\(\\) should return string but return statement is missing\\.$#" - count: 1 - path: lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php - - - - message: "#^Method Doctrine\\\\ORM\\\\Tools\\\\Export\\\\Driver\\\\AbstractExporter\\:\\:_getFetchModeString\\(\\) should return string but return statement is missing\\.$#" - count: 1 - path: lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php - - message: "#^Method Doctrine\\\\ORM\\\\Tools\\\\Export\\\\Driver\\\\AbstractExporter\\:\\:_getIdGeneratorTypeString\\(\\) should return string but return statement is missing\\.$#" count: 1 path: lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php - - - message: "#^Method Doctrine\\\\ORM\\\\Tools\\\\Export\\\\Driver\\\\AbstractExporter\\:\\:_getInheritanceTypeString\\(\\) should return string but return statement is missing\\.$#" - count: 1 - path: lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php - - message: "#^If condition is always true\\.$#" count: 2 diff --git a/phpstan-dbal2.neon b/phpstan-dbal2.neon index 51b44346945..852987a3bd4 100644 --- a/phpstan-dbal2.neon +++ b/phpstan-dbal2.neon @@ -64,5 +64,5 @@ parameters: # https://github.com/phpstan/phpstan/issues/7292 - - message: '#^Offset class\-string on array\\> in isset\(\) always exists and is not nullable\.$#' + message: '#^Offset class\-string on non\-empty\-array\\> in isset\(\) always exists and is not nullable\.$#' path: lib/Doctrine/ORM/UnitOfWork.php diff --git a/phpstan-persistence2.neon b/phpstan-persistence2.neon index 4447d0db94e..f2e12c625d7 100644 --- a/phpstan-persistence2.neon +++ b/phpstan-persistence2.neon @@ -42,5 +42,5 @@ parameters: # https://github.com/phpstan/phpstan/issues/7292 - - message: '#^Offset class\-string on array\\> in isset\(\) always exists and is not nullable\.$#' + message: '#^Offset class\-string on non\-empty\-array\\> in isset\(\) always exists and is not nullable\.$#' path: lib/Doctrine/ORM/UnitOfWork.php diff --git a/phpstan.neon b/phpstan.neon index c2025fdde4b..73ae586b4cd 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -53,5 +53,5 @@ parameters: # https://github.com/phpstan/phpstan/issues/7292 - - message: '#^Offset class\-string on array\\> in isset\(\) always exists and is not nullable\.$#' + message: '#^Offset class\-string on non\-empty\-array\\> in isset\(\) always exists and is not nullable\.$#' path: lib/Doctrine/ORM/UnitOfWork.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 82ae8e9b472..3a99edf0203 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + IterableResult