szepeviktor/phpstan-wordpress

Internal error: Internal error: Multiple variants - use selectFromArgs() instead.

arnaudpfu opened this issue ยท 13 comments

I am using PHPStan via the last version of szepeviktor/phpstan-wordpress.

I get this error when I want to analyse my code.

-- -------------------------------------------------------------------------------------------------------------------------
     Error                                                                                                                          
 -- -------------------------------------------------------------------------------------------------------------------------
     Internal error: Internal error: Multiple variants - use selectFromArgs() instead. in file /Users/arnaud/Local                  
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/core/AjaxSetup.php                                          
                                                                                                                                    
     Post the following stack trace to https://github.com/phpstan/phpstan/issues/new?template=Bug_report.md:                        
     #0 /Users/arnaud/Local                                                                                                         
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/szepeviktor/phpstan-wordpress/src/HookCallbackRule.  
     php(83): PHPStan\Reflection\ParametersAcceptorSelector::selectSingle(Array)                                                    
     #1 phar:///Users/arnaud/Local                                                                                                  
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnaly  
     ser.php(106): SzepeViktor\PHPStan\WordPress\HookCallbackRule->processNode(Object(PhpParser\Node\Expr\FuncCall),                
     Object(PHPStan\Analyser\MutatingScope))                                                                                        
     #2 phar:///Users/arnaud/Local                                                                                                  
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Node/ClassStatemen  
     tsGatherer.php(98): PHPStan\Analyser\FileAnalyser->PHPStan\Analyser\{closure}(Object(PhpParser\Node\Expr\FuncCall),            
     Object(PHPStan\Analyser\MutatingScope))                                                                                        
     #3 phar:///Users/arnaud/Local                                                                                                  
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(503): PHPStan\Node\ClassStatementsGatherer->__invoke(Object(PhpParser\Node\Expr\FuncCall),                        
     Object(PHPStan\Analyser\MutatingScope))                                                                                        
     #4 phar:///Users/arnaud/Local                                                                                                  
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(2460): PHPStan\Analyser\NodeScopeResolver::PHPStan\Analyser\{closure}(Object(PhpParser\Node\Expr\FuncCall),       
     Object(PHPStan\Analyser\MutatingScope))                                                                                        
     #5 phar:///Users/arnaud/Local                                                                                                  
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(1462): PHPStan\Analyser\NodeScopeResolver->callNodeCallbackWithExpression(Object(Closure),                        
     Object(PhpParser\Node\Expr\FuncCall), Object(PHPStan\Analyser\MutatingScope), Object(PHPStan\Analyser\ExpressionContext))      
     #6 phar:///Users/arnaud/Local                                                                                                  
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(555): PHPStan\Analyser\NodeScopeResolver->processExprNode(Object(PhpParser\Node\Expr\FuncCall),                   
     Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\ExpressionContext))                           
     #7 phar:///Users/arnaud/Local                                                                                                  
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(357): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Expression),                 
     Object(PHPStan\Analyser\MutatingScope), Object(Closure))                                                                       
     #8 phar:///Users/arnaud/Local                                                                                                  
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(734): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\Foreach_), Array,           
     Object(PHPStan\Analyser\MutatingScope), Object(Closure))                                                                       
     #9 phar:///Users/arnaud/Local                                                                                                  
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(357): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Foreach_),                   
     Object(PHPStan\Analyser\MutatingScope), Object(Closure))                                                                       
     #10 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(518): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\ClassMethod), Array,        
     Object(PHPStan\Analyser\MutatingScope), Object(Closure))                                                                       
     #11 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(357): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\ClassMethod),                
     Object(PHPStan\Analyser\MutatingScope), Object(PHPStan\Node\ClassStatementsGatherer))                                          
     #12 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(596): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\Class_), Array,             
     Object(PHPStan\Analyser\MutatingScope), Object(PHPStan\Node\ClassStatementsGatherer))                                          
     #13 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(357): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Class_),                     
     Object(PHPStan\Analyser\MutatingScope), Object(Closure))                                                                       
     #14 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(568): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\Namespace_), Array,         
     Object(PHPStan\Analyser\MutatingScope), Object(Closure))                                                                       
     #15 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScope  
     Resolver.php(327): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Namespace_),                 
     Object(PHPStan\Analyser\MutatingScope), Object(Closure))                                                                       
     #16 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnaly  
     ser.php(175): PHPStan\Analyser\NodeScopeResolver->processNodes(Array, Object(PHPStan\Analyser\MutatingScope),                  
     Object(Closure))                                                                                                               
     #17 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerComm  
     and.php(147): PHPStan\Analyser\FileAnalyser->analyseFile('/Users/arnaud/L...', Array, Object(PHPStan\Rules\LazyRegistry),      
     Object(PHPStan\Collectors\Registry), NULL)                                                                                     
     #18 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evene  
     ment/src/Evenement/EventEmitterTrait.php(97): PHPStan\Command\WorkerCommand->PHPStan\Command\{closure}(Array)                  
     #19 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/clue/ndjson-rea  
     ct/src/Decoder.php(110): _PHPStan_582a9cb8b\Evenement\EventEmitter->emit('data', Array)                                        
     #20 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evene  
     ment/src/Evenement/EventEmitterTrait.php(97): _PHPStan_582a9cb8b\Clue\React\NDJson\Decoder->handleData(Array)                  
     #21 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/sr  
     c/Util.php(62): _PHPStan_582a9cb8b\Evenement\EventEmitter->emit('data', Array)                                                 
     #22 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evene  
     ment/src/Evenement/EventEmitterTrait.php(97):                                                                                  
     _PHPStan_582a9cb8b\React\Stream\Util::_PHPStan_582a9cb8b\React\Stream\{closure}('{"action":"anal...')                          
     #23 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/sr  
     c/DuplexResourceStream.php(154): _PHPStan_582a9cb8b\Evenement\EventEmitter->emit('data', Array)                                
     #24 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loo  
     p/src/StreamSelectLoop.php(201): _PHPStan_582a9cb8b\React\Stream\DuplexResourceStream->handleData(Resource id #3450)           
     #25 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loo  
     p/src/StreamSelectLoop.php(173): _PHPStan_582a9cb8b\React\EventLoop\StreamSelectLoop->waitForStreamActivity(NULL)              
     #26 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerComm  
     and.php(107): _PHPStan_582a9cb8b\React\EventLoop\StreamSelectLoop->run()                                                       
     #27 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console  
     /Command/Command.php(259):                                                                                                     
     PHPStan\Command\WorkerCommand->execute(Object(_PHPStan_582a9cb8b\Symfony\Component\Console\Input\ArgvInput),                   
     Object(_PHPStan_582a9cb8b\Symfony\Component\Console\Output\ConsoleOutput))                                                     
     #28 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console  
     /Application.php(868):                                                                                                         
     _PHPStan_582a9cb8b\Symfony\Component\Console\Command\Command->run(Object(_PHPStan_582a9cb8b\Symfony\Component\Console\Input\A  
     rgvInput), Object(_PHPStan_582a9cb8b\Symfony\Component\Console\Output\ConsoleOutput))                                          
     #29 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console  
     /Application.php(259):                                                                                                         
     _PHPStan_582a9cb8b\Symfony\Component\Console\Application->doRunCommand(Object(PHPStan\Command\WorkerCommand),                  
     Object(_PHPStan_582a9cb8b\Symfony\Component\Console\Input\ArgvInput),                                                          
     Object(_PHPStan_582a9cb8b\Symfony\Component\Console\Output\ConsoleOutput))                                                     
     #30 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console  
     /Application.php(157):                                                                                                         
     _PHPStan_582a9cb8b\Symfony\Component\Console\Application->doRun(Object(_PHPStan_582a9cb8b\Symfony\Component\Console\Input\Arg  
     vInput), Object(_PHPStan_582a9cb8b\Symfony\Component\Console\Output\ConsoleOutput))                                            
     #31 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(124):       
     _PHPStan_582a9cb8b\Symfony\Component\Console\Application->run()                                                                
     #32 phar:///Users/arnaud/Local                                                                                                 
     Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(125):       
     _PHPStan_582a9cb8b\{closure}()                                                                                                 
     #33 /Users/arnaud/Local Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/phpstan/phpstan/phpstan(7):  
     require('phar:///Users/a...')                                                                                                  
     #34 /Users/arnaud/Local Sites/other-builder-version/app/public/wp-content/themes/creatorem/vendor/bin/phpstan(115):            
     include('/Users/arnaud/L...')                                                                                                  
     #35 {main}                                                                                                                     
     Child process error (exit code 1):                                                                                             
 -- -------------------------------------------------------------------------------------------------------------------------

I found that this issue comes from this section of code
This code trigger the issue:

class Setup {
	const ACTIONS = array(
		'save_page_builder_data' => 'update_page_builder_data',
	);

	public static function init(): void {
		foreach ( self::ACTIONS as $action => $method ) {
			add_action( 'wp_ajax_' . $action, array( self::class, $method ) );
			add_action( 'wp_ajax_nopriv_' . $action, array( self::class, $method ) );
		}
	}

	public static function update_page_builder_data(): void {
                // code here ...
	}
}

And this code works:

class Setup {
	public static function init(): void {
		add_action( 'wp_ajax_save_page_builder_data', array( self::class, 'update_page_builder_data' ) );
		add_action( 'wp_ajax_nopriv_save_page_builder_data', array( self::class, 'update_page_builder_data' ) );
	}

	public static function update_page_builder_data(): void {
                // code here ...
	}
}

How should I do to be able to use the first syntax ? I think that I need to tell to PHPStan than the values in the ACTIONS array corresponds to the name of method.

Hello Arnaud! ๐Ÿ‘‹๐Ÿป

I hope @johnbillion will reveal the cause of your error.

BTW The second version is more readable!

After looking at your intention I recommend my 3+1 hooking thingies :)
https://github.com/szepeviktor/Toolkit4WP/tree/master/src

Thanks for the report, I'll take a look.

Sorry @szepeviktor but I haven't found why your repo could help me. ๐Ÿ˜…

why your repo could help me.

Those 4 Hook classes replace add_action.

        /**
         * @hook one-of-the-four-ways
         */
	public static function update_page_builder_data(): void {

... that is all you need.

You find minimal documentation in the source code.

@szepeviktor I prefer the first syntax because it enables me to set several ajax hooks in the same place :

class Setup {
	const ACTIONS = array(
		'save_page_builder_data' => 'update_page_builder_data',
		'second_ajax_action' => 'second_ajax_handler',
		'third_ajax_action' => 'third_ajax_handler',
		'fourth_ajax_action' => 'fourth_ajax_handler',
		// ...
	);

	public static function init(): void {
		foreach ( self::ACTIONS as $action => $method ) {
			add_action( 'wp_ajax_' . $action, array( self::class, $method ) );
			add_action( 'wp_ajax_nopriv_' . $action, array( self::class, $method ) );
		}
	}

	public static function update_page_builder_data(): void {
                // code here ...
	}
}

I find that way of doing cleaner.

I have read the codebase of your hooks. However I can't see when I can set the name of the method, that is why I don't see how I could achieve to do what I want in this way.

@arnaudpfu Okay! Here are all four examples.

// 1.
        /**
         * @hook wp_ajax_save_page_builder_data
         */
	public static function update_page_builder_data(): void {

// and call self::hookMethods() method

// 2.
// call
HookConstructorTo::wp_ajax_save_page_builder_data(Setup::class, 10);
// if you want to instantiate a class in a hook

// 3.
// call
HookInitTo::plugins_loaded(Setup::class);
// if you want to instantiate a class and call its `init` method in a hook

// 4.
// call
self::lazyHookStaticMethod('wp_ajax_save_page_builder_data', [Setup::class, 'update_page_builder_data'])
// if you want to load a class and call a static method in a hook

@arnaudpfu Or the best way! You could match method names and AJAX action names!

    public function __construct()
    {
        $classReflection = new ReflectionClass(self::class);
        foreach ($classReflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
            // Do not hook constructor.
            if ($method->isConstructor()) {
                continue;
            }

            \add_action(
                'wp_ajax_nopriv_' . $method->name,
                [self::class, $method->name],
                10,
                0
            );
            \add_action(
                'wp_ajax_' . $method->name,
                [self::class, $method->name],
                10,
                0
            );
        }
    }

@szepeviktor Thanks a lot for your time! ๐Ÿ˜
Based on your last msg I tried this code:

public static function init(): void {

	$class_reflection = new ReflectionClass( self::class );
	foreach ( $class_reflection->getMethods( ReflectionMethod::IS_PUBLIC ) as $method ) {
		if ( ! in_array( $method->name, array_values( self::ACTIONS ), true ) ) {
			continue;
		}

		$action = array_flip( self::ACTIONS )[ $method->name ];

		add_action(
			'wp_ajax_' . $action,
			[ self::class, $method->name ],
			10,
			0
		);
		add_action(
			'wp_ajax_nopriv_' . $action,
			[ self::class, $method->name ],
			10,
			0
		);
	}
}

But I get the exact same error...
Moreover it annoys me a bit to use third party code to fix this kind of issue. As code works outside phpstan...

if ( ! in_array( $method->name, array_values( self::ACTIONS ), true ) ) {

๐Ÿ’ก The whole point of using reflections is to go without the ACTIONS array.

All right. I see you don't want to rename your methods ...

Okay I understand, yes I don't want to rename it.

I'm also seeing this problem on one of my own plugins now. I'm working on a fix but for some reason I can't get the same error to occur during the unit tests. Working on it.