From 67b23563726df22cc7350da605dc8fca288298f1 Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Thu, 15 Jan 2026 13:34:35 -0400 Subject: [PATCH 1/2] Make new sniffs better at negating conditions --- .../FlipForeachIfToContinueSniff.php | 44 ++++++++++++++++++- .../CodeAnalysis/FlipIfToEarlyReturnSniff.php | 44 ++++++++++++++++++- 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipForeachIfToContinueSniff.php b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipForeachIfToContinueSniff.php index 9a5973450c..9315b3287f 100644 --- a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipForeachIfToContinueSniff.php +++ b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipForeachIfToContinueSniff.php @@ -240,10 +240,16 @@ private function negateCondition( $condition ) { return substr( $condition, 2 ); } - if ( strpos( $condition, '!' ) === 0 ) { + if ( strpos( $condition, '!' ) === 0 && strpos( $condition, '!=' ) !== 0 ) { return substr( $condition, 1 ); } + // Try to flip comparison operators. + $flipped = $this->flipComparisonOperator( $condition ); + if ( $flipped !== false ) { + return $flipped; + } + // Otherwise, add negation. // If condition is simple (no spaces or operators at top level), just add ! // If complex, wrap in parentheses. @@ -254,6 +260,42 @@ private function negateCondition( $condition ) { return '! ( ' . $condition . ' )'; } + /** + * Try to flip a comparison operator in a condition. + * + * @param string $condition The condition to flip. + * + * @return string|false The flipped condition, or false if not a simple comparison. + */ + private function flipComparisonOperator( $condition ) { + // Map of operators to their opposites. + $operatorMap = array( + '!==' => '===', + '===' => '!==', + '!=' => '==', + '==' => '!=', + '>=' => '<', + '<=' => '>', + '>' => '<=', + '<' => '>=', + ); + + // Check for each operator (check longer ones first). + foreach ( $operatorMap as $op => $opposite ) { + $pos = strpos( $condition, $op ); + if ( $pos !== false ) { + // Make sure this is a simple comparison (no && or ||). + if ( strpos( $condition, '&&' ) !== false || strpos( $condition, '||' ) !== false ) { + return false; + } + + return substr( $condition, 0, $pos ) . $opposite . substr( $condition, $pos + strlen( $op ) ); + } + } + + return false; + } + /** * Check if a condition is simple (single variable/function call). * diff --git a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnSniff.php b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnSniff.php index eab0038041..88ae4f362f 100644 --- a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnSniff.php +++ b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnSniff.php @@ -248,10 +248,16 @@ private function negateCondition( $condition ) { return substr( $condition, 2 ); } - if ( strpos( $condition, '!' ) === 0 ) { + if ( strpos( $condition, '!' ) === 0 && strpos( $condition, '!=' ) !== 0 ) { return substr( $condition, 1 ); } + // Try to flip comparison operators. + $flipped = $this->flipComparisonOperator( $condition ); + if ( $flipped !== false ) { + return $flipped; + } + // Otherwise, add negation. // If condition is simple (no spaces or operators at top level), just add ! // If complex, wrap in parentheses. @@ -262,6 +268,42 @@ private function negateCondition( $condition ) { return '! ( ' . $condition . ' )'; } + /** + * Try to flip a comparison operator in a condition. + * + * @param string $condition The condition to flip. + * + * @return string|false The flipped condition, or false if not a simple comparison. + */ + private function flipComparisonOperator( $condition ) { + // Map of operators to their opposites. + $operatorMap = array( + '!==' => '===', + '===' => '!==', + '!=' => '==', + '==' => '!=', + '>=' => '<', + '<=' => '>', + '>' => '<=', + '<' => '>=', + ); + + // Check for each operator (check longer ones first). + foreach ( $operatorMap as $op => $opposite ) { + $pos = strpos( $condition, $op ); + if ( $pos !== false ) { + // Make sure this is a simple comparison (no && or ||). + if ( strpos( $condition, '&&' ) !== false || strpos( $condition, '||' ) !== false ) { + return false; + } + + return substr( $condition, 0, $pos ) . $opposite . substr( $condition, $pos + strlen( $op ) ); + } + } + + return false; + } + /** * Check if a condition is simple (single variable/function call). * From afa206391289a0f1211f9400295b47c97782aa32 Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Thu, 15 Jan 2026 13:37:08 -0400 Subject: [PATCH 2/2] Run php cs fixer --- .../Sniffs/CodeAnalysis/FlipForeachIfToContinueSniff.php | 4 +++- .../Sniffs/CodeAnalysis/FlipIfToEarlyReturnSniff.php | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipForeachIfToContinueSniff.php b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipForeachIfToContinueSniff.php index 9315b3287f..c3cb4b7acd 100644 --- a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipForeachIfToContinueSniff.php +++ b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipForeachIfToContinueSniff.php @@ -246,6 +246,7 @@ private function negateCondition( $condition ) { // Try to flip comparison operators. $flipped = $this->flipComparisonOperator( $condition ); + if ( $flipped !== false ) { return $flipped; } @@ -265,7 +266,7 @@ private function negateCondition( $condition ) { * * @param string $condition The condition to flip. * - * @return string|false The flipped condition, or false if not a simple comparison. + * @return false|string The flipped condition, or false if not a simple comparison. */ private function flipComparisonOperator( $condition ) { // Map of operators to their opposites. @@ -283,6 +284,7 @@ private function flipComparisonOperator( $condition ) { // Check for each operator (check longer ones first). foreach ( $operatorMap as $op => $opposite ) { $pos = strpos( $condition, $op ); + if ( $pos !== false ) { // Make sure this is a simple comparison (no && or ||). if ( strpos( $condition, '&&' ) !== false || strpos( $condition, '||' ) !== false ) { diff --git a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnSniff.php b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnSniff.php index 88ae4f362f..41a284cdf2 100644 --- a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnSniff.php +++ b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnSniff.php @@ -254,6 +254,7 @@ private function negateCondition( $condition ) { // Try to flip comparison operators. $flipped = $this->flipComparisonOperator( $condition ); + if ( $flipped !== false ) { return $flipped; } @@ -273,7 +274,7 @@ private function negateCondition( $condition ) { * * @param string $condition The condition to flip. * - * @return string|false The flipped condition, or false if not a simple comparison. + * @return false|string The flipped condition, or false if not a simple comparison. */ private function flipComparisonOperator( $condition ) { // Map of operators to their opposites. @@ -291,6 +292,7 @@ private function flipComparisonOperator( $condition ) { // Check for each operator (check longer ones first). foreach ( $operatorMap as $op => $opposite ) { $pos = strpos( $condition, $op ); + if ( $pos !== false ) { // Make sure this is a simple comparison (no && or ||). if ( strpos( $condition, '&&' ) !== false || strpos( $condition, '||' ) !== false ) {