本文目录导读:

- 策略一:定义接口(Interface)+ 依赖注入(DI)—— 最推荐
- 策略二:门面模式(Facade Pattern)—— 静态代理
- 策略三:策略模式(Strategy Pattern)—— 多实现切换
- 策略四:包一层“防腐层”(Anti-Corruption Layer)
- 策略五:基于配置文件 + 工厂模式(配合依赖注入容器)
- 总结:PHP 抽象第三方的 3 条黄金法则
在 PHP 中抽象第三方(第三方库、API、服务等)的核心目标是解耦和可替换性——让你的业务代码不直接依赖具体的第三方实现,而是依赖你自己定义的接口(Interface)或抽象类(Abstract Class)。
以下是 PHP 中抽象第三方的几种主流策略和最佳实践,按推荐程度从高到低排列:
定义接口(Interface)+ 依赖注入(DI)—— 最推荐
这是最标准、最解耦的方式,你定义自己的接口,然后写一个适配器(Adapter)来实现这个接口,内部去调用第三方库。
适用场景:第三方 SDK、支付网关、邮件服务、短信服务等。
步骤:
- 定义业务接口:用业务语言的视角定义方法,而不是第三方的视角。
- 编写适配器:写一个实现该接口的类,在方法内部调用第三方库。
- 依赖注入:通过构造函数或 Setter 将适配器注入到你的业务类中。
代码示例:
假设你要抽象一个短信发送服务(第三方是 AliyunSms)。
<?php
// 1. 定义你的业务接口 (不要引入任何第三方命名空间)
interface SmsSenderInterface
{
public function send(string $phone, string $message): bool;
}
// 2. 第三方 SDK 的模拟类 (假设是 Aliyun)
class AliyunSmsSDK {
public function sendSms($mobile, $templateCode, $params) {
// 实际的阿里云调用逻辑
echo "【阿里云】发送给: $mobile, 内容: " . json_encode($params) . PHP_EOL;
return true;
}
}
// 3. 编写适配器 (实现我们的接口,内部调用阿里云)
class AliyunSmsAdapter implements SmsSenderInterface
{
private AliyunSmsSDK $sdk;
public function __construct(AliyunSmsSDK $sdk)
{
$this->sdk = $sdk;
}
public function send(string $phone, string $message): bool
{
// 将我们的业务参数($message)转换为 SDK 需要的参数格式($params)
// 这就是“适配”的过程
$params = ['content' => $message];
// 调用第三方
return $this->sdk->sendSms($phone, 'SMS_CODE', $params);
}
}
// 4. 业务逻辑层(只依赖接口,不依赖具体实现)
class UserService
{
private SmsSenderInterface $smsSender;
// 依赖注入:传入接口类型
public function __construct(SmsSenderInterface $smsSender)
{
$this->smsSender = $smsSender;
}
public function register(string $phone): void
{
// ... 注册逻辑
$this->smsSender->send($phone, '欢迎注册!');
}
}
// --- 使用示例 ---
// 组装依赖(通常在框架的 ServiceProvider / 容器配置中进行)
$sdk = new AliyunSmsSDK();
$adapter = new AliyunSmsAdapter($sdk);
$service = new UserService($adapter);
$service->register('13800000000');
// 如果想要换成腾讯云短信,只需要再写一个 TencentSmsAdapter 实现 SmsSenderInterface,然后注入即可,业务代码无需改动。
优点:完全解耦;易于单元测试(可以 Mock 掉 SmsSenderInterface);符合 SOLID 原则(依赖倒置)。
门面模式(Facade Pattern)—— 静态代理
适用场景:当你不想在业务代码里处处 new 第三方类,想提供一个简单的静态入口时(类似 Laravel 的 Facade)。
实现方式:结合 __callStatic 魔术方法 + 服务容器。
<?php
// 假设我们有一个服务容器 Class (极简模拟)
class App {
public static array $instances = [];
public static function bind(string $key, callable $resolver): void {
self::$instances[$key] = $resolver;
}
public static function make(string $key) {
return (self::$instances[$key])();
}
}
// 适配器 (如上例所示 AliyunSmsAdapter 或 TencentSmsAdapter)
class SmsAdapter {
public function send(string $phone, string $message): bool {
echo "【通用适配器】发送 SMS" . PHP_EOL;
return true;
}
}
// 门面类
class SmsFacade
{
// 让静态调用转向实例方法
public static function __callStatic(string $name, array $arguments)
{
// 从容器中获取底层适配器实例
$instance = App::make('sms.sender');
return $instance->{$name}(...$arguments);
}
}
// 容器绑定
App::bind('sms.sender', function () { return new SmsAdapter(); });
// 业务使用(非常简洁)
SmsFacade::send('13900000000', '这是门面发送的短信!');
优点:调用简单(静态方法),不需要在构造函数中传递依赖。 缺点:隐藏了依赖关系(魔法),测试时需要 Mock 静态方法(比 Mock 实例方法稍微麻烦一点)。
策略模式(Strategy Pattern)—— 多实现切换
适用场景:多个第三方提供同类服务(比如多个支付网关,或多种存储引擎),需要根据业务逻辑或配置动态选择。
实现方式:维护一个“策略容器”,根据上下文(如请求参数、环境变量)返回对应的适配器实例。
<?php
// 支付接口
interface PaymentGatewayInterface {
public function pay(float $amount): string;
}
// 微信支付适配器
class WechatPay implements PaymentGatewayInterface {
public function pay(float $amount): string {
return "使用微信支付 $amount 元,订单号: WX-" . uniqid();
}
}
// 支付宝适配器
class Alipay implements PaymentGatewayInterface {
public function pay(float $amount): string {
return "使用支付宝支付 $amount 元,订单号: ALI-" . uniqid();
}
}
// 上下文管理器 (策略选择器)
class PaymentContext
{
private array $strategies;
public function __construct() {
$this->strategies = [
'wechat' => new WechatPay(),
'alipay' => new Alipay(),
];
}
public function pay(string $channel, float $amount): string {
if (!isset($this->strategies[$channel])) {
throw new \Exception("不支持的支付渠道: $channel");
}
return $this->strategies[$channel]->pay($amount);
}
}
// 使用
$context = new PaymentContext();
echo $context->pay('alipay', 100.50);
优点:逻辑清晰,便于横向扩展(添加新渠道只需增加实现类并注册)。 缺点:需要编写较多的类(一个渠道一个类)。
包一层“防腐层”(Anti-Corruption Layer)
适用场景:当第三方 API 的数据结构非常复杂,或者经常变化时(ERP 系统对接、外部云服务返回的数据模型),可以在接口层做数据的转换和校验,避免第三方的坏味道渗透到你的领域模型。
示例:第三方返回 XML,你希望业务层用数组或对象。
基于配置文件 + 工厂模式(配合依赖注入容器)
适用场景:对第三方连接配置(如 API Key、Secret)的管理,并实现“换供应商只改配置文件”。
// config/sms.php
return [
'default' => env('SMS_DRIVER', 'aliyun'),
'drivers' => [
'aliyun' => ['key' => '...', 'secret' => '...'],
'tencent' => ['key' => '...', 'secret' => '...'],
],
];
// SmsFactory.php
class SmsFactory {
public static function create(array $config): SmsSenderInterface {
return match ($config['default']) {
'aliyun' => new AliyunSmsAdapter(new AliyunSmsSDK($config['drivers']['aliyun'])),
'tencent' => new TencentSmsAdapter(new TencentSmsSDK($config['drivers']['tencent'])),
default => throw new \InvalidArgumentException('Unsupported driver'),
};
}
}
PHP 抽象第三方的 3 条黄金法则
- 永远不要在你的业务代码
use第三方包的类(除了在最外层的 Adapter 中)。 - 定义“你自己的接口”,接口的方法名和参数请用你业务的语言描述,而不是第三方的术语。
- 使用依赖注入(构造器或属性注入),而不是在类内部去
new第三方对象。
从策略一(接口 + 适配器)开始实践,它是最简单且收益最高的方式,如果项目引入了 Composer 和现代框架(如 Laravel 的 Container 或 Symfony 的 DI),上述过程将更加自动化。