|
6 | 6 | use Composer\Semver\VersionParser; |
7 | 7 | use DateTime; |
8 | 8 | use DateTimeImmutable; |
9 | | -use Doctrine\Common\Collections\ArrayCollection; |
10 | | -use Doctrine\ORM\EntityManagerInterface; |
11 | 9 | use Doctrine\ORM\Mapping\Column; |
12 | | -use Doctrine\ORM\Query\AST\TypedExpression; |
13 | | -use Doctrine\ORM\Tools\SchemaTool; |
14 | | -use PHPStan\Testing\PHPStanTestCase; |
15 | | -use PHPStan\Type\Accessory\AccessoryNumericStringType; |
16 | | -use PHPStan\Type\Constant\ConstantArrayTypeBuilder; |
17 | 10 | use PHPStan\Type\Constant\ConstantIntegerType; |
18 | 11 | use PHPStan\Type\Constant\ConstantStringType; |
19 | | -use PHPStan\Type\ConstantTypeHelper; |
20 | | -use PHPStan\Type\Doctrine\DescriptorRegistry; |
21 | 12 | use PHPStan\Type\FloatType; |
22 | | -use PHPStan\Type\IntegerRangeType; |
23 | 13 | use PHPStan\Type\IntegerType; |
24 | | -use PHPStan\Type\IntersectionType; |
25 | 14 | use PHPStan\Type\MixedType; |
26 | 15 | use PHPStan\Type\NullType; |
27 | 16 | use PHPStan\Type\ObjectType; |
28 | 17 | use PHPStan\Type\StringType; |
29 | 18 | use PHPStan\Type\Type; |
30 | 19 | use PHPStan\Type\TypeCombinator; |
31 | | -use PHPStan\Type\UnionType; |
32 | | -use PHPStan\Type\VerbosityLevel; |
33 | | -use QueryResult\Entities\Embedded; |
34 | | -use QueryResult\Entities\JoinedChild; |
35 | 20 | use QueryResult\Entities\Many; |
36 | 21 | use QueryResult\Entities\ManyId; |
37 | | -use QueryResult\Entities\NestedEmbedded; |
38 | 22 | use QueryResult\Entities\One; |
39 | 23 | use QueryResult\Entities\OneId; |
40 | | -use QueryResult\Entities\SingleTableChild; |
41 | | -use QueryResult\EntitiesEnum\EntityWithEnum; |
42 | 24 | use QueryResult\EntitiesEnum\IntEnum; |
43 | 25 | use QueryResult\EntitiesEnum\StringEnum; |
44 | | -use Throwable; |
45 | | -use function array_merge; |
46 | | -use function array_shift; |
47 | 26 | use function assert; |
48 | 27 | use function class_exists; |
49 | | -use function count; |
50 | 28 | use function property_exists; |
51 | | -use function sprintf; |
52 | 29 | use function strpos; |
53 | | -use function version_compare; |
54 | 30 | use const PHP_VERSION_ID; |
55 | 31 |
|
56 | | -final class QueryResultTypeWalkerTest extends PHPStanTestCase |
| 32 | +final class PDOQueryResultTypeWalkerTest extends QueryResultTypeWalkerTestCase |
57 | 33 | { |
58 | 34 |
|
59 | | - /** @var EntityManagerInterface */ |
60 | | - private static $em; |
61 | | - |
62 | | - /** @var DescriptorRegistry */ |
63 | | - private $descriptorRegistry; |
64 | | - |
65 | | - public static function getAdditionalConfigFiles(): array |
66 | | - { |
67 | | - return [ |
68 | | - __DIR__ . '/../data/QueryResult/config.neon', |
69 | | - ]; |
70 | | - } |
71 | | - |
72 | | - public static function setUpBeforeClass(): void |
73 | | - { |
74 | | - $em = require __DIR__ . '/../data/QueryResult/entity-manager.php'; |
75 | | - self::$em = $em; |
76 | | - |
77 | | - $schemaTool = new SchemaTool($em); |
78 | | - $classes = $em->getMetadataFactory()->getAllMetadata(); |
79 | | - $schemaTool->createSchema($classes); |
80 | | - |
81 | | - $dataOne = [ |
82 | | - 'intColumn' => [1, 2], |
83 | | - 'stringColumn' => ['A', 'B'], |
84 | | - 'stringNullColumn' => ['A', null], |
85 | | - ]; |
86 | | - |
87 | | - $dataMany = [ |
88 | | - 'intColumn' => [1, 2], |
89 | | - 'stringColumn' => ['A', 'B'], |
90 | | - 'stringNullColumn' => ['A', null], |
91 | | - ]; |
92 | | - |
93 | | - $dataJoinedInheritance = [ |
94 | | - 'parentColumn' => [1, 2], |
95 | | - 'parentNullColumn' => [1, null], |
96 | | - 'childColumn' => [1, 2], |
97 | | - 'childNullColumn' => [1, null], |
98 | | - ]; |
99 | | - |
100 | | - $dataSingleTableInheritance = [ |
101 | | - 'parentColumn' => [1, 2], |
102 | | - 'parentNullColumn' => [1, null], |
103 | | - 'childNullColumn' => [1, null], |
104 | | - ]; |
105 | | - |
106 | | - $id = 1; |
107 | | - |
108 | | - foreach (self::combinations($dataOne) as $combination) { |
109 | | - [$intColumn, $stringColumn, $stringNullColumn] = $combination; |
110 | | - $one = new One(); |
111 | | - $one->id = (string) $id++; |
112 | | - $one->intColumn = $intColumn; |
113 | | - $one->stringColumn = $stringColumn; |
114 | | - $one->stringNullColumn = $stringNullColumn; |
115 | | - $embedded = new Embedded(); |
116 | | - $embedded->intColumn = $intColumn; |
117 | | - $embedded->stringColumn = $stringColumn; |
118 | | - $embedded->stringNullColumn = $stringNullColumn; |
119 | | - $nestedEmbedded = new NestedEmbedded(); |
120 | | - $nestedEmbedded->intColumn = $intColumn; |
121 | | - $nestedEmbedded->stringColumn = $stringColumn; |
122 | | - $nestedEmbedded->stringNullColumn = $stringNullColumn; |
123 | | - $embedded->nestedEmbedded = $nestedEmbedded; |
124 | | - $one->embedded = $embedded; |
125 | | - $one->manies = new ArrayCollection(); |
126 | | - |
127 | | - foreach (self::combinations($dataMany) as $combinationMany) { |
128 | | - [$intColumnMany, $stringColumnMany, $stringNullColumnMany] = $combinationMany; |
129 | | - $many = new Many(); |
130 | | - $many->id = (string) $id++; |
131 | | - $many->intColumn = $intColumnMany; |
132 | | - $many->stringColumn = $stringColumnMany; |
133 | | - $many->stringNullColumn = $stringNullColumnMany; |
134 | | - $many->datetimeColumn = new DateTime('2001-01-01 00:00:00'); |
135 | | - $many->datetimeImmutableColumn = new DateTimeImmutable('2001-01-01 00:00:00'); |
136 | | - $many->simpleArrayColumn = ['foo']; |
137 | | - $many->one = $one; |
138 | | - $one->manies->add($many); |
139 | | - $em->persist($many); |
140 | | - } |
141 | | - |
142 | | - $em->persist($one); |
143 | | - } |
144 | | - |
145 | | - foreach (self::combinations($dataJoinedInheritance) as $combination) { |
146 | | - [$parentColumn, $parentNullColumn, $childColumn, $childNullColumn] = $combination; |
147 | | - $child = new JoinedChild(); |
148 | | - $child->id = (string) $id++; |
149 | | - $child->parentColumn = $parentColumn; |
150 | | - $child->parentNullColumn = $parentNullColumn; |
151 | | - $child->childColumn = $childColumn; |
152 | | - $child->childNullColumn = $childNullColumn; |
153 | | - $em->persist($child); |
154 | | - } |
155 | | - |
156 | | - foreach (self::combinations($dataSingleTableInheritance) as $combination) { |
157 | | - [$parentColumn, $parentNullColumn, $childNullColumn] = $combination; |
158 | | - $child = new SingleTableChild(); |
159 | | - $child->id = (string) $id++; |
160 | | - $child->parentColumn = $parentColumn; |
161 | | - $child->parentNullColumn = $parentNullColumn; |
162 | | - $child->childNullColumn = $childNullColumn; |
163 | | - $em->persist($child); |
164 | | - } |
165 | | - |
166 | | - if (property_exists(Column::class, 'enumType') && PHP_VERSION_ID >= 80100) { |
167 | | - assert(class_exists(StringEnum::class)); |
168 | | - assert(class_exists(IntEnum::class)); |
169 | | - |
170 | | - $entityWithEnum = new EntityWithEnum(); |
171 | | - $entityWithEnum->id = '1'; |
172 | | - $entityWithEnum->stringEnumColumn = StringEnum::A; |
173 | | - $entityWithEnum->intEnumColumn = IntEnum::A; |
174 | | - $entityWithEnum->intEnumOnStringColumn = IntEnum::A; |
175 | | - $em->persist($entityWithEnum); |
176 | | - } |
177 | | - |
178 | | - $em->flush(); |
179 | | - } |
180 | | - |
181 | | - public static function tearDownAfterClass(): void |
182 | | - { |
183 | | - self::$em->clear(); |
184 | | - } |
185 | | - |
186 | | - public function setUp(): void |
| 35 | + protected static function getEntityManagerPath(): string |
187 | 36 | { |
188 | | - $this->descriptorRegistry = self::getContainer()->getByType(DescriptorRegistry::class); |
189 | | - } |
190 | | - |
191 | | - /** @dataProvider getTestData */ |
192 | | - public function test(Type $expectedType, string $dql, ?string $expectedExceptionMessage = null, ?string $expectedDeprecationMessage = null): void |
193 | | - { |
194 | | - $em = self::$em; |
195 | | - |
196 | | - $query = $em->createQuery($dql); |
197 | | - |
198 | | - $typeBuilder = new QueryResultTypeBuilder(); |
199 | | - |
200 | | - if ($expectedExceptionMessage !== null) { |
201 | | - $this->expectException(Throwable::class); |
202 | | - $this->expectExceptionMessage($expectedExceptionMessage); |
203 | | - } elseif ($expectedDeprecationMessage !== null) { |
204 | | - $this->expectDeprecation(); |
205 | | - $this->expectDeprecationMessage($expectedDeprecationMessage); |
206 | | - } |
207 | | - |
208 | | - QueryResultTypeWalker::walk($query, $typeBuilder, $this->descriptorRegistry); |
209 | | - |
210 | | - $type = $typeBuilder->getResultType(); |
211 | | - |
212 | | - self::assertSame( |
213 | | - $expectedType->describe(VerbosityLevel::precise()), |
214 | | - $type->describe(VerbosityLevel::precise()) |
215 | | - ); |
216 | | - |
217 | | - // Double-check our expectations |
218 | | - |
219 | | - $query = $em->createQuery($dql); |
220 | | - |
221 | | - $result = $query->getResult(); |
222 | | - self::assertGreaterThan(0, count($result)); |
223 | | - |
224 | | - foreach ($result as $row) { |
225 | | - $rowType = ConstantTypeHelper::getTypeFromValue($row); |
226 | | - self::assertTrue( |
227 | | - $type->accepts($rowType, true)->yes(), |
228 | | - sprintf( |
229 | | - "The inferred type\n%s\nshould accept actual type\n%s", |
230 | | - $type->describe(VerbosityLevel::precise()), |
231 | | - $rowType->describe(VerbosityLevel::precise()) |
232 | | - ) |
233 | | - ); |
234 | | - } |
| 37 | + return __DIR__ . '/../data/QueryResult/entity-manager.php'; |
235 | 38 | } |
236 | 39 |
|
237 | 40 | /** |
@@ -1591,113 +1394,4 @@ public function getTestData(): iterable |
1591 | 1394 | ]; |
1592 | 1395 | } |
1593 | 1396 |
|
1594 | | - /** |
1595 | | - * @param array<int,array{0: ConstantIntegerType|ConstantStringType, 1: Type, 2?: bool}> $elements |
1596 | | - */ |
1597 | | - private function constantArray(array $elements): Type |
1598 | | - { |
1599 | | - $builder = ConstantArrayTypeBuilder::createEmpty(); |
1600 | | - |
1601 | | - foreach ($elements as $element) { |
1602 | | - $offsetType = $element[0]; |
1603 | | - $valueType = $element[1]; |
1604 | | - $optional = $element[2] ?? false; |
1605 | | - $builder->setOffsetValueType($offsetType, $valueType, $optional); |
1606 | | - } |
1607 | | - |
1608 | | - return $builder->getArray(); |
1609 | | - } |
1610 | | - |
1611 | | - private function numericStringOrInt(): Type |
1612 | | - { |
1613 | | - return new UnionType([ |
1614 | | - new IntegerType(), |
1615 | | - new IntersectionType([ |
1616 | | - new StringType(), |
1617 | | - new AccessoryNumericStringType(), |
1618 | | - ]), |
1619 | | - ]); |
1620 | | - } |
1621 | | - |
1622 | | - private function numericString(): Type |
1623 | | - { |
1624 | | - return new IntersectionType([ |
1625 | | - new StringType(), |
1626 | | - new AccessoryNumericStringType(), |
1627 | | - ]); |
1628 | | - } |
1629 | | - |
1630 | | - private function uint(): Type |
1631 | | - { |
1632 | | - return IntegerRangeType::fromInterval(0, null); |
1633 | | - } |
1634 | | - |
1635 | | - private function intStringified(): Type |
1636 | | - { |
1637 | | - return TypeCombinator::union( |
1638 | | - new IntegerType(), |
1639 | | - $this->numericString() |
1640 | | - ); |
1641 | | - } |
1642 | | - private function uintStringified(): Type |
1643 | | - { |
1644 | | - return TypeCombinator::union( |
1645 | | - $this->uint(), |
1646 | | - $this->numericString() |
1647 | | - ); |
1648 | | - } |
1649 | | - |
1650 | | - private function numericStringified(): Type |
1651 | | - { |
1652 | | - return TypeCombinator::union( |
1653 | | - new FloatType(), |
1654 | | - new IntegerType(), |
1655 | | - $this->numericString() |
1656 | | - ); |
1657 | | - } |
1658 | | - |
1659 | | - private function unumericStringified(): Type |
1660 | | - { |
1661 | | - return TypeCombinator::union( |
1662 | | - new FloatType(), |
1663 | | - IntegerRangeType::fromInterval(0, null), |
1664 | | - $this->numericString() |
1665 | | - ); |
1666 | | - } |
1667 | | - |
1668 | | - private function hasTypedExpressions(): bool |
1669 | | - { |
1670 | | - return class_exists(TypedExpression::class); |
1671 | | - } |
1672 | | - |
1673 | | - /** |
1674 | | - * @param array<mixed[]> $arrays |
1675 | | - * |
1676 | | - * @return iterable<mixed[]> |
1677 | | - */ |
1678 | | - private static function combinations(array $arrays): iterable |
1679 | | - { |
1680 | | - if ($arrays === []) { |
1681 | | - yield []; |
1682 | | - return; |
1683 | | - } |
1684 | | - |
1685 | | - $head = array_shift($arrays); |
1686 | | - |
1687 | | - foreach ($head as $elem) { |
1688 | | - foreach (self::combinations($arrays) as $combination) { |
1689 | | - yield array_merge([$elem], $combination); |
1690 | | - } |
1691 | | - } |
1692 | | - } |
1693 | | - |
1694 | | - private function isDoctrine211(): bool |
1695 | | - { |
1696 | | - $version = InstalledVersions::getVersion('doctrine/orm'); |
1697 | | - |
1698 | | - return $version !== null |
1699 | | - && version_compare($version, '2.11', '>=') |
1700 | | - && version_compare($version, '2.12', '<'); |
1701 | | - } |
1702 | | - |
1703 | 1397 | } |
0 commit comments