PHP企业级研发治理体系:从代码规范到组织协作的全链路落地手册

  下面把整套体系拆成 8 层,每一层都给你「干什么用」+「怎么落地」+「完整代码」+「大白话」。直接照抄就能跑。

  ---
  一、整体地图(先看全景再动手)

  ┌─────────────────────────────────────────────┐
  │ 第8层:组织协作(OKR / Code Review / 知识库)│
  │ 第7层:监控告警(Sentry / Prometheus)       │
  │ 第6层:CI/CD(GitLab CI / GitHub Actions)   │
  │ 第5层:Git 工作流(Git Flow + 提交规范)     │
  │ 第4层:安全审计(Composer Audit / Enlightn) │
  │ 第3层:自动化测试(PHPUnit / Pest)          │
  │ 第2层:静态分析(PHPStan / Psalm / Rector)  │
  │ 第1层:代码规范(PSR-12 / PHP-CS-Fixer)     │
  └─────────────────────────────────────────────┘

  大白话:底层是「写得对不对」,中层是「跑得稳不稳」,上层是「人协作得顺不顺」。一层都不能少。

  ---
  二、第1层:代码规范(PSR-12 + PHP-CS-Fixer)

  1. 安装

  composer require --dev friendsofphp/php-cs-fixer squizlabs/php_codesniffer

  2. .php-cs-fixer.dist.php

  <?php
  $finder = PhpCsFixer\Finder::create()
      ->in(__DIR__ . '/src')
      ->in(__DIR__ . '/tests')
      ->exclude('vendor');

  return (new PhpCsFixer\Config())
      ->setRiskyAllowed(true)
      ->setRules([
          '@PSR12'                    => true,
          '@PHP82Migration'           => true,
          'array_syntax'              => ['syntax' => 'short'],
          'declare_strict_types'      => true,
          'no_unused_imports'         => true,
          'ordered_imports'           => ['sort_algorithm' => 'alpha'],
          'single_quote'              => true,
          'trailing_comma_in_multiline' => true,
          'phpdoc_align'              => ['align' => 'vertical'],
      ])
      ->setFinder($finder);

  3. 一键修复

  vendor/bin/php-cs-fixer fix --diff --verbose

  大白话:这一层管「代码长得好不好看」。缩进、引号、空行这种鸡毛蒜皮的事,交给工具自动改,人不要花脑子去吵。

  ---
  三、第2层:静态分析(PHPStan)

  1. 安装

  composer require --dev phpstan/phpstan phpstan/extension-installer
  composer require --dev phpstan/phpstan-strict-rules

  2. phpstan.neon

  includes:
      - vendor/phpstan/phpstan-strict-rules/rules.neon

  parameters:
      level: 8                       # 0~9,9 最严,企业建议 8 起步
      paths:
          - src
          - tests
      excludePaths:
          - src/Legacy/*
      checkMissingIterableValueType: true
      treatPhpDocTypesAsCertain: false
      ignoreErrors:
          - '#Call to an undefined method.*::scopeWhere.*#'

  3. 跑

  vendor/bin/phpstan analyse --memory-limit=2G

  大白话:这一层管「代码逻辑有没有埋雷」。比如你 $user->getName()$user 可能是 null,PHPStan
  在你提交前就喊出来,不用等线上崩了才发现。

  ---
  四、第3层:自动化测试(Pest,比 PHPUnit 更顺手)

  1. 安装

  composer require --dev pestphp/pest pestphp/pest-plugin-laravel
  ./vendor/bin/pest --init

  2. tests/Unit/UserServiceTest.php

  <?php
  declare(strict_types=1);

  use App\Services\UserService;
  use App\Repositories\UserRepository;

  beforeEach(function () {
      $this->repo    = Mockery::mock(UserRepository::class);
      $this->service = new UserService($this->repo);
  });

  it('创建用户时邮箱必须唯一', function () {
      $this->repo->shouldReceive('existsByEmail')
          ->with('a@b.com')->once()->andReturnTrue();

      expect(fn () => $this->service->create('张三', 'a@b.com'))
          ->toThrow(DomainException::class, '邮箱已存在');
  });

  it('创建用户成功返回ID', function () {
      $this->repo->shouldReceive('existsByEmail')->andReturnFalse();
      $this->repo->shouldReceive('insert')->andReturn(1001);

      expect($this->service->create('张三', 'a@b.com'))->toBe(1001);
  });

  3. 覆盖率门槛(写进 phpunit.xml)

  <coverage>
      <report>
          <clover outputFile="coverage.xml"/>
          <html outputDirectory="coverage-html"/>
      </report>
  </coverage>

  vendor/bin/pest --coverage --min=80

  大白话:测试不是写给 QA 看的,是写给「半年后改代码的自己」看的。覆盖率 80% 不达标就不让合并,强制养成习惯。

  ---
  五、第4层:安全审计

  # 检查依赖有没有已知漏洞
  composer audit

  # 更专业的安全扫描(Laravel)
  composer require --dev enlightn/enlightn
  php artisan enlightn

  .gitleaks.toml(防止 AK/SK 提交):

  [allowlist]
  paths = ['''.*\.lock$''']

  [[rules]]
  id = "aws-key"
  regex = '''AKIA[0-9A-Z]{16}'''

  大白话:依赖里有漏洞、代码里漏了 AccessKey,这种事一旦发生就是大事故。让工具每天扫,比靠人记靠谱一万倍。

  ---
  六、第5层:Git 工作流 + 提交规范

  1. 分支模型(Git Flow 简化版)

  main      ←线上,只接受 release/hotfix 合并
  develop   ←集成分支
  feature/* ←功能分支(从 develop 切)
  release/* ←发版分支
  hotfix/*  ←紧急修复(从 main 切)

  2. Conventional Commits(用 commitlint 强制)

  commitlint.config.js:

  module.exports = {
      extends: ['@commitlint/config-conventional'],
      rules: {
          'type-enum': [2, 'always', [
              'feat', 'fix', 'docs', 'style', 'refactor',
              'perf', 'test', 'build', 'ci', 'chore', 'revert'
          ]],
          'subject-max-length': [2, 'always', 72],
      },
  };

  3. Husky 钩子 package.json

  {
    "scripts": { "prepare": "husky install" },
    "devDependencies": {
      "husky": "^9.0.0",
      "@commitlint/cli": "^19.0.0",
      "@commitlint/config-conventional": "^19.0.0"
    }
  }

  .husky/pre-commit:

  #!/usr/bin/env sh
  . "$(dirname -- "$0")/_/husky.sh"

  vendor/bin/php-cs-fixer fix --dry-run --diff || exit 1
  vendor/bin/phpstan analyse --no-progress     || exit 1
  vendor/bin/pest --parallel                   || exit 1

  .husky/commit-msg:

  #!/usr/bin/env sh
  . "$(dirname -- "$0")/_/husky.sh"
  npx --no -- commitlint --edit $1

  大白话:提交信息长成 feat(user): 增加邮箱校验,半年后看 git log 一目了然。乱写一句 fix bug 直接拒。

  ---
  七、第6层:CI/CD(GitHub Actions 完整版)

  .github/workflows/ci.yml:

  name: CI

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

  jobs:
    quality:
      runs-on: ubuntu-latest
      strategy:
        matrix: { php: ['8.2', '8.3'] }
      steps:
        - uses: actions/checkout@v4

        - name: Setup PHP
          uses: shivammathur/setup-php@v2
          with:
            php-version: ${{ matrix.php }}
            coverage: xdebug
            tools: composer:v2

        - name: Cache Composer
          uses: actions/cache@v4
          with:
            path: vendor
            key: composer-${{ hashFiles('composer.lock') }}

        - run: composer install --prefer-dist --no-progress
        - run: vendor/bin/php-cs-fixer fix --dry-run --diff
        - run: vendor/bin/phpstan analyse --error-format=github
        - run: vendor/bin/pest --coverage --min=80
        - run: composer audit

        - name: Upload coverage
          uses: codecov/codecov-action@v4
          with: { files: ./coverage.xml }

    deploy-staging:
      needs: quality
      if: github.ref == 'refs/heads/develop'
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v4
        - name: Deploy via SSH
          uses: appleboy/ssh-action@v1
          with:
            host:     ${{ secrets.STAGING_HOST }}
            username: ${{ secrets.STAGING_USER }}
            key:      ${{ secrets.STAGING_KEY }}
            script: |
              cd /var/www/app
              git pull origin develop
              composer install --no-dev --optimize-autoloader
              php artisan migrate --force
              php artisan config:cache
              php artisan queue:restart

  大白话:本地钩子是「软拦截」,CI 是「硬关卡」。本地能绕过,CI 必须过;过不了 CI,PR 按钮直接灰着。

  ---
  八、第7层:监控告警

  1. Sentry 接入

  // config/sentry.php
  return [
      'dsn'                   => env('SENTRY_LARAVEL_DSN'),
      'traces_sample_rate'    => 0.2,
      'profiles_sample_rate'  => 0.1,
      'send_default_pii'      => false,
      'environment'           => env('APP_ENV'),
  ];

  2. Prometheus 指标埋点

  use Prometheus\CollectorRegistry;

  class MetricsMiddleware
  {
      public function __construct(private CollectorRegistry $registry) {}

      public function handle($request, Closure $next)
      {
          $start = microtime(true);
          $response = $next($request);

          $histogram = $this->registry->getOrRegisterHistogram(
              'app', 'http_request_duration_seconds',
              'HTTP 请求耗时', ['method', 'route', 'status'],
              [0.05, 0.1, 0.3, 1, 3]
          );
          $histogram->observe(
              microtime(true) - $start,
              [$request->method(), $request->route()?->getName() ?? 'unknown', (string)$response->status()]
          );
          return $response;
      }
  }

  大白话:线上必须做到「错了立刻知道、慢了立刻知道、谁错的立刻知道」。Sentry 抓异常,Prometheus + Grafana
  看趋势,缺一不可。

  ---
  九、第8层:组织协作

  1. Code Review Checklist(贴 PR 模板)

  .github/pull_request_template.md:

  ## 改了什么
  <!-- 一句话讲清楚 -->

  ## 为什么改
  <!-- 关联需求/Bug 单 -->

  ## 自检清单
  - [ ] PHPStan level 8 通过
  - [ ] 单元测试覆盖率 ≥80%
  - [ ] 接口变更已更新 OpenAPI 文档
  - [ ] 数据库变更有可回滚的 migration
  - [ ] 没有提交 .env / 密钥 / 调试代码
  - [ ] 关键逻辑加了注释和日志

  ## 影响范围
  <!-- 涉及哪些模块、是否需要灰度 -->

  2. Review 三原则

  ┌─────────────┬────────────────────────────────────────────────────────┐
  │    原则     │                         大白话                         │
  ├─────────────┼────────────────────────────────────────────────────────┤
  │ 对事不对人  │ 说「这段如果输入为空会 NPE」,不说「你怎么连这都不写」 │
  ├─────────────┼────────────────────────────────────────────────────────┤
  │ 24 小时响应 │ PR 挂超过一天没人看,整个团队都在等                    │
  ├─────────────┼────────────────────────────────────────────────────────┤
  │ 小 PR 优先  │ 500 行以上拆,否则评审等于走过场                       │
  └─────────────┴────────────────────────────────────────────────────────┘

  3. 文档与知识沉淀

  docs/
  ├── adr/                  # 架构决策记录(Architecture Decision Records)
  │   └── 0001-用-laravel-octane-提性能.md
  ├── runbook/              # 线上故障应对手册
  │   └── 数据库主从延迟.md
  ├── onboarding.md         # 新人三天上手
  └── api/openapi.yaml      # 接口契约

  ADR 模板:

  # ADR-0001:使用 Octane 替代 PHP-FPM

  - 状态:已采纳
  - 日期:2026-06-16
  - 决策者:架构组

  ## 背景
  QPS 峰值 3000,PHP-FPM 冷启动开销大

  ## 决策
  切换到 Laravel Octane + Swoole

  ## 后果
  + 性能提升 5 倍
  - 内存泄漏风险,需引入定期 reload

  大白话:ADR 是「为什么这么做」的备忘录。三年后新人来了,不用再问「这破设计到底谁拍的」,翻文档就懂。

  ---
  十、落地节奏(最佳推进方式)

  ┌────────────┬────────┬──────────────────────────────────────────────────┐
  │    阶段    │  时间  │                      做什么                      │
  ├────────────┼────────┼──────────────────────────────────────────────────┤
  │ 第 1 周    │ 立规矩 │ 装 PHP-CS-Fixer + PHPStan level 5,本地能跑      │
  ├────────────┼────────┼──────────────────────────────────────────────────┤
  │ 第 2~3 周  │ 上 CI  │ GitHub Actions 跑通,PR 卡红线                   │
  ├────────────┼────────┼──────────────────────────────────────────────────┤
  │ 第 4~6 周  │ 补测试 │ 核心模块覆盖率拉到 60%,逐步爬到 80%             │
  ├────────────┼────────┼──────────────────────────────────────────────────┤
  │ 第 7~8 周  │ 提级别 │ PHPStan 升到 level 8,老代码标 baseline 慢慢还债 │
  ├────────────┼────────┼──────────────────────────────────────────────────┤
  │ 第 9 周    │ 上监控 │ Sentry + Prometheus 接入,建告警群               │
  ├────────────┼────────┼──────────────────────────────────────────────────┤
  │ 第 10 周起 │ 抓协作 │ 推 ADR、Runbook、PR 模板,固化 Review 文化       │
  └────────────┴────────┴──────────────────────────────────────────────────┘

  ---
  十一、最关键的三句话

  1. 工具能解决的问题,绝不靠人盯——风格、漏洞、覆盖率全部自动化。
  2. CI 是底线,不是上限——过了CI 只代表「没明显错」,不代表「写得好」,Code Review 才是真把关。
  3. 治理不是一次性运动,是持续的肌肉记忆——每周花10% 的时间还技术债,比每年来一次大重构划算 100 倍。

  照这套抄完,半年内代码质量会肉眼可见地变好,新人上手时间能从两周缩到三天。需要我针对某一层(比如 Octane 部署、Sentry
  告警分级、ADR 推广话术)再深挖,随时说。

更多推荐