详解Laravel—IOC容器

下面由laravel框架教程栏目给大家详解laravel—ioc容器,希望对需要的朋友有所帮助!

详解Laravel—IOC容器

1.依赖

IOC( inversion of controller )叫做控制反转模式,也可以称为(dependency injection ) 依赖注入模式。要理解依赖注入的概念我们先理解下什么依赖

//支付宝支付class Alipay {      public function __construct(){}      public function pay()      {          echo 'pay bill by alipay';      }}//微信支付class Wechatpay {      public function __construct(){}      public function pay()      {          echo 'pay bill by wechatpay';      }}//银联支付class Unionpay{      public function __construct(){}      public function pay()      {          echo 'pay bill by unionpay';      }}//支付账单class PayBill {      private $payMethod;      public function __construct( )      {          $this->payMethod= new Alipay ();      }      public function  payMyBill()      {           $this->payMethod->pay();      }}$pb = new PayBill ();$pb->payMyBill();

通过上面的代码我们知道,当我们创建一个class PayBill 的实例的时候, PayBill的构造函数里面有{ $this->payMethod= new Alipay (); }, 也就是实例化了一个class Alipay . 这个时候依赖就产生了, 这里可以理解为当我想用支付宝支付的时候, 那我首先要获取到一个支付宝的实例,或者理解为获取支付宝的功能支持. 当用我们完 new 关键字的时候, 依赖其实已经解决了,因为我们获取了Alipay 的实例.

其实在我知道ioc概念之前,我的代码中大部分都是这种模式 ~ _ ~ . 这种有什么问题呢, 简单来说, 比如当我想用的不是支付宝而是微信的时候怎么办, 你能做的就是修改Payment 的构造函数的代码,实例化一个微信支付Wechatpay.

如果我们的程序不是很大的时候可能还感觉不出什么,但是当你的代码非常复杂,庞大的时候,如果我们的需求经常改变,那么修改代码就变的非常麻烦了。所以ioc 的思想就是不要在 class Payment 里面用new 的方式去实例化解决依赖, 而且转为由外部来负责,简单一点就是内部没有new 的这个步骤,通过依赖注入的方式同样的能获取到支付的实例.

2.依赖注入

依赖我们知道了是什么意思,那依赖注入又是什么意思呢,我们把上面的代码拓展一下

//支付类接口interface Pay{    public function pay();}//支付宝支付class Alipay implements Pay {      public function __construct(){}      public function pay()      {          echo 'pay bill by alipay';      }}//微信支付class Wechatpay implements Pay  {      public function __construct(){}      public function pay()      {          echo 'pay bill by wechatpay';      }}//银联支付class Unionpay implements Pay  {      public function __construct(){}      public function pay()      {          echo 'pay bill by unionpay';      }}//付款class PayBill {      private $payMethod;      public function __construct( Pay $payMethod)      {          $this->payMethod= $payMethod;      }      public function  payMyBill()      {           $this->payMethod->pay();      }}//生成依赖$payMethod =  new Alipay();//注入依赖$pb = new PayBill( $payMethod );$pb->payMyBill();

上面的代码中,跟之前的比较的话,我们加入一个Pay 接口, 然后所有的支付方式都继承了这个接口并且实现了pay 这个功能. 可能大家会问为什么要用接口,这个我们稍后会讲到.

当我们实例化PayBill的之前, 我们首先是实例化了一个Alipay,这个步骤就是生成了依赖了,然后我们需要把这个依赖注入到PayBill 的实例当中,通过代码我们可以看到 { $pb = new PayBill( payMethod ); }, 我们是通过了构造函数把这个依赖注入了PayBill 里面. 这样一来 $pb 这个PayBill 的实例就有了支付宝支付的能力了.

把class Alipay 的实例通过constructor注入的方式去实例化一个 class PayBill. 在这里我们的注入是手动注入, 不是自动的. 而Laravel 框架实现则是自动注入.

3.反射

在介绍IOC 的容器之前我们先来理解下反射的概念(reflection),因为IOC 容器也是要通过反射来实现的.从网上抄了一段来解释反射是什么意思

“反射它指在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。这种动态获取的信息以及动态调用对象的方法的功能称为反射API。反射是操纵面向对象范型中元模型的API,其功能十分强大,可帮助我们构建复杂,可扩展的应用。其用途如:自动加载插件,自动生成文档,甚至可用来扩充PHP语言”

举个简单的例子

class B{}class A {    public function __construct(B $args)    {    }    public function dosomething()    {        echo 'Hello world';    }}//建立class A 的反射$reflection = new ReflectionClass('A');$b = new B();//获取class A 的实例$instance = $reflection ->newInstanceArgs( [ $b ]);$instance->dosomething(); //输出 ‘Hellow World’$constructor = $reflection->getConstructor();//获取class A 的构造函数$dependencies = $constructor->getParameters();//获取class A 的依赖类dump($constructor);dump($dependencies);

dump 的得到的$constructor 和 $dependencies 結果如下

//constructorReflectionMethod {#351         +name: "__construct"         +class: "A"         parameters: array:1 []         extra: array:3 []         modifiers: "public"}//$dependenciesarray:1 [        0 => ReflectionParameter {#352          +name: "args"          position: 0          typeHint: "B"      }]

通过上面的代码我们可以获取到 class A 的构造函数,还有构造函数依赖的类,这个地方我们依赖一个名字为 ‘args’ 的量,而且通过TypeHint可以知道他是类型为 Class B; 反射机制可以让我去解析一个类,能过获取一个类里面的属性,方法 ,构造函数, 构造函数需要的参数。 有个了这个才能实现Laravel 的IOC 容器.

4.IOC容器

接下来介绍一下Laravel 的IOC服务容器概念. 在laravel框架中, 服务容器是整个laravel的核心,它提供了整个系统功能及服务的配置, 调用. 容器按照字面上的理解就是装东西的东西,比如冰箱, 当我们需要冰箱里面的东西的时候直接从里面拿就行了. 服务容器也可以这样理解, 当程序开始运行的时候,我们把我们需要的一些服务放到或者注册到(bind)到容器里面,当我需要的时候直接取出来(make)就行了. 上面提到的 bind 和 make 就是注册 和 取出的 两个动作.

5. IOC 容器代码

好了,说了这么多,下面要上一段容器的代码了. 下面这段代码不是laravel 的源码, 而是来自一本书《laravel 框架关键技术解析》. 这段代码很好的还原了laravel 的服务容器的核心思想. 代码有点长, 小伙伴们要耐心看. 当然小伙伴完全可以试着运行一下这段代码,然后调试一下,这样会更有助于理解.

Calliper 文档对比神器 Calliper 文档对比神器

文档内容对比神器

Calliper 文档对比神器 28 查看详情 Calliper 文档对比神器

getClosure($abstract, $concrete);        }                $this->bindings[$abstract] = compact('concrete', 'shared');    }    //默认生成实例的回调函数    protected function getClosure($abstract, $concrete) {                return function($c) use ($abstract, $concrete) {            $method = ($abstract == $concrete) ? 'build' : 'make';            return $c->$method($concrete);        };            }    public function make($abstract) {        $concrete = $this->getConcrete($abstract);        if($this->isBuildable($concrete, $abstract)) {            $object = $this->build($concrete);        } else {            $object = $this->make($concrete);        }                return $object;    }    protected function isBuildable($concrete, $abstract) {        return $concrete === $abstract || $concrete instanceof Closure;    }    //获取绑定的回调函数    protected function getConcrete($abstract) {        if(!isset($this->bindings[$abstract])) {            return $abstract;        }        return $this->bindings[$abstract]['concrete'];    }    //实例化对象    public function build($concrete) {        if($concrete instanceof Closure) {            return $concrete($this);        }        $reflector = new ReflectionClass($concrete);        if(!$reflector->isInstantiable()) {            echo $message = "Target [$concrete] is not instantiable";        }        $constructor = $reflector->getConstructor();        if(is_null($constructor)) {            return new $concrete;        }        $dependencies = $constructor->getParameters();        $instances = $this->getDependencies($dependencies);        return $reflector->newInstanceArgs($instances);    }    //解决通过反射机制实例化对象时的依赖    protected function getDependencies($parameters) {        $dependencies = [];        foreach($parameters as $parameter) {            $dependency = $parameter->getClass();            if(is_null($dependency)) {                $dependencies[] = NULL;            } else {                $dependencies[] = $this->resolveClass($parameter);            }        }        return (array)$dependencies;    }    protected function resolveClass(ReflectionParameter $parameter) {        return $this->make($parameter->getClass()->name);    }}

上面的代码就生成了一个容器,下面是如何使用容器

$app = new Container();$app->bind("Pay", "Alipay");//Pay 为接口, Alipay 是 class Alipay$app->bind("tryToPayMyBill", "PayBill"); //tryToPayMyBill可以当做是Class PayBill 的服务别名//通过字符解析,或得到了Class PayBill 的实例$paybill = $app->make("tryToPayMyBill"); //因为之前已经把Pay 接口绑定为了 Alipay,所以调用pay 方法的话会显示 'pay bill by alipay '$paybill->payMyBill();

当我们实例化一个Container得到 $app 后, 我们就可以向其中填充东西了

$app->bind("Pay", "Alipay");$app->bind("tryToPayMyBill", "PayBill");

当执行完这两行绑定码后, $app 里面的属性 $bindings 就已经有了array 值,是啥样的呢,我们来看下

array:2 [ "AppHttpControllersPay" => array:2 [     "concrete" => Closure {#355        class: "AppHttpControllersContainer"        this:Container{[#354](http://127.0.0.4/ioc#sf-dump-254248394-ref2354) …}        parameters: array:1 [         "$c" => []       ]        use: array:2 [         "$abstract" => "AppHttpControllersPay"        "$concrete" => "AppHttpControllersAlipay"       ]        file: "C:projecttestappHttpControllersIOCController.php" line:       "119 to 122"   }    "shared" => false  ]"tryToPayMyBill" => array:2 [     "concrete" => Closure {#359          class: "AppHttpControllersContainer"          this:Container{[#354](http://127.0.0.4/ioc#sf-dump-254248394-ref2354) …}          parameters: array:1 [               "$c" => []         ]          use: array:2 [               "$abstract" => "tryToPayMyBill"                "$concrete" => "AppHttpControllersPayBill"         ]          file: "C:projecttestappHttpControllersIOCController.php" line: "119 to 122"   }      "shared" => false  ]]

当执行 $paybill = $app->make(“tryToPayMyBill”); 的时候, 程序就会用make方法通过闭包函数的回调开始解析了.

解析’tryToPayBill’ 这个字符串, 程序通过闭包函数 和build方法会得到 ‘PayBill’ 这个字符串,该字符串保存在$concrete 上. 这个是第一步. 然后程序还会以类似于递归方式 将$concrete 传入 build() 方法. 这个时候build里面就获取了$concrete = ‘PayBill’. 这个时候反射就派上了用场, 大家有没有发现,PayBill 不就是 class PayBill 吗? 然后在通过反射的方法ReflectionClass(‘PayBill’) 获取PayBill 的实例. 之后通过getConstructor(),和getParameters() 等方法知道了 Class PayBill 和 接口Pay 存在依赖

//$constructor = $reflector->getConstructor();ReflectionMethod {#374     +name: "__construct"     +class: "AppHttpControllersPayBill"     parameters: array:1 [          "$payMethod" => ReflectionParameter {#371               +name: "payMethod"               position: 0 typeHint: "AppHttpControllersPay"          }    ]     extra: array:3 [          "file" => "C:projecttestappHttpControllersIOCController.php"          "line" => "83 to 86"           "isUserDefined" => true       ]     modifiers: "public"}//$dependencies = $constructor->getParameters();array:1 [    0 => ReflectionParameter {#370         +name: "payMethod"         position: 0         typeHint: "AppHttpControllersPay"        }]

接着,我们知道了有’Pay’这个依赖之后呢,我们要做的就是解决这个依赖,通过 getDependencies($parameters), 和 resolveClass(ReflectionParameter $parameter) ,还有之前的绑定$app->bind(“Pay”, “Alipay”); 在build 一次的时候,通过 return new $concrete;到这里我们得到了这个Alipay 的实例

        if(is_null($constructor)) {            return new $concrete;        }

到这里我们总算结局了这个依赖, 这个依赖的结果就是实例化了一个 Alipay. 到这里还没结束

        $instances = $this->getDependencies($dependencies);

上面的$instances 数组只有一个element 那就是 Alipay 实例

  array:1 [0 =>Alipay      {#380} ]

最终通过 newInstanceArgs() 方法, 我们获取到了 PayBill 的实例。

 return $reflector->newInstanceArgs($instances);

到这里整个流程就结束了, 我们通过 bind 方式绑定了一些依赖关系, 然后通过make 方法 获取到到我们想要的实例. 在make中有牵扯到了闭包函数,反射等概念.

好了,当我们把容器的概念理解了之后,我们就可以理解下为什么要用接口这个问题了. 如果说我不想用支付宝支付,我要用微信支付怎么办,too easy.

$app->bind("Pay", "Wechatpay");$app->bind("tryToPayMyBill", "PayBill");$paybill = $app->make("tryToPayMyBill"); $paybill->payMyBill();

是不是很简单呢, 只要把绑定从’Alipay’ 改成 ‘Wechatpay’ 就行了,其他的都不用改. 这就是为什么我们要用接口. 只要你的支付方式继承了pay 这个接口,并且实现pay 这个方法,我们就能够通过绑定正常的使用. 这样我们的程序就非常容易被拓展,因为以后可能会出现成百上千种的支付方式.

好了,到这里不知道小伙伴有没有理解呢,我建议大家可以试着运行下这些代码, 这样理解起来会更快.同时推荐大家去看看 《laravel 框架关键技术解析》这本书,写的还是不错的.

以上就是详解Laravel—IOC容器的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/350306.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
高效编程:如何才能找到可靠的代码工具和资源?
上一篇 2025年11月5日 22:01:51
怎样设置红魔6pro氘锋透明版疾速散热_红魔6pro氘锋透明版设置疾速散热步骤分享
下一篇 2025年11月5日 22:02:00

相关推荐

  • composer require-dev和require有什么不同_Composer Require与Require-Dev区别解析

    require用于声明项目运行必需的依赖,如框架、数据库组件和第三方SDK,这些包会随项目部署到生产环境;2. require-dev用于声明仅在开发和测试阶段需要的工具,如PHPUnit、PHPStan、Faker等,不会默认部署到生产环境;3. 安装时composer install根据环境决定…

    2026年5月10日
    1000
  • 开源免费PHP工具 PHP开发效率提升利器

    推荐开源免费PHP开发工具以提升效率:VS Code、Sublime Text轻量高效,PhpStorm专业强大;调试用Xdebug、Kint、Ray;依赖管理选Composer;代码质量工具包括PHPStan、Psalm、PHP_CodeSniffer;数据库管理可用%ignore_a_1%MyA…

    2026年5月10日
    000
  • 深入理解 Laravel Session::put:避免常见陷阱与实现表单限流

    本文旨在深入探讨 laravel 框架中 `session::put` 方法的正确用法及其常见误区。针对用户在实现表单提交限流时遇到的问题,详细阐述了 `session::put` 必须提供键值对的原理,并提供了如何在控制器中利用会话机制有效防止重复提交的实战代码示例。通过本文,读者将掌握 lara…

    2026年5月10日
    000
  • Voyager 中关联关系的翻译问题解决方案

    本文档旨在解决在使用 TCGVoyager 管理后台时,关联模型无法正确翻译的问题。主要针对 Laravel 项目中,使用 Voyager 1.4 版本以及 Laravel 8.0 版本,并且已经配置多语言支持的情况下,如何确保关联关系中的可翻译字段能够根据当前应用语言环境进行正确翻译。通过修改 B…

    2026年5月10日
    000
  • 优化 Laravel Eloquent 查询:高效构建用户排行榜数据

    本教程详细讲解如何优化 Laravel Eloquent 查询以高效生成基于关联记录计数的排行榜。通过识别并消除冗余的 whereHas 子句,并巧妙利用 withCount 的条件闭包,我们能显著提升查询性能,大幅缩短数据获取时间,从而改善用户体验并降低数据库负载。 在 laravel 应用开发中…

    2026年5月10日
    000
  • 告别重复:使用Laravel Precognition统一前后端API验证

    本文旨在解决在Laravel后端与前端API交互中,如何高效复用后端验证规则的挑战。传统方案常限于表单元素,难以覆盖所有API请求。通过引入Laravel Precognition,开发者能够实现后端验证逻辑在前端的无缝应用,避免规则重复编写,从而提升开发效率与代码一致性,确保所有API请求的数据完…

    2026年5月10日
    200
  • Laravel Session::put 正确用法详解与常见误区规避

    本文详细探讨了 laravel 中 `session::put` 方法的正确用法,特别指出在仅提供键名而未指定值时可能导致会话数据未被正确设置的问题。通过示例代码,阐述了如何为会话数据赋予明确的值,并演示了如何正确地检查和获取会话数据,以确保会话管理功能按预期工作,有效避免常见的会话操作错误。 La…

    2026年5月10日
    000
  • PHP中批量为嵌套数组元素添加公共属性的教程

    本教程将详细介绍在php中如何高效地为包含多个关联数组的集合中的每个子数组添加一个或多个新的公共键值对。我们将探讨使用循环和数组合并函数实现这一目标的方法,并提供清晰的代码示例,帮助开发者处理此类数据结构转换。 在PHP开发中,我们经常会遇到处理复杂数据结构的需求,其中一种常见场景是拥有一个由多个关…

    2026年5月10日
    000
  • PHP框架的社区支持存在哪些痛点?

    php框架社区支持的痛点包括:文档匮乏或过时(1)、响应缓慢(2)、社区分散(3)。实战案例表明这些痛点可能导致开发进度受阻。改善方法包括:提供全面的文档、建立响应迅速的官方论坛、创建一个集成的社区平台。 PHP 框架社区支持存在的痛点及实战案例 PHP 框架为 Web 开发提供了强大的基础,但其社…

    2026年5月10日
    100
  • Laravel 8中Firebase Storage文件条件删除策略与实践

    本文针对Laravel 8环境下Firebase Storage无法直接按目录批量或条件删除文件的限制,提出了一套基于元数据管理的解决方案。通过在数据库中记录文件信息,结合Laravel的Artisan命令和Cron任务,实现对过期文件的精准识别与逐个删除,确保存储资源的有效管理。 Firebase…

    2026年5月10日
    000
  • php怎么安装_在云服务器上部署PHP环境的步骤

    答案:在云服务器上部署PHP环境需搭建LEMP栈(Linux+Nginx+MySQL+PHP-FPM),依次更新系统、安装Nginx、MariaDB、PHP-FPM及扩展,配置Nginx解析PHP并测试,最后通过权限控制、安全配置、防火墙和HTTPS等措施保障环境安全稳定。 在云服务器上部署PHP环…

    2026年5月10日
    000
  • Laravel 产品多图上传错误:foreach() 参数类型问题解决方案

    本文旨在解决 Laravel 应用中产品多图上传时遇到的 “foreach() argument must be of type array|object, null given” 错误。通过检查并确保循环遍历的变量为数组类型,避免因空值导致的错误,并提供代码示例和注意事项,…

    2026年5月10日
    200
  • PHP源码命令行工具开发_PHP源码命令行工具开发教程

    答案是使用PHP开发命令行工具需依托CLI SAPI,结合Composer管理依赖,并推荐采用Symfony Console等组件库来构建。首先确保PHP支持CLI模式,通过编写基础脚本并利用$argv和getopt()处理参数,但更优方式是引入Symfony Console组件进行命令定义与输入输…

    2026年5月10日
    000
  • PHP怎么运行创建_php脚本创建与执行流程解析

    PHP脚本需在服务器环境中通过解释器运行,不能双击执行。首先搭建环境(如XAMPP),然后编写.php文件并保存至服务器根目录,接着通过浏览器访问或命令行执行php命令运行脚本,服务器会调用PHP解释器解析代码并返回结果。 PHP脚本的运行依赖于服务器环境和解释器,不是直接像可执行程序那样双击运行。…

    2026年5月10日
    100
  • php中get_parent_class获取父类名_php在继承链中定位父类的应用场景

    get_parent_class函数用于获取类的父类名称,接收类名字符串返回父类名或false。示例中Dog类继承Animal,调用get_parent_class(__CLASS__)输出Animal。应用场景一:条件性调用父类方法,如构造函数中判断是否存在父类并调用其方法,提升灵活性。应用场景二…

    2026年5月10日
    100
  • 使用Laravel Blade动态渲染带标题的表格数据

    本文旨在详细指导如何在Laravel Blade模板中,利用`@foreach`循环和正确的索引策略,高效且准确地从嵌套数组结构中提取数据,并将其渲染成一个结构清晰、内容匹配的HTML表格,避免数据重复和错位问题。 在Web开发中,经常需要根据后端提供的数据动态生成HTML表格。特别是在处理具有行标…

    2026年5月10日
    000
  • Laravel模型中实现多语言数据自动过滤:重写newQuery()方法

    本教程详细介绍在laravel多语言应用中,如何通过重写模型(model)的`newquery()`方法,实现数据查询时自动根据当前应用语言环境进行过滤。这种方法提供了一种优雅且dry(don’t repeat yourself)的解决方案,避免了在每次数据查询时手动添加语言条件,确保了…

    2026年5月10日
    000
  • php学习有哪些

    PHP 学习途径:入门途径:在线教程:Codecademy、Udemy、Coursera 等书籍:《Head First PHP & MySQL》、《PHP in Action》官方文档:PHP 官方文档进阶学习:框架:Laravel、CodeIgniter 等数据库:MySQL、Postg…

    2026年5月10日
    100
  • 在 Laravel 中同时存储原始图片和 WebP 转换图片

    本文详细介绍了在 Laravel 应用中如何高效地处理图片上传,实现同时保存原始图片(如 JPG/PNG)及其 WebP 转换版本。通过利用 PHP 原生 GD 库功能,我们能够克服 Intervention Image 在特定场景下的路径写入问题,确保原始图片和优化后的 WebP 格式文件都能正确…

    2026年5月10日
    000
  • 解决AJAX响应中PHP输出JSON后出现多余HTML的问题

    本文旨在解决PHP脚本通过AJAX响应返回JSON数据时,出现JSON数据后方意外附带HTML内容的问题。通过在PHP脚本中JSON编码输出后立即使用die()或exit()函数,可以有效阻止后续不必要的输出,确保客户端接收到纯净、可解析的JSON响应,从而避免解析错误,提升前后端通信的健壮性。 理…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信