本文目录导读:

在PHP项目中清理冗余代码是提升可维护性、性能和团队效率的重要工作,下面是一个系统化的清理方法和实践指南。
识别冗余代码的类型
需要明确哪些代码属于冗余:
未使用的代码
// 从未被调用的函数
function unusedFunction() {
// ...
}
// 从未被使用的类
class UnusedClass {
// ...
}
死代码(Dead Code)
function someFunction() {
// 永远不会执行到的分支
if (false) {
echo "never reached";
}
return true;
// 后面的代码永远不会执行
$unreachable = "dead";
}
重复代码(Duplicated Code)
// 多处重复的数据库查询逻辑
class UserController {
public function getUser($id) {
$db = new Database();
$user = $db->query("SELECT * FROM user WHERE id = $id");
// ...
}
public function updateUser($id) {
$db = new Database();
$user = $db->query("SELECT * FROM user WHERE id = $id");
// ...
}
}
过时的注释代码
// 被注释掉的大量代码块
// function oldImplementation() {
// $result = complexLogic();
// return $result;
// }
工具辅助检测
静态分析工具
PHPStan(推荐)
# 安装 composer require --dev phpstan/phpstan # 运行分析 vendor/bin/phpstan analyse src/ --level max
PhpMetrics
# 安装 composer require --dev phpmetrics/phpmetrics # 生成报告 vendor/bin/phpmetrics src/ --report-html=report/
IDE内置功能
PhpStorm
Code → Inspect Code:全面代码检查Code → Run Inspection by Name→ "Unused":查找未使用代码Analyze → Locate Duplicates:查找重复代码
专用冗余检测工具
Phan
# 安装
composer require --dev phan/phan
# 配置 phan.config.php
return [
'directory_list' => ['src/'],
'exclude_analysis_directory_list' => ['vendor/'],
'suppress_issue_types' => [
'PhanUnreferencedMethod', // 可选:抑制某些警告
],
];
# 运行
vendor/bin/phan
实战清理步骤
步骤1:项目全局扫描
# 使用 grep 查找未使用的函数
grep -r "function unusedFunction" src/ --include="*.php"
grep -r "unusedFunction(" src/ --include="*.php"
# 查找 import 但未使用的 use
grep -r "^use " src/ --include="*.php" | sort
步骤2:清理未使用的文件
// 创建一个脚本来检查文件是否被引用
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('src/')
);
foreach ($files as $file) {
if ($file->isFile() && $file->getExtension() === 'php') {
$className = pathinfo($file->getFilename(), PATHINFO_FILENAME);
// 搜索项目中是否有引用
$result = shell_exec("grep -r '{$className}' src/ --include='*.php' | grep -v '{$file->getFilename()}'");
if (empty($result)) {
echo "可能未被使用的类: {$file->getPathname()}\n";
}
}
}
步骤3:处理重复代码
// 原有重复代码
class OrderService {
public function validateOrder($order) {
if (empty($order['items'])) {
throw new Exception("订单为空");
}
if ($order['total'] <= 0) {
throw new Exception("金额无效");
}
// 10行验证逻辑...
}
public function validateRefund($refund) {
// 完全相同的验证逻辑重复了
if (empty($refund['items'])) {
throw new Exception("退款为空");
}
if ($refund['total'] <= 0) {
throw new Exception("金额无效");
}
// 10行验证逻辑...
}
}
// 重构后
abstract class BaseValidator {
protected function validateItems($data) {
if (empty($data['items'])) {
throw new Exception("项目为空");
}
if ($data['total'] <= 0) {
throw new Exception("金额无效");
}
}
}
class OrderService extends BaseValidator {
public function validateOrder($order) {
$this->validateItems($order);
// 订单特有验证...
}
public function validateRefund($refund) {
$this->validateItems($refund);
// 退款特有验证...
}
}
步骤4:清理过时的注释和调试代码
// 清理前
class PaymentService {
/*
* 旧版本的支付处理逻辑
* @deprecated 请使用 processPaymentV2()
*/
// public function processPayment($order) {
// // 旧逻辑...
// }
public function processPaymentV2($order) {
// 新逻辑
// var_dump($order); // 调试代码应该删除
// die(); // 调试代码
$result = $this->handlePayment($order);
return $result;
}
// 无用的空方法
public function dummyMethod() {
// 什么也不做
}
}
// 清理后
class PaymentService {
public function processPayment($order) {
$result = $this->handlePayment($order);
return $result;
}
}
自动化清理策略
创建清理脚本
<?php
// cleanup.php - 自动化清理工具
class CodeCleaner {
private $filesToDelete = [];
private $logs = [];
public function findDeadCode($directory) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory)
);
foreach ($iterator as $file) {
if ($file->getExtension() !== 'php') continue;
$content = file_get_contents($file->getPathname());
// 查找被注释的大段代码(超过10行)
if (preg_match_all('/\/\*[\s\S]*?\*\//', $content, $matches)) {
foreach ($matches[0] as $comment) {
if (substr_count($comment, "\n") > 10) {
$this->logs[] = "大段注释代码: {$file->getPathname()}";
}
}
}
// 查找死代码(return 后的代码)
if (preg_match_all('/return.*;[\s\S]*?\}/', $content, $deadCode)) {
// 处理...
}
}
}
public function generateReport() {
echo "======= 清理报告 =======\n";
foreach ($this->logs as $log) {
echo "- $log\n";
}
echo "=======================\n";
}
}
// 使用
$cleaner = new CodeCleaner();
$cleaner->findDeadCode('src/');
$cleaner->generateReport();
集成到 CI/CD
# .github/workflows/code-cleanup.yml
name: Code Quality Check
on:
pull_request:
branches: [ main ]
jobs:
code-quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check unused code
run: |
vendor/bin/phpstan analyse src/ --level max
- name: Check duplicates
run: |
vendor/bin/phpmetrics src/ --report-html=report/
最佳实践清单
每日清理习惯
// 1. 删除调试代码
// 删除 var_dump(), print_r(), echo 调试信息
// 2. 保持函数单一职责
class UserService {
// 好:每个方法做一件事
public function getUser($id) { /* ... */ }
public function saveUser($data) { /* ... */ }
// 坏:一个方法做三件事
public function processUser($id, $data, $sendEmail) { /* ... */ }
}
// 3. 及时删除 @deprecated 标记的方法
/**
* @deprecated 请使用 newMethod()
*/
public function oldMethod() {
// 计划下个版本删除
}
版本迭代清理策略
-
版本规划
v1.0: 标记废弃代码 (@deprecated) v2.0: 删除标记的废弃代码 v3.0: 重构优化 -
代码审查清单
- [ ] 是否有未使用的变量/函数/类?
- [ ] 是否有重复的代码块?
- [ ] 是否有注释掉的旧代码?
- [ ] 是否有空的 catch 块?
- [ ] 是否有不必要的魔术方法?
高级技巧:使用 PHP 反射
class DependencyAnalyzer {
public function analyzeUnusedClasses($directory) {
$classes = [];
// 注册自动加载
spl_autoload_register(function ($class) use ($directory) {
$file = $directory . '/' . str_replace('\\', '/', $class) . '.php';
if (file_exists($file)) {
require_once $file;
}
});
// 扫描所有类
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory)
);
foreach ($files as $file) {
if ($file->getExtension() === 'php') {
$content = file_get_contents($file->getPathname());
preg_match('/^namespace\s+([^;]+)/m', $content, $namespace);
preg_match('/^class\s+(\w+)/m', $content, $className);
if (!empty($namespace) && !empty($className)) {
$fullClassName = $namespace[1] . '\\' . $className[1];
// 检查是否在其他地方被实例化
$usageCount = 0;
foreach ($files as $otherFile) {
if ($otherFile->getPathname() !== $file->getPathname()) {
$otherContent = file_get_contents($otherFile->getPathname());
if (strpos($otherContent, $fullClassName) !== false ||
strpos($otherContent, $className[1]) !== false) {
$usageCount++;
}
}
}
if ($usageCount === 0) {
echo "可能未使用的类: {$fullClassName}\n";
}
}
}
}
}
}
清理冗余代码是一个持续的过程,建议:
- 日常维护:每次提交代码前检查是否有死代码
- 工具辅助:使用 PHPStan、PhpMetrics 等工具定期扫描
- 团队规范:建立代码审查机制,禁止冗余代码进入仓库
- 版本管理:有计划地进行大规模清理,配合版本迭代
- 循序渐进:从最容易识别的冗余代码开始,逐步深入
冗余代码就像债务,不清理只会越滚越多,保持代码整洁不仅是对自己负责,也是对团队和未来的维护者负责。