Skip to content

代码风格工具

代码风格工具能够自动化执行编码标准检查和格式化,确保团队所有成员提交的代码风格一致。PHP 生态中主流的代码风格工具包括 PHP-CS-Fixer、PHP_CodeSniffer 和 IDE 内置格式化功能。本节将详细介绍这些工具的配置、使用方法以及 CI 集成方案。

前置知识

阅读本节前,建议先了解:命名规范PSR-12 编码标准

基础概念

为什么需要代码风格工具

  • 一致性:消除团队成员之间的风格差异
  • 自动化:减少 Code Review 中关于格式的讨论
  • 质量保障:在 CI 中自动检查,防止不符合规范的代码合并
  • 可维护性:统一的代码风格降低维护成本

PHP 代码风格工具生态

工具用途特点
PHP-CS-Fixer自动格式化代码基于 Symfony 团队维护,规则丰富
PHP_CodeSniffer (phpcs)检查代码规范支持自定义 sniff,广泛使用
PHP_CodeSniffer + PHP_CBF检查并自动修复phpcs 的修复工具
PHPStormIDE 内置格式化可视化配置,实时格式化
PintLaravel 专用格式化基于 PHP-CS-Fixer,开箱即用

PHP-CS-Fixer

安装与基本使用

bash
# 全局安装
composer global require friendsofphp/php-cs-fixer

# 项目安装(推荐)
composer require --dev friendsofphp/php-cs-fixer

# 验证安装
php-cs-fixer --version

基本命令

bash
# 查看当前可用的规则集
php-cs-fixer describe

# 查看特定规则的详情
php-cs-fixer describe single_class_insert_newline_before_brace

# 查看所有可用的规则
php-cs-fixer describe --list

# 干运行:仅显示会做哪些修改,不实际修改文件
php-cs-fixer fix --dry-run --diff

# 格式化指定文件
php-cs-fixer fix src/Service/UserService.php

# 格式化指定目录
php-cs-fixer fix src/

# 格式化并显示修改详情
php-cs-fixer fix src/ --diff --verbose

# 使用配置文件
php-cs-fixer fix --config=.php-cs-fixer.php

配置文件详解

PHP-CS-Fixer v3.x 使用 PHP 配置文件 .php-cs-fixer.php(位于项目根目录):

php
<?php
declare(strict_types=1);

use PhpCsFixer\Config;
use PhpCsFixer\Finder;

$finder = Finder::create()
    ->in([
        __DIR__ . '/src',
        __DIR__ . '/tests',
    ])
    ->name('*.php')
    ->exclude([
        'vendor',
        'storage',
        'cache',
        'bootstrap/cache',
    ])
    ->notName('*.blade.php')      // 排除 Blade 模板
    ->ignoreDotFiles(true)
    ->ignoreVCS(true);

return (new Config())
    ->setRiskyAllowed(true)
    ->setRules([
        // ============================
        // PSR-12 基础规则
        // ============================
        '@PSR12' => true,
        '@PSR12:risky' => true,

        // ============================
        // 数组语法
        // ============================
        'array_syntax' => ['syntax' => 'short'],  // 使用 [] 而非 array()

        // ============================
        // 导入排序
        // ============================
        'ordered_imports' => [
            'sort_algorithm' => 'alpha',
            'imports_order' => ['class', 'function', 'const'],
        ],
        'no_unused_imports' => true,               // 移除未使用的导入

        // ============================
        // 类型声明
        // ============================
        'declare_strict_types' => true,            // 强制 declare(strict_types=1)
        'fully_qualified_strict_types' => true,     // 未使用的 use 导入自动移除
        'no_superfluous_phpdoc_tags' => false,      // 保留 PHPDoc 类型标签

        // ============================
        // 空白与格式
        // ============================
        'blank_line_after_opening_tag' => true,
        'blank_line_before_statement' => [
            'statements' => ['return', 'throw', 'try'],
        ],
        'no_blank_lines_after_class_opening' => true,
        'no_blank_lines_after_phpdoc' => true,
        'no_extra_blank_lines' => [
            'tokens' => [
                'extra',
                'throw',
                'use',
            ],
        ],
        'single_blank_line_at_eof' => true,

        // ============================
        // 空格与缩进
        // ============================
        'method_chaining_indentation' => true,
        'array_indentation' => true,
        'indentation_type' => 'spaces',
        'no_spaces_around_offset' => true,

        // ============================
        // 运算符与赋值
        // ============================
        'binary_operator_spaces' => [
            'default' => 'single_space',
            'operators' => [
                '=>' => 'align',
                '=' => 'align',
            ],
        ],
        'concat_space' => ['spacing' => 'one'],     // 字符串连接符前后各一个空格
        'ternary_operator_spaces' => true,
        'unary_operator_spaces' => true,

        // ============================
        // 命名空间与 use
        // ============================
        'blank_lines_before_namespace' => true,
        'single_blank_line_before_namespace' => true,
        'clean_namespace' => true,
        'no_leading_namespace_slash' => true,

        // ============================
        // 控制结构
        // ============================
        'control_structure_braces' => true,
        'control_structure_continuation_position' => [
            'position' => 'next_line',
        ],
        'elseif' => true,                           // 使用 elseif 而非 else if
        'no_alternative_syntax' => true,             // 禁止替代语法
        'no_unneeded_control_parentheses' => true,
        'no_unneeded_curly_braces' => true,
        'simplified_if_return' => true,

        // ============================
        // 函数与方法
        // ============================
        'function_declaration' => [
            'closure_function_spacing' => 'one',
        ],
        'method_argument_space' => [
            'on_multiline' => 'ensure_fully_multiline',
        ],
        'no_trailing_comma_in_singleline_function_call' => true,
        'trailing_comma_in_multiline' => [
            'elements' => ['arrays', 'parameters', 'arguments', 'match'],
        ],
        'return_type_declaration' => [
            'space_before' => 'one',
        ],
        'void_return' => false,

        // ============================
        // 类与接口
        // ============================
        'class_attributes_order' => [
            'order' => [
                'use_trait',
                'case',
                'constant_public',
                'constant_protected',
                'constant_private',
                'property_public',
                'property_protected',
                'property_private',
                'constructor',
                'method_public',
                'method_protected',
                'method_private',
                'magic_method',
            ],
        ],
        'no_null_property_initialization' => true,
        'self_static_accessor' => true,

        // ============================
        // 现代化 PHP
        // ============================
        'modernize_strpos' => true,                 // strpos() === 0 → str_starts_with()
        'modernize_types_casting' => true,           // (string) → strval()
        'no_alias_functions' => true,               // sizeof() → count()
        'pow_to_exponentiation' => true,             // pow() → ** 运算符

        // ============================
        // 注释
        // ============================
        'single_line_comment_spacing' => true,
        'multiline_comment_opening_closing' => true,
        'phpdoc_add_missing_param_annotation' => false,
        'phpdoc_order' => true,                      // PHPDoc 标签排序
        'phpdoc_separation' => true,
        'phpdoc_single_line_var_spacing' => true,
        'phpdoc_trim' => true,
        'phpdoc_trim_consecutive_blank_line_separation' => true,
        'phpdoc_types' => true,                      // PHPDoc 类型标准化
        'phpdoc_var_without_name' => true,
        'no_empty_phpdoc' => true,
    ])
    ->setFinder($finder)
    ->setLineEnding("\n")
    ->setIndent('    ');  // 4 空格缩进

逐行格式化示例

php
<?php
// ===== 格式化前 =====
namespace App\Service;
use App\Model\User;
use App\Repository\UserRepository;
use function count;
class UserService{
    private $repository;
    public function __construct(UserRepository $repository){
        $this->repository=$repository;
    }
    public function findActiveUsers($limit=null){
        $users=$this->repository->findBy(['isActive'=>true],['createdAt'=>'DESC'],$limit);
        $count=count($users);
        if($count==0){return [];}
        return $users;
    }
    public function updateUser($id,$data){
        $user=$this->repository->find($id);
        if(!$user){throw new \Exception("User not found");}
        $user->setName($data['name']??'Unknown');
        $user->setEmail($data['email']);
        $this->repository->save($user);
        return $user;
    }
}

// ===== 格式化后 =====
<?php

declare(strict_types=1);

namespace App\Service;

use App\Model\User;
use App\Repository\UserRepository;

class UserService
{
    private UserRepository $repository;

    public function __construct(UserRepository $repository)
    {
        $this->repository = $repository;
    }

    public function findActiveUsers(?int $limit = null): array
    {
        $users = $this->repository->findBy(
            ['isActive' => true],
            ['createdAt' => 'DESC'],
            $limit,
        );

        $count = count($users);

        if ($count === 0) {
            return [];
        }

        return $users;
    }

    public function updateUser(int $id, array $data): User
    {
        $user = $this->repository->find($id);

        if (!$user) {
            throw new \Exception('User not found');
        }

        $user->setName($data['name'] ?? 'Unknown');
        $user->setEmail($data['email']);
        $this->repository->save($user);

        return $user;
    }
}

PHP_CodeSniffer (phpcs)

安装与基本使用

bash
# 全局安装
composer global require squizlabs/php_codesniffer

# 项目安装
composer require --dev squizlabs/php_codesniffer

# 验证安装
phpcs --version
phpcbf --version  # Code Beautifier and Fixer

# 列出内置标准
phpcs -i

# 查看特定标准的规则详情
phpcs --standard=PSR12 --report=full src/

基本命令

bash
# 使用 PSR-12 标准检查
phpcs --standard=PSR12 src/

# 使用自定义标准检查
phpcs --standard=phpcs.xml src/

# 检查指定文件
phpcs --standard=PSR12 src/Service/UserService.php

# 仅显示错误摘要
phpcs --standard=PSR12 --report=summary src/

# 生成 XML 报告(适用于 CI)
phpcs --standard=PSR12 --report=checkstyle --report-file=checkstyle.xml src/

# 自动修复可修复的违规
phpcbf --standard=PSR12 src/

# 显示每个违规的行列号和修复建议
phpcs --standard=PSR12 -s src/

自定义规则集 (phpcs.xml)

在项目根目录创建 phpcs.xml

xml
<?xml version="1.0"?>
<ruleset name="MyProject Coding Standard">
    <description>
        MyProject 编码标准
        基于 PSR-12,添加自定义规则
    </description>

    <!-- 继承 PSR-12 标准 -->
    <rule ref="PSR12"/>

    <!-- 扫描的文件和目录 -->
    <file>./src</file>
    <file>./tests</file>

    <!-- 排除的目录 -->
    <exclude-pattern>*/vendor/*</exclude-pattern>
    <exclude-pattern>*/cache/*</exclude-pattern>
    <exclude-pattern>*/storage/*</exclude-pattern>
    <exclude-pattern>*.blade.php</exclude-pattern>

    <!-- 文件扩展名 -->
    <arg name="extensions" value="php"/>

    <!-- 显示进度 -->
    <arg value="ps"/>

    <!-- 颜色输出 -->
    <arg name="colors"/>

    <!-- ==================== -->
    <!-- 安全性相关规则       -->
    <!-- ==================== -->

    <!-- 禁止使用 eval() -->
    <rule ref="Generic.PHP.ForbiddenFunctions">
        <properties>
            <property name="forbidden_functions" type="array"
                value="eval,assert,system,exec,passthru,shell_exec,proc_open,popen"
            />
        </properties>
    </rule>

    <!-- 禁止使用 var 关键字声明属性 -->
    <rule ref="Generic.PHP.NoSilencedErrors"/>

    <!-- ==================== -->
    <!-- 代码质量规则          -->
    <!-- ==================== -->

    <!-- 代码行长度限制 -->
    <rule ref="Generic.Files.LineLength">
        <properties>
            <property name="lineLimit" value="120"/>
            <property name="absoluteLineLimit" value="150"/>
        </properties>
    </rule>

    <!-- 函数参数数量限制 -->
    <rule ref="Generic.CodeAnalysis.MetricNComplexity">
        <properties>
            <property name="complexity" value="10"/>
        </properties>
    </rule>

    <!-- 类方法数量限制 -->
    <rule ref="Generic.CodeAnalysis.TooManyPublicMethods">
        <properties>
            <property name="maxMethods" value="20"/>
        </properties>
    </rule>

    <!-- 禁止超长函数 -->
    <rule ref="Generic.CodeAnalysis.TooManyPublicMethods">
        <properties>
            <property name="maxMethods" value="20"/>
        </properties>
    </rule>

    <!-- ==================== -->
    <!-- 命名规范检查          -->
    <!-- ==================== -->

    <!-- 类名必须是 PascalCase -->
    <rule ref="PEAR.NamingConventions.ValidClassName">
        <properties>
            <property name="allowed" value="A-Za-z0-9_"/>
        </properties>
    </rule>

    <!-- 函数/方法名必须是 camelCase -->
    <rule ref="PSR2.Methods.MethodDeclaration">
        <properties>
            <property name="requireMultiple" value="true"/>
        </properties>
    </rule>

    <!-- 常量必须是 UPPER_SNAKE_CASE -->
    <rule ref="Generic.NamingConventions.UpperCaseConstantName"/>

    <!-- 变量名检查 -->
    <rule ref="Generic.NamingConventions.CamelCapsVariableName">
        <properties>
            <property name="strict" value="true"/>
        </properties>
    </rule>

    <!-- ==================== -->
    <!-- 文档注释检查          -->
    <!-- ==================== -->

    <!-- 类必须有 PHPDoc 注释 -->
    <rule ref="PEAR.Commenting.ClassComment"/>

    <!-- 文件必须有头部注释 -->
    <rule ref="PEAR.Commenting.FileComment"/>

    <!-- 函数必须有 PHPDoc 注释 -->
    <rule ref="Squiz.Commenting.FunctionComment">
        <properties>
            <property name="minimumLinesBetweenTags" value="1"/>
        </properties>
    </rule>

    <!-- ==================== -->
    <!-- 安全相关额外检查      -->
    <!-- ==================== -->

    <!-- SQL 注入防护提醒 -->
    <rule ref="Generic.PHP.BacktickOperator"/>

    <!-- 禁止直接使用 $_GET/$_POST/$_REQUEST -->
    <rule ref="MySource.PHP.GetRequestAsString"/>

</ruleset>

自定义 Sniff

php
<?php
declare(strict_types=1);

namespace MyProject\Sniffs\Commenting;

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

/**
 * 检查类属性是否有类型声明(PHP 7.4+)
 */
class TypedPropertySniff implements Sniff
{
    /**
     * 注册监听的 token
     */
    public function register(): array
    {
        return [T_PRIVATE, T_PROTECTED, T_PUBLIC];
    }

    /**
     * 处理发现的 token
     */
    public function process(File $phpcsFile, $stackPtr): void
    {
        $tokens = $phpcsFile->getTokens();

        // 跳过非属性声明(如方法)
        $next = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true);

        if ($next === false) {
            return;
        }

        // 如果下一个是 function,说明是方法声明
        if ($tokens[$next]['code'] === T_FUNCTION) {
            return;
        }

        // 如果下一个是 static,继续查找
        if ($tokens[$next]['code'] === T_STATIC) {
            $next = $phpcsFile->findNext(T_WHITESPACE, $next + 1, null, true);
        }

        // 检查是否是 $ 符号开头(变量声明)
        if ($next === false || $tokens[$next]['code'] !== T_VARIABLE) {
            return;
        }

        // 检查变量名前是否有类型声明
        $prev = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true);

        if ($prev === false || $tokens[$prev]['code'] !== T_STRING) {
            // 没有类型声明,报告警告
            $phpcsFile->addWarning(
                'Property "%s" should have a type declaration (PHP 7.4+)',
                $next,
                'MissingTypedProperty',
                [$tokens[$next]['content']],
            );
        }
    }
}

注册自定义 Sniff:

xml
<?xml version="1.0"?>
<ruleset name="MyProject Coding Standard">
    <description>MyProject 自定义规则</description>

    <!-- 引入自定义 Sniff -->
    <rule ref="MyProject.Commenting.TypedPropertySniff">
        <exclude name="MyProject.Commenting.TypedPropertySniff.MissingTypedProperty"/>
    </rule>
</ruleset>

PHPStorm 格式化配置

代码风格设置

在 PHPStorm 中设置代码风格(Settings/Preferences > Editor > Code Style > PHP):

PSR-12 基础设置:
├── Tabs and Indents
│   ├── Use tab character: false
│   ├── Tab size: 4
│   ├── Indent: 4
│   └── Continuation indent: 8
├── Spaces
│   ├── Before parentheses (declaration): false
│   ├── Within parentheses (declaration): false
│   ├── Before parentheses (call): false
│   ├── Within parentheses (call): false
│   ├── Around operators: true
│   ├── Before keywords: true
│   └── Within: true
├── Wrapping and Braces
│   ├── Braces position: Next line
│   ├── Array initializer: 每个元素一行
│   └── Method call arguments: 每个参数一行(超长时)
├── Blank Lines
│   ├── Before class: 1
│   ├── After class: 1
│   ├── Before method: 1
│   ├── After method: 1
│   └── Keep maximum: 2
└── PHPDoc
    ├── Method params alignment: by name
    ├── Blank line before description: true
    └── Blank line before tags: true

导入与导出代码风格

xml
<!-- PHPStorm 代码风格方案文件 (ProjectSettings/Project.xml) -->
<!-- 可以通过 File > Manage schemes > Export 导出 -->

保存时自动格式化

设置路径:Settings/Preferences > Tools > Actions on Save

☑ Reformat code
☑ Optimize imports
☑ Rearrange code
☑ Run code cleanup

与 PHP-CS-Fixer 集成

在 PHPStorm 中配置外部工具(Settings > Tools > External Tools):

Program: $ProjectFileDir$/vendor/bin/php-cs-fixer
Arguments: fix $FilePath$ --config=.php-cs-fixer.php
Working directory: $ProjectFileDir$

然后配置 File Watcher(Settings > Tools > File Watchers):

File type: PHP
Program: $ProjectFileDir$/vendor/bin/php-cs-fixer
Arguments: fix $FilePath$ --config=.php-cs-fixer.php --quiet
Working directory: $ProjectFileDir$
☑ Auto-save edited files to trigger the watcher

Laravel Pint

Laravel Pint 是基于 PHP-CS-Fixer 的零配置代码格式化工具:

bash
# 安装(Laravel 项目中)
composer require laravel/pint --dev

# 格式化所有文件
./vendor/bin/pint

# 格式化指定文件或目录
./vendor/bin/pint src/Service/

# 仅测试(不修改文件)
./vendor/bin/pint --test

# 使用预设规则集
./vendor/bin/pint --preset laravel
./vendor/bin/pint --preset psr12
./vendor/bin/pint --preset symfony
./vendor/bin/pint --preset django

# 显示详细输出
./vendor/bin/pint -v

自定义 Pint 配置 (pint.json):

json
{
    "preset": "laravel",
    "rules": {
        "declare_strict_types": true,
        "fully_qualified_strict_types": true,
        "no_unused_imports": true,
        "array_syntax": {
            "syntax": "short"
        }
    },
    "exclude": [
        "vendor",
        "storage",
        "database/migrations"
    ],
    "notName": [
        "*.blade.php"
    ]
}

CI 集成

GitHub Actions 配置

yaml
# .github/workflows/php-coding-style.yml
name: PHP Coding Style

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

jobs:
  php-cs-fixer:
    name: PHP-CS-Fixer (Dry Run)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          coverage: none
          tools: composer:v2

      - name: Install dependencies
        run: composer install --prefer-dist --no-progress --no-interaction

      - name: Run PHP-CS-Fixer (Dry Run)
        run: vendor/bin/php-cs-fixer fix --dry-run --diff --config=.php-cs-fixer.php

  phpcs:
    name: PHP CodeSniffer
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          coverage: none

      - name: Install dependencies
        run: composer install --prefer-dist --no-progress --no-interaction

      - name: Run PHP CodeSniffer
        run: vendor/bin/phpcs --standard=phpcs.xml --report=full src/ tests/

  pint:
    name: Laravel Pint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          coverage: none

      - name: Install dependencies
        run: composer install --prefer-dist --no-progress --no-interaction

      - name: Run Pint
        run: vendor/bin/pint --test -v

GitLab CI 配置

yaml
# .gitlab-ci.yml
php-coding-style:
  stage: test
  image: composer:latest
  before_script:
    - composer install --prefer-dist --no-progress --no-interaction
  script:
    - vendor/bin/php-cs-fixer fix --dry-run --diff --config=.php-cs-fixer.php
    - vendor/bin/phpcs --standard=phpcs.xml --report=full src/ tests/
  artifacts:
    reports:
      codequality: checkstyle.xml
    when: on_failure
    paths:
      - checkstyle.xml

Git Pre-commit Hook

bash
#!/bin/bash
# .git/hooks/pre-commit 或使用 composer script

# PHP-CS-Fixer 检查
echo "Running PHP-CS-Fixer..."
./vendor/bin/php-cs-fixer fix --dry-run --diff --config=.php-cs-fixer.php

if [ $? -ne 0 ]; then
    echo ""
    echo "❌ PHP-CS-Fixer found style issues."
    echo "Run 'vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php' to fix them."
    exit 1
fi

# PHP CodeSniffer 检查
echo "Running PHP CodeSniffer..."
./vendor/bin/phpcs --standard=phpcs.xml --report=full src/ tests/

if [ $? -ne 0 ]; then
    echo ""
    echo "❌ PHP CodeSniffer found violations."
    echo "Run 'vendor/bin/phpcbf --standard=phpcs.xml src/ tests/' to fix them."
    exit 1
fi

echo "✅ All code style checks passed."
exit 0

使用 Composer 安装 pre-commit hook:

json
{
    "scripts": {
        "cs-check": "php-cs-fixer fix --dry-run --diff --config=.php-cs-fixer.php",
        "cs-fix": "php-cs-fixer fix --config=.php-cs-fixer.php",
        "phpcs": "phpcs --standard=phpcs.xml src/ tests/",
        "phpcbf": "phpcbf --standard=phpcs.xml src/ tests/",
        "style-check": [
            "@cs-check",
            "@phpcs"
        ],
        "style-fix": [
            "@cs-fix",
            "@phpcbf"
        ],
        "pre-commit": [
            "@style-check"
        ]
    }
}

实战示例

从零搭建代码风格工具链

bash
# 1. 创建项目
composer init -n

# 2. 安装开发依赖
composer require --dev friendsofphp/php-cs-fixer squizlabs/php_codesniffer

# 3. 创建 PHP-CS-Fixer 配置文件
cat > .php-cs-fixer.php << 'EOF'
<?php
declare(strict_types=1);

use PhpCsFixer\Config;
use PhpCsFixer\Finder;

$finder = Finder::create()
    ->in(__DIR__ . '/src')
    ->name('*.php');

return (new Config())
    ->setRiskyAllowed(true)
    ->setRules([
        '@PSR12' => true,
        'declare_strict_types' => true,
        'ordered_imports' => true,
        'no_unused_imports' => true,
        'array_syntax' => ['syntax' => 'short'],
        'trailing_comma_in_multiline' => ['elements' => ['arrays', 'parameters', 'arguments']],
    ])
    ->setFinder($finder)
    ->setLineEnding("\n")
    ->setIndent('    ');
EOF

# 4. 创建 PHP_CodeSniffer 配置文件
cat > phpcs.xml << 'EOF'
<?xml version="1.0"?>
<ruleset name="Project Standard">
    <rule ref="PSR12"/>
    <file>./src</file>
    <rule ref="Generic.Files.LineLength">
        <properties>
            <property name="lineLimit" value="120"/>
        </properties>
    </rule>
</ruleset>
EOF

# 5. 添加 Composer 脚本
composer config scripts.cs-check "php-cs-fixer fix --dry-run --diff"
composer config scripts.cs-fix "php-cs-fixer fix"
composer config scripts.phpcs "phpcs --standard=phpcs.xml src/"
composer config scripts.style-check "@cs-check && @phpcs"
composer config scripts.style-fix "@cs-fix && vendor/bin/phpcbf --standard=phpcs.xml src/"

# 6. 运行检查
composer style-check

# 7. 自动修复
composer style-fix

注意事项

工具选择建议

PHP-CS-Fixer vs PHP_CodeSniffer

  • PHP-CS-Fixer:更侧重代码格式化和自动修复,适合初学者和快速统一风格
  • PHP_CodeSniffer:更侧重规范检查和自定义规则,适合需要精细控制的团队
  • 推荐组合:同时使用两者,PHP-CS-Fixer 负责自动格式化,PHP_CodeSniffer 负责补充检查

常见陷阱

php
<?php
declare(strict_types=1);

// 注意:格式化工具可能改变代码逻辑,务必在格式化后运行测试

// 1. 对齐赋值可能改变
$name     = 'John';
$email    = 'john@example.com';  // 格式化工具可能重新对齐

// 2. 数组格式变化
$data = [
    'key1' => 'value1',
    'key2' => 'value2',
];
// 格式化工具可能重新排列为每个元素一行或多行

// 3. 注释格式变化
// 格式化工具可能调整注释中的空格和缩进

始终在 CI 中运行测试

代码格式化可能意外改变代码行为。在 CI 中,代码风格检查应与测试套件并行运行,确保格式化不会引入错误。

最佳实践

  1. 配置文件纳入版本控制:将 .php-cs-fixer.phpphpcs.xml 提交到 Git
  2. 使用 pre-commit hook:在提交前自动检查代码风格
  3. CI 中并行运行:代码风格检查与单元测试同时运行
  4. 渐进式引入:对已有项目使用 .php-cs-fixer.cache 和 baseline 机制
  5. 团队统一工具版本:在 composer.json 中锁定工具版本
  6. 定期更新规则:跟踪 PHP-CS-Fixer 和 PHP_CodeSniffer 的更新

下一节

继续学习:静态分析

参考链接