WordPress Coding Standards enforce consistent code style across PHP files in WordPress plugins and themes. PHP_CodeSniffer applies those standards automatically, flagging violations like wrong indentation, missing documentation, unsafe function calls, and naming convention issues. Running it manually before every commit works, but automated enforcement on every push catches problems before they reach a shared repository and removes the friction of remembering to run it manually. This guide walks through setting up a GitHub Actions workflow that runs PHP_CodeSniffer with WordPress Coding Standards on every push and pull request. For broader developer workflow context, see the Resources hub.
What PHP_CodeSniffer With WordPress Standards Checks
The WordPress Coding Standards sniff set covers more than formatting. It enforces naming conventions for functions, variables, and class names. It flags direct database queries that bypass WordPress's data preparation functions. It checks for proper use of nonces and capability checks on form submissions and AJAX handlers. It enforces comment requirements on functions and classes. It flags deprecated functions that have modern replacements.
For plugin development, the strict checks matter because plugins run in shared environments where code quality directly affects security and compatibility. A missed sanitization check or an undocumented function is not just a style issue; it is a potential problem that can affect sites running the plugin. Automated enforcement makes it harder to accidentally ship code with those issues.
Prerequisites
The workflow requires PHP and Composer to be available in the GitHub Actions runner, which the standard ubuntu runner handles. You need a composer.json file in your repository that specifies PHP_CodeSniffer and WordPress Coding Standards as development dependencies. The phpcs.xml configuration file in the repository root tells PHP_CodeSniffer which standards to apply, which files to check, and which rules to ignore.
// composer.json (require-dev section)
"require-dev": {
"squizlabs/php_codesniffer": "^3.7",
"wp-coding-standards/wpcs": "^3.0",
"phpcompatibility/phpcompatibility-wp": "*",
"dealerdirect/phpcodesniffer-composer-installer": "^1.0"
}
The dealerdirect/phpcodesniffer-composer-installer package automatically registers installed coding standards with PHP_CodeSniffer, which saves the manual step of running phpcs --config-set installed_paths.
The phpcs.xml Configuration
The phpcs.xml file at the repository root controls what PHP_CodeSniffer checks. A minimal configuration for a WordPress plugin looks like this:
<?xml version="1.0"?>
<ruleset name="Plugin Standards">
<description>WordPress Coding Standards for the plugin.</description>
<!-- Check all PHP files in the plugin. -->
<file>.</file>
<!-- Exclude generated files and vendor directory. -->
<exclude-pattern>/vendor/*</exclude-pattern>
<exclude-pattern>/node_modules/*</exclude-pattern>
<exclude-pattern>*.min.js</exclude-pattern>
<exclude-pattern>*.min.css</exclude-pattern>
<!-- Use WordPress standards. -->
<rule ref="WordPress">
<!-- Tabs-based indentation is the WordPress standard. -->
</rule>
<!-- Set minimum PHP version for compatibility checks. -->
<config name="minimum_supported_wp_version" value="6.0"/>
<config name="testVersion" value="7.4-"/>
</ruleset>
Adjust the minimum_supported_wp_version to match the minimum WordPress version your plugin supports. The testVersion range in the PHP compatibility check should match your plugin's minimum PHP requirement.
The GitHub Actions Workflow File
Create the workflow file at .github/workflows/coding-standards.yml. This file defines the trigger events and the steps that run PHP_CodeSniffer.
name: WordPress Coding Standards
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
jobs:
coding-standards:
name: PHP CodeSniffer
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
coverage: none
tools: composer
- name: Get Composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache Composer packages
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader
- name: Run PHP_CodeSniffer
run: ./vendor/bin/phpcs
The workflow triggers on pushes and pull requests to main and develop. Add or remove branches as needed for your workflow. The Composer cache step uses the composer.lock file hash as the cache key, so the cache invalidates when dependencies change and stays warm between runs when they do not.
What the Workflow Output Looks Like
When PHP_CodeSniffer finds violations, the workflow fails and the output in the Actions tab shows the specific files, line numbers, and rule names for each violation. A typical output entry looks like this:
FILE: includes/class-payment-gateway.php
----------------------------------------------------------------------
FOUND 3 ERRORS AFFECTING 3 LINES
----------------------------------------------------------------------
42 | ERROR | Missing wp_nonce_field() call.
87 | ERROR | Direct database call. Use $wpdb->prepare() instead.
103 | ERROR | Function comment is missing @param tag for $order_id.
---------------------------------------------------------------------- Each error is actionable. Fix the nonce, use the prepared statement, add the documentation comment. Run the workflow again. A green check means the code meets the standard.
Handling False Positives and Rule Exceptions
WordPress Coding Standards is strict and there are situations where a rule produces a false positive or where a specific file justifiably violates a rule. Rather than suppressing the entire ruleset, use inline comments to suppress specific rules on specific lines.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Legacy migration, reviewed.
$results = $wpdb->get_results( $query );
// phpcs:disable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
// External API response uses camelCase property names.
$transaction_id = $response->transactionId;
// phpcs:enable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
Keep suppression comments narrow and add an explanation. A suppression without a comment makes it harder to audit whether the exception is still valid when the code changes. For whole file exclusions, add the file pattern to phpcs.xml as an exclude-pattern rather than suppressing inline. Use file exclusions for vendor files, generated files, and files outside your control. Use inline suppression for intentional exceptions in your own code.
Integrating With Pull Request Reviews
The workflow integrates naturally with pull request reviews. Any PR that introduces coding standard violations fails the check and the failure is visible in the PR interface. This puts the responsibility on the contributor to fix the issues before the PR is merged. For teams with multiple contributors, this removes the need for manual code style review and ensures all merged code meets the standard.
For solo projects, the workflow still catches issues that manual review misses, especially in edited files where focus is on the new logic rather than the surrounding code's compliance. The automated check is a safety net rather than a gate.
For more on WordPress and WooCommerce development patterns, see the WooCommerce hub and the full resource index.