Skip to content

New sniff to prefer ! empty ternaries#2863

Merged
Crabcyborg merged 1 commit into
masterfrom
new_sniff_to_prefer_not_empty_ternaries
Jan 22, 2026
Merged

New sniff to prefer ! empty ternaries#2863
Crabcyborg merged 1 commit into
masterfrom
new_sniff_to_prefer_not_empty_ternaries

Conversation

@Crabcyborg
Copy link
Copy Markdown
Contributor

@Crabcyborg Crabcyborg commented Jan 22, 2026

This update prefers ! empty( A ) ? A : B over empty( A ) ? B : A which feels backwards since A ? A : B reads a lot better and ! empty is really a double negative / positive check.

Summary by CodeRabbit

  • Refactor

    • Standardized conditional logic across multiple helper classes for consistency and code quality.
  • Chores

    • Added a new code style rule to enforce consistent ternary operator patterns in the codebase.
    • Updated code analysis ruleset to include the new style rule.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 22, 2026

📝 Walkthrough

Walkthrough

This PR systematically refactors ternary conditional expressions across multiple helper classes, widgets, and Stripe integration files from the empty(A) ? B : A pattern to the logically equivalent !empty(A) ? A : B pattern. A new PHP_CodeSniffer sniff is introduced to enforce this coding pattern across the codebase going forward.

Changes

Cohort / File(s) Summary
Ternary Logic Refactoring — Core Helpers
classes/helpers/FrmAppHelper.php, classes/helpers/FrmFieldsHelper.php, classes/helpers/FrmFormsHelper.php, classes/helpers/FrmXMLHelper.php
Inverts ternary conditionals from checking empty() to checking !empty(), preserving functional behavior. FrmXMLHelper also updates notification from-address construction to rely on explicit inputs rather than defaults.
Ternary Logic Refactoring — UI Components
classes/views/styles/components/FrmSliderStyleComponent.php, classes/widgets/FrmShowForm.php
Converts ternary title/value assignments from empty() checks to !empty() checks for consistency and readability.
PHP_CodeSniffer Enforcement
phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/PreferNotEmptyTernarySniff.php
Introduces new sniff class that detects empty(A) ? B : A patterns, emits fixable errors, and applies automated fixes to convert to !empty(A) ? A : B. Handles nested ternaries and token navigation.
Configuration
phpcs-sniffs/Formidable/ruleset.xml
Registers the new PreferNotEmptyTernary rule in the CodeAnalysis section.
Ternary Logic Refactoring — Stripe Integration
stripe/helpers/FrmStrpLiteConnectHelper.php, stripe/helpers/FrmStrpLiteSubscriptionHelper.php
Refactors ternary conditionals in customer ID retrieval and plan option name assignment using inverted conditions with same fallback behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Empty checks now inverted with flair!
Ternaries hopping everywhere, fair and square,
A sniff enforces the way so grand,
Code patterns brighten the land! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: introducing a new code sniff that enforces a preference for the !empty() ternary pattern over the empty() pattern.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Crabcyborg Crabcyborg merged commit 77ce022 into master Jan 22, 2026
37 of 38 checks passed
@Crabcyborg Crabcyborg deleted the new_sniff_to_prefer_not_empty_ternaries branch January 22, 2026 13:38
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/PreferNotEmptyTernarySniff.php`:
- Around line 209-244: The method findTernaryEnd() currently treats
T_INLINE_THEN and T_INLINE_ELSE as immediate terminators and thus fails on
nested ternaries; add a ternary nesting counter (e.g. $ternaryDepth = 0) and
update the loop to increment it when encountering T_INLINE_THEN and decrement it
when encountering T_INLINE_ELSE, and only treat those tokens as terminators when
$ternaryDepth is zero; update any existing checks that return $end on
T_INLINE_THEN/T_INLINE_ELSE to instead respect the $ternaryDepth (mirroring the
nesting logic used in findTernaryColon()) so nested ternary branches are skipped
correctly.
🧹 Nitpick comments (1)
phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/PreferNotEmptyTernarySniff.php (1)

131-139: Drop unused $closeParen and $tokens to avoid lint noise.
PHPMD flags both as unused; removing them keeps the sniff clean.

♻️ Suggested cleanup
-			$this->applyFix(
-				$phpcsFile,
-				$stackPtr,
-				$closeParen,
-				$ternaryOperator,
-				$colonOperator,
-				$ternaryEnd,
-				$emptyContent,
-				$thenContent
-			);
+			$this->applyFix(
+				$phpcsFile,
+				$stackPtr,
+				$ternaryOperator,
+				$colonOperator,
+				$ternaryEnd,
+				$emptyContent,
+				$thenContent
+			);
@@
-	private function applyFix( File $phpcsFile, $emptyToken, $closeParen, $ternaryOperator, $colonOperator, $ternaryEnd, $emptyContent, $thenContent ) {
-		$tokens = $phpcsFile->getTokens();
+	private function applyFix( File $phpcsFile, $emptyToken, $ternaryOperator, $colonOperator, $ternaryEnd, $emptyContent, $thenContent ) {
 		$fixer  = $phpcsFile->fixer;

Also applies to: 307-310

Comment on lines +209 to +244
private function findTernaryEnd( File $phpcsFile, $start ) {
$tokens = $phpcsFile->getTokens();
$parenDepth = 0;
$end = false;

for ( $i = $start; $i < count( $tokens ); $i++ ) {
$code = $tokens[ $i ]['code'];

// Track parentheses.
if ( $code === T_OPEN_PARENTHESIS ) {
++$parenDepth;
continue;
}

if ( $code === T_CLOSE_PARENTHESIS ) {
if ( $parenDepth > 0 ) {
--$parenDepth;
continue;
}

// This closes the containing expression.
return $end;
}

// Only process at the same parenthesis level.
if ( $parenDepth > 0 ) {
if ( $tokens[ $i ]['code'] !== T_WHITESPACE ) {
$end = $i;
}
continue;
}

// Stop at statement terminators or operators that end the ternary.
if ( in_array( $code, array( T_SEMICOLON, T_CLOSE_TAG, T_COMMA, T_INLINE_THEN, T_INLINE_ELSE ), true ) ) {
return $end;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "*PreferNotEmpty*" -type f

Repository: Strategy11/formidable-forms

Length of output: 147


🏁 Script executed:

cat -n phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/PreferNotEmptyTernarySniff.php

Repository: Strategy11/formidable-forms

Length of output: 11353


Guard against nested ternaries in the else branch to prevent incorrect auto-fixes. The findTernaryEnd() method stops at the first T_INLINE_THEN or T_INLINE_ELSE without tracking nested ternary depth. For example, empty($a) ? $b : $a ? $c : $d would incorrectly have $a identified as the end of the else expression, causing the fixer to rewrite the expression with wrong semantics. Unlike findTernaryColon() which properly tracks nesting, findTernaryEnd() needs to track ternary depth to skip over nested ternary operators.

🔧 Suggested fix
 private function findTernaryEnd( File $phpcsFile, $start ) {
 	$tokens     = $phpcsFile->getTokens();
 	$parenDepth = 0;
+	$ternaryDepth = 0;
 	$end        = false;

 	for ( $i = $start; $i < count( $tokens ); $i++ ) {
 		$code = $tokens[ $i ]['code'];
@@
 		// Only process at the same parenthesis level.
 		if ( $parenDepth > 0 ) {
 			if ( $tokens[ $i ]['code'] !== T_WHITESPACE ) {
 				$end = $i;
 			}
 			continue;
 		}

-		// Stop at statement terminators or operators that end the ternary.
-		if ( in_array( $code, array( T_SEMICOLON, T_CLOSE_TAG, T_COMMA, T_INLINE_THEN, T_INLINE_ELSE ), true ) ) {
-			return $end;
-		}
+		// Track nested ternaries.
+		if ( $code === T_INLINE_THEN ) {
+			++$ternaryDepth;
+			continue;
+		}
+		if ( $code === T_INLINE_ELSE ) {
+			if ( $ternaryDepth > 0 ) {
+				--$ternaryDepth;
+				continue;
+			}
+			return $end;
+		}
+
+		// Stop at statement terminators once we are out of nested ternaries.
+		if ( 0 === $ternaryDepth && in_array( $code, array( T_SEMICOLON, T_CLOSE_TAG, T_COMMA ), true ) ) {
+			return $end;
+		}
🤖 Prompt for AI Agents
In `@phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/PreferNotEmptyTernarySniff.php`
around lines 209 - 244, The method findTernaryEnd() currently treats
T_INLINE_THEN and T_INLINE_ELSE as immediate terminators and thus fails on
nested ternaries; add a ternary nesting counter (e.g. $ternaryDepth = 0) and
update the loop to increment it when encountering T_INLINE_THEN and decrement it
when encountering T_INLINE_ELSE, and only treat those tokens as terminators when
$ternaryDepth is zero; update any existing checks that return $end on
T_INLINE_THEN/T_INLINE_ELSE to instead respect the $ternaryDepth (mirroring the
nesting logic used in findTernaryColon()) so nested ternary branches are skipped
correctly.

stephywells pushed a commit that referenced this pull request Apr 4, 2026
…y_ternaries

New sniff to prefer ! empty ternaries
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant