PHP依赖注入容器怎么用

wen PHP项目 2

本文目录导读:

PHP依赖注入容器怎么用

  1. 什么是依赖注入容器
  2. 基本使用示例
  3. 使用PHP-DI(功能更完整的容器)
  4. 使用Laravel的服务容器
  5. 自定义简单容器
  6. 最佳实践
  7. 注意事项

我来通过一个实际例子,详细介绍PHP依赖注入容器(DIC)的用法。

什么是依赖注入容器

依赖注入容器是管理类依赖的工具,它负责:

  • 创建对象及其依赖
  • 存储已注册的服务
  • 自动解析依赖关系

基本使用示例

使用Pimple容器(最流行的轻量级容器)

<?php
// 1. 安装
// composer require pimple/pimple
// 2. 基本容器使用
require 'vendor/autoload.php';
use Pimple\Container;
$container = new Container();
// 注册服务(延迟加载)
$container['db'] = function ($c) {
    return new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
};
// 使用服务
$db = $container['db'];

完整业务示例

<?php
use Pimple\Container;
class UserRepository
{
    private $db;
    public function __construct(PDO $db)
    {
        $this->db = $db;
    }
    public function findUser($id)
    {
        $stmt = $this->db->prepare('SELECT * FROM users WHERE id = ?');
        $stmt->execute([$id]);
        return $stmt->fetch();
    }
}
class UserService
{
    private $userRepository;
    private $logger;
    public function __construct(UserRepository $userRepository, Logger $logger)
    {
        $this->userRepository = $userRepository;
        $this->logger = $logger;
    }
    public function getUserProfile($userId)
    {
        $this->logger->log("Fetching user: $userId");
        return $this->userRepository->findUser($userId);
    }
}
class Logger
{
    public function log($message)
    {
        echo "[LOG] $message\n";
    }
}
// 配置容器
$container = new Container();
// 注册基础服务
$container['db'] = function ($c) {
    return new PDO('mysql:host=localhost;dbname=test', 'root', 'password');
};
$container['logger'] = function ($c) {
    return new Logger();
};
// 注册业务服务
$container['user_repository'] = function ($c) {
    return new UserRepository($c['db']);
};
$container['user_service'] = function ($c) {
    return new UserService(
        $c['user_repository'],
        $c['logger']
    );
};
// 使用容器获取服务
$userService = $container['user_service'];
$user = $userService->getUserProfile(123);
print_r($user);

使用PHP-DI(功能更完整的容器)

<?php
// 安装
// composer require php-di/php-di
use DI\Container;
use DI\ContainerBuilder;
// 创建容器
$containerBuilder = new ContainerBuilder();
$containerBuilder->useAutowiring(true); // 启用自动装配
$container = $containerBuilder->build();
// 简单使用 - 自动解析依赖
class Mailer
{
    public function send($to, $message)
    {
        echo "Sending mail to $to: $message\n";
    }
}
class UserNotifier
{
    private $mailer;
    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }
    public function notifyUser($userId)
    {
        $this->mailer->send("user$userId@example.com", "Welcome!");
    }
}
// 容器自动解析所有依赖
$notifier = $container->get(UserNotifier::class);
$notifier->notifyUser(1);
// 手动配置
$containerBuilder->addDefinitions([
    Mailer::class => function () {
        return new Mailer();
    },
    // 也可以绑定接口
    'Logger' => function () {
        return new Logger();
    },
]);
// 使用参数注入
class EmailService
{
    private $smtpHost;
    private $smtpPort;
    public function __construct($smtpHost = 'localhost', $smtpPort = 25)
    {
        $this->smtpHost = $smtpHost;
        $this->smtpPort = $smtpPort;
    }
}
$containerBuilder->addDefinitions([
    EmailService::class => function () {
        return new EmailService('smtp.example.com', 587);
    },
]);

使用Laravel的服务容器

<?php
// Laravel框架中的使用
// 1. 绑定服务
app()->bind('PaymentGateway', function ($app) {
    return new PaymentGateway('api-key');
});
// 2. 单例绑定
app()->singleton('Logger', function ($app) {
    return new Logger();
});
// 3. 自动注入到控制器
class OrderController extends Controller
{
    private $paymentGateway;
    // Laravel自动解析依赖
    public function __construct(PaymentGateway $paymentGateway)
    {
        $this->paymentGateway = $paymentGateway;
    }
    public function processOrder()
    {
        return $this->paymentGateway->charge();
    }
}
// 4. 使用门面/Facade
public function test()
{
    $payment = app('PaymentGateway');
    $logger = app('Logger');
}

自定义简单容器

<?php
class SimpleContainer
{
    private $bindings = [];
    private $instances = [];
    // 绑定服务
    public function bind($abstract, $concrete = null)
    {
        $this->bindings[$abstract] = $concrete ?? $abstract;
    }
    // 绑定单例
    public function singleton($abstract, $concrete = null)
    {
        $this->bind($abstract, $concrete);
        $this->bindings[$abstract] = function ($container) use ($abstract) {
            if (!isset($this->instances[$abstract])) {
                $this->instances[$abstract] = $container->build($abstract);
            }
            return $this->instances[$abstract];
        };
    }
    // 解析服务
    public function make($abstract)
    {
        if (isset($this->bindings[$abstract])) {
            $binding = $this->bindings[$abstract];
            if ($binding instanceof \Closure) {
                return $binding($this);
            }
            return $this->build($binding);
        }
        return $this->build($abstract);
    }
    // 自动构建类(实现简单的自动注入)
    private function build($class)
    {
        $reflector = new ReflectionClass($class);
        if (!$reflector->isInstantiable()) {
            throw new Exception("Class $class is not instantiable");
        }
        $constructor = $reflector->getConstructor();
        if (is_null($constructor)) {
            return new $class;
        }
        $parameters = $constructor->getParameters();
        $dependencies = [];
        foreach ($parameters as $parameter) {
            $dependency = $parameter->getType();
            if ($dependency && !$dependency->isBuiltin()) {
                // 递归解析依赖
                $dependencies[] = $this->make($dependency->getName());
            } else {
                // 基本类型参数,检查是否有默认值
                if ($parameter->isDefaultValueAvailable()) {
                    $dependencies[] = $parameter->getDefaultValue();
                } else {
                    throw new Exception("Cannot resolve dependency");
                }
            }
        }
        return $reflector->newInstanceArgs($dependencies);
    }
}
// 使用自定义容器
$container = new SimpleContainer();
// 注册服务
$container->bind('Logger', function () {
    return new Logger();
});
// 自动解析UserService及其所有依赖
$userService = $container->make(UserService::class);

最佳实践

<?php
// 1. 使用接口抽象
interface PaymentGatewayInterface
{
    public function charge(float $amount);
}
class StripeGateway implements PaymentGatewayInterface
{
    public function charge(float $amount)
    {
        // 实现
    }
}
class PaypalGateway implements PaymentGatewayInterface
{
    public function charge(float $amount)
    {
        // 实现
    }
}
// 2. 配置驱动
$config = [
    'payment_gateway' => 'stripe',
];
$container['payment_gateway'] = function ($c) use ($config) {
    switch ($config['payment_gateway']) {
        case 'stripe':
            return new StripeGateway();
        case 'paypal':
            return new PaypalGateway();
        default:
            throw new \Exception('Unknown gateway');
    }
};
// 3. 延迟加载
$container['heavy_service'] = function ($c) {
    // 仅有需要时才创建
    return new HeavyService();
};

注意事项

  1. 性能考虑:使用singleton减少重复创建
  2. 调试方便:容器应该支持查看已注册的服务
  3. 避免服务定位器反模式:不要过度依赖容器
  4. 依赖关系清晰:尽量明确显式的依赖

依赖注入容器的核心优势:

  • 解耦:类之间不直接实例化依赖
  • 可测试性:方便替换mock对象
  • 可维护性:统一管理服务
  • 灵活性:方便切换实现

选择合适的容器(Pimple、PHP-DI或框架内置的)取决于项目复杂度和你需要的高级特性。

抱歉,评论功能暂时关闭!