Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ services:
-
class: PHPStan\Type\Doctrine\Descriptors\DecimalType
tags: [phpstan.doctrine.typeDescriptor]
-
class: PHPStan\Type\Doctrine\Descriptors\EnumType
tags: [phpstan.doctrine.typeDescriptor]
-
class: PHPStan\Type\Doctrine\Descriptors\FloatType
tags: [phpstan.doctrine.typeDescriptor]
Expand All @@ -374,6 +377,9 @@ services:
-
class: PHPStan\Type\Doctrine\Descriptors\SimpleArrayType
tags: [phpstan.doctrine.typeDescriptor]
-
class: PHPStan\Type\Doctrine\Descriptors\SmallFloatType
tags: [phpstan.doctrine.typeDescriptor]
-
class: PHPStan\Type\Doctrine\Descriptors\SmallIntType
tags: [phpstan.doctrine.typeDescriptor]
Expand Down
33 changes: 33 additions & 0 deletions phpstan-baseline-dbal-4.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
parameters:
ignoreErrors:
-
message: '#^Class Doctrine\\DBAL\\Types\\EnumType not found\.$#'
identifier: class.notFound
count: 1
path: src/Type/Doctrine/Descriptors/EnumType.php

-
message: '#^Method PHPStan\\Type\\Doctrine\\Descriptors\\EnumType\:\:getType\(\) should return class\-string\<Doctrine\\DBAL\\Types\\Type\> but returns string\.$#'
identifier: return.type
count: 1
path: src/Type/Doctrine/Descriptors/EnumType.php

-
message: '#^Class Doctrine\\DBAL\\Types\\SmallFloatType not found\.$#'
identifier: class.notFound
count: 1
path: src/Type/Doctrine/Descriptors/SmallFloatType.php

-
message: '#^Method PHPStan\\Type\\Doctrine\\Descriptors\\SmallFloatType\:\:getType\(\) should return class\-string\<Doctrine\\DBAL\\Types\\Type\> but returns string\.$#'
identifier: return.type
count: 1
path: src/Type/Doctrine/Descriptors/SmallFloatType.php

-
message: '#^Class Doctrine\\DBAL\\Types\\EnumType not found\.$#'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first, I tried adding new doctrine versions to composer.json, but it would require bigger effort to adjust this repo to support those everywhere, so I just referenced those not-yet-existing classes.

identifier: class.notFound
count: 1
path: src/Type/Doctrine/Query/QueryResultTypeWalker.php


6 changes: 6 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,9 @@ parameters:
identifier: new.deprecatedClass
count: 1
path: tests/Type/Doctrine/DBAL/pdo.php

-
message: '#^Parameter references internal interface Doctrine\\ORM\\Query\\AST\\Phase2OptimizableConditional in its type\.$#'
identifier: parameter.internalInterface
count: 2
path: src/Type/Doctrine/Query/QueryResultTypeWalker.php
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ includes:
- phpstan-baseline.neon
- phpstan-baseline-deprecations.neon
- phpstan-baseline-dbal-3.neon
- phpstan-baseline-dbal-4.neon
- compatibility/orm-3-baseline.php
- vendor/phpstan/phpstan-strict-rules/rules.neon
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
Expand Down
31 changes: 31 additions & 0 deletions src/Type/Doctrine/Descriptors/EnumType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Doctrine\Descriptors;

use PHPStan\Type\StringType;
use PHPStan\Type\Type;

class EnumType implements DoctrineTypeDescriptor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I wrong or without this type the data is considered as mixed ?

Now with this, it will report error foo be 'A'|'B' but is string on level 8 because the values option is not considered in the following code

#[ORM\Column(name: 'foo', type: Types::ENUM, options: ['values' => ['A', 'B'])]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I wrong or without this type the data is considered as mixed ?

You are correct.

{

public function getType(): string
{
return \Doctrine\DBAL\Types\EnumType::class;
}

public function getWritableToPropertyType(): Type
{
return new StringType();
}

public function getWritableToDatabaseType(): Type
{
return new StringType();
}

public function getDatabaseInternalType(): Type
{
return new StringType();
}

}
13 changes: 13 additions & 0 deletions src/Type/Doctrine/Descriptors/SmallFloatType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Doctrine\Descriptors;

class SmallFloatType extends FloatType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No inheritance please

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure? I believe SmallFloatType IS FloatType in terms of type descriptor.

{

public function getType(): string
{
return \Doctrine\DBAL\Types\SmallFloatType::class;
}

}
2 changes: 2 additions & 0 deletions src/Type/Doctrine/Query/QueryResultTypeWalker.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Type\Doctrine\Query;

use BackedEnum;
use Doctrine\DBAL\Types\EnumType as DbalEnumType;
use Doctrine\DBAL\Types\StringType as DbalStringType;
use Doctrine\DBAL\Types\Type as DbalType;
use Doctrine\ORM\EntityManagerInterface;
Expand Down Expand Up @@ -1235,6 +1236,7 @@ public function walkSelectExpression($selectExpression): string
if (
$expr instanceof TypedExpression
&& !$expr->getReturnType() instanceof DbalStringType // StringType is no-op, so using TypedExpression with that does nothing
&& !$expr->getReturnType() instanceof DbalEnumType // EnumType is also no-op
) {
$dbalTypeName = DbalType::getTypeRegistry()->lookupName($expr->getReturnType());
$type = TypeCombinator::intersect( // e.g. count is typed as int, but we infer int<0, max>
Expand Down
29 changes: 29 additions & 0 deletions tests/Type/Doctrine/Query/QueryResultTypeWalkerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\VerbosityLevel;
use QueryResult\Entities\Dbal4Entity;
use QueryResult\Entities\Embedded;
use QueryResult\Entities\JoinedChild;
use QueryResult\Entities\Many;
Expand Down Expand Up @@ -187,6 +188,15 @@ public static function setUpBeforeClass(): void
$em->persist($entityWithEnum);
}

if (InstalledVersions::satisfies(new VersionParser(), 'doctrine/dbal', '>=4.2')) {
assert(class_exists(Dbal4Entity::class));

$dbal4Entity = new Dbal4Entity();
$dbal4Entity->enum = 'a';
$dbal4Entity->smallfloat = 1.1;
$em->persist($dbal4Entity);
}

$em->flush();
}

Expand Down Expand Up @@ -1532,6 +1542,25 @@ private function yieldConditionalDataset(): iterable
];
}

if (InstalledVersions::satisfies(new VersionParser(), 'doctrine/dbal', '>=4.2')) {
yield 'enum and smallfloat' => [
$this->constantArray([
[
new ConstantStringType('enum'),
new StringType(),
],
[
new ConstantStringType('smallfloat'),
new FloatType(),
],
]),
'
SELECT e.enum, e.smallfloat
FROM QueryResult\Entities\Dbal4Entity e
',
];
}

$ormVersion = InstalledVersions::getVersion('doctrine/orm');
$hasOrm3 = $ormVersion !== null && strpos($ormVersion, '3.') === 0;

Expand Down
36 changes: 36 additions & 0 deletions tests/Type/Doctrine/data/QueryResult/Entities/Dbal4Entity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php declare(strict_types=1);

namespace QueryResult\Entities;

use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;

/**
* @Entity
*/
class Dbal4Entity
{

/**
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
* @Id()
*
* @var int
*/
public $id;

/**
* @Column(type="enum", options={"values"={"a", "b", "c"}})
* @var string
*/
public $enum; // dbal 4.2+

/**
* @Column(type="smallfloat")
* @var float
*/
public $smallfloat; // dbal 4.1+
}
Loading