Skip to content

Latest commit

 

History

History
727 lines (546 loc) · 15.7 KB

File metadata and controls

727 lines (546 loc) · 15.7 KB

WPForms PHPCS Rules

This document describes how WPForms coding standards differ from standard WordPress PHPCS rules. Use this as a reference when writing code for WPForms projects.

Table of Contents

  1. Disabled WordPress PHPCS Rules
  2. Modified WordPress PHPCS Rules
  3. Formatting Rules
  4. PHP Rules
  5. Comment & Documentation Rules

Disabled WordPress PHPCS Rules

These WordPress PHPCS rules are disabled in WPForms projects:

Spacing & Indentation

  • Generic.WhiteSpace.ScopeIndent - Less strict indenting allowed
  • Generic.Functions.FunctionCallArgumentSpacing - Less strict argument spacing
  • WordPress.WhiteSpace.PrecisionAlignment - Precision alignment isn't enforced

File Naming (PSR-4 Support)

  • WordPress.Files.FileName.InvalidClassFileName - Allows PSR-4 class file naming
  • WordPress.Files.FileName.NotHyphenatedLowercase - Allows PSR-4 naming conventions

Comments

  • Squiz.Commenting.InlineComment.SpacingAfter - Less strict inline comment spacing
  • Squiz.Commenting.FileComment.* - File-level PHPDoc isn't required

Yoda Conditions

  • WordPress.PHP.YodaConditions.NotYoda - Yoda conditions are NOT required

What this means: Write conditions naturally ($var === 'value'), not in Yoda style ('value' === $var).

// ✅ CORRECT - Natural style
if ( $status === 'active' ) {
    // ...
}

// ❌ WRONG - Yoda style not required
if ( 'active' === $status ) {
    // ...
}

Array Syntax

  • Universal.Arrays.DisallowShortArraySyntax - Short array syntax [] is allowed and preferred
// ✅ CORRECT - Use short array syntax
$items = [ 'foo', 'bar', 'baz' ];

// ❌ WRONG - Long array syntax discouraged
$items = array( 'foo', 'bar', 'baz' );

Other

  • WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents - Direct file_get_contents() allowed

Modified WordPress PHPCS Rules

These rules have different thresholds in WPForms:

Cyclomatic Complexity

  • Complexity limit: 6 (warning)
  • Absolute complexity limit: 10 (error)

What this means: Keep functions simple with fewer conditional branches.

Nesting Level

  • Absolute nesting level: 3

What this means: Avoid deeply nested code. Maximum 3 levels of nesting.

// ✅ CORRECT - 2 levels of nesting
if ( $condition ) {
    foreach ( $items as $item ) {
        process( $item );
    }
}

// ❌ WRONG - Too many nesting levels
if ( $condition ) {
    if ( $other ) {
        foreach ( $items as $item ) {
            if ( $item->valid ) {  // 4 levels!
                process( $item );
            }
        }
    }
}

Formatting Rules

Empty Line Before Return

Rule: Add an empty line before return statements (except when return is the only statement in function body).

// ✅ CORRECT
public function get_name() {

    $name = $this->process_name();

    return $name;
}

// ✅ CORRECT - Single return statement, no empty line needed
public function get_id() {

    return $this->id;
}

// ❌ WRONG - Missing empty line before return
public function get_name() {

    $name = $this->process_name();
    return $name;
}

Empty Line After Function Declaration

Rule: Add an empty line after the opening brace of a function (except for empty functions).

// ✅ CORRECT
public function process() {

    $data = $this->get_data();
    return $data;
}

// ✅ CORRECT - Empty function, no empty line needed
public function __construct() {
}

// ❌ WRONG - Missing empty line after opening brace
public function process() {
    $data = $this->get_data();
    return $data;
}

Empty Line After Assignment

Rule: Add an empty line after assignment statements (with exceptions).

Exceptions: No empty line needed when followed by:

  • Closing brace }
  • Another assignment statement
  • Comments
  • break in switch statement
// ✅ CORRECT
$name = 'John';

$this->process( $name );

// ✅ CORRECT - Multiple assignments together
$first = 'John';
$last = 'Doe';

echo $first . ' ' . $last;

// ❌ WRONG - Missing empty line after assignment
$name = 'John';
$this->process( $name );

Switch Statement Formatting

Rules:

  1. Add empty line before switch statement
  2. Add empty line after switch statement closing brace
  3. No empty line between case statements
  4. Add empty line before case after break
  5. No empty line before break
  6. No empty line before closing brace of switch
// ✅ CORRECT
$result = some_function();

switch ( $status ) {
    case 'pending':
        do_pending();
        break;

    case 'active':
        do_active();
        break;

    default:
        do_default();
        break;
}

$next_step = process();

// ❌ WRONG - Missing empty lines
$result = some_function();
switch ( $status ) {
    case 'pending':
        do_pending();

        break;
    case 'active':
        do_active();
        break;
}
$next_step = process();

PHP Rules

Namespace & Use Statements

Rule 1: Prefer use statements over fully qualified names.

// ✅ CORRECT
use WPForms\Admin\Settings;

$settings = new Settings();

// ❌ WRONG - Don't use fully qualified names
$settings = new \WPForms\Admin\Settings();

Rule 2: Remove leading backslash when only one level deep.

// ✅ CORRECT
use DateTime;

// ❌ WRONG - Unnecessary leading backslash
use \DateTime;

Rule 3: Remove unused use statements.

Yoda Conditions Disabled

Rule: Write conditions in natural order (variable on left, value on right).

// ✅ CORRECT
if ( $count === 0 ) { }
if ( $item !== null ) { }
if ( $user->is_active() === true ) { }

// ❌ WRONG - Don't use Yoda conditions
if ( 0 === $count ) { }
if ( null !== $item ) { }

Note: The custom sniff actively enforces natural conditions.

Hooks Organization

Rule 1: All add_action() and add_filter() calls must be inside a hooks() method within classes.

// ✅ CORRECT
class MyClass {

    public function hooks() {

        add_action( 'init', [ $this, 'init' ] );
        add_filter( 'the_content', [ $this, 'filter_content' ] );
    }
}

// ❌ WRONG - Hooks outside of hooks() method
class MyClass {

    public function __construct() {

        add_action( 'init', [ $this, 'init' ] );
    }
}

Hook Names

Rule: Hook names should start with the fully qualified class name converted to snake_case.

// For class: WPForms\Admin\Settings

// ✅ CORRECT
do_action( 'wpforms_admin_settings_init' );
apply_filters( 'wpforms_admin_settings_data', $data );

// ❌ WRONG - Doesn't match class name pattern
do_action( 'my_custom_init' );

Text Domain Validation

Rule: Use the correct text domain for i18n functions based on file location.

// In wpforms-lite files
// ✅ CORRECT
__( 'Some text', 'wpforms-lite' );

// In wpforms pro files (src/Pro/)
// ✅ CORRECT
__( 'Some text', 'wpforms' );

// ❌ WRONG - Incorrect domain
__( 'Some text', 'wrong-domain' );

// ❌ WRONG - Missing domain
__( 'Some text' );

Comment & Documentation Rules

PHPDoc Descriptions

Rule 1: PHPDoc descriptions must end with punctuation (., !, ?).

// ✅ CORRECT
/**
 * Process user data.
 *
 * @since 1.5.0
 */

// ❌ WRONG - Missing punctuation
/**
 * Process user data
 *
 * @since 1.5.0
 */

Rule 2: PHPDoc description must start on the line after /**, not on the same line.

// ✅ CORRECT
/**
 * Process user data.
 */

// ❌ WRONG - Description on same line as opening
/** Process user data.
 */

@since Tag

Rule 1: All functions, classes, interfaces, traits, constants, class properties, and define() calls must have @since tag.

// ✅ CORRECT
/**
 * Process user data.
 *
 * @since 1.5.0
 *
 * @param array $data User data.
 *
 * @return bool
 */
public function process( $data ) { }

// ❌ WRONG - Missing @since tag
/**
 * Process user data.
 *
 * @param array $data User data.
 *
 * @return bool
 */
public function process( $data ) { }

Rule 2: @since tag must have a valid version number (X.Y.Z format).

// ✅ CORRECT
@since 1.5.0
@since 2.0.0

// ❌ WRONG
@since 1.5
@since v1.5.0
@since TBD

Rule 3: Add empty line after @since tag (except when followed by @deprecated or it's the last tag).

// ✅ CORRECT
/**
 * Description.
 *
 * @since 1.5.0
 *
 * @param string $name Name.
 *
 * @return void
 */

// ✅ CORRECT - No empty line before @deprecated
/**
 * Description.
 *
 * @since 1.5.0
 * @deprecated 2.0.0
 *
 * @param string $name Name.
 */

@deprecated Tag

Rule 1: @deprecated tag must have a valid version number.

Rule 2: Add empty line after @deprecated tag (unless it's the last tag).

@param and @return Tag Spacing

Rule: Only one space after @param and @return tags.

// ✅ CORRECT
@param string $name The name.
@return bool Success status.

// ❌ WRONG - Multiple spaces
@param  string $name The name.
@return  bool Success status.

Translator Comments

Rules for translators: comments:

  1. Use colon after "translators"
  2. Must have a description
  3. Description must end with punctuation (., !, ?)
  4. Use /* */ style comments
// ✅ CORRECT
/* translators: %s is the user's name. */
printf( __( 'Hello, %s!', 'wpforms' ), $name );

// ❌ WRONG - Missing colon
/* translators %s is the user's name. */
printf( __( 'Hello, %s!', 'wpforms' ), $name );

// ❌ WRONG - Missing description
/* translators: */
printf( __( 'Hello, %s!', 'wpforms' ), $name );

// ❌ WRONG - Missing punctuation
/* translators: %s is the user's name */
printf( __( 'Hello, %s!', 'wpforms' ), $name );

// ❌ WRONG - Wrong comment style
// translators: %s is the user's name.
printf( __( 'Hello, %s!', 'wpforms' ), $name );

Hook Documentation

Rule 1: All do_action() and apply_filters() calls must have PHPDoc comment directly above them (on the line immediately before).

// ✅ CORRECT
/**
 * Fires after settings are saved.
 *
 * @since 1.5.0
 *
 * @param array $settings The saved settings.
 */
do_action( 'wpforms_settings_saved', $settings );

// ❌ WRONG - Missing PHPDoc
do_action( 'wpforms_settings_saved', $settings );

// ❌ WRONG - PHPDoc not directly above
/**
 * Fires after settings are saved.
 */

do_action( 'wpforms_settings_saved', $settings );

Rule 2: Hook PHPDoc must have a short description ending with punctuation.

// ✅ CORRECT
/**
 * Fires after settings are saved.
 *
 * @since 1.5.0
 */
do_action( 'wpforms_settings_saved' );

// ❌ WRONG - Missing description
/**
 * @since 1.5.0
 */
do_action( 'wpforms_settings_saved' );

// ❌ WRONG - Missing punctuation
/**
 * Fires after settings are saved
 *
 * @since 1.5.0
 */
do_action( 'wpforms_settings_saved' );

Rule 3: Hook PHPDoc must have @since tag with a valid version.

Rule 4: Hook PHPDoc must have @param tags matching the number of arguments passed to the hook (excluding the hook name itself).

// ✅ CORRECT - 2 arguments, 2 @param tags
/**
 * Filters the settings data.
 *
 * @since 1.5.0
 *
 * @param array $settings The settings data.
 * @param int   $user_id  The user ID.
 */
apply_filters( 'wpforms_settings', $settings, $user_id );

// ❌ WRONG - 2 arguments but only 1 @param tag
/**
 * Filters the settings data.
 *
 * @since 1.5.0
 *
 * @param array $settings The settings data.
 */
apply_filters( 'wpforms_settings', $settings, $user_id );

Rule 5: Hook @param tags must be aligned (types, variable names, and descriptions should line up).

// ✅ CORRECT - Properly aligned
/**
 * @param array  $settings  The settings data.
 * @param int    $user_id   The user ID.
 * @param string $context   The context string.
 */

// ❌ WRONG - Not aligned
/**
 * @param array $settings The settings data.
 * @param int $user_id The user ID.
 * @param string $context The context string.
 */

Rule 6: Hook @param descriptions must end with punctuation.

Rule 7: For deprecated hooks, use do_action_deprecated() or apply_filters_deprecated() and include @deprecated tag.

// ✅ CORRECT
/**
 * Fires when user is processed.
 *
 * @since 1.0.0
 * @deprecated 2.0.0 Use wpforms_user_process_v2 instead.
 *
 * @param int $user_id User ID.
 */
do_action_deprecated( 'wpforms_user_process', [ $user_id ], '2.0.0', 'wpforms_user_process_v2' );

// ❌ WRONG - Using regular do_action for deprecated hook
/**
 * @since 1.0.0
 * @deprecated 2.0.0
 */
do_action( 'wpforms_user_process', $user_id );

// ❌ WRONG - Using do_action_deprecated without @deprecated tag
/**
 * @since 1.0.0
 */
do_action_deprecated( 'wpforms_user_process', [ $user_id ], '2.0.0' );

Define Constants Documentation

Rule 1: All define() calls must have PHPDoc comment directly above them.

Rule 2: define() PHPDoc must have a short description ending with punctuation.

Rule 3: define() PHPDoc must have @since tag with a valid version.

Rule 4: No empty line should appear after @since tag in define() PHPDoc (different from functions/classes).

// ✅ CORRECT
/**
 * The plugin version number.
 *
 * @since 1.0.0
 */
define( 'WPFORMS_VERSION', '1.8.0' );

// ❌ WRONG - Missing PHPDoc
define( 'WPFORMS_VERSION', '1.8.0' );

// ❌ WRONG - Missing punctuation
/**
 * The plugin version number
 *
 * @since 1.0.0
 */
define( 'WPFORMS_VERSION', '1.8.0' );

// ❌ WRONG - Empty line after @since
/**
 * The plugin version number.
 *
 * @since 1.0.0
 *
 */
define( 'WPFORMS_VERSION', '1.8.0' );

Language Injection Comments

Special comments allowed (won't trigger PHPCS errors):

// ✅ CORRECT - PhpStorm/IntelliJ language injection
// language=JSON
$json = '{"foo": "bar"}';

// ✅ CORRECT - PHPDoc language injection
/**
 * @lang SQL
 */
$sql = "SELECT * FROM users";

Quick Reference Summary

Most Important Rules for Daily Coding

Formatting

  1. Empty lines:

    • After function opening brace
    • Before return statement (unless it's the only statement)
    • After assignments (with exceptions)
    • Before/after switch statements
    • Before case statements after break
    • No empty line before break in switch
  2. No Yoda conditions — Write naturally: $var === 'value'

  3. Short array syntax — Use [] not array()

Documentation

  1. @since tag required on:

    • All functions, classes, interfaces, traits
    • Class properties
    • Constants and define() calls
    • All hooks (do_action/apply_filters)
  2. PHPDoc descriptions must:

    • End with punctuation (., !, ?)
    • Start on the next line after /** opening
    • Not be on the same line as /**
  3. Hook documentation:

    • PHPDoc required directly above all hooks
    • Must have description with punctuation
    • Must have @since tag
    • Must have @param tags matching argument count
    • @param tags must be aligned
    • Use *_deprecated() functions with @deprecated tag for old hooks
  4. Define constants:

    • Must have PHPDoc with description
    • Must have @since tag
    • No empty line after @since

Code Organization

  1. Hooks in hooks() method - All add_action()/add_filter() calls must be inside hooks() method in classes

  2. Text domain required - Always specify domain in i18n functions

  3. Use statements over fully qualified names — Import classes at top

  4. Hook names - Should start with the class name in snake_case

Quality

  1. Translator comments - Must have colon, description, and punctuation

  2. Complexity limits — Keep functions under 6 complexity, 3 nesting levels

  3. Tag spacing — Only one space after @param and @return