1. 程式人生 > 其它 >依賴注入的PHP實現(一)

依賴注入的PHP實現(一)

  突然好奇laravel的依賴注入是怎麼實現的,翻看了下laravel的原始碼,發現在laravel在src/Illuminate/Routing/Route.php下的run方法中開始執行控制器方法

    /**
     * Run the route action and return the response.
     *
     * @return mixed
     */
    public function run()
    {
        $this->container = $this->container ?: new Container;

        
try { if ($this->isControllerAction()) { return $this->runController(); } return $this->runCallable(); } catch (HttpResponseException $e) { return $e->getResponse(); } }

  追蹤runController方法,一直到src/Illuminate/Routing/RouteDependencyResolverTrait.php下,我們可以看到,laravel使用反射機制進行依賴注入

    /**
     * Resolve the object method's type-hinted dependencies.
     *
     * @param  array  $parameters
     * @param  object  $instance
     * @param  string  $method
     * @return array
     */
    protected function resolveClassMethodDependencies(array $parameters, $instance, $method)
    {
        
if (! method_exists($instance, $method)) { return $parameters; } return $this->resolveMethodDependencies( $parameters, new ReflectionMethod($instance, $method) ); }

  通過對laravel原始碼的檢視,網上資料,以及PHP手冊的查閱,自己寫了個簡單的依賴注入實現。

  首先假設存在一個model叫user

class User
{
    public $table = 'users';
}

  然後存在一個控制器叫UserController

class UserController
{

    /**
     * 獲取表名稱
     *
     * @param  \User  $user
     *
     * @return void
     */
    public static function getTableName(User $user){
        echo $user->table;
    }
}

  可以看到我們的getTableName方法中依賴注入了User類的例項,接下來我們需要通過反射來為這個方法注入引數,注入具體實現如下

class app
{
    /**
     * @param  mixed   $controller  控制器
     * @param  string  $method      方法
     *
     * @throws \ReflectionException
     * @throws \Exception
     */
    public static function run($controller, string $method){

        //判斷方法是否存在
        if (! method_exists($controller, $method))
        {
            throw New Exception("Method [$method] is not exist !");
        }

        //通過反射獲取方法資訊
        $reflectionMethod = New ReflectionMethod($controller,$method);

        //攜帶引數執行該方法
        $reflectionMethod->invokeArgs(new $controller,self::getMethodParams($reflectionMethod));

    }


    /**
     * 獲取方法的引數並例項化
     * @param $reflectionMethod
     *
     * @return array
     * @throws \Exception
     */
    public static function getMethodParams($reflectionMethod) : array
    {
        $result = [];

        foreach ($reflectionMethod->getParameters() as $key => $param) {
            $class = $param->getClass();

            if ($class)
            {
                $result[] = New $class->name(); 
            }else{
                throw New Exception('arguments ' . $key . 'is error!');
            }
        }

        return $result;
    }
}

  最後我們執行方法,打印出User類的table屬性,這樣我們就通過對映實現了一個簡單的依賴注入。

app::run('UserController','getTableName');

  

  所有例項程式碼如下
  

<?php

//model
class User
{
    public $table = 'users';
}

//控制器
class UserController
{

    /**
     * 獲取表名稱
     *
     * @param  \User  $user
     *
     * @return void
     */
    public static function getTableName(User $user){
        echo $user->table;
    }
}

//容器
class app
{
    /**
     * @param  mixed   $controller  控制器
     * @param  string  $method      方法
     *
     * @throws \ReflectionException
     * @throws \Exception
     */
    public static function run($controller, string $method){

        //判斷方法是否存在
        if (! method_exists($controller, $method))
        {
            throw New Exception("Method [$method] is not exist !");
        }

        //通過反射獲取方法資訊
        $reflectionMethod = New ReflectionMethod($controller,$method);

        //攜帶引數執行該方法
        $reflectionMethod->invokeArgs(new $controller,self::getMethodParams($reflectionMethod));

    }


    /**
     * 獲取方法的引數並例項化
     * @param $reflectionMethod
     *
     * @return array
     * @throws \Exception
     */
    public static function getMethodParams($reflectionMethod) : array
    {
        $result = [];

        foreach ($reflectionMethod->getParameters() as $key => $param) {
            $class = $param->getClass();

            if ($class)
            {
                $result[] = New $class->name(); 
            }else{
                throw New Exception('arguments ' . $key . 'is error!');
            }
        }

        return $result;
    }
}

app::run('UserController','getTableName');