本文目录导读:

在PHP项目中,实现“门禁检测”并在合并前执行代码校验,通常指的是代码审查流程中的Pre-merge Checks(合并前检查),这可以通过以下方式实现:
使用Git Hooks实现本地或服务器端校验
pre-receive hook(服务端钩子)
在远程仓库的 hooks/pre-receive 中添加校验逻辑:
#!/bin/bash
# .git/hooks/pre-receive
# 读取推送的引用
while read oldrev newrev refname; do
# 跳过非分支推送
if [[ $refname != refs/heads/* ]]; then
continue
fi
branch=${refname#refs/heads/}
# 检查目标分支是否为受保护分支(如 master, main)
if [[ "$branch" == "master" || "$branch" == "main" ]]; then
echo "禁止直接推送到 $branch 分支"
exit 1
fi
# 执行代码校验
# 获取要校验的文件
changed_files=$(git diff --name-only $oldrev $newrev | grep '\.php$')
if [ -n "$changed_files" ]; then
# PHP语法检查
for file in $changed_files; do
if ! php -l "$file" 2>/dev/null; then
echo "PHP语法错误: $file"
exit 1
fi
done
# 运行PHPUnit测试(可选)
# php artisan test --coverage || exit 1
fi
done
exit 0
pre-commit hook(本地钩子)
#!/bin/bash
# .git/hooks/pre-commit
# 获取暂存区的PHP文件
staged_files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.php$')
if [ -n "$staged_files" ]; then
echo "执行提交前代码校验..."
# 1. PHP语法检查
for file in $staged_files; do
if ! php -l "$file" 2>/dev/null; then
echo "❌ 语法错误: $file"
exit 1
fi
done
echo "✅ PHP语法检查通过"
# 2. PHP CodeSniffer 代码规范检查
if command -v phpcs &> /dev/null; then
if ! phpcs --standard=PSR12 $staged_files; then
echo "❌ 代码规范检查失败"
exit 1
fi
echo "✅ 代码规范检查通过"
fi
# 3. PHPStan 静态分析(可选)
if [ -f "phpstan.neon" ] || [ -f "phpstan.neon.dist" ]; then
if ! phpstan analyse --level=max $staged_files; then
echo "❌ 静态分析失败"
exit 1
fi
echo "✅ 静态分析通过"
fi
fi
exit 0
使用CI/CD工具实现门禁检测
GitHub Actions 示例
# .github/workflows/php-checks.yml
name: PHP Code Checks
on:
pull_request:
branches: [ main, develop ]
push:
branches: [ main ]
jobs:
php-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: mbstring, xml, curl
tools: composer, phpcs, phpstan
- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: PHP Syntax Check
run: |
find . -name "*.php" -not -path "./vendor/*" -exec php -l {} \;
- name: PHP CodeSniffer
run: phpcs --standard=PSR12 --extensions=php --ignore=vendor/ .
- name: PHPStan Analysis
run: phpstan analyse --level=max --memory-limit=2G
- name: Run Tests
run: php artisan test --coverage
- name: Security Check
run: php artisan security:check || true
GitLab CI 示例
# .gitlab-ci.yml
stages:
- code-quality
- test
- security
php-lint:
stage: code-quality
script:
- find . -name "*.php" -not -path "./vendor/*" -exec php -l {} \;
php-cs:
stage: code-quality
script:
- phpcs --standard=PSR12 --extensions=php --ignore=vendor/ .
phpstan:
stage: code-quality
script:
- phpstan analyse --level=max --memory-limit=2G
phpunit:
stage: test
script:
- php artisan test --coverage
coverage: '/^\s*Methods:\s*\d+.\d+%/'
security-check:
stage: security
script:
- php artisan security:check
allow_failure: true
自定义门禁检测脚本
创建独立的门禁检测脚本 gate-check.php:
<?php
// gate-check.php
class GateCheck
{
private array $errors = [];
private array $warnings = [];
public function run(): bool
{
$this->checkPhpSyntax();
$this->checkCodeStyle();
$this->checkStaticAnalysis();
$this->checkUnitTests();
$this->checkSecurity();
$this->report();
return empty($this->errors);
}
private function checkPhpSyntax(): void
{
$files = $this->getChangedPhpFiles();
foreach ($files as $file) {
exec("php -l {$file} 2>&1", $output, $returnCode);
if ($returnCode !== 0) {
$this->errors[] = "语法错误: {$file} - " . implode("\n", $output);
}
}
}
private function checkCodeStyle(): void
{
exec("phpcs --standard=PSR12 --report=full " . implode(' ', $this->getChangedPhpFiles()), $output, $returnCode);
if ($returnCode !== 0) {
$this->errors[] = "代码规范问题:\n" . implode("\n", $output);
}
}
private function checkStaticAnalysis(): void
{
exec("phpstan analyse --level=max " . implode(' ', $this->getChangedPhpFiles()), $output, $returnCode);
if ($returnCode !== 0) {
$this->errors[] = "静态分析问题:\n" . implode("\n", $output);
}
}
private function checkUnitTests(): void
{
exec("php artisan test --coverage 2>&1", $output, $returnCode);
if ($returnCode !== 0) {
$this->warnings[] = "测试失败:\n" . implode("\n", $output);
}
}
private function checkSecurity(): void
{
exec("composer audit 2>&1", $output, $returnCode);
if ($returnCode !== 0) {
$this->warnings[] = "安全漏洞警告:\n" . implode("\n", $output);
}
}
private function getChangedPhpFiles(): array
{
exec("git diff --name-only --cached", $output);
$files = array_filter($output, function($file) {
return pathinfo($file, PATHINFO_EXTENSION) === 'php';
});
return array_values($files);
}
private function report(): void
{
if (!empty($this->errors)) {
echo "❌ 门禁检测失败:\n";
foreach ($this->errors as $error) {
echo " - {$error}\n";
}
}
if (!empty($this->warnings)) {
echo "⚠️ 警告:\n";
foreach ($this->warnings as $warning) {
echo " - {$warning}\n";
}
}
if (empty($this->errors) && empty($this->warnings)) {
echo "✅ 门禁检测通过\n";
}
}
}
$check = new GateCheck();
exit($check->run() ? 0 : 1);
使用方式:
php gate-check.php
集成到Git工作流
使用husky(Node.js工具)管理Git hooks
// .husky/pre-commit #!/bin/sh . "$(dirname "$0")/_/husky.sh" npm run lint-staged
// package.json
{
"lint-staged": {
"*.php": [
"php -l",
"phpcs --standard=PSR12",
"phpstan analyse --level=max"
]
}
}
最佳实践建议
-
分层校验:
- 本地:语法检查、代码规范
- 服务器:单元测试、集成测试
- CI/CD:安全扫描、性能测试
-
配置门禁规则:
- 关键分支(master/main)必须有PR
- 所有检查必须通过才能合并
- 代码覆盖率不低于80%
-
使用门禁服务器:
# 使用Jenkins/GitLab CI等工具 pipeline { agent any stages { stage('门禁检测') { parallel { stage('语法检查') { steps { sh 'find . -name "*.php" -not -path "./vendor/*" -exec php -l {} \;' } } stage('代码规范') { steps { sh 'phpcs --standard=PSR12 --extensions=php --ignore=vendor/ .' } } stage('安全扫描') { steps { sh 'composer audit' } } } } } }
门禁检测流程总结
graph TD
A[开发者提交代码] --> B{本地pre-commit检查}
B -->|通过| C[推送到远程]
B -->|失败| A
C --> D{服务器pre-receive检查}
D -->|通过| E[创建合并请求]
D -->|失败| F[拒绝推送]
E --> G{CI/CD门禁检查}
G -->|通过| H[允许合并]
G -->|失败| I[标记失败并通知]
H --> J[代码合并到目标分支]
通过以上方法,你可以建立一个完整的PHP项目门禁检测系统,确保代码在合并前经过充分的校验,提高代码质量和项目稳定性。