SOLID原则在PHP开发中必须遵循,否则会导致if-else蔓延、new散落、修改耦合、测试困难等真实维护成本;SRP要求User只持数据和业务规则,I/O操作交由独立类;OCP通过策略模式实现扩展不修改;DIP强调依赖抽象而非具体实现;ISP和LSP进一步保障接口精简与子类可替换性。

PHP开发需要遵循SOLID原则,不是为了“写得更像教科书”,而是因为不遵循它,if-else会蔓延、new会散落在各处、改一个支付方式要动三个文件、加个日志就要重测全部用户流程——这些是真实发生的维护成本。
当User类开始发邮件、连数据库、校验密码时,你就已经违反SRP
单一职责原则(SRP)在PHP里最常被打破的场景,就是把“数据持有者”变成“全能执行者”。比如:
class User {
public $email;
public function save() { /* PDO操作 */ }
public function sendWelcomeEmail() { /* SwiftMailer调用 */ }
public function validatePassword($pwd) { /* 密码强度逻辑 */ }
}
问题不在语法错误,而在于:数据库驱动升级要改这个类,邮件服务商切换要改这个类,密码策略调整还要改这个类。三者互不相关,却被迫耦合。
- 真正该做的,是让
User只持数据和业务规则(如isPasswordStrong()),不碰I/O -
UserRepository专管save()/find(),换MySQL或MongoDB只动它 -
EmailService负责发送,对接SendGrid或SMTP只改它,不影响用户注册主流程
每次加一种支付方式都要改PaymentProcessor::pay(),说明OCP没落地
开闭原则(OCP)在PHP中失效的典型信号,就是函数里出现不断增长的switch或if ($type === 'xxx')分支。这不是“写得不够简洁”,而是设计上拒绝扩展。
正确做法是定义接口并组合:
interface PaymentStrategy {
public function charge(float $amount): bool;
}
class AlipayStrategy implements PaymentStrategy { /* ... */ }
class WechatPayStrategy implements PaymentStrategy { /* ... */ }
class OrderProcessor {
private PaymentStrategy $strategy;
public function __construct(PaymentStrategy $strategy) {
$this->strategy = $strategy;
}
}
- 新增银联支付?只需写
UnionPayStrategy,不碰OrderProcessor - 测试时可注入
MockPaymentStrategy,无需启动真实网关 - 如果框架不支持依赖注入(如原生PHP),至少用构造函数传参,别在方法里
new具体类
为什么new MySQLUserRepo()比new UserRepository()更危险?
依赖倒置原则(DIP)在PHP中容易被忽略,是因为动态语言不强制类型检查——但它的破坏力是静默的。当你在业务类里直接new具体实现,就等于把高层逻辑(如订单创建)和低层细节(MySQL连接参数、PDO异常处理)焊死在一起。
后果很实际:
- 单元测试必须连真实数据库,CI跑一次要10秒
- 想迁移到Redis缓存用户?得全局搜索
new MySQLUserRepo并逐个替换 - Laravel的
app(UserRepositoryInterface::class)能自动切换实现,前提是你的代码依赖的是UserRepositoryInterface,而不是MySQLUserRepo
接口隔离原则(ISP)和里氏替换原则(LSP)则进一步收紧这种松散依赖:ISP防止你定义一个包含read()/write()/backup()的大接口,结果只读模块也被迫实现backup();LSP确保你注入FileLogger或DBLogger时,调用log()不会因参数类型或返回值差异而崩掉。
真正难的不是记住五条原则,而是每次写new、加if、扩class前,多问一句:这个变化,未来会不会和其他变化互相干扰?如果是,现在拆,比三个月后重构省十倍力气。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











