PHP适配器模式核心是编写显式转换参数/返回值的中间类,而非仅实现接口;因interface不自动重排参数或转换类型,需在适配器中手动映射并严格匹配PHP 8.5类型声明。

PHP里实现适配器模式,核心不是“实现接口”,而是写一个明确包装旧对象、严格转换参数/返回值的中间类——否则光有implements根本跑不起来。
为什么直接 implements 同一个 interface 不起作用
常见错误是让新旧类都实现 MailInterface,就以为能互换。但老类方法可能是 sendMail($to, $subject, $body),新接口要求的是 send($recipient, $title, $content)。PHP 的 interface 只校验方法名和签名,不自动做参数重排或类型转换。
- 调用方传进来的参数顺序、键名、类型,必须由适配器显式接收并转给被适配对象
- 如果老类返回
bool,而新接口声明返回string|null,适配器里就得补上return $success ? 'ok' : null; - PHP 8.5 下,类型不匹配会直接报
Fatal error: Declaration of ... must be compatible with ...
对象适配器怎么写:构造函数 + 委托调用
PHP 不支持多继承,所以只用对象适配器(组合),不用类适配器(继承)。关键点在构造函数注入被适配对象,并在每个方法里手动转发。
class OldMailer
{
public function sendMail($to, $subject, $body)
{
// legacy logic
return true;
}
}
<p>interface MailInterface
{
public function send(string $recipient, string $title, string $content): bool;
}</p><p>class OldMailerAdapter implements MailInterface
{
public function __construct(private OldMailer $oldMailer) {}</p><pre class="brush:php;toolbar:false;">public function send(string $recipient, string $title, string $content): bool
{
// 参数顺序转换:$recipient → $to, $title → $subject, $content → $body
return $this->oldMailer->sendMail($recipient, $title, $content);
}}
- 构造函数必须显式接收
$oldMailer实例,不能靠new OldMailer()硬编码 - 方法体内不做业务逻辑,只做映射;异常也别吞掉,该 throw 就 throw,但建议统一转成你自己的
AdapterException - PHP 8.5 要求返回类型严格匹配,
sendMail()返回bool,那send()就不能声明为void或mixed
PHP 8.5 下最容易翻车的三个类型坑
PHP 8.5 没废掉适配器模式,但它会让旧适配器代码立刻暴露问题,尤其在类型声明模糊的地方。
- 老接口返回
array,新接口要list<string></string>?得在适配器里加array_values(array_filter($arr, 'is_string'))显式转换,不能依赖隐式行为 - 用了
#[\ReturnTypeWillChange]的旧适配器,在declare(strict_types=1)下可能升级为 fatal error,建议直接补全返回类型 -
__call()方法必须声明返回类型,比如底层可能返回string|false,那就得写成public function __call(string $name, array $arguments): string|false,写mixed是自欺欺人
什么时候不该用适配器模式
适配器是薄层转换,不是重构工具。以下情况硬套适配器只会让问题更糟:
- 被适配类和目标接口语义完全不同(比如把支付 SDK 适配成日志接口)——这不是接口不兼容,是设计错位
- 需要在适配器里大量判断分支、拼装数据、调用多个方法才能完成一个目标接口调用——说明抽象层级已断裂,该重新划界
- 老类本身没测试、文档、类型提示,且行为不稳定——先封住它的副作用,再适配,别一边适配一边猜它啥时候返回 null
真正难的从来不是写一个 implements,而是确认哪一层该切、谁负责创建被适配对象、以及当两边类型系统开始打架时,你敢不敢删掉那行 /** @var mixed */ 注释。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











