PHP_CodeSniffer(下称 PHPCS)是 PHP 生态里事实标准的代码风格校验工具,内置 PSR-12、PSR-2、PEAR、Squiz 等规范。但在团队项目中,直接套 PSR-12 往往太严(旧代码报一堆 MissingNamespace、MultiLineFunctionDeclaration),太松又失去约束意义。可行的做法是:以 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.xml 或 phpcs.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 里跑变更文件,可以在
checkout加fetch-depth: 0,然后用git diff --name-only ... | grep '\.php$' | xargs ./vendor/bin/phpcs,或改用navarr/phpcs-action@v3配only_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,DrupalPractice 或 WordPress 即可,ruleset 仍可通过 phpcs.xml 覆写。
七、常见
|
现象 |
原因 |
处理 |
|---|---|---|
|
自定义 standard 报”not found” |
|
|
|
ruleset 里 property 不生效 |
拼错 sniff 名 / property 名 |
用 |
|
CI 过本地不过 / 反之 |
两端 PHPCS 版本不一致 |
composer.lock 锁版本,CI 里走 |
|
老项目一次性全量修不动 |
规则太激进 |
先用 severity=0 静音,分模块逐个开启;或先用 |
落地节奏建议
-
第一天:仓库里丢一份
phpcs.xml,继承 PSR-12,exclude-pattern 把 vendor/tests 挡掉,全量扫一次看基线 -
第一周:按报错 Top 10 决定哪些 rule 用 severity=0 静音、哪些 property 要调(缩进/数组语法/方法签名换行最常见)
-
第二周:GitHub Actions / GitLab CI 挂上,仅
main分支 + MR 触发,失败不阻塞(观察期) -
一个月后:规则稳定,MR 门禁切为
fail on error,新代码强制合规,老代码渐进修
📌 规则文件本身也是代码,建议每半年由一两人 review 一次
phpcs.xml——团队规模变了、PHP 版本升了(比如上 8.2/8.3 的新语法),规则也得跟着动。

