深入Laravel服务容器

   2016-12-23 0
核心提示:在开始之前要明确一个概念,不管是设计模式,还是依赖注入等等,都是为了实现模块化.所谓模块化就是希一个软件是由很多子模块组成的,这些模块之间的依赖程度尽量的低,也就是如果系统中不需要某一个功能,那么只要移除这个功能所对应的模块就可以了.那么,我们今天

在开始之前要明确一个概念,不管是设计模式,还是依赖注入等等,都是为了实现模块化.所谓模块化就是希一个软件是由很多子模块组成的,这些模块之间的依赖程度尽量的低,也就是如果系统中不需要某一个功能,那么只要移除这个功能所对应的模块就可以了.

那么,我们今天要说的服务容器就是为了实现上面的功能.你应该听过,Laravel中的服务容器其本质上是一个IoC容器,但是好像队IoC又不是很了解,讲来讲去优点很多,功能很强劲.但是不懂原理怎么用都不踏实啊.所以,这里我们自己来实现一个IoC容器,洞察其本质.

在开始之前,先说明一点,阅读本篇文章至少要保证有一下的基础知识:

  • php反射用法

  • 闭包的use用法

如果不懂上面的内容,请先补充.避免阅读代码时候产生的不适感.

<?php

/**
 * Created by PhpStorm.
 * User: jiayao
 * Date: 2016/9/1
 * Time: 21:41
 */


class Container
{
    public $binding = [];

                   
    /**
     * @param $abstract
     * @param null $concrete
     * @param bool $shared  
     */
    public function bind($abstract, $concrete = null, $shared = false)
    {
        if (!$concrete instanceof Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }

        $this->binding[$abstract] = compact('concrete', 'shared');
    }



    protected function getClosure($abstract, $concrete)
    {
        return function ($c) use ($abstract, $concrete) {
            $method = ($abstract == $concrete) ? 'build' : 'make';
            return $c->$method($concrete);
        };
    }

    /**
     * @param $abstract
     * @return object
     *
     */
    public function make($abstract)
    {
        $concrete = $this->getConcrete($abstract);

        if ($this->isBuildable($concrete, $abstract)) {
            $object = $this->build($concrete);
        } else {
            $object = $this->make($concrete);
        }
        return $object;
    }

    /**
     * @param $concrete
     * @param $abstract
     * @return bool
     */
    public function isBuildable($concrete, $abstract)
    {
        return $concrete === $abstract || $concrete instanceof Closure;
    }

    /**
     * @param $abstract
     * @return mixed
     */
    protected function getConcrete($abstract)
    {
        if (!isset($this->binding[$abstract])) {
            return $abstract;
        }
        return $this->binding[$abstract]['concrete'];
    }


    /**
     * @param $concrete
     * @return object
     */
    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);
    }

    /**
     * @param $paramters
     * @return array
     * 获取构建类所需要的所有依赖,级构造函数所需要的参数 ,
     */
    protected function getDependencies($paramters) {
        $dependencies = [];
        
        foreach ($paramters as $paramter) {
            //获取到参数名称.
            $dep = $paramter->getClass();
            if(is_null($dep)){
                $dependencies = null;
            }else{
                $dependencies[] = $this->resolveClass($paramter);
            }
        }
        return (array)$dependencies;
    }

    /**
     * @param ReflectionParameter $parameter
     * @return object
     * 实例化 构造函数中所需要的参数.
     */
    protected function resolveClass(ReflectionParameter $parameter) {
        $name = $parameter->getClass()->name;
        return $this->make($name);
    }
    
}

这就是一个IoC容器的实现代码.乍一看,很麻烦.其实真的蛮麻烦的 =_=,如果是第一次接触的话,并不是那么好消化,这里再给出使用IoC容器的代码

<?php

/**
 * Created by PhpStorm.
 * User: jiayao
 * Date: 2016/9/1
 * Time: 21:37
 */
require __DIR__ . '/Container.php';


interface TrafficTool
{
    public function go();
}

class Train implements TrafficTool
{

    public function go()
    {
        echo "train....";
    }
}

class Leg implements TrafficTool
{
    public function go()
    {
        echo "leg..";
    }
}

class Traveller
{
    /**
     * @var Leg|null|Train
     * 旅行工具
     */
    protected $_trafficTool;

    public function __construct(TrafficTool$trafficTool)
    {
        $this->_trafficTool = $trafficTool;
    }

    public function visitTibet()
    {
        $this->_trafficTool->go();
    }
}

//实例化IoC容器
$app = new Container();

//绑定某一功能到IoC
$app->bind('TrafficTool', 'Train');
$app->bind('travellerA', 'Traveller');

// 实例化对象
$tra = $app->make('travellerA');
$tra->visitTibet();

运行例子发现会输出: train.. .这个例子假设旅行者去青藏旅行,可以坐火车(train)或者走路(leg)去青藏.

好了,其实这样子本篇文章就可以结束了,因为所有的答案都在IoC容器的实现中, 但是为了可以更好的理解上面的代码,我们继续往下分析.

首先,希望你可以运行一下上面的代码,虽然简单的运行代码并不会帮助你理解代码,但是一个可以运行的例子会让人比较踏实,能够更有把握的理解代码.

在深入每一行代码之前,我们从整体上来分析,IoC解决了一个什么问题?简单点说,就是我们再实例化对象的时候不用使用new了,有了IoC容器之后,我们调用make函数就可以实例化出一个对象了.然而,你发现,Traveller的构造函数是需要一个参数的,可是我们好像并没有提供这个参数?

这就是IoC强大之处了, 调用make实例化对象的时候,容器会使用 反射 功能,去分析我们要实例化对象的构造函数,获取构造函数所需的每个参数,然后分别去实例化这些参数,如果实例化这些参数也要参数,那么就再去实例化参数的参数.....=_=.到最后成功实例化我们所需要的traveller了.在Container的build函数就是使用反射来实例化对象.

但是,有一个问题了,IoC容器怎么知道实例化Traveller的时候需要的参数train,而不是leg?

其实,IoC容器什么都不知道,IoC会实例化哪些对象都是通过 bind 函数告诉IoC的,上面的例子两次调用bind函数,就是告诉Ioc可以实例化的对象有 TrainTraveller . 再通俗讲就是:当需要当我们需要 TrafficTool 这个服务的时候去实例化 Train 这个类,需要一个 travellerA 的旅行者的时候去实例化 Traveller 类.而 Train 这个就是 travellerA 就是去青藏的方式. 这样子如果想要走路去青藏的话只要把 $app->bind('Visit', 'Train'); 改为 $app->bind('Visit', 'Leg'); 就可以.

可是,这上面的这些有什么意义?直接 $tra = new Traveller($trafficTool) 来实例化对象好像也没有什么不好的.

使用new来实例化对象的时候,会产生依赖.比如上面 $tra = new Traveller($trafficTool) ,这说明我们要创建一个Traveller之前得有一个 $trafficTool ,即 Traveller 依赖于 trafficTool .当使用new来实例化 Traveller 的时候, TravellertrafficTool 之间就产生了 耦合 .这样,这两个组件就没办法分开了.

而使用IoC是怎么解决这个问题的,之前说过,如果想要如果想要走路去青藏的话只要把 $app->bind('Visit', 'Train'); 改为 $app->bind('Visit', 'Leg'); 就可以.这样子,使用何种方式去青藏,我们可以自由的选择.

我们站在Laravel框架设计者的角度去想,设计者肯定希望一个框架提供的功能越多越好,但是又要保证强大的同时又不会限制使用者.最好可以保证使用者想实现什么奇怪的需求都可以.那么功能强大但是又不局限的最好方法就是什么都不做,提供一个强大的IoC容器.所有需要实现的功能都变成一个个服务,需要什么服务就把服务注册(即调用bind函数)到IoC中,然后让IoC去管理依赖.

开发者想到一个变态的需求:走路去青藏,那么只要你实现了走路去青藏这个功能,然后把这个功能当做一个服务注册到IoC中,以后你需要这个服务的时候IoC就帮你实例化这个服务.当开发者回归正常之后觉得还是坐火车去吧,于是不注册走路这个功能,实现坐火车的功能,然后注册这个功能.下次IoC实例化的时候就是实例化坐火车这个功能了.

好了,剩下的部分就是一行一行的阅读Container的代码了,Laravel框架中的服务容器代码也是这个样子,只是功能更加强悍.但是核心是一样的,上面的代码懂了以后再使用Laravel框架就会更加游刃有余了.

文章虽短.但是内容很多.尤其是代码,虽然可能只是短短的一个例子,但是包含了很多内容.值得好好分析,这里放个彩蛋:Traveller中构造函数参数类似为TrafficTool,是一个接口.但是实例化的是Train.这里体现了设计模式的一个原则

面对接口编程,而不是面对实现编程.

 
标签: Laravel IOC
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • nginx 各类网站设置 (laravel , thinkphp , nod
    基础部分设置[root@centos ~]# vim /opt/nginx/conf/nginx.confuser www www;worker_processes auto;pid logs/nginx.pid;worker_rlimit_nofile 100000;events {use epoll;multi_accept on;worker_connections 65535 ;}http {include mime.types;default_type
    02-09
  • PHP trait 特性在 Laravel 中的使用个人心得
    trait 是在PHP5.4中为了方便代码复用的一种实现方式,但目前我在看的的PHP项目中较少看的有程序员去主动使用这个实现方式,在laravel中有很多 trait 的使用,关于trait 在 laravel 的使用请参看 Laravel 在哪些地方用了 trait?我曾在 Laravel 中大型项目面向
    02-09
  • 让我们用 laravel-mix 为 TypeScript 和 Sass
    介绍前端编译TypeScript、Sass、模板引擎等时经常用到Gulp和webpack。这是我个人的印象,但它们似乎都难以管理,因为它们的描述往往复杂而冗长。我不想积极进行,因为我要担心加载器的顺序并且有很多配置选项,我必须花时间去了解它们。我想推荐那里laravel
  • PHP Laravel软删除的实现方法介绍
    用Laravel 自带的 Eloquent ORM 来实现软删除。首先在数据迁移文件中添加删除时间字段./database/migrations/2014_10_12_000000_create_users_table.php?phpuse Illuminate\Database\Migrations\Migration;use Illuminate\Database\Schema\Blueprint;use Illu
  • Laravel中如何使用PHP的装饰器模式 php laravel
    本文小编为大家详细介绍“Laravel中如何使用PHP的装饰器模式”,内容详细,步骤清晰,细节处理妥当,希望这篇“Laravel中如何使用PHP的装饰器模式”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。装饰器模式定义:它可以帮助您在
    02-08 laravelphp
  • PHP laravel使用自定义邮件类实现发送邮件
    PHP laravel使用自定义邮件类实现发送邮件
    当登录邮箱为腾讯企业邮箱的时候。Phpmailer发送邮件就不好用了,具体哪里不好用,我没真没找到。但是,邮件得发啊,怎么办呢?我这里搞了一个自定义的发送邮件类,腾讯企业邮箱也可用。但是,邮件发送失败,不会返回报错信息,这个可能是有点坑。源码如下:?
  • 详解PHP laravel中的加密与解密函数
    目录一:简介二:配置三:使用加密/解密1:加密2:不使用序列化进行加密3:解密Laravel为我们提供了完整的加密方法及加密模式。我之前一般在加密的时候使用的是我自己写的加密函数,但是这个玩意,有的位置还是不太使用,当然,破解的话,基本上也是不可能的
  • PHP laravel缓存cache机制详解
    目录一、访问多个缓存存储二、从缓存中获取数据1.获取数据并设置默认值2.检查缓存项是否存在3.数值增加/减少4.获取存储5.获取删除三、缓存中存储数据1.获取存储数据2.缓存不存在时存储数据3.永久存储数据四、从缓存中移除数据Laravel中的cache为我们提供了三
  • PHP laravel实现导出PDF功能
    PHP laravel实现导出PDF功能
    目录一、laravel-tcpdf二、tcpdf三、TCPDF解决保存中文文件名的方法补充一、laravel-tcpdf导出PDF文件Laravel框架为我们集成了一个插件tcpdf。下载地址:https://github.com/elibyy/tcpdf-laravel然后使用composer进行安装就可以了。具体安装过程,请查看文末
  • PHP laravel缓存cache机制怎么实现
    今天小编给大家分享一下PHP laravel缓存cache机制怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Laravel中的cache为我们
点击排行