PHP中的依赖注入(Dependency Injection):完整指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

PHP中依赖注入(DI)简介



现代PHP开发离不开对组件松耦合原则的理解。依赖注入(DI)是创建灵活、可测试且易于维护的应用的关键设计模式之一。不同于类自身创建依赖(例如数据库对象或日志记录器),依赖是从外部传入的。这从根本上改变了代码的架构。



本文将详细探讨PHP中的依赖注入是什么、有哪些注入方法、DI容器如何工作,以及为什么该模式已成为Laravel、Symfony和Yii2等现代框架的标准。



1. 问题:紧耦合



在讨论解决方案之前,我们先看看问题所在。一个经典例子是UserController类,它在内部直接创建Database对象:



<?phpclass Database {    public function query(string $sql): array {        // 连接数据库并执行查询        return [];    }}

class UserController { private Database $db;

public function __construct() { // 紧耦合依赖——在构造函数内部创建对象 $this->db = new Database(); }

public function getUsers(): array { return $this->db->query('SELECT * FROM users'); }}?>


这种方法的缺点:

  • 测试困难:无法在单元测试中将Database替换为模拟对象。
  • 违反单一职责原则(SRP):类不仅要负责业务逻辑,还要负责创建依赖。
  • 修改困难:如果想将MySQL替换为PostgreSQL,就必须修改所有创建Database的类中的代码。


2. 解决方案:依赖注入



依赖注入建议从外部传入依赖。主要有三种注入方法:通过构造函数、通过setter方法以及通过接口(方法)。我们逐一来看。



2.1. 构造函数注入



这是最常见且推荐的方法。依赖在对象创建时一次性传入,后续无法更改。



<?phpclass UserController {    private Database $db;

// 依赖从外部传入 public function __construct(Database $db) { $this->db = $db; }

public function getUsers(): array { return $this->db->query('SELECT * FROM users'); }}

// 使用方式:$db = new Database();$controller = new UserController($db);?>


优点:确保依赖始终可用;对于必需依赖是理想选择。缺点:如果类有多个依赖,构造函数会变得臃肿。



2.2. Setter注入



适用于在对象创建后可设置的可选依赖。



<?phpclass Logger {    public function log(string $message): void {        echo $message;    }}

class UserController { private ?Logger $logger = null;

public function setLogger(Logger $logger): void { $this->logger = $logger; }

public function getUsers(): array { if ($this->logger) { $this->logger->log('正在获取用户...'); } // 获取用户的逻辑 return []; }}

// 使用方式:$controller = new UserController();$controller->setLogger(new Logger());?>


优点:

博客

书籍推荐