From efa4488fabb4361e8fded9fb94fc5d42448f7050 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sun, 5 Oct 2025 16:52:53 +0200 Subject: [PATCH 1/3] Fix BinaryType with dbal 4 --- src/Type/Doctrine/Descriptors/BinaryType.php | 21 ++++++++ .../Doctrine/ORM/EntityColumnRuleTest.php | 52 ++++++++++++++++--- tests/Rules/Doctrine/ORM/data/bug-659.php | 32 ++++++++++++ 3 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 tests/Rules/Doctrine/ORM/data/bug-659.php diff --git a/src/Type/Doctrine/Descriptors/BinaryType.php b/src/Type/Doctrine/Descriptors/BinaryType.php index 5b3c848a..21a38092 100644 --- a/src/Type/Doctrine/Descriptors/BinaryType.php +++ b/src/Type/Doctrine/Descriptors/BinaryType.php @@ -2,10 +2,13 @@ namespace PHPStan\Type\Doctrine\Descriptors; +use Composer\InstalledVersions; use PHPStan\Type\MixedType; use PHPStan\Type\ResourceType; use PHPStan\Type\StringType; use PHPStan\Type\Type; +use function class_exists; +use function strpos; class BinaryType implements DoctrineTypeDescriptor { @@ -17,6 +20,10 @@ public function getType(): string public function getWritableToPropertyType(): Type { + if ($this->hasDbal4()) { + return new StringType(); + } + return new ResourceType(); } @@ -30,4 +37,18 @@ public function getDatabaseInternalType(): Type return new StringType(); } + private function hasDbal4(): bool + { + if (!class_exists(InstalledVersions::class)) { + return false; + } + + $dbalVersion = InstalledVersions::getVersion('doctrine/dbal'); + if ($dbalVersion === null) { + return false; + } + + return strpos($dbalVersion, '4.') === 0; + } + } diff --git a/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php b/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php index 56878cc7..56b0eb55 100644 --- a/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php +++ b/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php @@ -271,12 +271,26 @@ public function testSuperclass(?string $objectManagerLoader): void { $this->allowNullablePropertyForRequiredField = false; $this->objectManagerLoader = $objectManagerLoader; - $this->analyse([__DIR__ . '/data/MyBrokenSuperclass.php'], [ - [ - 'Property PHPStan\Rules\Doctrine\ORM\MyBrokenSuperclass::$five type mapping mismatch: database can contain resource but property expects int.', - 17, - ], - ]); + + $dbalVersion = InstalledVersions::getVersion('doctrine/dbal'); + $hasDbal4 = $dbalVersion !== null && strpos($dbalVersion, '4.') === 0; + if ($hasDbal4) { + $errors = [ + [ + 'Property PHPStan\Rules\Doctrine\ORM\MyBrokenSuperclass::$five type mapping mismatch: database can contain string but property expects int.', + 17, + ], + ]; + } else { + $errors = [ + [ + 'Property PHPStan\Rules\Doctrine\ORM\MyBrokenSuperclass::$five type mapping mismatch: database can contain resource but property expects int.', + 17, + ], + ]; + } + + $this->analyse([__DIR__ . '/data/MyBrokenSuperclass.php'], $errors); } /** @@ -495,4 +509,30 @@ public function testBugSingleEnum(?string $objectManagerLoader): void $this->analyse([__DIR__ . '/data/bug-single-enum.php'], []); } + public function testBug659(?string $objectManagerLoader): void + { + $this->allowNullablePropertyForRequiredField = false; + $this->objectManagerLoader = $objectManagerLoader; + + $dbalVersion = InstalledVersions::getVersion('doctrine/dbal'); + $hasDbal4 = $dbalVersion !== null && strpos($dbalVersion, '4.') === 0; + if ($hasDbal4) { + $errors = [ + [ + 'Property PHPStan\Rules\Doctrine\ORM\MyEntity659::$binaryResource type mapping mismatch: database can contain string but property expects resource.', + 31, + ], + ]; + } else { + $errors = [ + [ + 'Property PHPStan\Rules\Doctrine\ORM\MyEntity659::$binaryString type mapping mismatch: database can contain resource but property expects string.', + 25, + ], + ]; + } + + $this->analyse([__DIR__ . '/data/bug-659.php'], $errors); + } + } diff --git a/tests/Rules/Doctrine/ORM/data/bug-659.php b/tests/Rules/Doctrine/ORM/data/bug-659.php new file mode 100644 index 00000000..e4ac3f68 --- /dev/null +++ b/tests/Rules/Doctrine/ORM/data/bug-659.php @@ -0,0 +1,32 @@ += 8.0 + +namespace PHPStan\Rules\Doctrine\ORM; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity() + */ +class MyEntity659 +{ + + /** + * @ORM\Id() + * @ORM\GeneratedValue() + * @ORM\Column(type="integer") + * @var int + */ + private $id; + + /** + * @var string + * @ORM\Column(type="binary") + */ + private $binaryString; + + /** + * @var resource + * @ORM\Column(type="binary") + */ + private $binaryResource; +} From 077f28ee258ab8d1c91fa2533e067f421f752e81 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 6 Oct 2025 11:39:22 +0200 Subject: [PATCH 2/3] Simplify --- .../Doctrine/ORM/EntityColumnRuleTest.php | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php b/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php index 56b0eb55..926c5b1b 100644 --- a/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php +++ b/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php @@ -28,6 +28,7 @@ use PHPStan\Type\Doctrine\ObjectMetadataResolver; use function array_unshift; use function class_exists; +use function sprintf; use function strpos; use const PHP_VERSION_ID; @@ -274,23 +275,16 @@ public function testSuperclass(?string $objectManagerLoader): void $dbalVersion = InstalledVersions::getVersion('doctrine/dbal'); $hasDbal4 = $dbalVersion !== null && strpos($dbalVersion, '4.') === 0; - if ($hasDbal4) { - $errors = [ - [ - 'Property PHPStan\Rules\Doctrine\ORM\MyBrokenSuperclass::$five type mapping mismatch: database can contain string but property expects int.', - 17, - ], - ]; - } else { - $errors = [ - [ - 'Property PHPStan\Rules\Doctrine\ORM\MyBrokenSuperclass::$five type mapping mismatch: database can contain resource but property expects int.', - 17, - ], - ]; - } - $this->analyse([__DIR__ . '/data/MyBrokenSuperclass.php'], $errors); + $this->analyse([__DIR__ . '/data/MyBrokenSuperclass.php'], [ + [ + sprintf( + 'Property PHPStan\Rules\Doctrine\ORM\MyBrokenSuperclass::$five type mapping mismatch: database can contain %s but property expects int.', + $hasDbal4 ? 'string' : 'resource', + ), + 17, + ], + ]); } /** From c357486c0e626d9e4c3b5fe0312d441198134389 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 6 Oct 2025 11:56:37 +0200 Subject: [PATCH 3/3] Fix rebase --- tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php b/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php index 926c5b1b..b2297774 100644 --- a/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php +++ b/tests/Rules/Doctrine/ORM/EntityColumnRuleTest.php @@ -503,6 +503,9 @@ public function testBugSingleEnum(?string $objectManagerLoader): void $this->analyse([__DIR__ . '/data/bug-single-enum.php'], []); } + /** + * @dataProvider dataObjectManagerLoader + */ public function testBug659(?string $objectManagerLoader): void { $this->allowNullablePropertyForRequiredField = false;