前言
上一篇文章,研究了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. 判斷給定的抽象類是否已解析
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. 獲得閉包函式
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. 檢測繫結沒有就去繫結
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. 重新整理給定目標和方法上的例項
public function refresh($abstract, $target, $method)
{
//將新的回撥繫結到reboundCallbacks,並且執行該方法
return $this->rebinding($abstract, function ($app, $instance) use ($target, $method) {
$target->{$method}($instance);
});
}
25. 執行reboundCallbacks匿名函式
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. 呼叫方法
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. 獲取給定抽象的具體型別
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. 解決非類提示原語依賴關係
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 協議》,轉載必須註明作者和本文連結