深入研究 Laravel 源码第三天


前言

上一篇文章,研究了larvael框架基础应用文件,这一篇深入研究 Illuminate\Foundation\Application的父类 Illuminate\Container\Container


Illuminate\Container\Container 文件分析

1. 引入

use Closure;  //引入php匿名函数类
use Exception; //php异常类
use ArrayAccess; //php预定接口 SPL
use LogicException; //php SPL逻辑异常类
use ReflectionClass;//php 反射类
use ReflectionParameter; //php 获取函数或者参数的相关信息
use Illuminate\Support\Arr;//框架支持的arr类
use Illuminate\Contracts\Container\BindingResolutionException;//框架契约
use Illuminate\Contracts\Container\Container as ContainerContract;//框架核心容器契约类

2. 定义类定义属性

class Container implements ArrayAccess, ContainerContract
{//定义Container类 实现ArrayAccess, ContainerContract
    protected static $instance;//当前可用实例
    protected $resolved = [];//已解析出的数组
    protected $bindings = [];//容器已绑定的数组
    protected $methodBindings = [];//容器们方法绑定的数组
    protected $instances = [];//容器已分享的实例数组
    protected $aliases = [];//实例别名数组
    protected $abstractAliases = [];//由抽象名称键入的注册别名。
    protected $extenders = [];//服务的扩展闭包数组
    protected $tags = [];//所有已注册的标签
    protected $buildStack = [];//等待建造的堆栈
    protected $with = [];//参数重写堆栈
    public $contextual = [];//上下文映射绑定
    protected $reboundCallbacks = [];//所有注册的回调
    protected $globalResolvingCallbacks = [];//所有全局解析回调
    protected $globalAfterResolvingCallbacks = [];//所有全局解析之后回调
    protected $resolvingCallbacks = [];//所有类的解析回调
    protected $afterResolvingCallbacks = [];//所有类的解析之后回调

3. 定义上下文绑定

1、 public function when($concrete)
    {
        $aliases = [];
        foreach (Arr::wrap($concrete) as $c) {
            $aliases[] = $this->getAlias($c);
        }
        return new ContextualBindingBuilder($this, $aliases);
    }

4. 判断给定的抽象类是否已被绑定(是否在bindings,instances,aliases)

public function bound($abstract)
    {
        return isset($this->bindings[$abstract]) ||
               isset($this->instances[$abstract]) ||
               $this->isAlias($abstract);
    }

5. 同bound方法

    public function has($id)
    {
        return $this->bound($id);
    }

6. 判断给定的抽象类是否已解析

1、isAlias
2、getAlias

public function resolved($abstract)
    {
        //判断是否别名
        if ($this->isAlias($abstract)) {
            //从别名取出抽象类
            $abstract = $this->getAlias($abstract);
        }

        return isset($this->resolved[$abstract]) ||
               isset($this->instances[$abstract]);
    }

7. 判断是否已共享

    public function isShared($abstract)
    {
        return isset($this->instances[$abstract]) ||
              (isset($this->bindings[$abstract]['shared']) &&
               $this->bindings[$abstract]['shared'] === true);
    }

8. 是否属于别名

public function isAlias($name)
    {
        return isset($this->aliases[$name]);
    }

9. 绑定

1、dropStaleInstances
2、getClosure
3、resolved
4、rebound

public function bind($abstract, $concrete = null, $shared = false)
    {
        //删除实例从$instances 和 $aliases数组释放
        $this->dropStaleInstances($abstract);
        if (is_null($concrete)) {
            $concrete = $abstract;
        }
        //判断传入实例是不是匿名函数
        if (! $concrete instanceof Closure) {
            //不是的话就获取闭包函数
            $concrete = $this->getClosure($abstract, $concrete);
        }
        //加入已绑定数组
        $this->bindings[$abstract] = compact('concrete', 'shared');
        //判断给定的抽象类型是否已解析实例
        if ($this->resolved($abstract)) {
            //执行reboundCallbacks回调函数
            1、解析实例
            2、执行匿名函数
            $this->rebound($abstract);
        }
    }

9. 获得闭包函数

1、build
2、make

 protected function getClosure($abstract, $concrete)
    {
        return function ($container, $parameters = []) use ($abstract, $concrete) {
            if ($abstract == $concrete) {
                return $container->build($concrete);
            }

            return $container->make($concrete, $parameters);
        };
    }

10. 是否容器方法绑定

public function hasMethodBinding($method)
    {
        return isset($this->methodBindings[$method]);
    }

11. 容器方法绑定

public function bindMethod($method, $callback)
    {
        $this->methodBindings[$this->parseBindMethod($method)] = $callback;
    }

12. 获取方法已 class@method格式绑定

 protected function parseBindMethod($method)
    {
        if (is_array($method)) {
            return $method[0].'@'.$method[1];
        }

        return $method;
    }

13. 调用容器已绑定方法

public function callMethodBinding($method, $instance)
    {
        return call_user_func($this->methodBindings[$method], $instance, $this);
    }

14. 容器添加上下文绑定关系

public function addContextualBinding($concrete, $abstract, $implementation)
    {
        $this->contextual[$concrete][$this->getAlias($abstract)] = $implementation;
    }

15. 检测绑定没有就去绑定

1、bound
2、bind

public function bindIf($abstract, $concrete = null, $shared = false)
    {
        //查看给定的抽象类是否已绑定
        if (! $this->bound($abstract)) {
            $this->bind($abstract, $concrete, $shared);
        }
    }

16. 绑定单例

1、bind

 public function singleton($abstract, $concrete = null)
    {
        //调用绑定方法
        $this->bind($abstract, $concrete, true);
    }

17. 扩展服务提供者

1、getAlias
2、resolved
3、rebound

public function extend($abstract, Closure $closure)
    {
        $abstract = $this->getAlias($abstract);
        //实例数组已存在该抽象对象实例
        if (isset($this->instances[$abstract])) {
            $this->instances[$abstract] = $closure($this->instances[$abstract], $this);//更新实例
            $this->rebound($abstract);//解析实例,执行reboundCallbacks匿名函数
        } else {
            //添加抽象类匿名函数到extenders数组
            $this->extenders[$abstract][] = $closure;
            //判断是否解析
            if ($this->resolved($abstract)) {
                $this->rebound($abstract);//执行reboundCallbacks匿名函数
            }
        }
    }

18. 绑定实例

1、removeAbstractAlias
2、rebound

    public function instance($abstract, $instance)
    {
        $this->removeAbstractAlias($abstract);//删除AbstractAlias里抽象对象
        $isBound = $this->bound($abstract);//是否已解析
        unset($this->aliases[$abstract]);//删除别名数组里抽象对象
        //添加对应实例到instances数组
        $this->instances[$abstract] = $instance;
        if ($isBound) {
            $this->rebound($abstract);//执行reboundCallbacks匿名函数
        }
        return $instance;//返回实例
    }

19. 删除AbstractAlias数组里抽象对象

    protected function removeAbstractAlias($searched)
    {
        if (! isset($this->aliases[$searched])) {
            return;
        }
        foreach ($this->abstractAliases as $abstract => $aliases) {
            foreach ($aliases as $index => $alias) {
                if ($alias == $searched) {
                    unset($this->abstractAliases[$abstract][$index]);
                }
            }
        }
    }

20. 将一组标记分配给给定的绑定

    public function tag($abstracts, $tags)
    {
        $tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1);//切割成数组

        foreach ($tags as $tag) {
            if (! isset($this->tags[$tag])) {
                $this->tags[$tag] = [];
            }

            foreach ((array) $abstracts as $abstract) {
                $this->tags[$tag][] = $abstract;
            }
        }
    }

21. 解析标记实例数组

public function tagged($tag)
    {
        $results = [];
        if (isset($this->tags[$tag])) {
            foreach ($this->tags[$tag] as $abstract) {
                $results[] = $this->make($abstract);
            }
        }
    return $results;
}

22. 设置别名

    public function alias($abstract, $alias)
    {
        $this->aliases[$alias] = $abstract;
        $this->abstractAliases[$abstract][] = $alias;//一个抽象类可能有多个别名
    }

23. 绑定抽象类匿名函数到reboundCallbacks

    public function rebinding($abstract, Closure $callback)
    {
        //绑定reboundCallbacks
        $this->reboundCallbacks[$abstract = $this->getAlias($abstract)][] = $callback;
        if ($this->bound($abstract)) {
            return $this->make($abstract);//解析实例
        }
    }

24. 刷新给定目标和方法上的实例

1、rebinding

    public function refresh($abstract, $target, $method)
    {
        //将新的回调绑定到reboundCallbacks,并且执行该方法
        return $this->rebinding($abstract, function ($app, $instance) use ($target, $method) {
            $target->{$method}($instance);
        });
    }

25. 执行reboundCallbacks匿名函数

1、make
2、getReboundCallbacks

 protected function rebound($abstract)
    {
        $instance = $this->make($abstract);//解析实例
        foreach ($this->getReboundCallbacks($abstract) as $callback) {
            //执行匿名函数
            call_user_func($callback, $this, $instance);
        }
    }

26. 获取该抽象对象reboundCallbacks绑定的匿名函数

 protected function getReboundCallbacks($abstract)
    {
        if (isset($this->reboundCallbacks[$abstract])) {
            return $this->reboundCallbacks[$abstract];
        }
        return [];
    }

27. 包装给定的闭包

1、call

 public function wrap(Closure $callback, array $parameters = [])
    {
        return function () use ($callback, $parameters) {
            return $this->call($callback, $parameters);
        };
    }

28. 调用方法

1、BoundMethod::call

public function call($callback, array $parameters = [], $defaultMethod = null)
    {
        return BoundMethod::call($this, $callback, $parameters, $defaultMethod);
    }

29. 解析容器中给定的闭包

1、make

 public function factory($abstract)
    {
        return function () use ($abstract) {
            return $this->make($abstract);
        };
    }

30. make方法别名

 public function makeWith($abstract, array $parameters = [])
    {
        return $this->make($abstract, $parameters);
    }

31. 解析实例

1、resolve

public function make($abstract, array $parameters = [])
    {
        return $this->resolve($abstract, $parameters);
    }

32. PSR-11 接口获取实例

public function get($id)
    {
        try {
            return $this->resolve($id);
        } catch (Exception $e) {
            if ($this->has($id)) {
                throw $e;
            }

            throw new EntryNotFoundException;
        }
    }

33. 从容器解析给定的抽象对象实例

1、getAlias
2、getContextualConcrete
3、getConcrete
4、isBuildable
5、make//递归调用
6、getExtenders
7、isShared
8、fireResolvingCallbacks

    protected function resolve($abstract, $parameters = [])
    {
        $abstract = $this->getAlias($abstract);//获取抽象类(如果是传入的是别名)
        //如果$parameters不为空或者找到绑定的类型
        $needsContextualBuild = ! empty($parameters) || ! is_null(
            $this->getContextualConcrete($abstract)
        );
        //如果实例数组存在并且需要构建绑定
        if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
            return $this->instances[$abstract];
        }
        $this->with[] = $parameters;//参数覆盖堆
        //获取绑定的具体类型
        $concrete = $this->getConcrete($abstract);
        //如果没有拿到具体绑定的类型或者是匿名函数
        if ($this->isBuildable($concrete, $abstract)) {
            $object = $this->build($concrete);//构建实例
        } else {
            $object = $this->make($concrete);//解析实例
        }
        //获取服务扩展闭包数组
        foreach ($this->getExtenders($abstract) as $extender) {
            $object = $extender($object, $this);//重置实例
        }
        //是否单个实例
        if ($this->isShared($abstract) && ! $needsContextualBuild) {
            $this->instances[$abstract] = $object;//返回新的实例
        }
        //执行resolving所有回调事件
        $this->fireResolvingCallbacks($abstract, $object);
        $this->resolved[$abstract] = true;//标注为已解析
        array_pop($this->with);//删除刚加入的参数覆盖堆
        return $object;//返回实例
    }

34. 获取给定抽象的具体类型

1、getContextualConcrete

protected function getConcrete($abstract)
    {
        if (! is_null($concrete = $this->getContextualConcrete($abstract))) {
            return $concrete;
        }
        if (isset($this->bindings[$abstract])) {
            return $this->bindings[$abstract]['concrete'];//返回绑定数组抽象类型的具体类型
        }
        return $abstract;//返回抽象类
    }

35. 获取给定抽象的上下文具体绑定

 protected function getContextualConcrete($abstract)
    {
        //在上下文绑定数组中找到给定抽象的具体绑定
        if (! is_null($binding = $this->findInContextualBindings($abstract))) {
            return $binding;
        }
        //如果在抽象类别名数组中为空
        if (empty($this->abstractAliases[$abstract])) {
            return;
        }
        foreach ($this->abstractAliases[$abstract] as $alias) {
            //传入别名
            if (! is_null($binding = $this->findInContextualBindings($alias))) {
                return $binding;
            }
        }
    }

36. 在上下文绑定数组中找到给定抽象的具体绑定

protected function findInContextualBindings($abstract)
    {
        if (isset($this->contextual[end($this->buildStack)][$abstract])) {
            return $this->contextual[end($this->buildStack)][$abstract];
        }
    }

37. 确定给定的实例是否可以构建

 protected function isBuildable($concrete, $abstract)
    {
        return $concrete === $abstract || $concrete instanceof Closure;
    }

38. 实例化实例

1、getLastParameterOverride //获取最后覆盖参数
2、new ReflectionClass
3、isInstantiable
4、notInstantiable //抛出类不可实例化的异常
5、getConstructor
6、getParameters
7、resolveDependencies //解析所有参数依赖项
8、newInstanceArgs

    public function build($concrete)
    {
        //如果传入的是匿名函数
        if ($concrete instanceof Closure) {
            return $concrete($this, $this->getLastParameterOverride());
        }

        $reflector = new ReflectionClass($concrete);
        //检查类是否可实例化
        if (! $reflector->isInstantiable()) {
            return $this->notInstantiable($concrete);//抛出异常
        }
        $this->buildStack[] = $concrete;//待构建堆数组

        $constructor = $reflector->getConstructor();//*获取类的构造函数
        if (is_null($constructor)) {
            array_pop($this->buildStack);//如果为空,删除刚才加入的待构造堆数组
            return new $concrete;
        }
        $dependencies = $constructor->getParameters();//获取参数
        //解析所有参数依赖项
        $instances = $this->resolveDependencies(
            $dependencies//依赖注入的对象
        );//返回实例或者默认值
        array_pop($this->buildStack);//删除刚才加入的待构造堆数组
        return $reflector->newInstanceArgs($instances);//*根据给定的参数创建一个新的类实例。
    }

39. 解析依赖项

1、hasParameterOverride//判断参数是否需要重写
2、getParameterOverride//获取重写参数
3、resolvePrimitive
4、resolveClass

    protected function resolveDependencies(array $dependencies)
    {
        $results = [];

        foreach ($dependencies as $dependency) {
            if ($this->hasParameterOverride($dependency)) {
                $results[] = $this->getParameterOverride($dependency);
                continue;
            }
            $results[] = is_null($dependency->getClass())//如果类为空(传入的字符串)
                            ? $this->resolvePrimitive($dependency)
                            : $this->resolveClass($dependency);
        }

        return $results;
    }

40. 确定给定的依赖项是否具有参数重写

protected function hasParameterOverride($dependency)
    {
        return array_key_exists(
            $dependency->name, $this->getLastParameterOverride()
        );
    }

41. 获取给定的依赖项参数

protected function getParameterOverride($dependency)
    {
        return $this->getLastParameterOverride()[$dependency->name];
    }

42. 获取最后覆盖参数

protected function getLastParameterOverride()
    {
        return count($this->with) ? end($this->with) : [];
    }

43. 解决非类提示原语依赖关系

1、getContextualConcrete

protected function resolvePrimitive(ReflectionParameter $parameter)
    {
        //如果可以获取上下文绑定具体类
        if (! is_null($concrete = $this->getContextualConcrete('$'.$parameter->name))) {
            return $concrete instanceof Closure ? $concrete($this) : $concrete;//如果是闭包就执行否则返回具体绑定类
        }
        //检测默认值是否可用
        if ($parameter->isDefaultValueAvailable()) {
            return $parameter->getDefaultValue();//返回默认值
        }
        //抛出异常
        $this->unresolvablePrimitive($parameter);
    }

44. 从容器中解决基于类的依赖关系

 protected function resolveClass(ReflectionParameter $parameter)
    {
        try {
            return $this->make($parameter->getClass()->name);//解析实例
        }
        catch (BindingResolutionException $e) {//绑定解析异常
            if ($parameter->isOptional()) {//检查参数是否可选(是否有默认值)
                return $parameter->getDefaultValue();//返回默认参数值
            }
            throw $e;
        }
    }

45. 抛出实例不可构建的异常

protected function notInstantiable($concrete)
    {
        if (! empty($this->buildStack)) {
            $previous = implode(', ', $this->buildStack);
            $message = "Target [$concrete] is not instantiable while building [$previous].";
        } else {
            $message = "Target [$concrete] is not instantiable.";
        }
        throw new BindingResolutionException($message);
    }

46. 抛出Unresolvable异常

protected function unresolvablePrimitive(ReflectionParameter $parameter)
    {
        $message = "Unresolvable dependency resolving [$parameter] in class {$parameter->getDeclaringClass()->getName()}";

        throw new BindingResolutionException($message);
    }

47. 注册一个新的解析回调

public function resolving($abstract, Closure $callback = null)
    {
        if (is_string($abstract)) {
            $abstract = $this->getAlias($abstract);
        }
        if (is_null($callback) && $abstract instanceof Closure) {
            $this->globalResolvingCallbacks[] = $abstract;
        } else {
            $this->resolvingCallbacks[$abstract][] = $callback;
        }
    }

48. 注册一个新的解析之后的回调

 public function afterResolving($abstract, Closure $callback = null)
    {
        if (is_string($abstract)) {
            $abstract = $this->getAlias($abstract);
        }
        if ($abstract instanceof Closure && is_null($callback)) {
            $this->globalAfterResolvingCallbacks[] = $abstract;
        } else {
            $this->afterResolvingCallbacks[$abstract][] = $callback;
        }
    }

49. 执行所有解析回调

1、fireCallbackArray
2、getCallbacksForType
3、fireAfterResolvingCallbacks

 protected function fireResolvingCallbacks($abstract, $object)
    {
        $this->fireCallbackArray($object, $this->globalResolvingCallbacks);//执行所有全局解析回调
        $this->fireCallbackArray(
            $object, $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks)
        );
        //执行所有解析之后的回调
        $this->fireAfterResolvingCallbacks($abstract, $object);
    }

50. 执行所有解析之后回调

protected function fireAfterResolvingCallbacks($abstract, $object)
    {
        $this->fireCallbackArray($object, $this->globalAfterResolvingCallbacks);

        $this->fireCallbackArray(
            $object, $this->getCallbacksForType($abstract, $object, $this->afterResolvingCallbacks)
        );
    }

51. 获取给定类型的所有回调

    protected function getCallbacksForType($abstract, $object, array $callbacksPerType)
    {
        $results = [];

        foreach ($callbacksPerType as $type => $callbacks) {
            if ($type === $abstract || $object instanceof $type) {
                $results = array_merge($results, $callbacks);
            }
        }
        return $results;
    }

52. 对象触发回调函数

protected function fireCallbackArray($object, array $callbacks)
    {
        foreach ($callbacks as $callback) {
            $callback($object, $this);
        }
    }

53. 获取所有绑定

 public function getBindings()
    {
        return $this->bindings;
    }

54. 获取给定对象的别名

 public function getAlias($abstract)
    {
        if (! isset($this->aliases[$abstract])) {
            return $abstract;
        }

        if ($this->aliases[$abstract] === $abstract) {
            throw new LogicException("[{$abstract}] is aliased to itself.");
        }
        return $this->getAlias($this->aliases[$abstract]);
    }

55. 获取服务扩展数组

protected function getExtenders($abstract)
    {
        $abstract = $this->getAlias($abstract);
        if (isset($this->extenders[$abstract])) {
            return $this->extenders[$abstract];
        }
        return [];
    }

56. 移除服务扩展数组

public function forgetExtenders($abstract)
    {
        unset($this->extenders[$this->getAlias($abstract)]);
    }

57. 删除给定对象的instances和aliases数组

protected function dropStaleInstances($abstract)
    {
        unset($this->instances[$abstract], $this->aliases[$abstract]);
    }

58. 移除给定对象的instances

public function forgetInstance($abstract)
    {
        unset($this->instances[$abstract]);
    }

59. 移除所有instances

public function forgetInstances()
    {
        $this->instances = [];
    }

60. 清除容器中所有绑定和解析和实例

 public function flush()
    {
        $this->aliases = [];
        $this->resolved = [];
        $this->bindings = [];
        $this->instances = [];
        $this->abstractAliases = [];
    }

61. 获取容器的全局可用实例

public static function getInstance()
    {
        if (is_null(static::$instance)) {
            static::$instance = new static;
        }
        return static::$instance;
    }

62. 设置容器的全局可用实例

 public static function setInstance(ContainerContract $container = null)
    {
        return static::$instance = $container;
    }

63. 确定存在给定的偏移量 实现接口ArrayAccess必须

public function offsetExists($key)
    {
        return $this->bound($key);
    }

64. 获取给定偏移量的值 实现接口ArrayAccess必须

public function offsetGet($key)
    {
        return $this->make($key);
    }

65. 设置给定偏移量的值 实现接口ArrayAccess必须

 public function offsetSet($key, $value)
    {
        $this->bind($key, $value instanceof Closure ? $value : function () use ($value) {
            return $value;
        });
    }

66. 删除给定偏移量的值 实现接口ArrayAccess必须

public function offsetUnset($key)
    {
        unset($this->bindings[$key], $this->instances[$key], $this->resolved[$key]);
    }

67. 动态访问容器服务

public function __get($key)
    {
        return $this[$key];
    }

68. 动态设置容器服务

public function __set($key, $value)
    {
        $this[$key] = $value;
    }
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 5年前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 3

虽然LZ标题的Laravel都打错了, 但是文章是很不错滴~ :smirk:

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!