首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 依賴注入容器實作:從原理到程式碼範例

     編輯:PHP 綜合知識

前言

依賴注入 是現代 PHP 開發的核心模式,透過容器管理類別相依性,實現鬆耦合與可測試性。本文提供一個精簡的 DI 容器實作,展示如何以反射自動解析建構子相依、支援介面綁定與閉包工廠。

容器核心實作

檔案 container.php:

<?php

class Container
{
    protected $bindings = [];

    public function set($abstract, $concrete = null)
    {
        if ($concrete === null) {
            $concrete = $abstract;
        }
        $this->bindings[$abstract] = $concrete;
    }

    public function get($abstract, $parameters = [])
    {
        if (!isset($this->bindings[$abstract])) {
            return null;
        }
        return $this->build($this->bindings[$abstract], $parameters);
    }

    protected function build($concrete, $parameters)
    {
        if ($concrete instanceof Closure) {
            return $concrete($this, $parameters);
        }

        $reflector = new ReflectionClass($concrete);

        if (!$reflector->isInstantiable()) {
            throw new Exception("Class {$concrete} is not instantiable");
        }

        $constructor = $reflector->getConstructor();

        if (is_null($constructor)) {
            return $reflector->newInstance();
        }

        $dependencies = $this->getDependencies($constructor->getParameters());
        return $reflector->newInstanceArgs($dependencies);
    }

    protected function getDependencies($parameters)
    {
        $dependencies = [];
        foreach ($parameters as $parameter) {
            $dependency = $parameter->getClass();
            if ($dependency === null) {
                if ($parameter->isDefaultValueAvailable()) {
                    $dependencies[] = $parameter->getDefaultValue();
                } else {
                    throw new Exception("Cannot resolve class dependency {$parameter->name}");
                }
            } else {
                $dependencies[] = $this->get($dependency->name);
            }
        }
        return $dependencies;
    }
}

關鍵點解析

  1. 綁定儲存:`set()` 以鍵值對儲存抽象型別到具體實作的對應,若未指定具體類別則預設綁定自身。
  2. 遞迴解析:`get()` 呼叫 `build()`,若為閉包則直接執行;否則透過 `ReflectionClass` 檢查可實例化性。
  3. 建構子注入:取得建構子參數清單,對每個參數:
    • 若有型別提示為類別,遞迴呼叫 `get()` 解析相依
    • 若為純量參數且有預設值,使用預設值
    • 否則拋出例外
  4. 實例化:以解析好的相依陣列呼叫 `newInstanceArgs()` 建立物件。

使用範例

<?php
require 'container.php';

interface MyInterface {}
class Foo implements MyInterface {}
class Bar implements MyInterface {}

class Baz
{
    public function __construct(MyInterface $foo)
    {
        $this->foo = $foo;
    }
}

$container = new Container();
$container->set('Baz', 'Baz');
$container->set('MyInterface', 'Foo');

$baz = $container->get('Baz');
print_r($baz); // Baz 內部注入 Foo 實例

$container->set('MyInterface', 'Bar');
$baz = $container->get('Baz');
print_r($baz); // 同一 Baz 類別,現注入 Bar 實例

執行流程

  1. 請求 `Baz` → 容器發現建構子需 `MyInterface`
  2. 查找 `MyInterface` 綁定 → 目前為 `Foo` → 建立 `Foo` 實例
  3. 以 `Foo` 實例建構 `Baz` 並回傳
  4. 重新綁定 `MyInterface` 至 `Bar`
  5. 再次請求 `Baz` → 此次注入 `Bar` 實例

擴充建議

  • 單例支援:在 `build()` 中快取已建立的實例,後續請求直接回傳。
  • 標籤/上下文綁定:允許同一介面在不同情境綁定不同實作。
  • 方法注入/屬性注入:擴充 `build()` 支援 `@Inject` 註解或 setter 注入。
  • 服務提供者:引入 `register()` / `boot()` 機制模組化註冊綁定。

總結

這個精簡容器展示了 DI 核心機制:綁定宣告 → 反射分析 → 遞迴解析 → 實例化。雖功能輕量,但已足以支援介面導向設計與單元測試的相依替換。實際專案建議採用成熟套件(如 PHP-DI、Laravel Container、Symfony DependencyInjection)以獲得效能優化、PSR-11 相容與豐富生態。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved