Skip to content

Issue with QueryBuilderGetQueryDynamicReturnTypeExtension when a variable typed as an interface is used #501

Closed
@Kocal

Description

@Kocal

Hi everyone,

When upgrading from 1.3.50 to 1.3.53, we started to have the following issue:

 -- ----------------------------------------------------------------------------------------------------------------------------
     Error
 -- ----------------------------------------------------------------------------------------------------------------------------
     Internal error: Internal error: Class 'App\Common\Entity\Behavior\StatsOwnerInterface' does not exist while analysing file
     /Users/halliaume/workspace/international-website/src/Core/Handler/Analytics/StatsHandler.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):
 -- ----------------------------------------------------------------------------------------------------------------------------

App\Common\Entity\Behavior\StatsOwnerInterface is indeed not a class, but it exists as an interface.

When running PHPStan with --debug:

Uncaught Doctrine\Persistence\Mapping\MappingException: Class 'App\Common\Entity\Behavior\StatsOwnerInterface' does not exist in /PATH/TO/PROJECT/vendor/doctrine/persistence/src/Persistence/Mapping/MappingException.php:80
#0 /PATH/TO/PROJECT/vendor/doctrine/persistence/src/Persistence/Mapping/RuntimeReflectionService.php(39): Doctrine\Persistence\Mapping\MappingException::nonExistingClass('App\\Common\\Enti...')
#1 /PATH/TO/PROJECT/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php(283): Doctrine\Persistence\Mapping\RuntimeReflectionService->getParentClasses('App\\Common\\Enti...')
#2 /PATH/TO/PROJECT/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php(318): Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->getParentClasses('App\\Common\\Enti...')
#3 /PATH/TO/PROJECT/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php(207): Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->loadMetadata('App\\Common\\Enti...')
#4 /PATH/TO/PROJECT/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php(318): Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->getMetadataFor('App\\Common\\Enti...')
#5 /PATH/TO/PROJECT/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php(1263): Doctrine\ORM\EntityManager->getClassMetadata('App\\Common\\Enti...')
#6 /PATH/TO/PROJECT/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php(898): Doctrine\ORM\Query\Parser->UpdateClause()
#7 /PATH/TO/PROJECT/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php(854): Doctrine\ORM\Query\Parser->UpdateStatement()
#8 /PATH/TO/PROJECT/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php(257): Doctrine\ORM\Query\Parser->QueryLanguage()
#9 /PATH/TO/PROJECT/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php(357): Doctrine\ORM\Query\Parser->getAST()
#10 /PATH/TO/PROJECT/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/Query/QueryResultTypeWalker.php(119): Doctrine\ORM\Query\Parser->parse()
#11 /PATH/TO/PROJECT/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php(205): PHPStan\Type\Doctrine\Query\QueryResultTypeWalker::walk(Object(Doctrine\ORM\Query), Object(PHPStan\Type\Doctrine\Query\QueryResultTypeBuilder), Object(PHPStan\Type\Doctrine\DescriptorRegistry))
#12 /PATH/TO/PROJECT/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php(188): PHPStan\Type\Doctrine\QueryBuilder\QueryBuilderGetQueryDynamicReturnTypeExtension->getQueryType('UPDATE App\\Comm...')
#13 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(3495): PHPStan\Type\Doctrine\QueryBuilder\QueryBuilderGetQueryDynamicReturnTypeExtension->getTypeFromMethodCall(Object(PHPStan\Reflection\ResolvedMethodReflection), Object(PhpParser\Node\Expr\MethodCall), Object(PHPStan\Analyser\MutatingScope))
#14 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1379): PHPStan\Analyser\MutatingScope->methodCallReturnType(Object(PHPStan\Type\Doctrine\QueryBuilder\BranchingQueryBuilderType), 'getQuery', Object(PhpParser\Node\Expr\MethodCall))
#15 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1385): PHPStan\Analyser\MutatingScope->PHPStan\Analyser\{closure}()
#16 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(567): PHPStan\Analyser\MutatingScope->resolveType('$updateQb->getQ...', Object(PhpParser\Node\Expr\MethodCall))
#17 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1379): PHPStan\Analyser\MutatingScope->getType(Object(PhpParser\Node\Expr\MethodCall))
#18 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1385): PHPStan\Analyser\MutatingScope->PHPStan\Analyser\{closure}()
#19 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(567): PHPStan\Analyser\MutatingScope->resolveType('$updateQb->getQ...', Object(PhpParser\Node\Expr\MethodCall))
#20 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(1574): PHPStan\Analyser\MutatingScope->getType(Object(PhpParser\Node\Expr\MethodCall))
#21 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(627): PHPStan\Analyser\NodeScopeResolver->findEarlyTerminatingExpr(Object(PhpParser\Node\Expr\MethodCall), Object(PHPStan\Analyser\MutatingScope))
#22 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(402): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Expression), Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#23 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(794): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\Foreach_), Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#24 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(402): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Foreach_), Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#25 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(567): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\ClassMethod), Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#26 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(3652): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\ClassMethod), Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#27 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(3668): PHPStan\Analyser\NodeScopeResolver->processNodesForCalledMethod(Object(PhpParser\Node\Stmt\Class_), '/PATH/TO/PR...', Object(PHPStan\Reflection\ResolvedMethodReflection), Object(Closure))
#28 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(3664): PHPStan\Analyser\NodeScopeResolver->processNodesForCalledMethod(Array, '/PATH/TO/PR...', Object(PHPStan\Reflection\ResolvedMethodReflection), Object(Closure))
#29 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(3668): PHPStan\Analyser\NodeScopeResolver->processNodesForCalledMethod(Object(PhpParser\Node\Stmt\Namespace_), '/PATH/TO/PR...', Object(PHPStan\Reflection\ResolvedMethodReflection), Object(Closure))
#30 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(3587): PHPStan\Analyser\NodeScopeResolver->processNodesForCalledMethod(Array, '/PATH/TO/PR...', Object(PHPStan\Reflection\ResolvedMethodReflection), Object(Closure))
#31 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(1813): PHPStan\Analyser\NodeScopeResolver->processCalledMethod(Object(PHPStan\Reflection\ResolvedMethodReflection))
#32 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(628): PHPStan\Analyser\NodeScopeResolver->processExprNode(Object(PhpParser\Node\Expr\MethodCall), Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\ExpressionContext))
#33 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(402): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Expression), Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#34 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(567): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\ClassMethod), Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#35 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(402): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\ClassMethod), Object(PHPStan\Analyser\MutatingScope), Object(PHPStan\Node\ClassStatementsGatherer), Object(PHPStan\Analyser\StatementContext))
#36 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(669): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\Class_), Array, Object(PHPStan\Analyser\MutatingScope), Object(PHPStan\Node\ClassStatementsGatherer), Object(PHPStan\Analyser\StatementContext))
#37 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(402): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Class_), Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#38 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(641): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\Namespace_), Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#39 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(371): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Namespace_), Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#40 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(166): PHPStan\Analyser\NodeScopeResolver->processNodes(Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure))
#41 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/Analyser.php(72): PHPStan\Analyser\FileAnalyser->analyseFile('/PATH/TO/PR...', Array, Object(PHPStan\Rules\LazyRegistry), Object(PHPStan\Collectors\Registry), NULL)
#42 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Command/AnalyserRunner.php(62): PHPStan\Analyser\Analyser->analyse(Array, Object(Closure), NULL, true, Array)
#43 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Command/AnalyseApplication.php(209): PHPStan\Command\AnalyserRunner->runAnalyser(Array, Array, Object(Closure), NULL, true, true, '/PATH/TO/PR...', Object(_PHPStan_d147f4cc6\Symfony\Component\Console\Input\ArgvInput))
#44 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Command/AnalyseApplication.php(101): PHPStan\Command\AnalyseApplication->runAnalyser(Array, Array, true, '/PATH/TO/PR...', Object(PHPStan\Command\Symfony\SymfonyOutput), Object(PHPStan\Command\Symfony\SymfonyOutput), Object(_PHPStan_d147f4cc6\Symfony\Component\Console\Input\ArgvInput))
#45 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Command/AnalyseCommand.php(198): PHPStan\Command\AnalyseApplication->analyse(Array, false, Object(PHPStan\Command\Symfony\SymfonyOutput), Object(PHPStan\Command\Symfony\SymfonyOutput), false, true, '/PATH/TO/PR...', Array, Object(_PHPStan_d147f4cc6\Symfony\Component\Console\Input\ArgvInput))
#46 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259): PHPStan\Command\AnalyseCommand->execute(Object(_PHPStan_d147f4cc6\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_d147f4cc6\Symfony\Component\Console\Output\ConsoleOutput))
#47 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870): _PHPStan_d147f4cc6\Symfony\Component\Console\Command\Command->run(Object(_PHPStan_d147f4cc6\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_d147f4cc6\Symfony\Component\Console\Output\ConsoleOutput))
#48 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261): _PHPStan_d147f4cc6\Symfony\Component\Console\Application->doRunCommand(Object(PHPStan\Command\AnalyseCommand), Object(_PHPStan_d147f4cc6\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_d147f4cc6\Symfony\Component\Console\Output\ConsoleOutput))
#49 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157): _PHPStan_d147f4cc6\Symfony\Component\Console\Application->doRun(Object(_PHPStan_d147f4cc6\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_d147f4cc6\Symfony\Component\Console\Output\ConsoleOutput))
#50 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(124): _PHPStan_d147f4cc6\Symfony\Component\Console\Application->run()
#51 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(125): _PHPStan_d147f4cc6\{closure}()
#52 /PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan(8): require('phar:///Users/h...')
#53 /PATH/TO/PROJECT/vendor/bin/phpstan(119): include('/PATH/TO/PR...')
#54 {main}

We can see the exception is thrown at this line:

#16 phar:///PATH/TO/PROJECT/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(567): PHPStan\Analyser\MutatingScope->resolveType('$updateQb->getQ...', Object(PhpParser\Node\Expr\MethodCall))

which refers to the following PHP code:

<?php
class A {
	/**
	 * @param array<string, StatsOwnerInterface> $entitiesByUrl
	 */
	private function retrieveStatsForCurrentSlidingPeriod(
	    Website $website,
	    HasStats $statSettings,
	    \DateTimeZone $timezone,
	    array $entitiesByUrl,
	): void {
	    $periodStart = $this->getPeriodStart($statSettings, $timezone);
	    $periodEnd = $this->getPeriodEnd($timezone);
	    $lastPeriodStats = $this->googleReportingApi->getStatsForPeriod(
	        $website,
	        $periodStart,
	        $periodEnd,
	        array_keys($entitiesByUrl),
	        $this->getEntitiesIds($entitiesByUrl),
	        $statSettings,
	    );
	    foreach ($lastPeriodStats as $url => $reportData) {
	        if (null === $ownerEntity = ($entitiesByUrl[$url] ?? null)) {
	            continue;
	        }
	        $updateQb = $this->entityManager
	            ->getRepository($ownerEntity::class)
	            ->createQueryBuilder('entity');
	        $updateQb->update()
	            ->set('entity.lastPageViewCount', $reportData->pageviews)
	            ->set('entity.lastSessionCount', $reportData->sessions)
	            ->where('entity = :entity')
	            ->setParameter('entity', $ownerEntity);
	        $updateQb->getQuery()->execute();
	    }
	    $this->entityManager->flush();
	}
}

Here, the $ownerEntity (used in $this->entityManager->getRepository()) is an implementation of StatsOwnerInterface, due to the method's PHPDoc, and so it makes the extension crash.

I didn't check the extension code yet, but maybe early-checking if we really have a class would be great, or catch a MappingException exception to return a new QueryType($dql, null).

WDYT?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions