0

0

懂得 PHP 依赖注入 | Laravel IoC容器

php中文网

php中文网

发布时间:2016-06-13 12:28:22

|

910人浏览过

|

来源于php中文网

原创

理解 PHP 依赖注入 | Laravel IoC容器

  laravel框架的依赖注入确实很强大,并且通过容器实现依赖注入可以有选择性的加载需要的服务,减少初始化框架的开销,下面是我在网上看到的一个帖子,写的很好拿来与大家分享,文章从开始按照传统的类设计数据库连接一直到通过容器加载服务这个高度解耦的设计展示了依赖注入的强大之处,值得我们借鉴和学习。

-----------------------------------------------------------分割线下面是大牛的原文----------------------------------------------------------

      原文连接(http://www.yuansir-web.com/2014/03/20)

  首先,我们假设,我们要开发一个组件命名为SomeComponent。这个组件中现在将要注入一个数据库连接。在这个例子中,数据库连接在component中被创建,这种方法是不切实际的,这样做的话,我们将不能改变数据库连接参数及数据库类型等一些参数。

 1 php 2  3 class SomeComponent 4 { 5  6     /** 7      * The instantiation of the connection is hardcoded inside 8      * the component so is difficult to replace it externally 9      * or change its behavior10      */11     public function someDbTask()12     {13         $connection = new Connection(array(14             "host" => "localhost",15             "username" => "root",16             "password" => "secret",17             "dbname" => "invo"18         ));19 20         // ...21     }22 23 }24 25 $some = new SomeComponent();26 $some->someDbTask();

为了解决上面所说的问题,我们需要在使用前创建一个外部连接,并注入到容器中。就目前而言,这看起来是一个很好的解决方案:

 1 php 2  3 class SomeComponent 4 { 5  6     protected $_connection; 7  8     /** 9      * Sets the connection externally10      */11     public function setConnection($connection)12     {13         $this->_connection = $connection;14     }15 16     public function someDbTask()17     {18         $connection = $this->_connection;19 20         // ...21     }22 23 }24 25 $some = new SomeComponent();26 27 //Create the connection28 $connection = new Connection(array(29     "host" => "localhost",30     "username" => "root",31     "password" => "secret",32     "dbname" => "invo"33 ));34 35 //Inject the connection in the component36 $some->setConnection($connection);37 38 $some->someDbTask();

  现在我们来考虑一个问题,我们在应用程序中的不同地方使用此组件,将多次创建数据库连接。使用一种类似全局注册表的方式,从这获得一个数据库连接实例,而不是使用一次就创建一次。

立即学习PHP免费学习笔记(深入)”;

 1 php 2  3 class Registry 4 { 5  6     /** 7      * Returns the connection 8      */ 9     public static function getConnection()10     {11        return new Connection(array(12             "host" => "localhost",13             "username" => "root",14             "password" => "secret",15             "dbname" => "invo"16         ));17     }18 19 }20 21 class SomeComponent22 {23 24     protected $_connection;25 26     /**27      * Sets the connection externally28      */29     public function setConnection($connection){30         $this->_connection = $connection;31     }32 33     public function someDbTask()34     {35         $connection = $this->_connection;36 37         // ...38     }39 40 }41 42 $some = new SomeComponent();43 44 //Pass the connection defined in the registry45 $some->setConnection(Registry::getConnection());46 47 $some->someDbTask();

  现在,让我们来想像一下,我们必须在组件中实现两个方法,首先需要创建一个新的数据库连接,第二个总是获得一个共享连接:

 1 php 2  3 class Registry 4 { 5  6     protected static $_connection; 7  8     /** 9      * Creates a connection10      */11     protected static function _createConnection()12     {13         return new Connection(array(14             "host" => "localhost",15             "username" => "root",16             "password" => "secret",17             "dbname" => "invo"18         ));19     }20 21     /**22      * Creates a connection only once and returns it23      */24     public static function getSharedConnection()25     {26         if (self::$_connection===null){27             $connection = self::_createConnection();28             self::$_connection = $connection;29         }30         return self::$_connection;31     }32 33     /**34      * Always returns a new connection35      */36     public static function getNewConnection()37     {38         return self::_createConnection();39     }40 41 }42 43 class SomeComponent44 {45 46     protected $_connection;47 48     /**49      * Sets the connection externally50      */51     public function setConnection($connection){52         $this->_connection = $connection;53     }54 55     /**56      * This method always needs the shared connection57      */58     public function someDbTask()59     {60         $connection = $this->_connection;61 62         // ...63     }64 65     /**66      * This method always needs a new connection67      */68     public function someOtherDbTask($connection)69     {70 71     }72 73 }74 75 $some = new SomeComponent();76 77 //This injects the shared connection78 $some->setConnection(Registry::getSharedConnection());79 80 $some->someDbTask();81 82 //Here, we always pass a new connection as parameter83 $some->someOtherDbTask(Registry::getConnection());

  到此为止,我们已经看到了如何使用依赖注入解决我们的问题。不是在代码内部创建依赖关系,而是让其作为一个参数传递,这使得我们的程序更容易维护,降低程序代码的耦合度,实现一种松耦合。但是从长远来看,这种形式的依赖注入也有一些缺点。

  例如,如果组件中有较多的依赖关系,我们需要创建多个setter方法传递,或创建构造函数进行传递。另外,每次使用组件时,都需要创建依赖组件,使代码维护不太易,我们编写的代码可能像这样:

 1 php 2  3 //Create the dependencies or retrieve them from the registry 4 $connection = new Connection(); 5 $session = new Session(); 6 $fileSystem = new FileSystem(); 7 $filter = new Filter(); 8 $selector = new Selector(); 9 10 //Pass them as constructor parameters11 $some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);12 13 // ... or using setters14 15 $some->setConnection($connection);16 $some->setSession($session);17 $some->setFileSystem($fileSystem);18 $some->setFilter($filter);19 $some->setSelector($selector);

  我想,我们不得不在应用程序的许多地方创建这个对象。如果你不需要依赖的组件后,我们又要去代码注入部分移除构造函数中的参数或者是setter方法。为了解决这个问题,我们再次返回去使用一个全局注册表来创建组件。但是,在创建对象之前,它增加了一个新的抽象层:

 1 php 2  3 class SomeComponent 4 { 5  6     // ... 7  8     /** 9      * Define a factory method to create SomeComponent instances injecting its dependencies10      */11     public static function factory()12     {13 14         $connection = new Connection();15         $session = new Session();16         $fileSystem = new FileSystem();17         $filter = new Filter();18         $selector = new Selector();19 20         return new self($connection, $session, $fileSystem, $filter, $selector);21     }22 23 }

  这一刻,我们好像回到了问题的开始,我们正在创建组件内部的依赖,我们每次都在修改以及找寻一种解决问题的办法,但这都不是很好的做法。

  一种实用和优雅的来解决这些问题,是使用容器的依赖注入,像我们在前面看到的,容器作为全局注册表,使用容器的依赖注入做为一种桥梁来解决依赖可以使我们的代码耦合度更低,很好的降低了组件的复杂性:

 1 php 2  3 class SomeComponent 4 { 5  6     protected $_di; 7  8     public function __construct($di) 9     {10         $this->_di = $di;11     }12 13     public function someDbTask()14     {15 16         // Get the connection service17         // Always returns a new connection18         $connection = $this->_di->get('db');19 20     }21 22     public function someOtherDbTask()23     {24 25         // Get a shared connection service,26         // this will return the same connection everytime27         $connection = $this->_di->getShared('db');28 29         //This method also requires a input filtering service30         $filter = $this->_db->get('filter');31 32     }33 34 }35 36 $di = new Phalcon\DI();37 38 //Register a "db" service in the container39 $di->set('db', function(){40     return new Connection(array(41         "host" => "localhost",42         "username" => "root",43         "password" => "secret",44         "dbname" => "invo"45     ));46 });47 48 //Register a "filter" service in the container49 $di->set('filter', function(){50     return new Filter();51 });52 53 //Register a "session" service in the container54 $di->set('session', function(){55     return new Session();56 });57 58 //Pass the service container as unique parameter59 $some = new SomeComponent($di);60 61 $some->someTask();

现在,该组件只有访问某种service的时候才需要它,如果它不需要,它甚至不初始化,以节约资源。该组件是高度解耦。他们的行为,或者说他们的任何其他方面都不会影响到组件本身。
我们的实现办法¶

Phalcon\DI 是一个实现了服务的依赖注入功能的组件,它本身也是一个容器。

由于Phalcon高度解耦,Phalcon\DI 是框架用来集成其他组件的必不可少的部分,开发人员也可以使用这个组件依赖注入和管理应用程序中不同类文件的实例。

基本上,这个组件实现了 Inversion of Control 模式。基于此,对象不再以构造函数接收参数或者使用setter的方式来实现注入,而是直接请求服务的依赖注入。这就大大降低了整体程序的复杂性,因为只有一个方法用以获得所需要的一个组件的依赖关系。

此外,这种模式增强了代码的可测试性,从而使它不容易出错。
在容器中注册服务¶

依赖注入容器的PHP库
依赖注入容器的PHP库

依赖注入容器的PHP库

下载

框架本身或开发人员都可以注册服务。当一个组件A要求调用组件B(或它的类的一个实例),可以从容器中请求调用组件B,而不是创建组件B的一个实例。

这种工作方式为我们提供了许多优点:

我们可以更换一个组件,从他们本身或者第三方轻松创建。
在组件发布之前,我们可以充分的控制对象的初始化,并对对象进行各种设置。
我们可以使用统一的方式从组件得到一个结构化的全局实例

服务可以通过以下几种方式注入到容器:

 1 php 2  3 //Create the Dependency Injector Container 4 $di = new Phalcon\DI(); 5  6 //By its class name 7 $di->set("request", 'Phalcon\Http\Request'); 8  9 //Using an anonymous function, the instance will lazy loaded10 $di->set("request", function(){11     return new Phalcon\Http\Request();12 });13 14 //Registering directly an instance15 $di->set("request", new Phalcon\Http\Request());16 17 //Using an array definition18 $di->set("request", array(19     "className" => 'Phalcon\Http\Request'20 ));

在上面的例子中,当向框架请求访问一个请求数据时,它将首先确定容器中是否存在这个”reqeust”名称的服务。

容器会反回一个请求数据的实例,开发人员最终得到他们想要的组件。

在上面示例中的每一种方法都有优缺点,具体使用哪一种,由开发过程中的特定场景来决定的。

用一个字符串来设定一个服务非常简单,但缺少灵活性。设置服务时,使用数组则提供了更多的灵活性,而且可以使用较复杂的代码。lambda函数是两者之间一个很好的平衡,但也可能导致更多的维护管理成本。

Phalcon\DI 提供服务的延迟加载。除非开发人员在注入服务的时候直接实例化一个对象,然后存存储到容器中。在容器中,通过数组,字符串等方式存储的服务都将被延迟加载,即只有在请求对象的时候才被初始化。

 1 php 2  3 //Register a service "db" with a class name and its parameters 4 $di->set("db", array( 5     "className" => "Phalcon\Db\Adapter\Pdo\Mysql", 6     "parameters" => array( 7           "parameter" => array( 8                "host" => "localhost", 9                "username" => "root",10                "password" => "secret",11                "dbname" => "blog"12           )13     )14 ));15 16 //Using an anonymous function17 $di->set("db", function(){18     return new Phalcon\Db\Adapter\Pdo\Mysql(array(19          "host" => "localhost",20          "username" => "root",21          "password" => "secret",22          "dbname" => "blog"23     ));24 });

以上这两种服务的注册方式产生相同的结果。然后,通过数组定义的,在后面需要的时候,你可以修改服务参数:

1 php2 3 $di->setParameter("db", 0, array(4     "host" => "localhost",5     "username" => "root",6     "password" => "secret"7 ));

从容器中获得服务的最简单方式就是使用”get”方法,它将从容器中返回一个新的实例:

1 php2      $request = $di->get("request");

或者通过下面这种魔术方法的形式调用:

1 php2 3 $request = $di->getRequest();4 5 Phalcon\DI 同时允许服务重用,为了得到一个已经实例化过的服务,可以使用 getShared() 方法的形式来获得服务。

具体的 Phalcon\Http\Request 请求示例:

1 php2 3 $request = $di->getShared("request");

参数还可以在请求的时候通过将一个数组参数传递给构造函数的方式:

1 php2 3 $component = $di->get("MyComponent", array("some-parameter", "other"))

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java 抽象方法
java 抽象方法

本专题整合了java抽象方法定义、作用教程等内容,阅读专题下面的文章了解更多详细内容。

0

2026.02.05

Eclipse创建jsp文件教程合集
Eclipse创建jsp文件教程合集

本专题整合了Eclipse创建jsp文件、创建jsp项目等等内容,阅读专题下面的文章了解更多详细教程。

0

2026.02.05

java 字符串转数字
java 字符串转数字

本专题整合了java如何字符串转数字相关内容,阅读专题下面的文章了解更多详细教程。

2

2026.02.05

java中jlabel的作用
java中jlabel的作用

本专题整合了java中label相关内容,阅读专题下面的文章了解更多详细教程。

0

2026.02.05

java return合集
java return合集

本专题整合看java中return关键词的用途,语句的使用等等内容,阅读专题下面的文章了解更多详细内容。

2

2026.02.05

AO3官网入口与镜像站汇总 Archive of Our Own访问路径及最新入口
AO3官网入口与镜像站汇总 Archive of Our Own访问路径及最新入口

本专题专注于提供Archive of Our Own (AO3) 的最新官网入口与镜像站地址,详细整理了可用的访问路径,包括中文镜像站入口和网页版直达链接,帮助用户轻松找到最稳定的访问方式,确保顺畅浏览AO3内容。

8

2026.02.05

192.168.1.1路由器后台管理入口与设置登录指南
192.168.1.1路由器后台管理入口与设置登录指南

本专题汇总了192.168.1.1路由器的后台管理入口、登录网址以及无线网络设置的方法,帮助用户快速进入路由器管理页面,进行网络配置、密码修改等常见操作,提升家庭网络的管理与优化效率。

1

2026.02.05

Python 数据库优化与性能调优
Python 数据库优化与性能调优

本专题专注讲解 Python 在数据库性能优化中的应用,包括数据库连接池管理、SQL 查询优化、索引设计与使用、数据库事务管理、分布式数据库与缓存系统的结合。通过分析常见性能瓶颈,帮助开发者掌握 如何优化数据库操作,提升 Python 项目在数据库层的响应速度与处理能力。

2

2026.02.05

Java 微服务与 Spring Cloud 实战
Java 微服务与 Spring Cloud 实战

本专题讲解 Java 微服务架构的开发与实践,重点使用 Spring Cloud 实现服务注册与发现、负载均衡、熔断与限流、分布式配置管理、API Gateway 和消息队列。通过实际项目案例,帮助开发者理解 如何将传统单体应用拆分为高可用、可扩展的微服务架构,并有效管理和调度分布式系统中的各个组件。

0

2026.02.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
AngularJS教程
AngularJS教程

共24课时 | 3.3万人学习

CSS3实现按钮特效视频教程
CSS3实现按钮特效视频教程

共15课时 | 3.3万人学习

细说PHP第三季
细说PHP第三季

共58课时 | 11.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号