PHP_CodeSniffer

Code QualityLinterPHPDevOpsStatic AnalysisPSRCoding Standards

DevOps Tool

PHP_CodeSniffer

Overview

PHP_CodeSniffer is a PHP-specific static code analysis tool that tokenizes PHP files and detects violations of defined coding standards. It achieves code quality checking compliant with standards like PSR-12, consisting of two tools: phpcs for violation detection and phpcbf for automatic correction. Supporting PHP, JavaScript, and CSS files, it includes signature verification through GitHub Artifact Attestations. The 2025 version continues development under the new PHPCSStandards organization with enhanced security, evolving as the standard code quality management tool for PHP development.

Details

PHP_CodeSniffer (PHPCS) was originally developed by Squiz Labs but is now maintained by the PHPCSStandards organization as of 2025. It serves as the industry-standard tool for enforcing coding standard compliance such as PSR-12 and maintaining code quality consistency in PHP development.

Key Features

  • Comprehensive Code Analysis: Tokenization and analysis of PHP, JavaScript, and CSS files
  • Full PSR Standards Support: Compliance with PHP standards like PSR-1, PSR-2, PSR-12
  • Dual Tool Architecture: Combination of phpcs (detection) and phpcbf (auto-correction)
  • Custom Standards Support: Definition and application of custom coding standards
  • Enhanced Security: PHAR signing and GitHub Artifact Attestations verification
  • IDE Integration: Support for major editors like PhpStorm, VS Code
  • CI/CD Integration: Automated quality checking in Jenkins, GitHub Actions, etc.
  • Flexible Configuration: Detailed customization via phpcs.xml configuration files
  • Rich Reporting: Various output formats including XML, JSON, HTML

2025 New Features

  • New Organization Management: Active maintenance by PHPCSStandards organization
  • Signature Verification: PHAR verification through GitHub Artifact Attestations
  • Cross-version Compatibility: Integration of PHP Compatibility Coding Standard
  • Enhanced Reporting: XML summary reports optimized for CI/CD pipelines

Pros and Cons

Pros

  • Automatic enforcement of industry-standard coding conventions in PHP development
  • Quality assurance for modern PHP code through PSR-12 compliance
  • Significant reduction in manual correction work through phpcbf auto-correction
  • Flexibility to support various coding standards
  • Real-time code quality feedback through IDE integration
  • Automated quality gates through CI/CD integration
  • Support for enterprise/project-specific requirements through custom standard definition
  • Quality status visualization through rich reporting formats
  • Reliable tool distribution through enhanced security
  • Unified quality management through PHP, JavaScript, CSS support

Cons

  • Introduction burden from massive warnings in large legacy codebases
  • Risk of development speed reduction due to excessive standard enforcement
  • Complexity and learning costs of custom standard creation
  • Long execution times (especially for large projects)
  • Confusion and migration work due to original repository abandonment
  • Limitation to unified tool usage in multilingual projects due to PHP-only focus
  • Incompleteness of some auto-corrections (manual verification required)
  • Need for coding standard consensus formation within teams
  • Maintenance burden from configuration file complexity
  • Limited support for older PHP versions

Reference Links

Code Examples

Installation and Basic Setup

Installation via Composer

# Project local installation (recommended)
composer require --dev squizlabs/php_codesniffer

# Global installation
composer global require squizlabs/php_codesniffer

# Specific version specification
composer require --dev "squizlabs/php_codesniffer:^3.10"

# Installation verification
./vendor/bin/phpcs --version
./vendor/bin/phpcbf --version

# Global installation verification
phpcs --version
phpcbf --version

Installation via PHAR

# Download PHAR files
curl -OL https://phars.phpcodesniffer.com/phpcs.phar
curl -OL https://phars.phpcodesniffer.com/phpcbf.phar

# Grant execution permissions
chmod +x phpcs.phar phpcbf.phar

# Place in system PATH
sudo mv phpcs.phar /usr/local/bin/phpcs
sudo mv phpcbf.phar /usr/local/bin/phpcbf

# GitHub Artifact Attestations verification (2025 new feature)
gh attestation verify phpcs.phar --owner PHPCSStandards

# Signature verification
gpg --verify phpcs.phar.asc phpcs.phar

Basic Execution Methods

# Basic execution
./vendor/bin/phpcs src/

# Check specific file
./vendor/bin/phpcs src/User.php

# Check with PSR-12 standard
./vendor/bin/phpcs --standard=PSR12 src/

# Combination of multiple standards
./vendor/bin/phpcs --standard=PSR12,Generic.Files.LineLength src/

# Detailed output
./vendor/bin/phpcs -v src/

# Progress display
./vendor/bin/phpcs -p src/

# Colored output
./vendor/bin/phpcs --colors src/

# Specific extensions only
./vendor/bin/phpcs --extensions=php,js src/

# Specify exclusion patterns
./vendor/bin/phpcs --ignore=*/tests/*,*/vendor/* src/

# Specify report format
./vendor/bin/phpcs --report=summary src/
./vendor/bin/phpcs --report=xml src/
./vendor/bin/phpcs --report=json src/

Auto-correction (phpcbf)

# Basic auto-correction
./vendor/bin/phpcbf src/

# Correction with specific standard
./vendor/bin/phpcbf --standard=PSR12 src/

# Dry run (verify changes)
./vendor/bin/phpcbf --dry-run src/

# Correction with detailed output
./vendor/bin/phpcbf -v src/

# Specific file only correction
./vendor/bin/phpcbf src/Models/User.php

# Correction with backup creation
./vendor/bin/phpcbf --suffix=.bak src/

Configuration File (phpcs.xml)

<?xml version="1.0"?>
<ruleset name="Custom PHP Standards">
    <description>Project PHP Coding Standards</description>

    <!-- Target files -->
    <file>src</file>
    <file>tests</file>

    <!-- Exclusion patterns -->
    <exclude-pattern>*/vendor/*</exclude-pattern>
    <exclude-pattern>*/node_modules/*</exclude-pattern>
    <exclude-pattern>*.min.js</exclude-pattern>
    <exclude-pattern>*/migrations/*</exclude-pattern>

    <!-- PSR-12 basic standards -->
    <rule ref="PSR12">
        <!-- Exclude specific rules -->
        <exclude name="PSR12.Files.FileHeader.SpacingAfterBlock"/>
    </rule>

    <!-- Additional standards -->
    <rule ref="Generic.Files.LineLength">
        <properties>
            <property name="lineLimit" value="120"/>
            <property name="absoluteLineLimit" value="150"/>
        </properties>
    </rule>

    <rule ref="Generic.Metrics.CyclomaticComplexity">
        <properties>
            <property name="complexity" value="10"/>
            <property name="absoluteComplexity" value="15"/>
        </properties>
    </rule>

    <rule ref="Generic.Metrics.NestingLevel">
        <properties>
            <property name="nestingLevel" value="5"/>
            <property name="absoluteNestingLevel" value="8"/>
        </properties>
    </rule>

    <!-- Custom rules -->
    <rule ref="Squiz.PHP.ForbiddenFunctions">
        <properties>
            <property name="forbiddenFunctions" type="array">
                <element key="eval" value="null"/>
                <element key="exec" value="null"/>
                <element key="shell_exec" value="null"/>
                <element key="var_dump" value="null"/>
                <element key="print_r" value="null"/>
            </property>
        </properties>
    </rule>

    <!-- Configuration options -->
    <arg name="colors"/>
    <arg name="parallel" value="8"/>
    <arg value="p"/>

    <!-- PHP version setting -->
    <config name="php_version" value="80100"/>
</ruleset>

Laravel Project Configuration

<?xml version="1.0"?>
<ruleset name="Laravel Project Standards">
    <description>Laravel Project Coding Standards</description>

    <!-- Laravel directory structure -->
    <file>app</file>
    <file>config</file>
    <file>database</file>
    <file>routes</file>
    <file>tests</file>

    <!-- Laravel exclusion patterns -->
    <exclude-pattern>*/vendor/*</exclude-pattern>
    <exclude-pattern>*/storage/*</exclude-pattern>
    <exclude-pattern>*/bootstrap/cache/*</exclude-pattern>
    <exclude-pattern>*/node_modules/*</exclude-pattern>
    <exclude-pattern>*/public/hot</exclude-pattern>
    <exclude-pattern>*/public/storage</exclude-pattern>
    <exclude-pattern>*/.phpstorm.meta.php</exclude-pattern>
    <exclude-pattern>*/_ide_helper.php</exclude-pattern>

    <!-- PSR-12 + Laravel specific adjustments -->
    <rule ref="PSR12"/>

    <!-- Laravel Eloquent model adjustments -->
    <rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
        <exclude-pattern>*/database/migrations/*</exclude-pattern>
        <exclude-pattern>*/database/seeders/*</exclude-pattern>
    </rule>

    <!-- Blade template configuration -->
    <rule ref="Generic.Files.LineLength">
        <exclude-pattern>*.blade.php</exclude-pattern>
    </rule>

    <!-- Laravel Configuration -->
    <rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
    <rule ref="Generic.PHP.ForbiddenFunctions">
        <properties>
            <property name="forbiddenFunctions" type="array">
                <element key="dd" value="null"/>
                <element key="dump" value="null"/>
                <element key="var_dump" value="null"/>
            </property>
        </properties>
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>
</ruleset>

CI/CD Integration Examples

GitHub Actions

# .github/workflows/phpcs.yml
name: PHP CodeSniffer

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  phpcs:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
        tools: composer
        coverage: none
    
    - name: Cache Composer dependencies
      uses: actions/cache@v4
      with:
        path: vendor
        key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
        restore-keys: ${{ runner.os }}-composer-
    
    - name: Install dependencies
      run: composer install --prefer-dist --no-progress --no-suggest
    
    - name: Run PHP CodeSniffer
      run: ./vendor/bin/phpcs --standard=PSR12 --report=github src/
    
    - name: Auto-fix with PHP CBF (if push to main)
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: |
        ./vendor/bin/phpcbf --standard=PSR12 src/
        if [ -n "$(git status --porcelain)" ]; then
          git config --local user.email "[email protected]"
          git config --local user.name "GitHub Action"
          git add -A
          git commit -m "Auto-fix PHP CodeSniffer violations" -a
          git push
        fi

GitLab CI/CD

# .gitlab-ci.yml
variables:
  COMPOSER_CACHE_DIR: ".composer-cache"

cache:
  key: composer-cache
  paths:
    - .composer-cache/

stages:
  - quality

phpcs:
  stage: quality
  image: php:8.2-alpine
  before_script:
    - apk add --no-cache git
    - curl -sS https://getcomposer.org/installer | php
    - php composer.phar install --prefer-dist --no-progress --no-suggest
  script:
    - ./vendor/bin/phpcs --standard=PSR12 --report=gitlab src/
    - ./vendor/bin/phpcs --standard=PSR12 --report=xml --report-file=phpcs-report.xml src/
  artifacts:
    reports:
      codequality: phpcs-report.xml
    paths:
      - phpcs-report.xml
    expire_in: 1 week
  only:
    - main
    - develop
    - merge_requests

IDE Integration Configuration

PhpStorm Configuration

# PhpStorm configuration steps
# File → Settings → PHP → Quality Tools → PHP_CodeSniffer

# Configuration path:
# Local: ./vendor/bin/phpcs
# Global: /usr/local/bin/phpcs

# Coding standard: PSR12
# or custom: ./phpcs.xml

# Auto-execution configuration
# File → Settings → Editor → Inspections → PHP → Quality tools
# Check PHP_CodeSniffer validation

# External Tools configuration
# File → Settings → Tools → External Tools → Add
Name: PHP_CodeSniffer
Description: PHP Code quality checker
Program: $ProjectFileDir$/vendor/bin/phpcs
Arguments: --standard=PSR12 $FilePath$
Working Directory: $ProjectFileDir$

Name: PHP_CBF
Description: PHP Code auto-fixer
Program: $ProjectFileDir$/vendor/bin/phpcbf
Arguments: --standard=PSR12 $FilePath$
Working Directory: $ProjectFileDir$

VS Code Configuration

// .vscode/settings.json
{
  "php.validate.executablePath": "/usr/bin/php",
  "phpcs.enable": true,
  "phpcs.executablePath": "./vendor/bin/phpcs",
  "phpcs.standard": "PSR12",
  "phpcbf.enable": true,
  "phpcbf.executablePath": "./vendor/bin/phpcbf",
  "phpcbf.standard": "PSR12",
  "phpcbf.onsave": true,
  "[php]": {
    "editor.defaultFormatter": "bmewburn.vscode-intelephense-client",
    "editor.formatOnSave": true,
    "editor.rulers": [120]
  },
  "files.associations": {
    "*.php": "php"
  }
}

// tasks.json for VS Code tasks
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "PHP CodeSniffer",
      "type": "shell",
      "command": "./vendor/bin/phpcs",
      "args": ["--standard=PSR12", "src/"],
      "group": "build",
      "presentation": {
        "echo": true,
        "reveal": "always",
        "focus": false,
        "panel": "shared"
      }
    },
    {
      "label": "PHP CBF Auto-fix",
      "type": "shell",
      "command": "./vendor/bin/phpcbf",
      "args": ["--standard=PSR12", "src/"],
      "group": "build"
    }
  ]
}

Custom Sniff (Custom Rules) Creation

<?php
// CustomSniffs/Sniffs/Functions/ForbiddenFunctionsSniff.php

namespace CustomSniffs\Sniffs\Functions;

use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;

class ForbiddenFunctionsSniff implements Sniff
{
    /**
     * Forbidden functions list
     */
    public $forbiddenFunctions = [
        'eval',
        'exec',
        'shell_exec',
        'system',
        'passthru',
        'var_dump',
        'print_r',
        'debug_print_backtrace',
    ];

    /**
     * Tokens to monitor
     */
    public function register()
    {
        return [T_STRING];
    }

    /**
     * Token processing
     */
    public function process(File $phpcsFile, $stackPtr)
    {
        $tokens = $phpcsFile->getTokens();
        $functionName = $tokens[$stackPtr]['content'];

        if (!in_array($functionName, $this->forbiddenFunctions)) {
            return;
        }

        // Check if it's a function call
        $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
        if ($tokens[$nextToken]['code'] !== T_OPEN_PARENTHESIS) {
            return;
        }

        $error = 'Forbidden function "%s" found. Use alternative approach.';
        $data = [$functionName];
        $phpcsFile->addError($error, $stackPtr, 'Found', $data);
    }
}
<!-- Custom sniff loading configuration -->
<?xml version="1.0"?>
<ruleset name="Custom Standards">
    <description>Custom Coding Standards</description>

    <!-- Custom sniff directory -->
    <config name="installed_paths" value="./CustomSniffs"/>

    <!-- Basic standards -->
    <rule ref="PSR12"/>

    <!-- Custom rules -->
    <rule ref="CustomSniffs.Functions.ForbiddenFunctions">
        <properties>
            <property name="forbiddenFunctions" type="array">
                <element key="eval" value="Dangerous function"/>
                <element key="var_dump" value="Debug function"/>
            </property>
        </properties>
    </rule>
</ruleset>

Advanced Usage and Troubleshooting

# Performance optimization
./vendor/bin/phpcs --parallel=8 src/

# Show only errors
./vendor/bin/phpcs --severity=error src/

# Warning level and above only
./vendor/bin/phpcs --warning-severity=0 src/

# Use cache
./vendor/bin/phpcs --cache=.phpcs-cache src/

# Debug output
./vendor/bin/phpcs -vvv src/User.php

# Check installed standards
./vendor/bin/phpcs -i

# Detailed rule information
./vendor/bin/phpcs --explain src/User.php

# Statistics output
./vendor/bin/phpcs --report=info src/

# git hooks integration
# .git/hooks/pre-commit
#!/bin/bash
FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep '\.php$')
if [ -n "$FILES" ]; then
    ./vendor/bin/phpcs --standard=PSR12 $FILES
    if [ $? -ne 0 ]; then
        echo "PHP CodeSniffer found violations. Please fix and try again."
        exit 1
    fi
fi

# Makefile integration
.PHONY: phpcs phpcbf quality

phpcs:
	./vendor/bin/phpcs --standard=PSR12 src/ tests/

phpcbf:
	./vendor/bin/phpcbf --standard=PSR12 src/ tests/

quality: phpcs
	@echo "Code quality check completed"