PHP_CodeSniffer 定制团队规则并集成 CI:从 ruleset.xml 到 GitHub Actions / GitLab CI 的完整落地

发布于
2

PHP_CodeSniffer(下称 PHPCS)是 PHP 生态里事实标准的代码风格校验工具,内置 PSR-12、PSR-2、PEAR、Squiz 等规范。但在团队项目中,直接套 PSR-12 往往太严(旧代码报一堆 MissingNamespaceMultiLineFunctionDeclaration),太松又失去约束意义。可行的做法是:以 PSR-12 为底座,用 ruleset.xml 做减法 + 微调参数,再把 phpcs.xml 纳入仓库、接入 CI 让它成为合门禁。​ 下面按这条线走一遍可复用的流程。

一、安装与基础命令

推荐项目级局部安装,避免全局版本冲突:

composer require --dev squizlabs/php_codesniffer

安装后二进制位于 vendor/bin/phpcs(Linux/macOS)与 vendor/bin/phpcbf(自动修复)。快速验证:

./vendor/bin/phpcs --version
./vendor/bin/phpcs --standard=PSR12 src/

phpcbf 是同包附带的自动修复工具,能修掉 phpcs 输出中标 [x] 的那类问题(缩进、换行、空格等格式级违规)。

二、phpcs.xml 与 ruleset.xml 的关系(关键点)

PHPCS 启动时会自动在当前目录及向上递归查找phpcs.xmlphpcs.xml.dist,找到就用它当 --standard,无需再手动指定。因此团队项目的常规做法是:

  • phpcs.xml​ —— 放仓库里,提交,包含所有路径、排除、参数,CI 和本地都直接用

  • phpcs.xml.dist​ —— 作为”分发模板”,个人可复制成 phpcs.xml 覆写局部路径,不改 .dist 原版

两者语法完全一致,都是 ruleset 格式。ruleset.xml 这个名字更多出现在自定义标准包(比如你自己写一个 MyStandard/ruleset.xml 然后通过 installed_paths 注册)的场景。

💡 很多团队踩过的坑:命令行写了一长串 --ignore=*/vendor/* --standard=PSR12,换个人机器就忘了参数。把规则固化成 phpcs.xml 进版本库是唯一可持续的做法。

三、定制 ruleset:在 PSR-12 上做”减法 + 调参”

下面是一份可直接落地的团队规则样例,兼顾新项目约束与老项目迁移:

<?xml version="1.0"?>
<ruleset name="TeamPHP">
    <description>团队 PHP 规范,基于 PSR-12 裁剪</description>

    <!-- 1. 继承 PSR-12 整体 -->
    <rule ref="PSR12"/>

    <!-- 2. 老项目没强行加 namespace 的,先放过 -->
    <rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
        <severity>0</severity>
    </rule>

    <!-- 3. 强制短数组 [],禁 array() -->
    <rule ref="Generic.Arrays.DisallowLongArraySyntax"/>

    <!-- 4. 一行一条语句 -->
    <rule ref="Generic.Formatting.MultipleStatementAlignment"/>

    <!-- 5. 缩进由默认 4 改 2(按团队约定) -->
    <rule ref="Generic.WhiteSpace.ScopeIndent">
        <properties>
            <property name="indent" value="2"/>
        </properties>
    </rule>
    <rule ref="PSR2.Methods.FunctionCallSignature">
        <properties>
            <property name="indent" value="2"/>
            <property name="requiredSpacesAfterOpen" value="1"/>
            <property name="requiredSpacesBeforeClose" value="1"/>
        </properties>
    </rule>

    <!-- 6. 文件级排除 -->
    <exclude-pattern>*/vendor/*</exclude-pattern>
    <exclude-pattern>*/node_modules/*</exclude-pattern>
    <exclude-pattern>*/tests/*</exclude-pattern>
    <exclude-pattern>*/storage/*</exclude-pattern>
</ruleset>

这套样例中几个决策点:

  • <severity>0</severity>​ 等价于禁用该 rule,比 <exclude> 更灵活(exclude 是按 name 剔子项,severity=0 是整条规则静音)

  • property 调参​ 是 PHPCS 定制最被低估的能力——同一条 ScopeIndent,4 空格团队和 2 空格团队用的就是 indent 这一个值

  • <exclude-pattern> 在 ruleset 层生效,比命令行 --ignore 更稳定,因为 CI 不会因为某人忘了传 --ignore 而扫 vendor

把上述内容存为项目根目录的 phpcs.xml,之后本地直接:

./vendor/bin/phpcs src/
./vendor/bin/phpcbf src/   # 能自动修的部分直接修

四、Composer 脚本再包一层(可选但推荐)

composer.json 里加两段脚本,让团队成员用统一入口:

{
    "scripts": {
        "cs:check": "phpcs --extensions=php --report=full src/ tests/",
        "cs:fix":  "phpcbf --extensions=php src/ tests/"
    }
}

之后 composer cs:check / composer cs:fix 即可,新人不用记路径。

五、集成 GitHub Actions

推荐走”裸跑 vendor/bin/phpcs”而不是第三方 action——少一个依赖,行为更可控。工作流文件 .github/workflows/phpcs.yml

name: PHPCS Check
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  phpcs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

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

      - name: Run PHPCS
        run: ./vendor/bin/phpcs

要点:

  • 根目录已有 phpcs.xml,所以 ./vendor/bin/phpcs 后面不必再写 --standard,它会自动读取

  • 如果想只在 PR 里跑变更文件,可以在 checkoutfetch-depth: 0,然后用 git diff --name-only ... | grep '\.php$' | xargs ./vendor/bin/phpcs,或改用 navarr/phpcs-action@v3only_changed_files: true

  • 报错即 exit code 1 → PR 红,天然形成门禁


六、集成 GitLab CI

.gitlab-ci.yml 里加一段 php_cs job 即可:

php_cs:
  stage: test
  image: php:8.1-cli
  script:
    - composer install --no-progress --prefer-dist
    - ./vendor/bin/phpcs
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH == "main"'

如果项目是 Drupal / WordPress 这类已有专属标准的,把 --standard 换成 Drupal,DrupalPracticeWordPress 即可,ruleset 仍可通过 phpcs.xml 覆写。

七、常见

现象

原因

处理

自定义 standard 报”not found”

installed_paths 没注册

./vendor/bin/phpcs --config-set installed_paths /path/to/MyStandard 或在 composer.json 里引 dealerdirect/phpcodesniffer-composer-installer 自动处理

ruleset 里 property 不生效

拼错 sniff 名 / property 名

./vendor/bin/phpcs -s 看每条报错归属的 sniff 全称再对

CI 过本地不过 / 反之

两端 PHPCS 版本不一致

composer.lock 锁版本,CI 里走 composer install 而非全局 phpcs

老项目一次性全量修不动

规则太激进

先用 severity=0 静音,分模块逐个开启;或先用 phpcbf 修格式类,再补语义类

落地节奏建议

  1. 第一天:仓库里丢一份 phpcs.xml,继承 PSR-12,exclude-pattern 把 vendor/tests 挡掉,全量扫一次看基线

  2. 第一周:按报错 Top 10 决定哪些 rule 用 severity=0 静音、哪些 property 要调(缩进/数组语法/方法签名换行最常见)

  3. 第二周:GitHub Actions / GitLab CI 挂上,仅 main 分支 + MR 触发,失败不阻塞(观察期)

  4. 一个月后:规则稳定,MR 门禁切为 fail on error,新代码强制合规,老代码渐进修

📌 规则文件本身也是代码,建议每半年由一两人 review 一次 phpcs.xml——团队规模变了、PHP 版本升了(比如上 8.2/8.3 的新语法),规则也得跟着动。

常见问题(FAQ)

如何在团队项目中定制 PHP_CodeSniffer 的规则?
在团队项目中,可以通过创建 `ruleset.xml` 文件来定制规则。首先继承 PSR-12 规范,然后根据团队需求进行调整,如禁用某些规则、调整参数(例如缩进空格数)或排除特定路径。将 `ruleset.xml` 提交到版本库,确保 CI 和本地开发环境使用相同的规则。
如何将 PHP_CodeSniffer 集成到 GitHub Actions 中?
在 GitHub Actions 中,可以通过创建 `phpcs.yml` 工作流文件进行集成。使用 Composer 安装依赖后,执行 `./vendor/bin/phpcs` 命令。推荐使用裸跑 `vendor/bin/phpcs` 而不是第三方 Action,以保持可控性。可以配置在 PR 或 Push 时触发检查,并设置失败时阻止合并。
PHP_CodeSniffer 报错 `not found` 是什么原因?
当自定义 standard 报 `not found` 错误时,通常是因为 `installed_paths` 没有正确注册。可以通过运行命令 `./vendor/bin/phpcs --config-set installed_paths /path/to/MyStandard` 来注册自定义标准路径,或者在 `composer.json` 中使用 `dealerdirect/phpcodesniffer-composer-installer` 包自动处理。
如何处理 CI 环境与本地开发环境检查结果不一致的问题?
如果 CI 过本地不过或反之,可能是因为两端 PHP_CodeSniffer 版本不一致。建议在 `composer.lock` 中锁定版本,并在 CI 中使用 `composer install` 而非全局安装。确保 CI 环境与开发环境使用相同版本的 PHP 和 PHP_CodeSniffer。
如何优化旧项目代码的 PHP_CodeSniffer 检查?
对于旧项目,一次性全量修复可能因规则过于激进而不可行。建议先通过设置 `severity=0` 静音部分规则,然后分模块逐步开启。优先使用 `phpcbf` 自动修复格式类问题,再手动处理语义类违规。逐步推进,避免影响代码稳定性。
0 讨论
热门最新
总结
暂无总结
0 / 600
嗨,下午好!
所有的成功,都源自一个勇敢的开始
¥10.00
10元抵扣券
已过期