Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4a761b6

Browse files
Add support for symfony uuid
1 parent cd42b17 commit 4a761b6

File tree

10 files changed

+433
-28
lines changed

10 files changed

+433
-28
lines changed

‎composer.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
"phpstan/phpstan-strict-rules": "^2.0",
3737
"phpunit/phpunit": "^9.6.20",
3838
"ramsey/uuid": "^4.2",
39-
"symfony/cache": "^5.4"
39+
"symfony/cache": "^5.4",
40+
"symfony/uid": "^5.4 || ^6.4 || ^7.3"
4041
},
4142
"config": {
4243
"sort-packages": true,

‎extension.neon‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,16 @@ services:
418418
tags: [phpstan.doctrine.typeDescriptor]
419419
arguments:
420420
uuidTypeName: Ramsey\Uuid\Doctrine\UuidBinaryOrderedTimeType
421+
-
422+
class: PHPStan\Type\Doctrine\Descriptors\Symfony\UuidTypeDescriptor
423+
tags: [phpstan.doctrine.typeDescriptor]
424+
arguments:
425+
uuidTypeName: Symfony\Bridge\Doctrine\Types\UuidType
426+
-
427+
class: PHPStan\Type\Doctrine\Descriptors\Symfony\UlidTypeDescriptor
428+
tags: [phpstan.doctrine.typeDescriptor]
429+
arguments:
430+
uuidTypeName: Symfony\Bridge\Doctrine\Types\UlidType
421431

422432
# Doctrine Collection
423433
-

‎src/Type/Doctrine/Descriptors/Ramsey/UuidTypeDescriptor.php‎

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,29 @@
22

33
namespace PHPStan\Type\Doctrine\Descriptors\Ramsey;
44

5-
use PHPStan\Rules\Doctrine\ORM\FakeTestingUuidType;
6-
use PHPStan\ShouldNotHappenException;
75
use PHPStan\Type\Doctrine\Descriptors\DoctrineTypeDescriptor;
86
use PHPStan\Type\ObjectType;
97
use PHPStan\Type\StringType;
108
use PHPStan\Type\Type;
119
use PHPStan\Type\TypeCombinator;
1210
use Ramsey\Uuid\UuidInterface;
13-
use function in_array;
14-
use function sprintf;
1511

1612
class UuidTypeDescriptor implements DoctrineTypeDescriptor
1713
{
1814

19-
private const SUPPORTED_UUID_TYPES = [
20-
'Ramsey\Uuid\Doctrine\UuidType',
21-
'Ramsey\Uuid\Doctrine\UuidBinaryType',
22-
'Ramsey\Uuid\Doctrine\UuidBinaryOrderedTimeType',
23-
FakeTestingUuidType::class,
24-
];
25-
15+
/** @var class-string<\Doctrine\DBAL\Types\Type> */
2616
private string $uuidTypeName;
2717

28-
public function __construct(
29-
string $uuidTypeName
30-
)
18+
/**
19+
* @param class-string<\Doctrine\DBAL\Types\Type> $uuidTypeName
20+
*/
21+
public function __construct(string $uuidTypeName)
3122
{
32-
if (!in_array($uuidTypeName, self::SUPPORTED_UUID_TYPES, true)) {
33-
throw new ShouldNotHappenException(sprintf(
34-
'Unexpected UUID column type "%s" provided',
35-
$uuidTypeName,
36-
));
37-
}
38-
3923
$this->uuidTypeName = $uuidTypeName;
4024
}
4125

4226
public function getType(): string
4327
{
44-
/** @var class-string<\Doctrine\DBAL\Types\Type> */
4528
return $this->uuidTypeName;
4629
}
4730

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Doctrine\Descriptors\Symfony;
4+
5+
use PHPStan\Type\Doctrine\Descriptors\DoctrineTypeDescriptor;
6+
use PHPStan\Type\ObjectType;
7+
use PHPStan\Type\StringType;
8+
use PHPStan\Type\Type;
9+
use PHPStan\Type\TypeCombinator;
10+
use Symfony\Component\Uid\Ulid;
11+
12+
class UlidTypeDescriptor implements DoctrineTypeDescriptor
13+
{
14+
15+
/** @var class-string<\Doctrine\DBAL\Types\Type> */
16+
private string $uuidTypeName;
17+
18+
/**
19+
* @param class-string<\Doctrine\DBAL\Types\Type> $uuidTypeName
20+
*/
21+
public function __construct(string $uuidTypeName)
22+
{
23+
$this->uuidTypeName = $uuidTypeName;
24+
}
25+
26+
public function getType(): string
27+
{
28+
return $this->uuidTypeName;
29+
}
30+
31+
public function getWritableToPropertyType(): Type
32+
{
33+
return new ObjectType(Ulid::class);
34+
}
35+
36+
public function getWritableToDatabaseType(): Type
37+
{
38+
return TypeCombinator::union(
39+
new StringType(),
40+
new ObjectType(Ulid::class),
41+
);
42+
}
43+
44+
public function getDatabaseInternalType(): Type
45+
{
46+
return new StringType();
47+
}
48+
49+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Doctrine\Descriptors\Symfony;
4+
5+
use PHPStan\Type\Doctrine\Descriptors\DoctrineTypeDescriptor;
6+
use PHPStan\Type\ObjectType;
7+
use PHPStan\Type\StringType;
8+
use PHPStan\Type\Type;
9+
use PHPStan\Type\TypeCombinator;
10+
use Symfony\Component\Uid\Uuid;
11+
12+
class UuidTypeDescriptor implements DoctrineTypeDescriptor
13+
{
14+
15+
/** @var class-string<\Doctrine\DBAL\Types\Type> */
16+
private string $uuidTypeName;
17+
18+
/**
19+
* @param class-string<\Doctrine\DBAL\Types\Type> $uuidTypeName
20+
*/
21+
public function __construct(string $uuidTypeName)
22+
{
23+
$this->uuidTypeName = $uuidTypeName;
24+
}
25+
26+
public function getType(): string
27+
{
28+
return $this->uuidTypeName;
29+
}
30+
31+
public function getWritableToPropertyType(): Type
32+
{
33+
return new ObjectType(Uuid::class);
34+
}
35+
36+
public function getWritableToDatabaseType(): Type
37+
{
38+
return TypeCombinator::union(
39+
new StringType(),
40+
new ObjectType(Uuid::class),
41+
);
42+
}
43+
44+
public function getDatabaseInternalType(): Type
45+
{
46+
return new StringType();
47+
}
48+
49+
}

‎tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php‎

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
use PHPStan\Type\Doctrine\Descriptors\EnumType;
2222
use PHPStan\Type\Doctrine\Descriptors\IntegerType;
2323
use PHPStan\Type\Doctrine\Descriptors\JsonType;
24-
use PHPStan\Type\Doctrine\Descriptors\Ramsey\UuidTypeDescriptor;
24+
use PHPStan\Type\Doctrine\Descriptors\Ramsey\UuidTypeDescriptorasRamseyUuidTypeDescriptor;
2525
use PHPStan\Type\Doctrine\Descriptors\ReflectionDescriptor;
2626
use PHPStan\Type\Doctrine\Descriptors\SimpleArrayType;
2727
use PHPStan\Type\Doctrine\Descriptors\StringType;
28+
use PHPStan\Type\Doctrine\Descriptors\Symfony\UlidTypeDescriptor as SymfonyUlidTypeDescriptor;
29+
use PHPStan\Type\Doctrine\Descriptors\Symfony\UuidTypeDescriptor as SymfonyUuidTypeDescriptor;
2830
use PHPStan\Type\Doctrine\ObjectMetadataResolver;
2931
use function array_unshift;
3032
use function class_exists;
@@ -41,6 +43,8 @@ class EntityColumnRuleTest extends RuleTestCase
4143

4244
private ?string $objectManagerLoader = null;
4345

46+
private bool $useSymfonyUuid = false;
47+
4448
protected function getRule(): Rule
4549
{
4650
if (!Type::hasType(CustomType::NAME)) {
@@ -49,8 +53,23 @@ protected function getRule(): Rule
4953
if (!Type::hasType(CustomNumericType::NAME)) {
5054
Type::addType(CustomNumericType::NAME, CustomNumericType::class);
5155
}
52-
if (!Type::hasType(FakeTestingUuidType::NAME)) {
53-
Type::addType(FakeTestingUuidType::NAME, FakeTestingUuidType::class);
56+
if ($this->useSymfonyUuid) {
57+
if (!Type::hasType(FakeTestingSymfonyUuidType::NAME)) {
58+
Type::addType(FakeTestingSymfonyUuidType::NAME, FakeTestingSymfonyUuidType::class);
59+
} else {
60+
// Override Ramsay definition
61+
Type::overrideType(FakeTestingSymfonyUuidType::NAME, FakeTestingSymfonyUuidType::class);
62+
}
63+
if (!Type::hasType(FakeTestingSymfonyUlidType::NAME)) {
64+
Type::addType(FakeTestingSymfonyUlidType::NAME, FakeTestingSymfonyUlidType::class);
65+
}
66+
} else {
67+
if (!Type::hasType(FakeTestingRamseyUuidType::NAME)) {
68+
Type::addType(FakeTestingRamseyUuidType::NAME, FakeTestingRamseyUuidType::class);
69+
} else {
70+
// Override Symfony definition
71+
Type::overrideType(FakeTestingRamseyUuidType::NAME, FakeTestingRamseyUuidType::class);
72+
}
5473
}
5574
if (!Type::hasType('carbon')) {
5675
Type::addType('carbon', CarbonType::class);
@@ -76,8 +95,10 @@ protected function getRule(): Rule
7695
new IntegerType(),
7796
new StringType(),
7897
new SimpleArrayType(),
79-
new UuidTypeDescriptor(FakeTestingUuidType::class),
8098
new EnumType(),
99+
new RamseyUuidTypeDescriptor(FakeTestingRamseyUuidType::class),
100+
new SymfonyUuidTypeDescriptor(FakeTestingSymfonyUuidType::class),
101+
new SymfonyUlidTypeDescriptor(FakeTestingSymfonyUlidType::class),
81102
new ReflectionDescriptor(CarbonImmutableType::class, $this->createReflectionProvider(), self::getContainer()),
82103
new ReflectionDescriptor(CarbonType::class, $this->createReflectionProvider(), self::getContainer()),
83104
new ReflectionDescriptor(CustomType::class, $this->createReflectionProvider(), self::getContainer()),
@@ -461,6 +482,27 @@ public function testBug677(?string $objectManagerLoader): void
461482
$this->analyse([__DIR__ . '/data/bug-677.php'], []);
462483
}
463484

485+
/**
486+
* @dataProvider dataObjectManagerLoader
487+
*/
488+
public function testSymfonyUuid(?string $objectManagerLoader): void
489+
{
490+
$this->allowNullablePropertyForRequiredField = true;
491+
$this->objectManagerLoader = $objectManagerLoader;
492+
$this->useSymfonyUuid = true;
493+
494+
$this->analyse([__DIR__ . '/data/EntityWithSymfonyUid.php'], [
495+
[
496+
'Property PHPStan\Rules\Doctrine\ORM\EntityWithSymfonyUid::$uuidInvalidType type mapping mismatch: database can contain Symfony\Component\Uid\Uuid but property expects string.',
497+
32,
498+
],
499+
[
500+
'Property PHPStan\Rules\Doctrine\ORM\EntityWithSymfonyUid::$ulidInvalidType type mapping mismatch: database can contain Symfony\Component\Uid\Ulid but property expects string.',
501+
44,
502+
],
503+
]);
504+
}
505+
464506
/**
465507
* @dataProvider dataObjectManagerLoader
466508
*/

‎tests/Rules/Doctrine/ORM/FakeTestingUuidType.php‎ renamed to ‎tests/Rules/Doctrine/ORM/FakeTestingRamseyUuidType.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* From https://github.com/ramsey/uuid-doctrine/blob/fafebbe972cdaba9274c286ea8923e2de2579027/src/UuidType.php
1414
* Copyright (c) 2012-2022 Ben Ramsey <ben@benramsey.com>
1515
*/
16-
final class FakeTestingUuidType extends GuidType
16+
final class FakeTestingRamseyUuidType extends GuidType
1717
{
1818

1919
public const NAME = 'uuid';
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Doctrine\ORM;
4+
5+
use Doctrine\DBAL\Platforms\AbstractPlatform;
6+
use Doctrine\DBAL\Types\ConversionException;
7+
use Doctrine\DBAL\Types\Type;
8+
use InvalidArgumentException;
9+
use Symfony\Component\Uid\AbstractUid;
10+
use Symfony\Component\Uid\Ulid;
11+
use function is_string;
12+
13+
/**
14+
* From https://github.com/symfony/doctrine-bridge/blob/f5a2780640342e3bf0599fbfc9b3dd759db9b037/Types/UuidType.php
15+
* Copyright (c) Fabien Potencier <fabien@symfony.com>
16+
*/
17+
final class FakeTestingSymfonyUlidType extends Type
18+
{
19+
20+
public const NAME = 'ulid';
21+
22+
/**
23+
* @not-deprecated
24+
*/
25+
public function getName(): string
26+
{
27+
return self::NAME;
28+
}
29+
30+
protected function getUidClass(): string
31+
{
32+
return Ulid::class;
33+
}
34+
35+
/**
36+
* {@inheritdoc}
37+
*/
38+
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
39+
{
40+
if ($this->hasNativeGuidType($platform)) {
41+
return $platform->getGuidTypeDeclarationSQL($column);
42+
}
43+
44+
return $platform->getBinaryTypeDeclarationSQL([
45+
'length' => '16',
46+
'fixed' => true,
47+
]);
48+
}
49+
50+
/**
51+
* {@inheritdoc}
52+
*
53+
* @throws ConversionException
54+
*/
55+
public function convertToPHPValue($value, AbstractPlatform $platform): ?AbstractUid
56+
{
57+
if ($value instanceof AbstractUid || $value === null) {
58+
return $value;
59+
}
60+
61+
if (!is_string($value)) {
62+
throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'string', AbstractUid::class]);
63+
}
64+
65+
try {
66+
/** @phpstan-ignore-next-line method.dynamicName */
67+
return $this->getUidClass()::fromString($value);
68+
} catch (InvalidArgumentException $e) {
69+
throw ConversionException::conversionFailed($value, $this->getName(), $e);
70+
}
71+
}
72+
73+
/**
74+
* {@inheritdoc}
75+
*
76+
* @throws ConversionException
77+
*/
78+
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
79+
{
80+
$toString = $this->hasNativeGuidType($platform) ? 'toRfc4122' : 'toBinary';
81+
82+
if ($value instanceof AbstractUid) {
83+
/** @phpstan-ignore-next-line method.dynamicName */
84+
return $value->$toString();
85+
}
86+
87+
if ($value === null || $value === '') {
88+
return null;
89+
}
90+
91+
if (!is_string($value)) {
92+
throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'string', AbstractUid::class]);
93+
}
94+
95+
try {
96+
/** @phpstan-ignore-next-line method.dynamicName */
97+
return $this->getUidClass()::fromString($value)->$toString();
98+
} catch (InvalidArgumentException $e) {
99+
throw ConversionException::conversionFailed($value, $this->getName());
100+
}
101+
}
102+
103+
public function requiresSQLCommentHint(AbstractPlatform $platform): bool
104+
{
105+
return true;
106+
}
107+
108+
private function hasNativeGuidType(AbstractPlatform $platform): bool
109+
{
110+
return $platform->getGuidTypeDeclarationSQL([]) !== $platform->getStringTypeDeclarationSQL(['fixed' => true, 'length' => 36]);
111+
}
112+
113+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /