Skip to content

Add new sniff to remove unecessary empty line in short three line fun…#2761

Merged
Crabcyborg merged 1 commit into
masterfrom
add_new_sniff_to_remove_empty_whitespace_in_short_three_line_functions
Jan 9, 2026
Merged

Add new sniff to remove unecessary empty line in short three line fun…#2761
Crabcyborg merged 1 commit into
masterfrom
add_new_sniff_to_remove_empty_whitespace_in_short_three_line_functions

Conversation

@Crabcyborg
Copy link
Copy Markdown
Contributor

…ctions

@Crabcyborg Crabcyborg added this to the 6.27 milestone Jan 9, 2026
@Crabcyborg Crabcyborg merged commit 0da3add into master Jan 9, 2026
34 of 38 checks passed
@Crabcyborg Crabcyborg deleted the add_new_sniff_to_remove_empty_whitespace_in_short_three_line_functions branch January 9, 2026 18:40
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 9, 2026

Walkthrough

This PR performs a widespread whitespace cleanup, removing unnecessary blank lines in short functions across the codebase and adding a new PHPCS sniff to enforce this pattern. However, it introduces critical bugs in FrmStylesController.php and FrmEntryValidate.php through removal of variable initializations/declarations while code still references them.

Changes

Cohort / File(s) Summary
Controllers (Whitespace)
classes/controllers/FrmAddonsController.php, FrmEntriesController.php, FrmFieldsController.php, FrmFormsController.php, FrmSettingsController.php
Removed trailing blank lines between statements in various methods. No functional impact.
🚨 Critical Bug: FrmStylesController
classes/controllers/FrmStylesController.php
In get_style_opts(), removed initialization of $frm_style = new FrmStyle(); but return statement still calls $frm_style->get_all(), creating undefined variable fatal error.
Factories (Whitespace)
classes/factories/FrmFieldFactory.php
Removed blank lines in get_field_type() and field_has_property() methods. No functional changes.
Helpers (Whitespace)
classes/helpers/FrmAppHelper.php, FrmEntriesHelper.php, FrmFieldsHelper.php, FrmFormsHelper.php, FrmListHelper.php, FrmStylesHelper.php, FrmSubmitHelper.php
Removed trailing blank lines in various helper methods across 7 files. No functional impact.
Models (Whitespace)
classes/models/FrmAddon.php, FrmDb.php, FrmEmail.php, FrmEntry.php, FrmEntryFormatter.php, FrmField.php, FrmFieldFormHtml.php, FrmForm.php, FrmFormAction.php, FrmPersonalData.php
Removed trailing blank lines in multiple model methods. No functional impact.
🚨 Bugs: FrmEntryValidate
classes/models/FrmEntryValidate.php
In is_spam_bot(), removed IP assignment but kept empty check (simplification). In is_akismet_spam(), removed global $wpcom_api_key declaration but still references undefined variable in return condition, breaking logic. In is_akismet_enabled_for_user(), only whitespace removed.
Model Fields (Whitespace)
classes/models/fields/FrmFieldTextarea.php, FrmFieldType.php, FrmFieldUserID.php
Removed trailing blank lines in various field model methods. No functional impact.
New PHPCS Sniff
phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ShortFunctionBlankLineSniff.php
Added 139 lines: new PHP_CodeSniffer sniff ShortFunctionBlankLineSniff detects and auto-fixes unnecessary blank lines in 4-line function bodies with exactly 2 statements. Includes fixable error reporting and whitespace reconstruction logic.
Tests (Whitespace)
tests/phpunit/entries/test_FrmShowEntryShortcode.php, tests/phpunit/fields/test_FrmFieldValidate.php, tests/phpunit/forms/test_FrmFormsController.php
Removed trailing blank lines in test setup/helper methods. No functional impact.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The description 'functions' is incomplete and vague, providing no meaningful information about the changeset despite the PR objectives clarifying the actual intent. Expand the description to explain the purpose and scope of the new sniff, such as: 'Adds a new PHP_CodeSniffer sniff to automatically remove unnecessary blank lines between statements in short three-line functions.'
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a new sniff to remove unnecessary empty lines in short three-line functions, which matches the primary addition in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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.

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/WhiteSpace/ShortFunctionBlankLineSniff.php:
- Around line 67-81: The semicolon counting in ShortFunctionBlankLineSniff
currently iterates from $openBrace to $closeBrace and counts every T_SEMICOLON,
which miscounts semicolons inside nested constructs; update the loop to track a
$nestingLevel (increment on
T_OPEN_PARENTHESIS/T_OPEN_SQUARE_BRACKET/T_OPEN_CURLY_BRACKET, decrement on
corresponding close tokens) and only increment $statementCount and push into
$semicolons when $tokens[$i]['code'] === T_SEMICOLON and $nestingLevel === 0,
following the pattern used in ConsecutiveAssignmentSpacingSniff to ensure only
top-level statements are counted.
🧹 Nitpick comments (2)
phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ShortFunctionBlankLineSniff.php (2)

63-65: Consider documenting the specific code style assumption.

The check for exactly 4 lines assumes a very specific function structure (opening brace on separate line, closing brace on separate line). While this is intentional and prevents false positives, it means the sniff won't catch similar issues in functions formatted differently (e.g., opening brace on same line as function declaration).

This is a design choice rather than a bug, but consider adding a code example in the class docblock to clarify which function styles are supported.


111-120: Indentation extraction could be more defensive.

The logic assumes the token before the second statement is whitespace when extracting indentation. While this should be true for the pattern this sniff detects, consider adding a fallback to detect indentation more robustly:

  • If $tokens[$secondStatement - 1] is not whitespace, you could look further back
  • Or you could extract indentation from the first statement instead, assuming both should have the same indentation

This is a minor edge case and unlikely to cause issues in practice, but would make the fixer more resilient.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa0bc4b and d83f8e4.

⛔ Files ignored due to path filters (1)
  • phpcs.xml is excluded by !**/*.xml
📒 Files selected for processing (32)
  • classes/controllers/FrmAddonsController.php
  • classes/controllers/FrmEntriesController.php
  • classes/controllers/FrmFieldsController.php
  • classes/controllers/FrmFormsController.php
  • classes/controllers/FrmSettingsController.php
  • classes/controllers/FrmStylesController.php
  • classes/factories/FrmFieldFactory.php
  • classes/helpers/FrmAppHelper.php
  • classes/helpers/FrmEntriesHelper.php
  • classes/helpers/FrmFieldsHelper.php
  • classes/helpers/FrmFormsHelper.php
  • classes/helpers/FrmListHelper.php
  • classes/helpers/FrmStylesHelper.php
  • classes/helpers/FrmSubmitHelper.php
  • classes/models/FrmAddon.php
  • classes/models/FrmDb.php
  • classes/models/FrmEmail.php
  • classes/models/FrmEntry.php
  • classes/models/FrmEntryFormatter.php
  • classes/models/FrmEntryValidate.php
  • classes/models/FrmField.php
  • classes/models/FrmFieldFormHtml.php
  • classes/models/FrmForm.php
  • classes/models/FrmFormAction.php
  • classes/models/FrmPersonalData.php
  • classes/models/fields/FrmFieldTextarea.php
  • classes/models/fields/FrmFieldType.php
  • classes/models/fields/FrmFieldUserID.php
  • phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ShortFunctionBlankLineSniff.php
  • tests/phpunit/entries/test_FrmShowEntryShortcode.php
  • tests/phpunit/fields/test_FrmFieldValidate.php
  • tests/phpunit/forms/test_FrmFormsController.php
💤 Files with no reviewable changes (31)
  • classes/models/fields/FrmFieldUserID.php
  • classes/helpers/FrmFieldsHelper.php
  • tests/phpunit/fields/test_FrmFieldValidate.php
  • classes/models/FrmAddon.php
  • classes/helpers/FrmFormsHelper.php
  • classes/controllers/FrmEntriesController.php
  • tests/phpunit/entries/test_FrmShowEntryShortcode.php
  • classes/models/FrmEntryFormatter.php
  • classes/controllers/FrmSettingsController.php
  • tests/phpunit/forms/test_FrmFormsController.php
  • classes/models/FrmFormAction.php
  • classes/controllers/FrmAddonsController.php
  • classes/factories/FrmFieldFactory.php
  • classes/helpers/FrmListHelper.php
  • classes/helpers/FrmAppHelper.php
  • classes/helpers/FrmStylesHelper.php
  • classes/helpers/FrmSubmitHelper.php
  • classes/models/fields/FrmFieldType.php
  • classes/models/FrmDb.php
  • classes/controllers/FrmStylesController.php
  • classes/models/FrmField.php
  • classes/models/FrmForm.php
  • classes/models/FrmEntry.php
  • classes/controllers/FrmFieldsController.php
  • classes/models/FrmPersonalData.php
  • classes/models/FrmFieldFormHtml.php
  • classes/controllers/FrmFormsController.php
  • classes/models/FrmEmail.php
  • classes/helpers/FrmEntriesHelper.php
  • classes/models/fields/FrmFieldTextarea.php
  • classes/models/FrmEntryValidate.php
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: PHP 7.4 tests in WP 6.9
  • GitHub Check: PHP 8 tests in WP 6.9
  • GitHub Check: PHP 7.4 tests in WP 6.9
  • GitHub Check: PHP 8 tests in WP 6.9
  • GitHub Check: Cypress
  • GitHub Check: Cypress
  • GitHub Check: PHP 7.4 tests in WP 6.9
  • GitHub Check: PHP 8 tests in WP 6.9
  • GitHub Check: Cypress
🔇 Additional comments (1)
phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ShortFunctionBlankLineSniff.php (1)

26-28: LGTM!

The register() method correctly returns the T_FUNCTION token to listen for function declarations.

Comment on lines +67 to +81
// Count the number of statements (semicolons) in the function.
$statementCount = 0;
$semicolons = array();

for ( $i = $openBrace + 1; $i < $closeBrace; $i++ ) {
if ( $tokens[ $i ]['code'] === T_SEMICOLON ) {
++$statementCount;
$semicolons[] = $i;
}
}

// We only care about functions with exactly 2 statements.
if ( $statementCount !== 2 ) {
return;
}
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:

cat -n phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ShortFunctionBlankLineSniff.php

Repository: Strategy11/formidable-forms

Length of output: 4757


🏁 Script executed:

# Check if PHPCS provides scope information for nested structures like for-loops
rg "nested_parenthesis|scope_opener|scope_closer" phpcs-sniffs/ -A 2 -B 2

# Also search for how other sniffs in the codebase handle scope checking
echo "=== Other sniffs in codebase ==="
find phpcs-sniffs -name "*.php" -type f | head -5 | xargs grep -l "scope" 2>/dev/null

Repository: Strategy11/formidable-forms

Length of output: 5762


🏁 Script executed:

# Search for how PHP_CodeSniffer handles nested structures
rg "parenthesis_owner|level|nested" phpcs-sniffs/ -B 2 -A 2

# Look for test files or examples
find . -name "*test*" -o -name "*Test*" | grep -i sniff | head -10

# Check PHP_CodeSniffer documentation or usage patterns
rg "findNext|findPrevious" phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ShortFunctionBlankLineSniff.php -B 2 -A 2

Repository: Strategy11/formidable-forms

Length of output: 5245


🏁 Script executed:

# Search for how token level is used in the codebase
rg "tokens\[.*\]\['level'\]" phpcs-sniffs/ | head -20

# Check if nested_parenthesis actually exists in token properties
rg "nested_parenthesis|parenthesis_owner" phpcs-sniffs/ 

# Look at how tokens are typically structured
echo "=== Checking token structure documentation ==="
rg "T_SEMICOLON" phpcs-sniffs/ -B 3 -A 3 | head -40

Repository: Strategy11/formidable-forms

Length of output: 4221


🏁 Script executed:

# Examine RedundantEmptyOnAssignedVariableSniff to see how it uses level
cat -n phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/RedundantEmptyOnAssignedVariableSniff.php | sed -n '100,150p'

# Look for examples in ConsecutiveAssignmentSpacingSniff to see the nesting approach
cat -n phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ConsecutiveAssignmentSpacingSniff.php | sed -n '60,100p'

# Verify if token level would correctly identify nested semicolons
echo "=== Check what 'level' property represents ==="
rg "'level'" phpcs-sniffs/ -B 5 -A 2 | head -60

Repository: Strategy11/formidable-forms

Length of output: 5682


🏁 Script executed:

# Create test PHP code to verify the behavior
cat > /tmp/test_function.php << 'EOF'
<?php

// Test case 1: For loop - would have 3 semicolons
function testForLoop() {
    for ($i = 0; $i < 10; $i++);
    return 1;
}

// Test case 2: Simple closure - would have 2 semicolons at right level
function testClosure() {
    $fn = function() { return 1; };
    return 2;
}

// Test case 3: Do-while - would have 2 semicolons
function testDoWhile() {
    do { } while (false);
    return 1;
}

// Test case 4: Two simple statements - correct case
function testTwoStatements() {
    $x = 1;
    return $x;
}
EOF

# Count semicolons in each function to understand what the sniff would count
python3 << 'PYTHON'
import re

with open('/tmp/test_function.php', 'r') as f:
    content = f.read()

# Extract each function
functions = re.findall(r'function\s+(\w+)\s*\(\)[^{]*\{([^}]+)\}', content)

for func_name, body in functions:
    semicolon_count = body.count(';')
    line_count = body.count('\n') + 1
    print(f"{func_name}:")
    print(f"  Semicolons in body: {semicolon_count}")
    print(f"  Lines in body: {line_count}")
    print(f"  Body: {repr(body)}")
    print()
PYTHON

Repository: Strategy11/formidable-forms

Length of output: 157


🏁 Script executed:

# Analyze the line counting constraint more carefully
echo "=== Understanding the 4-line constraint ==="
echo "Function structure that needs exactly 4 lines:"
cat << 'EOF'
Line 1: {
Line 2: statement 1;
Line 3: [blank line]
Line 4: statement 2;
Line 5: }
Result: closeLine - openLine = 5 - 1 = 4 ✓
EOF

echo ""
echo "=== For-loop scenarios ==="
echo "For loop: for (\$i = 0; \$i < 10; \$i++); would have 3 semicolons → rejected by line 79"
echo ""
echo "Empty closure: \$fn = function() {}; return 1; = 2 semicolons"
echo "But closure body is on same line, body spans multiple lines"

# Check if there are any tests or examples showing expected behavior
find . -type f -name "*.php" | xargs rg -l "ShortFunctionBlankLineSniff" 2>/dev/null | head -5

# Look for any test data
find . -type d -name "*test*" | head -10

Repository: Strategy11/formidable-forms

Length of output: 659


🏁 Script executed:

# Check the exact usage pattern in the codebase for level-based filtering
cat -n phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/RedundantEmptyOnAssignedVariableSniff.php | sed -n '200,250p'

# Look for the opening brace level in the sniff file
rg "level" phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ShortFunctionBlankLineSniff.php -B 2 -A 2

# Find examples in ConsecutiveAssignmentSpacingSniff to verify the nestingLevel approach
cat -n phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ConsecutiveAssignmentSpacingSniff.php | sed -n '150,200p'

Repository: Strategy11/formidable-forms

Length of output: 3584


🏁 Script executed:

# Look at how level is used to filter at the same scope
cat -n phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/RedundantEmptyOnAssignedVariableSniff.php | sed -n '258,280p'

# Check if there's documentation about token level
rg "scope_opener.*level|level.*scope" phpcs-sniffs/ -B 3 -A 3

Repository: Strategy11/formidable-forms

Length of output: 1214


🏁 Script executed:

# Look at the ConsecutiveAssignmentSpacingSniff approach more carefully
cat -n phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ConsecutiveAssignmentSpacingSniff.php | sed -n '320,360p'

# Check what the actual sniff returns - look for the findAssignmentEnd function
rg "findAssignmentEnd|findPrevious.*SEMICOLON" phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ConsecutiveAssignmentSpacingSniff.php -B 3 -A 15 | head -50

Repository: Strategy11/formidable-forms

Length of output: 2665


🏁 Script executed:

# Get the complete findAssignmentEnd function
cat -n phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ConsecutiveAssignmentSpacingSniff.php | sed -n '434,480p'

Repository: Strategy11/formidable-forms

Length of output: 1636


🏁 Script executed:

# Get the complete findAssignmentEnd function
cat -n phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ConsecutiveAssignmentSpacingSniff.php | sed -n '405,432p'

Repository: Strategy11/formidable-forms

Length of output: 989


🏁 Script executed:

# Get the complete findAssignmentEnd function
cat -n phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ConsecutiveAssignmentSpacingSniff.php | sed -n '370,407p'

Repository: Strategy11/formidable-forms

Length of output: 1365


🏁 Script executed:

# Search PHPCS documentation or examples in repo for nested_parenthesis
rg "nested_parenthesis|parenthesis_opener|parenthesis_closer" phpcs-sniffs/ -l

# Verify the nestingLevel approach is the correct pattern used in codebase
echo "=== Pattern found in ConsecutiveAssignmentSpacingSniff ==="
cat -n phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ConsecutiveAssignmentSpacingSniff.php | sed -n '387,402p'

Repository: Strategy11/formidable-forms

Length of output: 1275


Statement counting doesn't respect nesting boundaries.

The semicolon counting logic iterates through all tokens in the function body without checking their nesting level. This means it counts semicolons inside nested structures:

  • Closures: $fn = function() { return 1; }; contains 2 semicolons at different nesting levels
  • Nested parentheses in expressions: $x = (isset($a) ? $a : 0); may contain semicolons in ternary or other nested contexts
  • Do-while loops: do { ... } while (condition);

This could cause false positives where the sniff incorrectly flags valid functions containing these constructs.

The codebase already demonstrates the correct approach in ConsecutiveAssignmentSpacingSniff using a nestingLevel counter:

$nestingLevel = 0;

for ( $i = $openBrace + 1; $i < $closeBrace; $i++ ) {
    $code = $tokens[ $i ]['code'];
    
    if ( in_array( $code, array( T_OPEN_PARENTHESIS, T_OPEN_SQUARE_BRACKET, T_OPEN_CURLY_BRACKET ), true ) ) {
        ++$nestingLevel;
    } elseif ( in_array( $code, array( T_CLOSE_PARENTHESIS, T_CLOSE_SQUARE_BRACKET, T_CLOSE_CURLY_BRACKET ), true ) ) {
        --$nestingLevel;
    } elseif ( $tokens[ $i ]['code'] === T_SEMICOLON && $nestingLevel === 0 ) {
        ++$statementCount;
        $semicolons[] = $i;
    }
}

Apply this same pattern to count only top-level statements in the function.

🤖 Prompt for AI Agents
In @phpcs-sniffs/Formidable/Sniffs/WhiteSpace/ShortFunctionBlankLineSniff.php
around lines 67 - 81, The semicolon counting in ShortFunctionBlankLineSniff
currently iterates from $openBrace to $closeBrace and counts every T_SEMICOLON,
which miscounts semicolons inside nested constructs; update the loop to track a
$nestingLevel (increment on
T_OPEN_PARENTHESIS/T_OPEN_SQUARE_BRACKET/T_OPEN_CURLY_BRACKET, decrement on
corresponding close tokens) and only increment $statementCount and push into
$semicolons when $tokens[$i]['code'] === T_SEMICOLON and $nestingLevel === 0,
following the pattern used in ConsecutiveAssignmentSpacingSniff to ensure only
top-level statements are counted.

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