本文目录导读:

在PHP开发中,抽取公共模块(或公共代码)的核心原则可以概括为:“高内聚、低耦合” + “避免重复(DRY)” + “职责单一(SRP)”。
但实际落地时,针对不同粒度(函数、类、包、服务),有很多具体的坑要避开,以下是PHP特有的、且实用性极强的公共模块抽取原则:
核心架构原则
依赖倒置(面向接口,而非具体实现)
- 原则:抽取公共模块时,不要让上层业务依赖底层模块的具体类,而应依赖其接口或抽象类。
- PHP实践:使用 Composer 管理包时,通过
composer.json中的require引入接口包,具体实现由容器(DI Container)注入,这样,当底层从Redis换成Memcached时,上层业务代码零改动。
优先组合,而不是继承
- 原则:Java/C++ 喜欢用抽象类(Abstract Class)来抽取公共逻辑,但 PHP(尤其是现代PHP)更推荐使用 Trait 或 组合模式。
- PHP具体场景:
- Trait:用于抽取出多个不相关类中的重复方法(例如日志记录、权限校验),虽然好,但不要滥用,Trait 用多了会导致代码难以追踪。
- 组合/服务类:如果一个模块有状态(比如数据库连接、文件句柄),应该拆成一个独立的 Service 类(如
LoggerService),通过构造函数注入,而不是让业务类去继承它。
单一职责(Single Responsibility Principle)
- 原则:一个公共模块只做一件事,并且做好。
- PHP反例:不要创建一个
CommonHelper.php文件,里面塞满了formatDate()、sendSms()、generateToken()这些毫无关联的函数,这种“上帝类”很难测试和维护。 - 正解:拆分为
DateHelper、SmsNotifier、TokenService。
开闭原则(Open-Closed)
- 原则:对扩展开放,对修改关闭。
- PHP实践:公共模块抽出来后,需求变更时应通过新增代码(如策略模式、回调函数)来扩展,而不是直接修改公共模块的代码本身。
PHP 工程实现原则(具体细节)
避免“万能”函数文件
- 错误:有些项目习惯建立一个
app/helpers.php,把乱七八糟的全局函数塞进去,并用 Composer 的files自动加载。 - 正确:尽量用静态方法或门面(Facade),除非是极简的数组操作或字符串操作(如
array_get),函数文件一旦多了,命名冲突(尤其和第三方包冲突)是致命的。
明确“公共”的边界(上下文无关)
- 原则:抽出来的模块不能携带具体业务上下文。
- PHP反例:在
Common/ExcelExport模块中,exportUserList方法里包含WHERE user_status = 1这样的SQL,这就是错误,公共模块只负责接收参数并输出文件,具体查询逻辑应留在业务层调用。
配置外部化(Config Externalization)
- 原则:公共模块中不要写死任何环境相关的常数(如API Key、数据库表名、文件路径)。
- PHP实践:使用环境变量(
getenv()或$_ENV),通过依赖注入的方式将配置传递给公共模块。
分层的粒度控制(不要过度抽取)
- 应用层(Laravel/ThinkPHP):只抽取跨项目的通用逻辑(如支付接口、物流查询、统一响应格式)。不要把业务表字段的格式化逻辑抽到公共层。
- 基础层(框架无关):抽取纯逻辑(日期计算、字符串加密、设计模式组件),这部分则一定要保证和框架完全解耦(不依赖
Illuminate或ThinkPHP的类)。
命名与依赖管理原则
依赖关系严格定向
- 公共模块 不允许 反向依赖业务模块。
- 依赖方向:
业务代码->业务公共模块->基础公共模块->框架/Vendor包。
命名空间(Namespace)的统一
- 抽取的模块要有固定的命名空间前缀,
App\Lib\或App\Support\。 - 防止和
Vendor中的全局命名空间冲突,避免简单的 前缀命名(如class _Common是古老的PHP4风格,不推荐)。
用 Composer 进行物理隔离(重要)
- 如果项目是多应用程序(如管理后台 + API + 前端网站),应将公共模块抽取为独立的内部 Composer 包(private repository),而不是用
git submodule或复制目录。 - 这能让版本管理更清晰(
require acme/common: ^1.2)。
实战反例诊断(哪些不该抽?)
- “看起来像”但不属于公共的业务规则:
UserModel::getGold()方法,如果只有这一个项目使用,它属于业务模型,不要抽入公共Common目录。
- 只出现过一次的代码:如果一个代码块只在两个极端场景下被复用,但逻辑差异巨大,强行通过参数切换只能导致“面条式”代码。
- 包含 HTTP 请求或 Session 的逻辑:公共模块严禁直接用
$_SESSION或$_GET,如果需要,必须通过参数传递跨层数据。
判断是否抽取成功的“三看”清单
| 指标 | 优秀 | 差 |
|---|---|---|
| 一看依赖 | 模块不依赖外部的全局状态,测试时只需new即可。 | 模块里写死了 DB::table('xxx') 或 file_put_contents。 |
| 二看调用 | 调用者只依赖于1个方法或1个职责。 | 调用者必须传入 $flag=1 或者 $type='admin' 来走不同的分支。 |
| 三看变更 | 修改同步需求时,只需改公共模块1处。 | 修改了公共模块,导致另外3个无关业务报错(强耦合)。 |
最后的建议:在PHP中,“不成熟的抽象”(The Wrong Abstraction)比“重复代码”(Duplication)更危险,如果你不确定那个重复逻辑未来会不会变,先复制一份,等第三次出现时再考虑抽取,这往往比一开始就设计过度要好。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播