Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal error when a model has a legacy factory via legacy factories package and a regular factory #1810

Closed
jamiewood678 opened this issue Jan 3, 2024 · 3 comments · Fixed by #1922

Comments

@jamiewood678
Copy link

jamiewood678 commented Jan 3, 2024

Versions

Other Notes

  • Using PHPStan bleeding edge

Description

When Laravel code calls the factory static function provided by the Illuminate\Database\Eloquent\Factories\HasFactory trait on a model an internal error is thrown.

This circumstance only happens when a model has both a legacy factory and a new factory. This can arise when legacy codebases that started on older versions of Laravel as the laravel/legacy-factories were introduced to help migrate codebases that referenced deprecated factories in a large volume of locations.

Steps to reproduce

The PR/repository provided above contains a code base that can be used to create the issue

Here are the code steps to reproduce the bug. Assuming you already have a codebase installed with Laravel + Larastan

  • Install laravel/legacy-factories
  • Create a new model
  • Create a legacy factory for the model
  • Create a regular factory for the model
  • Add the Illuminate\Database\Eloquent\Factories\HasFactory trait to the model
  • Create a static function in the model called newFactory which returns the newly created regular factory
  • Call the static function factory on the model in another PHP file which will be analysed by phpstan
  • Run phpstan

Laravel code where the issue was found

<?php

declare(strict_types=1);

namespace Database\Factories\User;

use App\Models\Post;
use App\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

class PostFactory extends Factory
{
    protected $models = Post::class;

    public function definition(): array
    {
        return [
            'user_id' => User::factory(), # If this line is commented out, then phpstan will not error.
        ];
    }
}

Internal Error Thrown

Internal error: Internal error: Call to a member function define() on null while analysing file /workspaces/larastan-legacy-factories-exception/database/factories/PostFactory.php                                
                                                                                                                                                                                                                       
     Post the following stack trace to https://github.com/phpstan/phpstan/issues/new?template=Bug_report.yaml:                                                                                                         
     ## /workspaces/larastan-legacy-factories-exception/database/factories/UserFactory.php(18)                                                                                                                         
     #0 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/composer/ClassLoader.php(576): include()                                                                     
     #1 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/composer/ClassLoader.php(427): Composer\Autoload\{closure}('/workspaces/lar...')                             
     #2 [internal function]: Composer\Autoload\ClassLoader->loadClass('Database\\Factor...')                                                                                                                           
     #3 /workspaces/larastan-legacy-factories-exception/vendor/larastan/larastan/src/ReturnTypes/ModelFactoryDynamicStaticMethodReturnTypeExtension.php(72): class_exists('Database\\Factor...')                       
     #4 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(3597):                                                                               
     Larastan\Larastan\ReturnTypes\ModelFactoryDynamicStaticMethodReturnTypeExtension->getTypeFromStaticMethodCall(Object(PHPStan\Reflection\ResolvedMethodReflection), Object(PhpParser\Node\Expr\StaticCall),        
     Object(PHPStan\Analyser\MutatingScope))                                                                                                                                                                           
     #5 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1446):                                                                               
     PHPStan\Analyser\MutatingScope->methodCallReturnType(Object(PHPStan\Type\ObjectType), 'factory', Object(PhpParser\Node\Expr\StaticCall))                                                                          
     #6 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1452): PHPStan\Analyser\MutatingScope->PHPStan\Analyser\{closure}()                  
     #7 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(575): PHPStan\Analyser\MutatingScope->resolveType('\\App\\User::fact...',            
     Object(PhpParser\Node\Expr\StaticCall))                                                                                                                                                                           
     #8 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1146):                                                                               
     PHPStan\Analyser\MutatingScope->getType(Object(PhpParser\Node\Expr\StaticCall))                                                                                                                                   
     #9 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Reflection/InitializerExprTypeResolver.php(454):                                                                
     PHPStan\Analyser\MutatingScope->PHPStan\Analyser\{closure}(Object(PhpParser\Node\Expr\StaticCall))                                                                                                                
     #10 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1145):                                                                              
     PHPStan\Reflection\InitializerExprTypeResolver->getArrayType(Object(PhpParser\Node\Expr\Array_), Object(Closure))                                                                                                 
     #11 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(575): PHPStan\Analyser\MutatingScope->resolveType('['user_id' => \\...',            
     Object(PhpParser\Node\Expr\Array_))                                                                                                                                                                               
     #12 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Rules/FunctionReturnTypeCheck.php(52):                                                                         
     PHPStan\Analyser\MutatingScope->getType(Object(PhpParser\Node\Expr\Array_))                                                                                                                                       
     #13 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Rules/Methods/ReturnTypeRule.php(43):                                                                          
     PHPStan\Rules\FunctionReturnTypeCheck->checkReturnType(Object(PHPStan\Analyser\MutatingScope), Object(PHPStan\Type\ArrayType), Object(PhpParser\Node\Expr\Array_), Object(PhpParser\Node\Stmt\Return_), 'Method   
     Database...', 'Method Database...', 'Method Database...', 'Method Database...', false)                                                                                                                            
     #14 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(107):                                                                                
     PHPStan\Rules\Methods\ReturnTypeRule->processNode(Object(PhpParser\Node\Stmt\Return_), Object(PHPStan\Analyser\MutatingScope))                                                                                    
     #15 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Node/ClassStatementsGatherer.php(108):                                                                         
     PHPStan\Analyser\FileAnalyser->PHPStan\Analyser\{closure}(Object(PhpParser\Node\Stmt\Return_), Object(PHPStan\Analyser\MutatingScope))                                                                            
     #16 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(568):                                                                           
     PHPStan\Node\ClassStatementsGatherer->__invoke(Object(PhpParser\Node\Stmt\Return_), Object(PHPStan\Analyser\MutatingScope))                                                                                       
     #17 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(459):                                                                           
     PHPStan\Analyser\NodeScopeResolver::PHPStan\Analyser\{closure}(Object(PhpParser\Node\Stmt\Return_), Object(PHPStan\Analyser\MutatingScope))                                                                       
     #18 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(402):                                                                           
     PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Return_), Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))                      
     #19 phar:///workspaces/larastan-legacy-factories-exception/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))          
     #20 phar:///workspaces/larastan-legacy-factories-exception/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))                                                                                                                                                                        
     #21 phar:///workspaces/larastan-legacy-factories-exception/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))                                                                                                                                                                        
     #22 phar:///workspaces/larastan-legacy-factories-exception/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))                       
     #23 phar:///workspaces/larastan-legacy-factories-exception/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))           
     #24 phar:///workspaces/larastan-legacy-factories-exception/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))                   
     #25 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(166): PHPStan\Analyser\NodeScopeResolver->processNodes(Array,                        
     Object(PHPStan\Analyser\MutatingScope), Object(Closure))                                                                                                                                                          
     #26 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(132): PHPStan\Analyser\FileAnalyser->analyseFile('/workspaces/lar...', Array,        
     Object(PHPStan\Rules\LazyRegistry), Object(PHPStan\Collectors\Registry), NULL)                                                                                                                                    
     #27 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):                                                
     PHPStan\Command\WorkerCommand->PHPStan\Command\{closure}(Array)                                                                                                                                                   
     #28 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/clue/ndjson-react/src/Decoder.php(117): _PHPStan_39fe102d2\Evenement\EventEmitter->emit('data', Array)      
     #29 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):                                                
     _PHPStan_39fe102d2\Clue\React\NDJson\Decoder->handleData(Array)                                                                                                                                                   
     #30 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/Util.php(62): _PHPStan_39fe102d2\Evenement\EventEmitter->emit('data', Array)               
     #31 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):                                                
     _PHPStan_39fe102d2\React\Stream\Util::_PHPStan_39fe102d2\React\Stream\{closure}('{"action":"anal...')                                                                                                             
     #32 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/DuplexResourceStream.php(154): _PHPStan_39fe102d2\Evenement\EventEmitter->emit('data',     
     Array)                                                                                                                                                                                                            
     #33 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(201):                                                             
     _PHPStan_39fe102d2\React\Stream\DuplexResourceStream->handleData(Resource id #4522)                                                                                                                               
     #34 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(173):                                                             
     _PHPStan_39fe102d2\React\EventLoop\StreamSelectLoop->waitForStreamActivity(NULL)                                                                                                                                  
     #35 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(98): _PHPStan_39fe102d2\React\EventLoop\StreamSelectLoop->run()                      
     #36 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259):                                                                   
     PHPStan\Command\WorkerCommand->execute(Object(_PHPStan_39fe102d2\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_39fe102d2\Symfony\Component\Console\Output\ConsoleOutput))                           
     #37 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870):                                                                       
     _PHPStan_39fe102d2\Symfony\Component\Console\Command\Command->run(Object(_PHPStan_39fe102d2\Symfony\Component\Console\Input\ArgvInput),                                                                           
     Object(_PHPStan_39fe102d2\Symfony\Component\Console\Output\ConsoleOutput))                                                                                                                                        
     #38 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261):                                                                       
     _PHPStan_39fe102d2\Symfony\Component\Console\Application->doRunCommand(Object(PHPStan\Command\WorkerCommand), Object(_PHPStan_39fe102d2\Symfony\Component\Console\Input\ArgvInput),                               
     Object(_PHPStan_39fe102d2\Symfony\Component\Console\Output\ConsoleOutput))                                                                                                                                        
     #39 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157):                                                                       
     _PHPStan_39fe102d2\Symfony\Component\Console\Application->doRun(Object(_PHPStan_39fe102d2\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_39fe102d2\Symfony\Component\Console\Output\ConsoleOutput))  
     #40 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(124): _PHPStan_39fe102d2\Symfony\Component\Console\Application->run()                                  
     #41 phar:///workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(125): _PHPStan_39fe102d2\{closure}()                                                                   
     #42 /workspaces/larastan-legacy-factories-exception/vendor/phpstan/phpstan/phpstan(8): require('phar:///workspa...')                                                                                              
     #43 /workspaces/larastan-legacy-factories-exception/vendor/bin/phpstan(119): include('/workspaces/lar...')                                                                                                        
     #44 {main}                                                                                                                                                                                                        
     Child process error (exit code 1):
@calebdw
Copy link
Contributor

calebdw commented Apr 17, 2024

The issue here likely stems from the fact that the laravel/legacy-factories package overrides the Illuminate\Database\Eloquent\Factory class with a custom implementation.

Edit: I missed that the new factory is now Illuminate\Database\Eloquent\Factories\Factory so that shouldn't matter

@calebdw
Copy link
Contributor

calebdw commented Apr 17, 2024

Ah, I found the issue---Larastan does not actually take into account the newFactory method and only looks for factories that match the expected pattern:

$factoryName = Factory::resolveFactoryName(ltrim($class->toCodeString(), '\\')); // @phpstan-ignore-line

@canvural
Copy link
Collaborator

Hi,

Given this is an edge case since it only happens when a model has both a legacy factory and a new factory, also nobody else reported it, I think we will not fix it for now. Thank you!

@canvural canvural closed this as not planned Won't fix, can't repro, duplicate, stale Oct 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants