phpstan / phpstan-doctrine

Doctrine extensions for PHPStan
MIT License
568 stars 93 forks source link

support for doctrine/orm 3+? #551

Open arderyp opened 4 months ago

arderyp commented 4 months ago

I just upgraded orm from 2.19.0 to 3.1.0`, and as a result all of my repositories are now showing phpstan errors that this package was formerly handling, such as:

Method App\Users\Repository\UsersRolesRepository::findAll() should return array<App\Entity\UsersRoles>  
but returns mixed.

This method was triggering no such error before the orm upgrade.

I've confirmed the issue also exists on the latest orm 3.0.x

arderyp commented 4 months ago

I'm running:

phpstan-doctrine 1.3.62 orm 3.1.0 dbal 3.8.3

When I upgraded orm from latest 2.x to 3.0.x and 3.1.x, I got lots of errors about EntityManager methods returning mixed.

Am I missing something, or is orm3 only supported WITH dbal4?

I opened another issue before finding this one.

EDIT: bumping to dbal4 is throwing different errors now:

1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 -- -------------------------------------------------------------------------------------------------------------------------------------------- 
     Error                                                                                                                                       
 -- -------------------------------------------------------------------------------------------------------------------------------------------- 
     Internal error: Internal error: Doctrine\DBAL\Connection\StaticServerVersionProvider::__construct(): Argument #1 ($version) must be of      
     type string, float given, called in /app/vendor/doctrine/dbal/src/Connection.php on line 186 while  
     analysing file /app/src/Common/Repository/ApplicationsRepository.php                                
     Run PHPStan with -v option and post the stack trace to:                                                                                     
     https://github.com/phpstan/phpstan/issues/new?template=Bug_report.yaml                                                                      
     Child process error (exit code 1):                                                                                                          
 -- -------------------------------------------------------------------------------------------------------------------------------------------- 

 [ERROR] Found 2 errors
arderyp commented 4 months ago

related to https://github.com/phpstan/phpstan-doctrine/issues/529

arderyp commented 4 months ago

perhaps this is also related: https://github.com/phpstan/phpstan-doctrine/issues/308#issuecomment-1551310265

ondrejmirtes commented 4 months ago

Please post the code that leads to "Method App\Users\Repository\UsersRolesRepository::findAll() should return array<App\Entity\UsersRoles>
but returns mixed.".

And please open a separate issue for the internal error, and post the stack trace as the error message instructs you to.

arderyp commented 4 months ago

Sure. Again, no errors at all on orm 2.x and dbal 3.x. No other changes other than updating the doctrine packages.

[orm3/dbal3] findAll() should return array<App\Entity\UsersRoles> but returns mixed

<?php

declare(strict_types=1);

namespace App\Repository;

use App\Entity\Applications;
use Doctrine\ORM\EntityRepository;

/** @extends EntityRepository<Applications> */
class ApplicationsRepository extends EntityRepository
{
    /** @return Applications[] */
    public function findAll(): array
    {
        return $this->getEntityManager()->createQueryBuilder()
            ->select('a', 'ae')
            ->from(Applications::class, 'a')
            ->leftJoin('a.expirations', 'ae')
            ->getQuery()
            ->getResult();
    }
}

[orm3/dbal3] Internal error

Note: Using configuration file /app/phpstan.neon.
 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%  1 sec

 -- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 
     Error                                                                                                                                                                   
 -- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 
     Internal error: Internal error: Doctrine\DBAL\Connection\StaticServerVersionProvider::__construct(): Argument #1 ($version) must be of                                  
     type string, float given, called in /app/vendor/doctrine/dbal/src/Connection.php on line 186 while                              
     analysing file /app/src/Common/Repository/ApplicationsRepository.php                                                            

     Post the following stack trace to https://github.com/phpstan/phpstan/issues/new?template=Bug_report.yaml:                                                               
     ## /app/vendor/doctrine/dbal/src/Connection/StaticServerVersionProvider.php(11)                                                 
     #0 /app/vendor/doctrine/dbal/src/Connection.php(186):                                                                           
     Doctrine\DBAL\Connection\StaticServerVersionProvider->__construct()                                                                                                     
     #1 /app/vendor/doctrine/orm/src/Mapping/ClassMetadataFactory.php(724):                                                          
     Doctrine\DBAL\Connection->getDatabasePlatform()                                                                                                                         
     #2 /app/vendor/doctrine/orm/src/Mapping/ClassMetadataFactory.php(546):                                                          
     Doctrine\ORM\Mapping\ClassMetadataFactory->getTargetPlatform()                                                                                                          
     #3 /app/vendor/doctrine/orm/src/Mapping/ClassMetadataFactory.php(174):                                                          
     Doctrine\ORM\Mapping\ClassMetadataFactory->completeIdGeneratorMapping()                                                                                                 
     #4 /app/vendor/doctrine/doctrine-bundle/Mapping/ClassMetadataFactory.php(18):                                                   
     Doctrine\ORM\Mapping\ClassMetadataFactory->doLoadMetadata()                                                                                                             
     #5 /app/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php(343):                              
     Doctrine\Bundle\DoctrineBundle\Mapping\ClassMetadataFactory->doLoadMetadata()                                                                                           
     #6 /app/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php(207):                              
     Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->loadMetadata()                                                                                               
     #7 /app/vendor/doctrine/orm/src/EntityManager.php(215):                                                                         
     Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->getMetadataFor()                                                                                             
     #8 /app/vendor/doctrine/orm/src/Query/Parser.php(1570):                                                                         
     Doctrine\ORM\EntityManager->getClassMetadata()                                                                                                                          
     #9 /app/vendor/doctrine/orm/src/Query/Parser.php(1414):                                                                         
     Doctrine\ORM\Query\Parser->RangeVariableDeclaration()                                                                                                                   
     #10 /app/vendor/doctrine/orm/src/Query/Parser.php(1183):                                                                        
     Doctrine\ORM\Query\Parser->IdentificationVariableDeclaration()                                                                                                          
     #11 /app/vendor/doctrine/orm/src/Query/Parser.php(769): Doctrine\ORM\Query\Parser->FromClause()                                 
     #12 /app/vendor/doctrine/orm/src/Query/Parser.php(740):                                                                         
     Doctrine\ORM\Query\Parser->SelectStatement()                                                                                                                            
     #13 /app/vendor/doctrine/orm/src/Query/Parser.php(221): Doctrine\ORM\Query\Parser->QueryLanguage()                              
     #14 /app/vendor/doctrine/orm/src/Query/Parser.php(309): Doctrine\ORM\Query\Parser->getAST()                                     
     #15 /app/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/Query/QueryResultTypeWalker.php(121):                                
     Doctrine\ORM\Query\Parser->parse()                                                                                                                                      
     #16                                                                                                                                                                     
     /app/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php(198):    
     PHPStan\Type\Doctrine\Query\QueryResultTypeWalker::walk()                                                                                                               
     #17                                                                                                                                                                     
     /app/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php(181):    
     PHPStan\Type\Doctrine\QueryBuilder\QueryBuilderGetQueryDynamicReturnTypeExtension->getQueryType()                                                                       
     #18 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(3619):                                       
     PHPStan\Type\Doctrine\QueryBuilder\QueryBuilderGetQueryDynamicReturnTypeExtension->getTypeFromMethodCall()                                                              
     #19 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1416):                                       
     PHPStan\Analyser\MutatingScope->methodCallReturnType()                                                                                                                  
     #20 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1422):                                       
     PHPStan\Analyser\MutatingScope->PHPStan\Analyser\{closure}()                                                                                                            
     #21 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(587):                                        
     PHPStan\Analyser\MutatingScope->resolveType()                                                                                                                           
     #22                                                                                                                                                                     
     /app/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/QueryBuilder/ReturnQueryBuilderExpressionTypeResolverExtension.php(78):  
     PHPStan\Analyser\MutatingScope->getType()                                                                                                                               
     #23                                                                                                                                                                     
     /app/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/QueryBuilder/ReturnQueryBuilderExpressionTypeResolverExtension.php(46):  
     PHPStan\Type\Doctrine\QueryBuilder\ReturnQueryBuilderExpressionTypeResolverExtension->getMethodReflection()                                                             
     #24 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(605):                                        
     PHPStan\Type\Doctrine\QueryBuilder\ReturnQueryBuilderExpressionTypeResolverExtension->getType()                                                                         
     #25 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(587):                                        
     PHPStan\Analyser\MutatingScope->resolveType()                                                                                                                           
     #26 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Rules/FunctionReturnTypeCheck.php(52):                                  
     PHPStan\Analyser\MutatingScope->getType()                                                                                                                               
     #27 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Rules/Methods/ReturnTypeRule.php(43):                                   
     PHPStan\Rules\FunctionReturnTypeCheck->checkReturnType()                                                                                                                
     #28 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(107):                                         
     PHPStan\Rules\Methods\ReturnTypeRule->processNode()                                                                                                                     
     #29 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Node/ClassStatementsGatherer.php(108):                                  
     PHPStan\Analyser\FileAnalyser->PHPStan\Analyser\{closure}()                                                                                                             
     #30 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(577):                                    
     PHPStan\Node\ClassStatementsGatherer->__invoke()                                                                                                                        
     #31 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(468):                                    
     PHPStan\Analyser\NodeScopeResolver::PHPStan\Analyser\{closure}()                                                                                                        
     #32 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(411):                                    
     PHPStan\Analyser\NodeScopeResolver->processStmtNode()                                                                                                                   
     #33 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(576):                                    
     PHPStan\Analyser\NodeScopeResolver->processStmtNodes()                                                                                                                  
     #34 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(411):                                    
     PHPStan\Analyser\NodeScopeResolver->processStmtNode()                                                                                                                   
     #35 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(678):                                    
     PHPStan\Analyser\NodeScopeResolver->processStmtNodes()                                                                                                                  
     #36 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(411):                                    
     PHPStan\Analyser\NodeScopeResolver->processStmtNode()                                                                                                                   
     #37 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(650):                                    
     PHPStan\Analyser\NodeScopeResolver->processStmtNodes()                                                                                                                  
     #38 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(380):                                    
     PHPStan\Analyser\NodeScopeResolver->processStmtNode()                                                                                                                   
     #39 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(166):                                         
     PHPStan\Analyser\NodeScopeResolver->processNodes()                                                                                                                      
     #40 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(132):                                         
     PHPStan\Analyser\FileAnalyser->analyseFile()                                                                                                                            
     #41                                                                                                                                                                     
     phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):             
     PHPStan\Command\WorkerCommand->PHPStan\Command\{closure}()                                                                                                              
     #42 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/clue/ndjson-react/src/Decoder.php(117):                              
     _PHPStan_cc8d35ffb\Evenement\EventEmitter->emit()                                                                                                                       
     #43                                                                                                                                                                     
     phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):             
     _PHPStan_cc8d35ffb\Clue\React\NDJson\Decoder->handleData()                                                                                                              
     #44 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/Util.php(62):                                       
     _PHPStan_cc8d35ffb\Evenement\EventEmitter->emit()                                                                                                                       
     #45                                                                                                                                                                     
     phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):             
     _PHPStan_cc8d35ffb\React\Stream\Util::_PHPStan_cc8d35ffb\React\Stream\{closure}()                                                                                       
     #46                                                                                                                                                                     
     phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/DuplexResourceStream.php(154):                          
     _PHPStan_cc8d35ffb\Evenement\EventEmitter->emit()                                                                                                                       
     #47                                                                                                                                                                     
     phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(201):                          
     _PHPStan_cc8d35ffb\React\Stream\DuplexResourceStream->handleData()                                                                                                      
     #48                                                                                                                                                                     
     phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(173):                          
     _PHPStan_cc8d35ffb\React\EventLoop\StreamSelectLoop->waitForStreamActivity()                                                                                            
     #49 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(98):                                          
     _PHPStan_cc8d35ffb\React\EventLoop\StreamSelectLoop->run()                                                                                                              
     #50                                                                                                                                                                     
     phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259):                                
     PHPStan\Command\WorkerCommand->execute()                                                                                                                                
     #51 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870):                                
     _PHPStan_cc8d35ffb\Symfony\Component\Console\Command\Command->run()                                                                                                     
     #52 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261):                                
     _PHPStan_cc8d35ffb\Symfony\Component\Console\Application->doRunCommand()                                                                                                
     #53 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157):                                
     _PHPStan_cc8d35ffb\Symfony\Component\Console\Application->doRun()                                                                                                       
     #54 phar:///app/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(124):                                                           
     _PHPStan_cc8d35ffb\Symfony\Component\Console\Application->run()                                                                                                         
     #55 phar:///app/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(125):                                                           
     _PHPStan_cc8d35ffb\{closure}()                                                                                                                                          
     #56 /app/vendor/phpstan/phpstan/phpstan(8): require('...')                                                                      
     #57 /app/vendor/bin/phpstan(119): include('...')                                                                                
     #58 {main}                                                                                                                                                              
     Child process error (exit code 1):                                                                                                                                      
 -- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 

 [ERROR] Found 2 errors                                                                                                 

Used memory: 132.5 MB
arderyp commented 4 months ago

if there's something you'd like me to test @ondrejmirtes, or some other info I can provide to help, just let me know.

I know it's a bit thorny to debug these more complex dependency issues remotely, since it can't be easily replicated on phpstan,org

as always, I value all of your work!

arderyp commented 1 week ago

hey @ondrejmirtes, just wanted to check in on this

or perhaps the answer is, it simply isn't supported at this time (due to https://github.com/phpstan/phpstan-doctrine/issues/529)?

ondrejmirtes commented 1 week ago

Hi, phpstan-doctrine fully supports ORM 3 and DBAL 4.

You're experiencing this error:

Internal error: Internal error: Doctrine\DBAL\Connection\StaticServerVersionProvider::__construct(): Argument #1 ($version) must be of type string, float given, called in /app/vendor/doctrine/dbal/src/Connection.php on line 186 while analysing file /app/src/Common/Repository/ApplicationsRepository.php

So in your case the analysis is crashing because of these lines:

https://github.com/doctrine/dbal/blob/90424473eb144659a89fb1b5c9bca37297085351/src/Connection.php#L185-L189

Meaning this is not a fault of phpstan-doctrine, but of your own Connection configuration. $this->params['serverVersion'] and $this->params['primary']['serverVersion'] need to contain a string, not a float.

Please refer to the Doctrine documentation to figure out how to configure this properly.

It's also possible this is a bug in DBAL itself.

As for:

Method App\Users\Repository\UsersRolesRepository::findAll() should return array<App\Entity\UsersRoles>
but returns mixed.

I'm not sure what goes wrong in this case. We have tests for this scenario that are passing even with ORM 3/DBAL 4.

arderyp commented 1 week ago

extremely helpful, I really appreciate you taking the time to provide such a helpful response!

arderyp commented 1 week ago

@ondrejmirtes I've just tested and getting the same results on orm 3.2.1 using ->serverVersion('8.0.25') in my doctrine config. Is there something I should debug/dump that would be helpful?

EDIT: this is while running dbal 3.8.6, trying 4.x now...

arderyp commented 1 week ago

@ondrejmirtes I think the serverVersion issue demonstrated here, which you explained how to fix, was a red herring. This issue was suddenly triggered on dbal 4 because a new deprecation was introduced in 4 that required a verbose version number (like 1.2.3 instead o 1.2, not to mention the string vs float issue)

Once I addressed that, the original issues resurfaced, lots of expected mixed despite apparently proper configuration and no issues on orm 3.1.0/dbal 3.8.3. I am not running:

dbal 4.0.4 orm 3.2.1 phpstan-doctrine 1.4.3

Examples:

Method App\Repository\ApplicationsRepository::findAll() should return array<App\Entity\Applications> but returns mixed. 

<?php

declare(strict_types=1);

namespace App\Repository;

use App\Entity\Applications;
use Doctrine\ORM\EntityRepository;

/** @extends EntityRepository<Applications> */
class ApplicationsRepository extends EntityRepository
{
    /** @return Applications[] */
    public function findAll(): array
    {
        return $this->getEntityManager()->createQueryBuilder()
            ->select('a', 'ae')
            ->from(Applications::class, 'a')
            ->leftJoin('a.expirations', 'ae')
            ->getQuery()
            ->getResult();
    }
}

Oddly enough, it resolves this issue: https://github.com/phpstan/phpstan-doctrine/issues/525