婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁(yè) > 知識(shí)庫(kù) > 詳解如何實(shí)現(xiàn)Laravel的服務(wù)容器的方法示例

詳解如何實(shí)現(xiàn)Laravel的服務(wù)容器的方法示例

熱門標(biāo)簽:七魚外呼系統(tǒng)停用嗎 阿里云400電話申請(qǐng)加工單 九江外呼系統(tǒng) 智能電話機(jī)器人排名前十名南京 海南人工外呼系統(tǒng)有效果嗎 地下城堡2圖九地圖標(biāo)注 抖音有個(gè)地圖標(biāo)注是什么意思 西區(qū)企業(yè)怎么做地圖標(biāo)注入駐 保定crm外呼系統(tǒng)運(yùn)營(yíng)商

1. 容器的本質(zhì)

  • 服務(wù)容器本身就是一個(gè)數(shù)組,鍵名就是服務(wù)名,值就是服務(wù)。
  • 服務(wù)可以是一個(gè)原始值,也可以是一個(gè)對(duì)象,可以說是任意數(shù)據(jù)。
  • 服務(wù)名可以是自定義名,也可以是對(duì)象的類名,也可以是接口名。
// 服務(wù)容器
$container = [
  // 原始值
  'text' => '這是一個(gè)字符串',
  // 自定義服務(wù)名
  'customName' => new StdClass(),
  // 使用類名作為服務(wù)名
  'StdClass' => new StdClass(),
  // 使用接口名作為服務(wù)名
  'Namespace\\StdClassInterface' => new StdClass(),
];

// ----------- ↓↓↓↓示例代碼↓↓↓↓ ----------- //

// 綁定服務(wù)到容器
$container['standard'] = new StdClass();
// 獲取服務(wù)
$standard = $container['standard'];
var_dump($standard);

2. 封裝成類

為了方便維護(hù),我們把上面的數(shù)組封裝到類里面。

$instances還是上面的容器數(shù)組。我們?cè)黾觾蓚€(gè)方法,instance用來綁定服務(wù),get用來從容器中獲取服務(wù)。

class BaseContainer
{

  // 已綁定的服務(wù)
  protected $instances = [];

  // 綁定服務(wù)
  public function instance($name, $instance)
  {
    $this->instances[$name] = $instance;
  }

  // 獲取服務(wù)
  public function get($name)
  {
    return isset($this->instances[$name]) ? $this->instances[$name] : null;
  }
}

// ----------- ↓↓↓↓示例代碼↓↓↓↓ ----------- //

$container = new BaseContainer();
// 綁定服務(wù)
$container->instance('StdClass', new StdClass());
// 獲取服務(wù)
$stdClass = $container->get('StdClass');
var_dump($stdClass);

3. 按需實(shí)例化

現(xiàn)在我們?cè)诮壎ㄒ粋€(gè)對(duì)象服務(wù)的時(shí)候,就必須要先把類實(shí)例化,如果綁定的服務(wù)沒有被用到,那么類就會(huì)白白實(shí)例化,造成性能浪費(fèi)。

為了解決這個(gè)問題,我們?cè)黾右粋€(gè)bind函數(shù),它支持綁定一個(gè)回調(diào)函數(shù),在回調(diào)函數(shù)中實(shí)例化類。這樣一來,我們只有在使用服務(wù)時(shí),才回調(diào)這個(gè)函數(shù),這樣就實(shí)現(xiàn)了按需實(shí)例化。

這時(shí)候,我們獲取服務(wù)時(shí),就不只是從數(shù)組中拿到服務(wù)并返回了,還需要判斷如果是回調(diào)函數(shù),就要執(zhí)行回調(diào)函數(shù)。所以我們把get方法的名字改成make。意思就是生產(chǎn)一個(gè)服務(wù),這個(gè)服務(wù)可以是已綁定的服務(wù),也可以是已綁定的回調(diào)函數(shù),也可以是一個(gè)類名,如果是類名,我們就直接實(shí)例化該類并返回。

然后,我們?cè)黾右粋€(gè)新數(shù)組$bindings,用來存儲(chǔ)綁定的回調(diào)函數(shù)。然后我們把bind方法改一下,判斷下$instance如果是一個(gè)回調(diào)函數(shù),就放到$bindings數(shù)組,否則就用make方法實(shí)例化類。

class DeferContainer extend BaseContainer
{
  // 已綁定的回調(diào)函數(shù)
  protected $bindings = [];

  // 綁定服務(wù)
  public function bind($name, $instance)
  {
    if ($instance instanceof Closure) {
      // 如果$instance是一個(gè)回調(diào)函數(shù),就綁定到bindings。
      $this->bindings[$name] = $instance;
    } else {
      // 調(diào)用make方法,創(chuàng)建實(shí)例
      $this->instances[$name] = $this->make($name);
    }
  }

  // 獲取服務(wù)
  public function make($name)
  {
    if (isset($this->instances[$name])) {
      return $this->instances[$name];
    }

    if (isset($this->bindings[$name])) {
      // 執(zhí)行回調(diào)函數(shù)并返回
      $instance = call_user_func($this->bindings[$name]);
    } else {
      // 還沒有綁定到容器中,直接new.
      $instance = new $name();
    }

    return $instance;
  }
}

// ----------- ↓↓↓↓示例代碼↓↓↓↓ ----------- //

$container = new DeferContainer();
// 綁定服務(wù)
$container->bind('StdClass', function () {
  echo "我被執(zhí)行了\n";
  return new StdClass();
});
// 獲取服務(wù)
$stdClass = $container->make('StdClass');
var_dump($stdClass);

StdClass這個(gè)服務(wù)綁定的是一個(gè)回調(diào)函數(shù),在回調(diào)函數(shù)中才會(huì)真正的實(shí)例化類。如果沒有用到這個(gè)服務(wù),那回調(diào)函數(shù)就不會(huì)被執(zhí)行,類也不會(huì)被實(shí)例化。

4. 單例

從上面的代碼中可以看出,每次調(diào)用make方法時(shí),都會(huì)執(zhí)行一次回調(diào)函數(shù),并返回一個(gè)新的類實(shí)例。但是在某些情況下,我們希望這個(gè)實(shí)例是一個(gè)單例,無論make多少次,只實(shí)例化一次。

這時(shí)候,我們給bind方法增加第三個(gè)參數(shù)$shared,用來標(biāo)記是否是單例,默認(rèn)不是單例。然后把回調(diào)函數(shù)和這個(gè)標(biāo)記都存到$bindings數(shù)組里。

為了方便綁定單例服務(wù),再增加一個(gè)新的方法singleton,它直接調(diào)用bind,并且$shared參數(shù)強(qiáng)制為true。

對(duì)于make方法,我們也要做修改。在執(zhí)行$bindings里的回調(diào)函數(shù)以后,做一個(gè)判斷,如果之前綁定時(shí)標(biāo)記的shared是true,就把回調(diào)函數(shù)返回的結(jié)果存儲(chǔ)到$instances里。由于我們是先從$instances里找服務(wù),所以這樣下次再make的時(shí)候就會(huì)直接返回,而不會(huì)再次執(zhí)行回調(diào)函數(shù)。這樣就實(shí)現(xiàn)了單例的綁定。

class SingletonContainer extends DeferContainer
{
  // 綁定服務(wù)
  public function bind($name, $instance, $shared = false)
  {
    if ($instance instanceof Closure) {
      // 如果$instance是一個(gè)回調(diào)函數(shù),就綁定到bindings。
      $this->bindings[$name] = [
        'callback' => $instance,
        // 標(biāo)記是否單例
        'shared' => $shared
      ];
    } else {
      // 調(diào)用make方法,創(chuàng)建實(shí)例
      $this->instances[$name] = $this->make($name);
    }
  }

  // 綁定一個(gè)單例
  public function singleton($name, $instance)
  {
    $this->bind($name, $instance, true);
  }

  // 獲取服務(wù)
  public function make($name)
  {
    if (isset($this->instances[$name])) {
      return $this->instances[$name];
    }

    if (isset($this->bindings[$name])) {
      // 執(zhí)行回調(diào)函數(shù)并返回
      $instance = call_user_func($this->bindings[$name]['callback']);

      if ($this->bindings[$name]['shared']) {
        // 標(biāo)記為單例時(shí),存儲(chǔ)到服務(wù)中
        $this->instances[$name] = $instance;
      }
    } else {
      // 還沒有綁定到容器中,直接new.
      $instance = new $name();
    }

    return $instance;
  }
}

// ----------- ↓↓↓↓示例代碼↓↓↓↓ ----------- //

$container = new SingletonContainer();
// 綁定服務(wù)
$container->singleton('anonymous', function () {
  return new class
  {
    public function __construct()
    {
      echo "我被實(shí)例化了\n";
    }
  };
});
// 無論make多少次,只會(huì)實(shí)例化一次
$container->make('anonymous');
$container->make('anonymous');
// 獲取服務(wù)
$anonymous = $container->make('anonymous');
var_dump($anonymous)

上面的代碼用singleton綁定了一個(gè)名為anonymous的服務(wù),回調(diào)函數(shù)里返回了一個(gè)匿名類的實(shí)例。這個(gè)匿名類在被實(shí)例化時(shí)會(huì)輸出一段文字。無論我們make多少次anonymous,這個(gè)回調(diào)函數(shù)只會(huì)被執(zhí)行一次,匿名類也只會(huì)被實(shí)例化一次。

5. 自動(dòng)注入

自動(dòng)注入是Ioc容器的核心,沒有自動(dòng)注入就無法做到控制反轉(zhuǎn)。

自動(dòng)注入就是指,在實(shí)例化一個(gè)類時(shí),用反射類來獲取__construct所需要的參數(shù),然后根據(jù)參數(shù)的類型,從容器中找到已綁定的服務(wù)。我們只要有了__construct方法所需的所有參數(shù),就能自動(dòng)實(shí)例化該類,實(shí)現(xiàn)自動(dòng)注入。

現(xiàn)在,我們?cè)黾右粋€(gè)build方法,它只接收一個(gè)參數(shù),就是類名。build方法會(huì)用反射類來獲取__construct方法所需要的參數(shù),然后返回實(shí)例化結(jié)果。

另外一點(diǎn)就是,我們之前在調(diào)用make方法時(shí),如果傳的是一個(gè)未綁定的類,我們直接new了這個(gè)類。現(xiàn)在我們把未綁定的類交給build方法來構(gòu)建,因?yàn)樗С肿詣?dòng)注入。

class InjectionContainer extends SingletonContainer
{

  // 獲取服務(wù)
  public function make($name)
  {
    if (isset($this->instances[$name])) {
      return $this->instances[$name];
    }
    if (isset($this->bindings[$name])) {
      // 執(zhí)行回調(diào)函數(shù)并返回
      $instance = call_user_func($this->bindings[$name]['callback']);

      if ($this->bindings[$name]['shared']) {
        // 標(biāo)記為單例時(shí),存儲(chǔ)到服務(wù)中
        $this->instances[$name] = $instance;
      }
    } else {
      // 使用build方法構(gòu)建此類
      $instance = $this->build($name);
    }

    return $instance;
  }

  // 構(gòu)建一個(gè)類,并自動(dòng)注入服務(wù)
  public function build($class)
  {

    $reflector = new ReflectionClass($class);

    $constructor = $reflector->getConstructor();

    if (is_null($constructor)) {
      // 沒有構(gòu)造函數(shù),直接new
      return new $class();
    }

    $dependencies = [];

    // 獲取構(gòu)造函數(shù)所需的參數(shù)
    foreach ($constructor->getParameters() as $dependency) {
      if (is_null($dependency->getClass())) {
        // 參數(shù)類型不是類時(shí),無法從容器中獲取依賴
        if ($dependency->isDefaultValueAvailable()) {
          // 查找參數(shù)的默認(rèn)值,如果有就使用默認(rèn)值
          $dependencies[] = $dependency->getDefaultValue();
        } else {
          // 無法提供類所依賴的參數(shù)
          throw new Exception('找不到依賴參數(shù):' . $dependency->getName());
        }
      } else {
        // 參數(shù)類型是類時(shí),就用make方法構(gòu)建該類
        $dependencies[] = $this->make($dependency->getClass()->name);
      }
    }

    return $reflector->newInstanceArgs($dependencies);
  }
}

// ----------- ↓↓↓↓示例代碼↓↓↓↓ ----------- //

class Redis
{
}

class Cache
{
  protected $redis;

  // 構(gòu)造函數(shù)中依賴Redis服務(wù)
  public function __construct(Redis $redis)
  {
    $this->redis = $redis;
  }
}

$container = new InjectionContainer();
// 綁定Redis服務(wù)
$container->singleton(Redis::class, function () {
  return new Redis();
});
// 構(gòu)建Cache類
$cache = $container->make(Cache::class);
var_dump($cache);

6. 自定義依賴參數(shù)

現(xiàn)在有個(gè)問題,如果類依賴的參數(shù)不是類或接口,只是一個(gè)普通變量,這時(shí)候就無法從容器中獲取依賴參數(shù)了,也就無法實(shí)例化類了。

那么接下來我們就支持一個(gè)新功能,在調(diào)用make方法時(shí),支持傳第二個(gè)參數(shù)$parameters,這是一個(gè)數(shù)組,無法從容器中獲取的依賴,就從這個(gè)數(shù)組中找。

當(dāng)然,make方法是用不到這個(gè)參數(shù)的,因?yàn)樗回?fù)責(zé)實(shí)例化類,它直接傳給build方法。在build方法尋找依賴的參數(shù)時(shí),就先從$parameters中找。這樣就實(shí)現(xiàn)了自定義依賴參數(shù)。

需要注意的一點(diǎn)是,build方法是按照參數(shù)的名字來找依賴的,所以parameters中的鍵名也必須跟__construct中參數(shù)名一致。

class ParametersContainer extends InjectionContainer
{
  // 獲取服務(wù)
  public function make($name, array $parameters = [])
  {
    if (isset($this->instances[$name])) {
      return $this->instances[$name];
    }
    if (isset($this->bindings[$name])) {
      // 執(zhí)行回調(diào)函數(shù)并返回
      $instance = call_user_func($this->bindings[$name]['callback']);

      if ($this->bindings[$name]['shared']) {
        // 標(biāo)記為單例時(shí),存儲(chǔ)到服務(wù)中
        $this->instances[$name] = $instance;
      }
    } else {
      // 使用build方法構(gòu)建此類
      $instance = $this->build($name, $parameters);
    }

    return $instance;
  }

  // 構(gòu)建一個(gè)類,并自動(dòng)注入服務(wù)
  public function build($class, array $parameters = [])
  {
    $reflector = new ReflectionClass($class);

    $constructor = $reflector->getConstructor();

    if (is_null($constructor)) {
      // 沒有構(gòu)造函數(shù),直接new
      return new $class();
    }

    $dependencies = [];

    // 獲取構(gòu)造函數(shù)所需的參數(shù)
    foreach ($constructor->getParameters() as $dependency) {

      if (isset($parameters[$dependency->getName()])) {
        // 先從自定義參數(shù)中查找
        $dependencies[] = $parameters[$dependency->getName()];
        continue;
      }

      if (is_null($dependency->getClass())) {
        // 參數(shù)類型不是類或接口時(shí),無法從容器中獲取依賴
        if ($dependency->isDefaultValueAvailable()) {
          // 查找默認(rèn)值,如果有就使用默認(rèn)值
          $dependencies[] = $dependency->getDefaultValue();
        } else {
          // 無法提供類所依賴的參數(shù)
          throw new Exception('找不到依賴參數(shù):' . $dependency->getName());
        }
      } else {
        // 參數(shù)類型是類時(shí),就用make方法構(gòu)建該類
        $dependencies[] = $this->make($dependency->getClass()->name);
      }
    }

    return $reflector->newInstanceArgs($dependencies);
  }
}

// ----------- ↓↓↓↓示例代碼↓↓↓↓ ----------- //

class Redis
{
}

class Cache
{
  protected $redis;

  protected $name;

  protected $default;

  // 構(gòu)造函數(shù)中依賴Redis服務(wù)和name參數(shù),name的類型不是類,無法從容器中查找
  public function __construct(Redis $redis, $name, $default = '默認(rèn)值')
  {
    $this->redis = $redis;
    $this->name = $name;
    $this->default = $default;
  }
}

$container = new ParametersContainer();
// 綁定Redis服務(wù)
$container->singleton(Redis::class, function () {
  return new Redis();
});
// 構(gòu)建Cache類
$cache = $container->make(Cache::class, ['name' => 'test']);
var_dump($cache);

提示:實(shí)際上,Laravel容器的build方法并沒有第二個(gè)參數(shù)$parameters,它是用類屬性來維護(hù)自定義參數(shù)。原理都是一樣的,只是實(shí)現(xiàn)方式不一樣。這里為了方便理解,不引入過多概念。

7. 服務(wù)別名

別名可以理解成小名、外號(hào)。服務(wù)別名就是給已綁定的服務(wù)設(shè)置一些外號(hào),使我們通過外號(hào)也能找到該服務(wù)。

這個(gè)就比較簡(jiǎn)單了,我們?cè)黾右粋€(gè)新的數(shù)組$aliases,用來存儲(chǔ)別名。再增加一個(gè)方法alias,用來讓外部注冊(cè)別名。

唯一需要我們修改的地方,就是在make時(shí),要先從$aliases中找到真實(shí)的服務(wù)名。

class AliasContainer extends ParametersContainer
{
  // 服務(wù)別名
  protected $aliases = [];

  // 給服務(wù)綁定一個(gè)別名
  public function alias($alias, $name)
  {
    $this->aliases[$alias] = $name;
  }

  // 獲取服務(wù)
  public function make($name, array $parameters = [])
  {
    // 先用別名查找真實(shí)服務(wù)名
    $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name;

    return parent::make($name, $parameters);
  }
}

// ----------- ↓↓↓↓示例代碼↓↓↓↓ ----------- //

$container = new AliasContainer();

// 綁定服務(wù)
$container->instance('text', '這是一個(gè)字符串');
// 給服務(wù)注冊(cè)別名
$container->alias('string', 'text');
$container->alias('content', 'text');

var_dump($container->make('string'));
var_dump($container->make('content'));

8. 擴(kuò)展綁定

有時(shí)候我們需要給已綁定的服務(wù)做一個(gè)包裝,這時(shí)候就用到擴(kuò)展綁定了。我們先看一個(gè)實(shí)際的用法,理解它的作用后,才看它是如何實(shí)現(xiàn)的。

// 綁定日志服務(wù)
$container->singleton('log', new Log());

// 對(duì)已綁定的服務(wù)再次包裝
$container->extend('log', function(Log $log){
  // 返回了一個(gè)新服務(wù)
  return new RedisLog($log);
});

現(xiàn)在我們看它是如何實(shí)現(xiàn)的。增加一個(gè)$extenders數(shù)組,用來存放擴(kuò)展器。再增加一個(gè)extend方法,用來注冊(cè)擴(kuò)展器。

然后在make方法返回$instance之前,按順序依次調(diào)用之前注冊(cè)的擴(kuò)展器。

class ExtendContainer extends AliasContainer
{
  // 存放擴(kuò)展器的數(shù)組
  protected $extenders = [];

  // 給服務(wù)綁定擴(kuò)展器
  public function extend($name, $extender)
  {
    if (isset($this->instances[$name])) {
      // 已經(jīng)實(shí)例化的服務(wù),直接調(diào)用擴(kuò)展器
      $this->instances[$name] = $extender($this->instances[$name]);
    } else {
      $this->extenders[$name][] = $extender;
    }
  }

  // 獲取服務(wù)
  public function make($name, array $parameters = [])
  {
    $instance = parent::make($name, $parameters);

    if (isset($this->extenders[$name])) {
      // 調(diào)用擴(kuò)展器
      foreach ($this->extenders[$name] as $extender) {
        $instance = $extender($instance);
      }
    }

    return $instance;
  }
}

// ----------- ↓↓↓↓示例代碼↓↓↓↓ ----------- //

class Redis
{
  public $name;

  public function __construct($name = 'default')
  {
    $this->name = $name;
  }

  public function setName($name)
  {
    $this->name = $name;
  }
}

$container = new ExtendContainer();

// 綁定Redis服務(wù)
$container->singleton(Redis::class, function () {
  return new Redis();
});

// 給Redis服務(wù)綁定一個(gè)擴(kuò)展器
$container->extend(Redis::class, function (Redis $redis) {
  $redis->setName('擴(kuò)展器');
  return $redis;
});
$redis = $container->make(Redis::class);
var_dump($redis->name);

9. 上下文綁定

有時(shí)侯我們可能有兩個(gè)類使用同一個(gè)接口,但希望在每個(gè)類中注入不同的實(shí)現(xiàn),例如兩個(gè)控制器,分別為它們注入不同的Log服務(wù)。

class ApiController
{
  public function __construct(Log $log)
  {
  }
}

class WebController
{
  public function __construct(Log $log)
  {
  }
}

最終我們要用以下方式實(shí)現(xiàn):

// 當(dāng)ApiController依賴Log時(shí),給它一個(gè)RedisLog
$container->addContextualBinding('ApiController','Log',new RedisLog());

// 當(dāng)WebController依賴Log時(shí),給它一個(gè)FileLog
$container->addContextualBinding('WebController','Log',new FileLog());

為了更直觀更方便更語(yǔ)義化的使用,我們把這個(gè)過程改成鏈?zhǔn)讲僮鳎?/p>

$container->when('ApiController')
    ->needs('Log')
    ->give(new RedisLog());

我們?cè)黾右粋€(gè)$context數(shù)組,用來存儲(chǔ)上下文。同時(shí)增加一個(gè)addContextualBinding方法,用來注冊(cè)上下文綁定。以ApiController為例,$context的真實(shí)模樣是:

$context['ApiController']['Log'] = new RedisLog();

然后build方法實(shí)例化類時(shí),先從上下文中查找依賴參數(shù),就實(shí)現(xiàn)了上下文綁定。

接下來,看看鏈?zhǔn)讲僮魇侨绾螌?shí)現(xiàn)的。

首先定義一個(gè)類Context,這個(gè)類有兩個(gè)方法,needs和give。

然后在容器中,增加一個(gè)when方法,它返回一個(gè)Context對(duì)象。在Context對(duì)象的give方法中,我們已經(jīng)具備了注冊(cè)上下文所需要的所有參數(shù),所以就可以在give方法中調(diào)用addContextualBinding來注冊(cè)上下文了。

class ContextContainer extends ExtendContainer
{
  // 依賴上下文
  protected $context = [];

  // 構(gòu)建一個(gè)類,并自動(dòng)注入服務(wù)
  public function build($class, array $parameters = [])
  {
    $reflector = new ReflectionClass($class);

    $constructor = $reflector->getConstructor();

    if (is_null($constructor)) {
      // 沒有構(gòu)造函數(shù),直接new
      return new $class();
    }

    $dependencies = [];

    // 獲取構(gòu)造函數(shù)所需的參數(shù)
    foreach ($constructor->getParameters() as $dependency) {

      if (isset($this->context[$class])  isset($this->context[$class][$dependency->getName()])) {
        // 先從上下文中查找
        $dependencies[] = $this->context[$class][$dependency->getName()];
        continue;
      }

      if (isset($parameters[$dependency->getName()])) {
        // 從自定義參數(shù)中查找
        $dependencies[] = $parameters[$dependency->getName()];
        continue;
      }

      if (is_null($dependency->getClass())) {
        // 參數(shù)類型不是類或接口時(shí),無法從容器中獲取依賴
        if ($dependency->isDefaultValueAvailable()) {
          // 查找默認(rèn)值,如果有就使用默認(rèn)值
          $dependencies[] = $dependency->getDefaultValue();
        } else {
          // 無法提供類所依賴的參數(shù)
          throw new Exception('找不到依賴參數(shù):' . $dependency->getName());
        }
      } else {
        // 參數(shù)類型是一個(gè)類時(shí),就用make方法構(gòu)建該類
        $dependencies[] = $this->make($dependency->getClass()->name);
      }
    }

    return $reflector->newInstanceArgs($dependencies);
  }

  // 綁定上下文
  public function addContextualBinding($when, $needs, $give)
  {
    $this->context[$when][$needs] = $give;
  }

  // 支持鏈?zhǔn)椒绞浇壎ㄉ舷挛?
  public function when($when)
  {
    return new Context($when, $this);
  }
}

class Context
{
  protected $when;

  protected $needs;

  protected $container;

  public function __construct($when, ContextContainer $container)
  {
    $this->when = $when;
    $this->container = $container;
  }

  public function needs($needs)
  {
    $this->needs = $needs;

    return $this;
  }

  public function give($give)
  {
    // 調(diào)用容器綁定依賴上下文
    $this->container->addContextualBinding($this->when, $this->needs, $give);
  }
}

// ----------- ↓↓↓↓示例代碼↓↓↓↓ ----------- //

class Dog
{
  public $name;

  public function __construct($name)
  {
    $this->name = $name;
  }
}

class Cat
{
  public $name;

  public function __construct($name)
  {
    $this->name = $name;
  }
}

$container = new ContextContainer();

// 給Dog類設(shè)置上下文綁定
$container->when(Dog::class)
  ->needs('name')
  ->give('小狗');
// 給Cat類設(shè)置上下文綁定
$container->when(Cat::class)
  ->needs('name')
  ->give('小貓');

$dog = $container->make(Dog::class);
$cat = $container->make(Cat::class);
var_dump('Dog:' . $dog->name);
var_dump('Cat:' . $cat->name);

10. 完整代碼

class Container
{
  // 已綁定的服務(wù)
  protected $instances = [];
  // 已綁定的回調(diào)函數(shù)
  protected $bindings = [];
  // 服務(wù)別名
  protected $aliases = [];
  // 存放擴(kuò)展器的數(shù)組
  protected $extenders = [];
  // 依賴上下文
  protected $context = [];

  // 綁定服務(wù)實(shí)例
  public function instance($name, $instance)
  {
    $this->instances[$name] = $instance;
  }

  // 綁定服務(wù)
  public function bind($name, $instance, $shared = false)
  {
    if ($instance instanceof Closure) {
      // 如果$instance是一個(gè)回調(diào)函數(shù),就綁定到bindings。
      $this->bindings[$name] = [
        'callback' => $instance,
        // 標(biāo)記是否單例
        'shared' => $shared
      ];
    } else {
      // 調(diào)用make方法,創(chuàng)建實(shí)例
      $this->instances[$name] = $this->make($name);
    }
  }

  // 綁定一個(gè)單例
  public function singleton($name, $instance)
  {
    $this->bind($name, $instance, true);
  }

  // 給服務(wù)綁定一個(gè)別名
  public function alias($alias, $name)
  {
    $this->aliases[$alias] = $name;
  }

  // 給服務(wù)綁定擴(kuò)展器
  public function extend($name, $extender)
  {
    if (isset($this->instances[$name])) {
      // 已經(jīng)實(shí)例化的服務(wù),直接調(diào)用擴(kuò)展器
      $this->instances[$name] = $extender($this->instances[$name]);
    } else {
      $this->extenders[$name][] = $extender;
    }
  }

  // 獲取服務(wù)
  public function make($name, array $parameters = [])
  {
    // 先用別名查找真實(shí)服務(wù)名
    $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name;

    if (isset($this->instances[$name])) {
      return $this->instances[$name];
    }

    if (isset($this->bindings[$name])) {
      // 執(zhí)行回調(diào)函數(shù)并返回
      $instance = call_user_func($this->bindings[$name]['callback']);

      if ($this->bindings[$name]['shared']) {
        // 標(biāo)記為單例時(shí),存儲(chǔ)到服務(wù)中
        $this->instances[$name] = $instance;
      }
    } else {
      // 使用build方法構(gòu)建此類
      $instance = $this->build($name, $parameters);
    }

    if (isset($this->extenders[$name])) {
      // 調(diào)用擴(kuò)展器
      foreach ($this->extenders[$name] as $extender) {
        $instance = $extender($instance);
      }
    }

    return $instance;
  }

  // 構(gòu)建一個(gè)類,并自動(dòng)注入服務(wù)
  public function build($class, array $parameters = [])
  {
    $reflector = new ReflectionClass($class);

    $constructor = $reflector->getConstructor();

    if (is_null($constructor)) {
      // 沒有構(gòu)造函數(shù),直接new
      return new $class();
    }

    $dependencies = [];

    // 獲取構(gòu)造函數(shù)所需的參數(shù)
    foreach ($constructor->getParameters() as $dependency) {

      if (isset($this->context[$class])  isset($this->context[$class][$dependency->getName()])) {
        // 先從上下文中查找
        $dependencies[] = $this->context[$class][$dependency->getName()];
        continue;
      }

      if (isset($parameters[$dependency->getName()])) {
        // 從自定義參數(shù)中查找
        $dependencies[] = $parameters[$dependency->getName()];
        continue;
      }

      if (is_null($dependency->getClass())) {
        // 參數(shù)類型不是類或接口時(shí),無法從容器中獲取依賴
        if ($dependency->isDefaultValueAvailable()) {
          // 查找默認(rèn)值,如果有就使用默認(rèn)值
          $dependencies[] = $dependency->getDefaultValue();
        } else {
          // 無法提供類所依賴的參數(shù)
          throw new Exception('找不到依賴參數(shù):' . $dependency->getName());
        }
      } else {
        // 參數(shù)類型是一個(gè)類時(shí),就用make方法構(gòu)建該類
        $dependencies[] = $this->make($dependency->getClass()->name);
      }
    }

    return $reflector->newInstanceArgs($dependencies);
  }

  // 綁定上下文
  public function addContextualBinding($when, $needs, $give)
  {
    $this->context[$when][$needs] = $give;
  }

  // 支持鏈?zhǔn)椒绞浇壎ㄉ舷挛?
  public function when($when)
  {
    return new Context($when, $this);
  }
}

class Context
{
  protected $when;

  protected $needs;

  protected $container;

  public function __construct($when, Container $container)
  {
    $this->when = $when;
    $this->container = $container;
  }

  public function needs($needs)
  {
    $this->needs = $needs;

    return $this;
  }

  public function give($give)
  {
    // 調(diào)用容器綁定依賴上下文
    $this->container->addContextualBinding($this->when, $this->needs, $give);
  }
}

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

您可能感興趣的文章:
  • Laravel服務(wù)容器綁定的幾種方法總結(jié)
  • 詳解Laravel服務(wù)容器的綁定與解析
  • laravel ajax curd 搜索登錄判斷功能的實(shí)現(xiàn)
  • Laravel中Kafka的使用詳解
  • laravel使用redis隊(duì)列實(shí)例講解
  • Laravel的加密解密與哈希實(shí)例講解
  • Laravel中10個(gè)有用的用法小結(jié)
  • 詳解Laravel服務(wù)容器的優(yōu)勢(shì)

標(biāo)簽:昭通 十堰 九江 韶關(guān) 甘肅 遼陽(yáng) 梅河口 涼山

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《詳解如何實(shí)現(xiàn)Laravel的服務(wù)容器的方法示例》,本文關(guān)鍵詞  詳解,如何,實(shí)現(xiàn),Laravel,的,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《詳解如何實(shí)現(xiàn)Laravel的服務(wù)容器的方法示例》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于詳解如何實(shí)現(xiàn)Laravel的服務(wù)容器的方法示例的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    天天综合网天天综合色| 精品少妇一区二区三区在线视频| 全部av―极品视觉盛宴亚洲| 亚洲精品成a人| 樱桃视频在线观看一区| 日韩电影在线一区二区三区| 美女一区二区久久| 精品成人在线观看| 白白色亚洲国产精品| 91成人免费在线| 亚洲免费观看在线观看| 欧美撒尿777hd撒尿| 亚洲综合视频在线| 成人app下载| 久久免费午夜影院| 午夜欧美视频在线观看| 日韩国产精品久久久久久亚洲| 国产麻豆欧美日韩一区| 欧美mv日韩mv| 色av一区二区| 欧美一区二区视频观看视频| 国产精品自拍在线| 日韩福利视频网| 色悠悠亚洲一区二区| 2023国产精品| 亚洲亚洲人成综合网络| 麻豆精品一区二区av白丝在线| 播五月开心婷婷综合| 欧美久久一区二区| 国产精品久久精品日日| 久久国产精品区| 欧美色倩网站大全免费| 中文字幕精品一区二区精品绿巨人 | 欧美一区二区高清| 亚洲一区二区三区四区在线观看| 成人性生交大片| 337p日本欧洲亚洲大胆色噜噜| 亚洲韩国精品一区| 91污在线观看| 国产精品国产三级国产| 久久国产日韩欧美精品| 91精品国产综合久久精品| 一区二区三区自拍| 成人黄页在线观看| 久久久久久夜精品精品免费| 奇米影视7777精品一区二区| 91成人国产精品| 亚洲欧美一区二区久久| 在线观看免费亚洲| 亚洲视频在线一区| 欧美日韩黄视频| 极品少妇一区二区| 日韩欧美成人午夜| 波多野结衣一区二区三区 | 高清成人免费视频| 日韩女优制服丝袜电影| 国产精品一区二区你懂的| 国产精品久久久久久久久搜平片| 国产麻豆精品视频| 亚洲欧美日韩一区| 精品国产一区二区三区久久影院| 韩国视频一区二区| 一区二区高清免费观看影视大全| 欧美群妇大交群的观看方式| 美国毛片一区二区三区| 17c精品麻豆一区二区免费| 欧美精品xxxxbbbb| 懂色一区二区三区免费观看| 亚洲欧美国产高清| 久久伊99综合婷婷久久伊| 91天堂素人约啪| 国产在线精品一区二区| 亚洲精品第一国产综合野| 精品粉嫩aⅴ一区二区三区四区| 色噜噜狠狠成人网p站| 高清久久久久久| 国产成人午夜视频| 美腿丝袜亚洲色图| 亚洲第一在线综合网站| 怡红院av一区二区三区| 亚洲桃色在线一区| 中文字幕在线观看一区二区| 久久精品视频免费观看| 日韩欧美中文一区二区| 欧美成人一区二区三区在线观看| 欧美日韩国产片| 欧美自拍偷拍午夜视频| 欧美色图在线观看| 国模少妇一区二区三区| 日韩亚洲欧美一区| 久久久久久亚洲综合影院红桃| 日韩欧美aaaaaa| 国产精品亚洲午夜一区二区三区| 亚洲精品你懂的| 日韩电影免费一区| 国产原创一区二区| 91丨九色丨蝌蚪丨老版| 欧美日韩久久一区| 日韩欧美在线123| 久久久久久久性| 亚洲成人黄色影院| 久久精品免费看| av成人免费在线| 欧美不卡一区二区三区| 1区2区3区欧美| 波多野结衣在线一区| 欧美精品一区二区三区四区 | 久久一日本道色综合| 一区二区三区在线免费视频 | 久久电影网站中文字幕| 99精品国产99久久久久久白柏| 欧美大尺度电影在线| 日韩制服丝袜av| 大白屁股一区二区视频| 欧美高清性hdvideosex| 亚洲精品久久久蜜桃| 一本色道久久加勒比精品| 中文字幕在线一区免费| 久久精品国产99| 精品国产区一区| 免费不卡在线观看| 日韩欧美一区二区视频| 亚洲福利一二三区| 欧美三级三级三级爽爽爽| 亚洲啪啪综合av一区二区三区| 成人福利在线看| 日韩一区中文字幕| 91成人免费电影| 日本欧美肥老太交大片| 欧美日韩国产美女| 一区二区三区四区视频精品免费| 99久久er热在这里只有精品15| 国产精品国产三级国产普通话蜜臀| 国产激情偷乱视频一区二区三区| 一区二区三区不卡视频在线观看 | 日韩一区在线免费观看| 欧美一区二区国产| 3d动漫精品啪啪一区二区竹菊| 成人h动漫精品一区二| 国产高清成人在线| 国产成人8x视频一区二区| 中文字幕第一区综合| 亚洲一区二区三区国产| 在线播放91灌醉迷j高跟美女 | 7777精品伊人久久久大香线蕉的| 蜜臀av一区二区在线免费观看| 国产精品你懂的| 日韩一级二级三级精品视频| 99re66热这里只有精品3直播| 免费观看日韩av| 亚洲综合视频在线| 日韩区在线观看| 日韩欧美国产一区二区在线播放| 91蜜桃在线观看| 99久久久免费精品国产一区二区| 首页综合国产亚洲丝袜| 日本一区二区视频在线观看| 制服丝袜av成人在线看| 91麻豆精品国产无毒不卡在线观看| 99国产精品国产精品毛片| 国产精品自产自拍| 看国产成人h片视频| 天堂久久久久va久久久久| 日韩国产精品大片| 亚洲与欧洲av电影| 精品无码三级在线观看视频| 亚洲国产日韩a在线播放性色| 天天影视涩香欲综合网| 5858s免费视频成人| 国产黄色精品视频| 日韩vs国产vs欧美| 夜夜揉揉日日人人青青一国产精品| 久久久亚洲精品一区二区三区| 91精品国产丝袜白色高跟鞋| 欧美视频一区二区在线观看| 91麻豆精品在线观看| 成人a免费在线看| 国产91丝袜在线观看| 久久成人久久爱| 看电影不卡的网站| 日韩综合一区二区| 视频一区二区国产| 亚洲国产wwwccc36天堂| 亚洲制服丝袜av| 亚洲愉拍自拍另类高清精品| 亚洲精品国产成人久久av盗摄| 中文字幕一区二区三| 1区2区3区精品视频| 亚洲另类在线制服丝袜| 日韩毛片在线免费观看| 亚洲人被黑人高潮完整版| 亚洲欧美日韩成人高清在线一区| 亚洲视频精选在线| 国产精品国产精品国产专区不蜜 | 五月天激情综合| 亚洲第一福利视频在线| 五月天亚洲精品| 极品瑜伽女神91| 成人av在线看| 欧美日韩一区在线观看|