Skip to content

Conversation

@shakaran
Copy link

@shakaran shakaran commented Dec 9, 2025

Q A
Type improvement

Summary

Related #7248

This splitted pull request makes minor improvements to the type safety and clarity of several unit tests by adding explicit type annotations for variables that hold DateTime objects. These changes help clarify the expected types and can improve static analysis and IDE support.

it avoids use of assert or assertInstanceOf

Test improvements (type annotations):

  • Added /** @var DateTime $date */ annotation before assignments to $date in testDateResetsNonDatePartsToZeroUnixTimeValues and testDateRestsSummerTimeAffection in tests/Types/DateTest.php.
  • Added /** @var DateTime $actual */ annotation in testConvertsNonMatchingFormatToPhpValueWithParser in tests/Types/DateTimeTest.php.
  • Added /** @var DateTime $time */ annotation in testDateFieldResetInPHPValue in tests/Types/TimeTest.php.

Resolves the phpstan errors:

./vendor/bin/phpstan analyze --level=9 tests/Types/
Note: Using configuration file phpstan.neon.dist.                                              
 33/33 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%                                                                                                
                                                                                                                                          
 ------ ---------------------------------------                                                                                           
  Line   DateTest.php                                                                                                                     
 ------ ---------------------------------------                                                                                           
  :38    Cannot call method format() on mixed.                                                                                            
         🪪  method.nonObject                                                                                                              
  :46    Cannot call method format() on mixed.                                                                                            
         🪪  method.nonObject                                                                                                              
  :47    Cannot call method format() on mixed.                                                                                            
         🪪  method.nonObject                                                                                                              
  :50    Cannot call method format() on mixed.                                                                                            
         🪪  method.nonObject                                                                                                              
  :51    Cannot call method format() on mixed.                                                                                            
         🪪  method.nonObject                                                                                                              
 ------ ---------------------------------------                                                                                           
                                                                                                                                          
 ------ ---------------------------------------                                                                                           
  Line   DateTimeTest.php                                                                                                                 
 ------ ---------------------------------------                                                                                           
  :53    Cannot call method format() on mixed.                                                                                            
         🪪  method.nonObject                                                                                                              
 ------ ------------------------
 
 ------ --------------------------------------- 
  Line   tests/Types/TimeTest.php               
 ------ --------------------------------------- 
  :33    Cannot call method format() on mixed.  
         🪪  method.nonObject                   
  :34    Cannot call method format() on mixed.  
         🪪  method.nonObject                   
 ------ --------------------------------------- 

Comment on lines +36 to 37
/** @var DateTime $date */
$date = $this->type->convertToPHPValue('1985-09-01', $this->platform);
Copy link
Member

Choose a reason for hiding this comment

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

The problem here is that PHPStan does not understand the type of $this->type. Your annotation is a workaround, not a solution.

Copy link
Author

Choose a reason for hiding this comment

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

Which I see meanwhile I fix this, it is src/Types/Type.php is an abstract class.

And the problem using convertToPHPValue in that class, it is that return as mixed, but other clases like tests/Types/TimeTest.php are overriding the abstract return, for example with ?DateTime

In src/Types/VarDateTimeImmutableType.php is ?DateTimeImmutable.

So I think that the problem is that the method should be abtract in src/Types/Type.php and no contain body

That means which src/Types/TypeWithConstructor.php should have also the method to avoid:

Line TypeWithConstructor.php


:10 Non-abstract class Doctrine\DBAL\Tests\Types\TypeWithConstructor contains abstract method convertToPHPValue() from class
Doctrine\DBAL\Types\Type.


I made that changes at 1abb899

So phpstan is a static analyzer, it doesn't execute code or infer types based on which concrete subclass is instantiated at runtime.

In TimeTest.php, $this->type is declared as Type (the base class), even though it's initialized as new TimeType().

When you call $this->type->convertToPHPValue('01:23:34', $this->platform), phpstan sees the return as mixed because that's what the base class declares.

It doesn't "know" that $this->type is actually a TimeType instance that returns ?DateTime.

So I understan which this is a limitation of static analysis: it prioritizes declared types over inferred runtime types to avoid false positives.

And level 9 requires explicit types where ambiguity exists, even if the code is "obviously" correct at runtime.

Other options that I can try:

  1. Instead of $this->type being Type, declare it as the specific subclass (e.g., protected TimeType $type; in TimeTest). This tells PHPStan the exact type, so the return type is inferred correctly (but reduces polymorphism in tests and it will need separate properties for each type)
  2. Cast to the expected type: $time = (DateTime) $this->type->convertToPHPValue
  3. Use assert (already explored and you dont like it)

Copy link
Member

Choose a reason for hiding this comment

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

So I think that the problem is that the method should be abtract in src/Types/Type.php and no contain body

No. A subclass may narrow the return type of a method. This has nothing to do with the parent class/method being abstract or not.

Other options that I can try:

  1. Make BaseDateTypeTestCase generic. ✌🏻

{
return $value;
}
abstract public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed;
Copy link
Member

Choose a reason for hiding this comment

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

This is a breaking change. We can't do this in a minor release.

Copy link
Author

Choose a reason for hiding this comment

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

So, we use the phpstan annotations for the minor only? and the other solution for a major?

Copy link
Member

Choose a reason for hiding this comment

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

I don't think that this method needs to be abstract, tbh. And no, I'm not planning major releases for the sake of satisfying a static analyser.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants