PHP适配器模式关键在于选用对象适配器(implements目标接口+组合源实例),因其兼容不可修改类、支持多源适配、避免继承限制与测试难题,而类适配器因PHP无多继承、final类限制及类型安全风险在现代项目中基本弃用。

PHP 实现适配器模式的关键不在于“写得多”,而在于“选对关系”:绝大多数真实项目该用对象适配器(委托),而不是类适配器(继承)。
什么时候必须用对象适配器(implements + 组合)
当你需要适配一个已存在的类,且它无法被修改(比如第三方 SDK、框架内置类、final 类),或者你希望适配多个不同源时,只能走对象适配器路线。
-
Adapter类必须implements目标接口(如PaymentGateway),不能只继承源类 -
Adapter内部持有源对象实例(如$this->legacySystem),不是extends LegacyPaymentSystem - 如果源类方法名、参数顺序、返回值与目标接口不一致,就在
Adapter的方法体内做显式转换,别试图“重命名源方法”来凑合 - 注意构造函数类型提示:传入
LegacyPaymentSystem时,要写LegacyPaymentSystem $legacySystem,否则 PHP 8+ 会报TypeError
为什么类适配器(extends + implements)在现代 PHP 中基本不用
类适配器要求 Adapter 同时继承源类并实现目标接口,看似省代码,但实际踩坑率极高。
- PHP 不支持多继承,一旦源类已继承自其他类(比如
class StripeClient extends ApiClient),你就没法再让Adapter继承它 - 源类是
final的话(Laravel 很多核心类、Symfony 的Response等),直接语法报错:Cannot extend final class - 即使能继承,你也无法安全地替换源类的依赖——比如源类内部调用了
$this->httpClient,而你没控制权,就可能引发行为不一致 - 测试困难:mock 一个继承链过深的
Adapter,比 mock 一个纯组合的类麻烦得多
__call() 能不能用来做通用适配器?
不能当主力方案,仅适合极简胶水层。它掩盖了接口契约,破坏 IDE 自动补全和静态分析(PHPStan/psalm 会报 Call to undefined method)。
立即学习“PHP免费学习笔记(深入)”;
- 如果你在
Adapter里写public function __call($name, $args) { return $this->adaptee->$name(...$args); },等于放弃类型安全 - 一旦源类方法签名变更(比如加了个必填参数),运行时才崩,而不是在
phpstan analyse阶段暴露 - 只有在快速原型或临时兼容旧脚本时可酌情用,上线前务必补全显式方法封装
真实项目中容易被忽略的边界点
适配器不是“写完就扔”的一次性工具,它的生命周期和错误处理逻辑必须跟源系统对齐。
- 源类抛出异常类型(如
LegacyApiException)不能原样透传给上层——客户端只认PaymentException,得在processPayment()里catch后重新 throw - 如果源类有状态(比如连接池、token 缓存),
Adapter实例是否应复用?通常要设计为无状态,把状态交给源实例管理 - 日志打点别只记
Adapter::processPayment,要带上源类实际执行的方法名和耗时,否则排查超时问题时找不到根因



















