适配器模式在php中需手动桥接不兼容接口,不能仅靠实现同一接口;必须显式注入被适配对象、实现目标接口、仅做参数转换等薄层适配,且注意自动加载配置与类型安全。

适配器模式在 PHP 中不是靠“实现接口”就能自动生效的
PHP 的接口(interface)只约束方法签名,不提供行为转换能力。所谓“适配”,本质是手动桥接两个不兼容的接口或类——比如老代码用 sendEmail($to, $subject, $body),新邮件服务只接受 send(Message $msg)。这时候不能靠改接口定义来“兼容”,得写一个中间类把调用转过去。
常见错误是以为只要让新旧类都实现同一个 MailSenderInterface 就算完成适配,结果发现老逻辑传参方式根本进不去新实现里。
- 适配器必须显式接收被适配对象(如老类实例),并在自己方法中调用它
- 适配器自身要实现目标接口(如新系统期望的
MessageSenderInterface),否则无法注入或替换 - 不要试图在适配器里重写业务逻辑,只做参数重组、返回值包装、异常转换等薄层转换
写一个标准适配器类:注意构造函数参数和方法委托
以适配旧版 LegacyMailer 到新版 MessageSenderInterface 为例。关键点不在“有没有 interface”,而在“谁负责创建旧对象”和“怎么传参”。
class LegacyMailerAdapter implements MessageSenderInterface
{
private LegacyMailer $legacy;
public function __construct(LegacyMailer $legacy)
{
$this->legacy = $legacy;
}
public function send(Message $message): bool
{
// 把 Message 对象拆成三个字符串参数,推给旧类
return $this->legacy->sendEmail(
$message->getTo(),
$message->getSubject(),
$message->getBody()
);
}
}
这里容易踩坑的是:$legacy 必须由外部传入(DI 容器或手动 new),不能在适配器里自己 new——否则就失去替换灵活性;send() 方法体内不做额外判断或格式化,那是 Message 类该干的事。
类型提示冲突时,用 DTO 或封装类绕过 strict 检查
当旧类方法返回 array,而新接口要求返回 ResponseInterface,直接强转会报错,且破坏类型安全。这时别硬 cast,而是封装一层轻量 DTO。
- 新建
LegacyResponseWrapper类,构造时接收原始array,提供getStatusCode()等方法 - 适配器的
send()返回这个 wrapper 实例,而不是试图伪造ResponseInterface实现 - 如果下游强依赖
ResponseInterface,再写一个继承自SymfonyComponentHttpFoundationResponse的子类,仅用于测试或过渡期
硬塞 implements ResponseInterface 到 wrapper 里,却只实现部分方法,会在运行时触发 “Call to undefined method” 错误,比类型不匹配更难排查。
Composer 自动加载与命名空间对适配器可见性的影响
适配器类如果放在 src/Adapter/ 下但未在 composer.json 的 "autoload" 中声明 PSR-4 映射,会导致 Class 'AppAdapterLegacyMailerAdapter' not found。这不是逻辑问题,是加载失败。
- 检查
composer.json是否包含:"App\": "src/" - 确认文件路径为
src/Adapter/LegacyMailerAdapter.php,且命名空间是AppAdapter - 运行
composer dump-autoload,尤其在新增类后忘记执行这步,会卡住半天
很多团队把适配器扔进 app/ 目录下随意命名,结果上线后因 autoloader 缓存没刷新,生产环境报错而本地正常——这种环境差异最消耗调试时间。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











