深入研究 Laravel 原始碼第三天

chonghua_123發表於2018-12-02

前言

上一篇文章,研究了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 協議》,轉載必須註明作者和本文連結

相關文章