发布于: 2022-06-24 11:40:01

<?php

namespace  App\Ioc;


use Closure;

use ReflectionClass;

use ReflectionParameter;



/**

 * 容器类用于装实例和提供实例的回调函数

 * Class Container 容器类

 * @package App\Ioc

 */

class Container {


    //没有用的变量,调试使用

    public  static $debug_count = 0;


    //声明数组,用于装实例的回调函数,真正的容器还会装实例等其他内容 从而实现单例等高级功能

    protected $bindings = [];


    //绑定接口 和 生成相应实例的回调函数

    public function bind( $abstract , $concrete = null , $shared = false) {

        Container::$debug_count++;

        echo '步骤:'.Container::$debug_count.',进入"bind(绑定)"方法:';

        echo '$abstract="'.$abstract.'" ,  ';

        echo $concrete instanceof Closure?'$concrete 是一个回调函数   ': '$concrete不是回调函数:$concrete="'.$concrete.'"   ';

        echo '<br>';


        echo '判断:当前$concrete是否已经是回调函数,如果是直接绑定,如果不是则调用getClosure获取闭包';

        echo '<br>';


        //判断当前$concrete是否已经是回调函数,如果是直接绑定,如果不是则调用getClosure获取回调函数后再绑定,

        //注意:getClosure获取的回调函数不是直接可以使用功能的回调函数,这里面还要去make或者build能使用正常功能的回调函数。

        if (!$concrete instanceof Closure) {

            echo '当前$concrete不是闭包函数,即将进入getClosure函数生成闭包 <br>';

            $concrete = $this->getClosure($abstract, $concrete);

        }


        echo '$concrete已是闭包,存入$bindings数组 <br>';


        //先将concrete 和 shared 组合为数组,然后又将数组存入 bindings[$abstract]数组。

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

    }


    //默认生成实例的回调函数

    protected function getClosure($abstract, $concrete) {

        Container::$debug_count++;

        echo '步骤:'.Container::$debug_count.',进入到"getClosure(获取闭包函数)"方法';

        echo '$abstract="'.$abstract.'" , ';

        echo '$concrete="'.$concrete.'" , ';

        echo '将\'带有make/build的\'回调函数绑定到$abstract';

        echo '<br>';



        echo '返回闭包函数<br>';

        //到这里绑定的动作完成。 等make的时候才会调用函数体里面

        return function ($c) use ($abstract, $concrete) {

            Container::$debug_count++;

            echo '步骤:'.Container::$debug_count.' 进入"闭包函数里面记录:"';

            echo '$abstract="'.$abstract.'"';

            echo '$concrete="'.$concrete.'"';

            echo '<br>';


            $method = ($abstract == $concrete) ? 'build' : 'make';

            echo '即将调用$container的函数:$container->"'.$method.'"('.$concrete.')';

            echo '<br>';


            //如果$abstract和$concrete相等调用: $c->build($concrete) ,  不相等调用:$c->make($concrete)

            return $c -> $method($concrete);

        };


    }



    /**************************绑定的时候调用上面的函数,要往外取出实例的时候调用下面的函数************************/



    public function make($abstract)

    {

        echo '进入make方法:$abstract别名='.$abstract.' , make('.$abstract.')';

        echo '<br>';


        echo '去获取闭包函数 调用 ->getConcrete()';

        echo '<br>';

        //获取绑定的闭包函数

        $concrete = $this->getConcrete($abstract);


        //判断是用build还是用make 如果$concrete===$abstract或者$concrete是闭包函数就用build

        if ($this->isBuildable($concrete, $abstract)) {

            echo '($concrete是闭包 或者 $concrete===$abstract). 在make方法里使用build';

            echo $concrete instanceof Closure?' <br> $concrete是一个闭包函数   <br>': '<br> $concrete="'.$concrete.'"   <br>';

            $object = $this->build($concrete);

        } else {

            echo '$concrete不是闭包,$concrete和$abstract也不相等,在make方法里使用make<br>';

            $object = $this->make($concrete);

        }


        return $object;

    }


    protected function isBuildable($concrete, $abstract)

    {

        return $concrete === $abstract || $concrete instanceof Closure;

    }


    //获取回调函数里面 make/build 后的实现

    protected function getConcrete($abstract)

    {

        //判断实现的时候判断一下有没有绑定到bindings数组里。如果没有绑直接返回$abstract

        if (!isset($this->bindings[$abstract])) {

            echo '没有获取到闭包函数,直接返回:'.$abstract;

            echo '<br>';

            return $abstract;

        }

        echo '  getConcrete方法里 获取到闭包函数'.' 返回闭包 <br >';

        return $this->bindings[$abstract]['concrete'];

    }


    //实例化对象

    public function build($concrete)

    {

        Container::$debug_count++;

        echo '步骤:'.Container::$debug_count.' 进入build,首先判断$concrete是不是闭包,是闭包就调用自己。<br>';

        //判断$concrete是不是闭包,如果是闭包函数再递归调用make方法,直到$abstract是类名位置

        if ($concrete instanceof Closure) {

            echo '是闭包,调用自己<br>';

            return $concrete($this);

        }

        echo '不是闭包,创建反射类<br>';

        try{

            echo '创建'.$concrete.'的反射类<br>';

            $reflector = new ReflectionClass($concrete);

            //判断$concrete类是否可实例化

            if (!$reflector->isInstantiable()) {

                echo $message = "Target [$concrete] is not instantiable";

            }


            //获取类的构造函数

            $constructor = $reflector->getConstructor();

            if (is_null($constructor)) {

                //如果$concrete类没有构造函数直接在这里new 然后返回实例

                echo '要实例化的类没有构造函数。直接new 返回<br>';

                return new $concrete;

            }


            echo '要实例化的类有构造函数,要去获取依赖 <br>';

            //如果$concrete类有构造函数先获取构造函数的参数

            $dependencies = $constructor->getParameters();


            //获取参数的依赖,比如用没用其他类对象作为参数传递

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


            return $reflector->newInstanceArgs($instances);

        }catch (\ReflectionException $exception){

            echo "异常<br>".$exception->getMessage()."<br>";

        }

    }


    //解决通过反射机制实例化对象时的依赖

    protected function getDependencies($parameters)

    {

        Container::$debug_count++;

        echo '步骤'.Container::$debug_count.' 获取依赖 <br>';

        $dependencies = [];


        //循环依赖类 有几个依赖的类就循环几次

        foreach ($parameters as $parameter) {


            //获取依赖的类

            $dependency = $parameter->getClass();

            if (is_null($dependency)) {

                $dependencies[] = NULL;

            } else {

                //如果依赖了哪些类,就先实例化这些类的对象,存入数组,new实例的时候要作为参数传递

                $dependencies[] = $this->resolveClass($parameter);

            }

        }


        //返回依赖类的对象些

        return (array)$dependencies;

    }


    /**

     * 创建实例时需要检测依赖,如果有依赖的类,就会先创建依赖类的对象。这里就是创建依赖类的对象

     * @param ReflectionParameter $parameter

     * @return mixed|object

     */

    protected function resolveClass(ReflectionParameter $parameter) {

        //$parameter->getClass()->name,就是类型提示类 ,就是要实例化类的构造函数里的那个参数类的名字

        return $this->make($parameter->getClass()->name);

    }


}


延伸阅读
    发表评论