Skip to content

Commit c7acb30

Browse files
committed
Performance/CacheValueOverride: bug fix - false positive for PHP 8.1+ first class callable assignment
If a PHP 8.1+ first class callable is assigned to a variable, the assignment captures the callable, not the result of calling the callable. This commit adds some dedicated code to handle this better and prevent the false positive. Includes tests.
1 parent 0cb596e commit c7acb30

File tree

3 files changed

+33
-6
lines changed

3 files changed

+33
-6
lines changed

WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,27 @@ public function getGroups() {
4141
* @return void
4242
*/
4343
public function process_matched_token( $stackPtr, $group_name, $matched_content ) {
44+
$openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true );
45+
if ( $openBracket === false || isset( $this->tokens[ $openBracket ]['parenthesis_closer'] ) === false ) {
46+
// Import use statement for function or parse error/live coding. Ignore.
47+
return;
48+
}
49+
50+
$closeBracket = $this->tokens[ $openBracket ]['parenthesis_closer'];
51+
$firstNonEmpty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $openBracket + 1 ), null, true );
52+
$nextNonEmpty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $firstNonEmpty + 1 ), null, true );
53+
if ( $nextNonEmpty === false ) {
54+
// Parse error/live coding. Ignore.
55+
return;
56+
}
57+
58+
if ( $this->tokens[ $firstNonEmpty ]['code'] === T_ELLIPSIS
59+
&& $nextNonEmpty === $closeBracket
60+
) {
61+
// First class callable. Ignore.
62+
return;
63+
}
64+
4465
$variablePos = $this->isVariableAssignment( $stackPtr );
4566
if ( $variablePos === false ) {
4667
// Not a variable assignment.
@@ -50,12 +71,6 @@ public function process_matched_token( $stackPtr, $group_name, $matched_content
5071
$variableToken = $this->tokens[ $variablePos ];
5172
$variableName = $variableToken['content'];
5273

53-
// Find the next non-empty token.
54-
$openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true );
55-
56-
// Find the closing bracket.
57-
$closeBracket = $this->tokens[ $openBracket ]['parenthesis_closer'];
58-
5974
$nextVariableOccurrence = $this->phpcsFile->findNext( T_VARIABLE, $closeBracket + 1, null, false, $variableName );
6075

6176
$rightAfterNextVariableOccurence = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextVariableOccurrence + 1, null, true, null, true );

WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,10 @@ function fqnFunctionCallBad() {
8282
$shouldBeCaught = \wp_cache_get();
8383
$shouldBeCaught = false; // Bad.
8484
}
85+
86+
// Ignore PHP 8.1 first class callable.
87+
// Ignore as the assignment is not for the return value of the function, but for the callable.
88+
function firstClassCallable() {
89+
$callable = wp_cache_get(...);
90+
$callable = false; // OK.
91+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
// Parse error/live coding test.
4+
// This should be the only test in the file.
5+
$liveCoding = wp_cache_get()

0 commit comments

Comments
 (0)