From aef212e73c45a65bdd7bb0138c4279048e7bc56a Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 30 Jul 2025 04:00:09 +0200 Subject: [PATCH 1/6] Performance/CacheValueOverride: rename test case file Preliminary commit to allow for adding additional test case files. --- ...est.inc => CacheValueOverrideUnitTest.1.inc} | 0 .../Performance/CacheValueOverrideUnitTest.php | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) rename WordPressVIPMinimum/Tests/Performance/{CacheValueOverrideUnitTest.inc => CacheValueOverrideUnitTest.1.inc} (100%) diff --git a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.inc b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc similarity index 100% rename from WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.inc rename to WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc diff --git a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php index 71f89f1f..97ba9397 100644 --- a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php +++ b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php @@ -19,12 +19,21 @@ class CacheValueOverrideUnitTest extends AbstractSniffUnitTest { /** * Returns the lines where errors should occur. * + * @param string $testFile The name of the file being tested. + * * @return array Key is the line number, value is the number of expected errors. */ - public function getErrorList() { - return [ - 5 => 1, - ]; + public function getErrorList( $testFile = '' ) { + + switch ( $testFile ) { + case 'CacheValueOverrideUnitTest.1.inc': + return [ + 5 => 1, + ]; + + default: + return []; + } } /** From 4a885628cd5c1e1e648028f4dc94119616b08792 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 30 Jul 2025 01:47:13 +0200 Subject: [PATCH 2/6] Performance/CacheValueOverride: extend AbstractFunctionRestrictionsSniff As things were, the determination of whether or not a `T_STRING` is a call to the global WP native `wp_cache_get()` function was severely flawed. By switching the sniff over to be based on the WordPressCS `AbstractFunctionRestrictionsSniff` class, this flaw is mitigated. This flaw did not lead to false positive due to the subsequent token walking being very rigid. Includes adding a slew of additional tests to document the sniff behaviour. Additionally, the tests have been made more comprehensive and varied by: * Testing against false positives for calls to methods or namespaced function calls (= the issue being addressed in this PR). * Testing against false positives for attribute class using the same name as the function. * Ensure function import `use` statements are not flagged. We're not interested in those. * Adding more variations to the pre-existing tests: - Non-lowercase function call(s). --- .../Performance/CacheValueOverrideSniff.php | 66 ++++------------ .../CacheValueOverrideUnitTest.1.inc | 78 +++++++++++++++++-- .../CacheValueOverrideUnitTest.php | 3 +- 3 files changed, 88 insertions(+), 59 deletions(-) diff --git a/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php b/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php index 83bdccc4..30277551 100644 --- a/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php +++ b/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php @@ -10,46 +10,38 @@ namespace WordPressVIPMinimum\Sniffs\Performance; use PHP_CodeSniffer\Util\Tokens; -use WordPressVIPMinimum\Sniffs\Sniff; +use WordPressCS\WordPress\AbstractFunctionRestrictionsSniff; /** * This sniff check whether a cached value is being overridden. */ -class CacheValueOverrideSniff extends Sniff { +class CacheValueOverrideSniff extends AbstractFunctionRestrictionsSniff { /** - * Returns the token types that this sniff is interested in. + * Groups of functions to restrict. * - * @return array + * @return array>> */ - public function register() { - return [ T_STRING ]; + public function getGroups() { + return [ + 'wp_cache_get' => [ + 'functions' => [ 'wp_cache_get' ], + ], + ]; } - /** - * Processes the tokens that this sniff is interested in. + * Process a matched token. * - * @param int $stackPtr The position in the stack where the token was found. + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched + * in lowercase. * * @return void */ - public function process_token( $stackPtr ) { - - $functionName = $this->tokens[ $stackPtr ]['content']; - - if ( $functionName !== 'wp_cache_get' ) { - // Not a function we are looking for. - return; - } - - if ( $this->isFunctionCall( $stackPtr ) === false ) { - // Not a function call. - return; - } - + public function process_matched_token( $stackPtr, $group_name, $matched_content ) { $variablePos = $this->isVariableAssignment( $stackPtr ); - if ( $variablePos === false ) { // Not a variable assignment. return; @@ -82,32 +74,6 @@ public function process_token( $stackPtr ) { } } - /** - * Check whether the examined code is a function call. - * - * @param int $stackPtr The position of the current token in the stack. - * - * @return bool - */ - private function isFunctionCall( $stackPtr ) { - - // Find the next non-empty token. - $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); - - if ( $this->tokens[ $openBracket ]['code'] !== T_OPEN_PARENTHESIS ) { - // Not a function call. - return false; - } - - // Find the previous non-empty token. - $search = Tokens::$emptyTokens; - $search[] = T_BITWISE_AND; - $previous = $this->phpcsFile->findPrevious( $search, $stackPtr - 1, null, true ); - - // It's a function definition, not a function call, so return false. - return ! ( $this->tokens[ $previous ]['code'] === T_FUNCTION ); - } - /** * Check whether the examined code is a variable assignment. * diff --git a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc index 61bbed03..055aad34 100644 --- a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc +++ b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc @@ -1,17 +1,79 @@ wp_cache_get(); $methodCall = false; +$nullsafeMethodCall = $this?->wp_cache_get(); $nullsafeMethodCall = false; +$staticMethodCall = MyClass::wp_cache_get(); $staticMethodCall = false; +echo WP_CACHE_GET; +$namespacedFunction = namespace\wp_cache_get(); $namespacedFunction = false; + +function &wp_cache_get() {} + +// Looks like a function call, but is a PHP 8.0+ class instantiation via an attribute. +#[WP_Cache_Get('text')] +function foo() {} -if ( empty( $bad_wp_users ) ) { - $bad_wp_users = get_users(); -} -// Good code start. +/* + * These should all be okay. + */ $good_wp_users = wp_cache_get( md5( self::CACHE_KEY . '_wp_users'), self::CACHE_GROUP ); if ( empty( $good_wp_users ) ) { $good_wp_users = false; $good_wp_users = get_users(); -} \ No newline at end of file +} + +function NoAssignment() { + $unrelated_var = false; + \wp_cache_get( $key, $group ) === false && do_something_to_create_new_cache(); + $unrelated_var = false; +} + +function functionCallsAreNotCaseSensitiveGood() { + $retrieved = WP_Cache_Get( ...$params ); + if ( empty( $retrieved ) ) { + $retrieved = false; + } +} + +function fqnFunctionCallGood() { + $no_params_not_our_concern = \wp_cache_get(); + if ( empty( $no_params_not_our_concern ) ) { + $no_params_not_our_concern = false; + } +} + +function cacheOverrideDoesntAssignFalse() { + $not_false = wp_cache_get( ...$params ); + $not_false = true; +} + + +function cacheOverrideAssignsUnknownValue( $unknown ) { + $not_false = wp_cache_get( ...$params ); + $not_false = $unknown; +} + + +/* + * These should all be flagged. + */ +$bad_wp_users = wp_cache_get( md5( self::CACHE_KEY . '_wp_users'), self::CACHE_GROUP ); +$bad_wp_users = false; // Bad. + +if ( empty( $bad_wp_users ) ) { + $bad_wp_users = get_users(); +} + +function functionCallsAreNotCaseSensitiveBad() { + $cache_retrieved = WP_Cache_Get( ...$params ); + // Comment. + do_something_else(); + $cache_retrieved = false; // Bad. +} diff --git a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php index 97ba9397..21773c7e 100644 --- a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php +++ b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php @@ -28,7 +28,8 @@ public function getErrorList( $testFile = '' ) { switch ( $testFile ) { case 'CacheValueOverrideUnitTest.1.inc': return [ - 5 => 1, + 68 => 1, + 78 => 1, ]; default: From 0cb596ede2aeb7eb65a2ac5e77758fe717774a63 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 30 Jul 2025 01:53:41 +0200 Subject: [PATCH 3/6] Performance/CacheValueOverride: bug fix - false negative with fully qualified function call The `isVariableAssignment()` method walks back from the function call to see if the result is being assigned, but did not take fully qualified function calls into account. Fixed now. Includes test. --- .../Sniffs/Performance/CacheValueOverrideSniff.php | 3 ++- .../Tests/Performance/CacheValueOverrideUnitTest.1.inc | 5 +++++ .../Tests/Performance/CacheValueOverrideUnitTest.php | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php b/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php index 30277551..12a4a77c 100644 --- a/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php +++ b/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php @@ -83,9 +83,10 @@ public function process_matched_token( $stackPtr, $group_name, $matched_content */ private function isVariableAssignment( $stackPtr ) { - // Find the previous non-empty token. + // Find the previous non-empty token, but allow for FQN function calls. $search = Tokens::$emptyTokens; $search[] = T_BITWISE_AND; + $search[] = T_NS_SEPARATOR; $previous = $this->phpcsFile->findPrevious( $search, $stackPtr - 1, null, true ); if ( $this->tokens[ $previous ]['code'] !== T_EQUAL ) { diff --git a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc index 055aad34..ffe54015 100644 --- a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc +++ b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc @@ -77,3 +77,8 @@ function functionCallsAreNotCaseSensitiveBad() { do_something_else(); $cache_retrieved = false; // Bad. } + +function fqnFunctionCallBad() { + $shouldBeCaught = \wp_cache_get(); + $shouldBeCaught = false; // Bad. +} diff --git a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php index 21773c7e..dbdb350b 100644 --- a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php +++ b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php @@ -30,6 +30,7 @@ public function getErrorList( $testFile = '' ) { return [ 68 => 1, 78 => 1, + 83 => 1, ]; default: From c7acb304963d2ad2f7e2fe71fbbd2088347e5126 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 30 Jul 2025 02:38:48 +0200 Subject: [PATCH 4/6] 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. --- .../Performance/CacheValueOverrideSniff.php | 27 ++++++++++++++----- .../CacheValueOverrideUnitTest.1.inc | 7 +++++ .../CacheValueOverrideUnitTest.2.inc | 5 ++++ 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.2.inc diff --git a/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php b/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php index 12a4a77c..f008ba24 100644 --- a/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php +++ b/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php @@ -41,6 +41,27 @@ public function getGroups() { * @return void */ public function process_matched_token( $stackPtr, $group_name, $matched_content ) { + $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + if ( $openBracket === false || isset( $this->tokens[ $openBracket ]['parenthesis_closer'] ) === false ) { + // Import use statement for function or parse error/live coding. Ignore. + return; + } + + $closeBracket = $this->tokens[ $openBracket ]['parenthesis_closer']; + $firstNonEmpty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $openBracket + 1 ), null, true ); + $nextNonEmpty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $firstNonEmpty + 1 ), null, true ); + if ( $nextNonEmpty === false ) { + // Parse error/live coding. Ignore. + return; + } + + if ( $this->tokens[ $firstNonEmpty ]['code'] === T_ELLIPSIS + && $nextNonEmpty === $closeBracket + ) { + // First class callable. Ignore. + return; + } + $variablePos = $this->isVariableAssignment( $stackPtr ); if ( $variablePos === false ) { // Not a variable assignment. @@ -50,12 +71,6 @@ public function process_matched_token( $stackPtr, $group_name, $matched_content $variableToken = $this->tokens[ $variablePos ]; $variableName = $variableToken['content']; - // Find the next non-empty token. - $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); - - // Find the closing bracket. - $closeBracket = $this->tokens[ $openBracket ]['parenthesis_closer']; - $nextVariableOccurrence = $this->phpcsFile->findNext( T_VARIABLE, $closeBracket + 1, null, false, $variableName ); $rightAfterNextVariableOccurence = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextVariableOccurrence + 1, null, true, null, true ); diff --git a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc index ffe54015..664001a8 100644 --- a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc +++ b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc @@ -82,3 +82,10 @@ function fqnFunctionCallBad() { $shouldBeCaught = \wp_cache_get(); $shouldBeCaught = false; // Bad. } + +// Ignore PHP 8.1 first class callable. +// Ignore as the assignment is not for the return value of the function, but for the callable. +function firstClassCallable() { + $callable = wp_cache_get(...); + $callable = false; // OK. +} diff --git a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.2.inc b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.2.inc new file mode 100644 index 00000000..37435fd1 --- /dev/null +++ b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.2.inc @@ -0,0 +1,5 @@ + Date: Wed, 30 Jul 2025 03:55:36 +0200 Subject: [PATCH 5/6] Performance/CacheValueOverride: bug fix - false positives for variables in different scopes As things were, the searching for the "next use of the variable to which the cache was assigned" did not limit itself to the current variable scope. In effect, that meant that "function local" variables could be mistaken for global variables and visa versa, as demonstrated by the new tests. Fixed now. Includes tests. --- .../Performance/CacheValueOverrideSniff.php | 32 +++++++++++++++-- .../CacheValueOverrideUnitTest.1.inc | 36 +++++++++++++++++++ .../CacheValueOverrideUnitTest.php | 1 + 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php b/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php index f008ba24..054a636f 100644 --- a/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php +++ b/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php @@ -10,6 +10,8 @@ namespace WordPressVIPMinimum\Sniffs\Performance; use PHP_CodeSniffer\Util\Tokens; +use PHPCSUtils\Tokens\Collections; +use PHPCSUtils\Utils\Conditions; use WordPressCS\WordPress\AbstractFunctionRestrictionsSniff; /** @@ -71,16 +73,40 @@ public function process_matched_token( $stackPtr, $group_name, $matched_content $variableToken = $this->tokens[ $variablePos ]; $variableName = $variableToken['content']; - $nextVariableOccurrence = $this->phpcsFile->findNext( T_VARIABLE, $closeBracket + 1, null, false, $variableName ); + // Figure out the scope we need to search in. + $searchEnd = $this->phpcsFile->numTokens; + $functionPtr = Conditions::getLastCondition( $this->phpcsFile, $stackPtr, [ T_FUNCTION, T_CLOSURE ] ); + if ( $functionPtr !== false && isset( $this->tokens[ $functionPtr ]['scope_closer'] ) ) { + $searchEnd = $this->tokens[ $functionPtr ]['scope_closer']; + } + + $nextVariableOccurrence = false; + for ( $i = $closeBracket + 1; $i < $searchEnd; $i++ ) { + if ( $this->tokens[ $i ]['code'] === T_VARIABLE && $this->tokens[ $i ]['content'] === $variableName ) { + $nextVariableOccurrence = $i; + break; + } + + // Skip over any and all closed scopes. + if ( isset( Collections::closedScopes()[ $this->tokens[ $i ]['code'] ] ) ) { + if ( isset( $this->tokens[ $i ]['scope_closer'] ) ) { + $i = $this->tokens[ $i ]['scope_closer']; + } + } + } + + if ( $nextVariableOccurrence === false ) { + return; + } - $rightAfterNextVariableOccurence = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextVariableOccurrence + 1, null, true, null, true ); + $rightAfterNextVariableOccurence = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextVariableOccurrence + 1, $searchEnd, true, null, true ); if ( $this->tokens[ $rightAfterNextVariableOccurence ]['code'] !== T_EQUAL ) { // Not a value override. return; } - $valueAfterEqualSign = $this->phpcsFile->findNext( Tokens::$emptyTokens, $rightAfterNextVariableOccurence + 1, null, true, null, true ); + $valueAfterEqualSign = $this->phpcsFile->findNext( Tokens::$emptyTokens, $rightAfterNextVariableOccurence + 1, $searchEnd, true, null, true ); if ( $this->tokens[ $valueAfterEqualSign ]['code'] === T_FALSE ) { $message = 'Obtained cached value in `%s` is being overridden. Disabling caching?'; diff --git a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc index 664001a8..75874857 100644 --- a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc +++ b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.1.inc @@ -89,3 +89,39 @@ function firstClassCallable() { $callable = wp_cache_get(...); $callable = false; // OK. } + +// Bug fix - the variable re-assignment must be in the same scope. +function controlStructuresAreNotClosedScopes() { + if (false === ($cache = \wp_cache_get( $key, $group ))) { + // Create new cache. + } + $cache = false; // Bad. +} + + +function closedScopeA() { + $myCache = \wp_cache_get(); +} + +function closedScopeB($myCache = false) {} // OK. + +function closedScopeC() { + $yourCache = wp_cache_get(); + + $closure = function () { + $yourCache = false; // OK. + }; +} + +$globalScope = wp_cache_get(); +function closedScopeD() { + $globalScope = false; // OK. +} + +$globalScope = wp_cache_get(); +function closedScopeE($globalScope = false;) {} // OK. + +$globalScope = wp_cache_get(); +class FooBar { + function method($globalScope = false) {} // OK. +} diff --git a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php index dbdb350b..5f02033d 100644 --- a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php +++ b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.php @@ -31,6 +31,7 @@ public function getErrorList( $testFile = '' ) { 68 => 1, 78 => 1, 83 => 1, + 98 => 1, ]; default: From 3202749f067bccef07d9edacd64fc7d88e029c40 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 30 Jul 2025 03:57:47 +0200 Subject: [PATCH 6/6] Performance/CacheValueOverride: add some more defensive coding ... to guard against the sniff throwing PHP notices/warnings due to parse errors/live coding. Any such PHP notice/warning stops the PHPCS run for a file, so should be avoided. Includes tests. --- .../Sniffs/Performance/CacheValueOverrideSniff.php | 6 ++++-- .../Tests/Performance/CacheValueOverrideUnitTest.3.inc | 6 ++++++ .../Tests/Performance/CacheValueOverrideUnitTest.4.inc | 6 ++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.3.inc create mode 100644 WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.4.inc diff --git a/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php b/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php index 054a636f..4a06a7f1 100644 --- a/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php +++ b/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php @@ -101,14 +101,16 @@ public function process_matched_token( $stackPtr, $group_name, $matched_content $rightAfterNextVariableOccurence = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextVariableOccurrence + 1, $searchEnd, true, null, true ); - if ( $this->tokens[ $rightAfterNextVariableOccurence ]['code'] !== T_EQUAL ) { + if ( $rightAfterNextVariableOccurence === false + || $this->tokens[ $rightAfterNextVariableOccurence ]['code'] !== T_EQUAL + ) { // Not a value override. return; } $valueAfterEqualSign = $this->phpcsFile->findNext( Tokens::$emptyTokens, $rightAfterNextVariableOccurence + 1, $searchEnd, true, null, true ); - if ( $this->tokens[ $valueAfterEqualSign ]['code'] === T_FALSE ) { + if ( $valueAfterEqualSign !== false && $this->tokens[ $valueAfterEqualSign ]['code'] === T_FALSE ) { $message = 'Obtained cached value in `%s` is being overridden. Disabling caching?'; $data = [ $variableName ]; $this->phpcsFile->addError( $message, $nextVariableOccurrence, 'CacheValueOverride', $data ); diff --git a/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.3.inc b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.3.inc new file mode 100644 index 00000000..e7bf536a --- /dev/null +++ b/WordPressVIPMinimum/Tests/Performance/CacheValueOverrideUnitTest.3.inc @@ -0,0 +1,6 @@ +