Laravel服務容器

luzhuqun發表於2019-02-16

laravel框架底層解析

本文參考陳昊《Laravel框架關鍵技術解析》,搭建一個屬於自己的簡化版服務容器。
其中涉及到反射、自動載入,還是需要去了解一下。

laravel服務容器

  1. 建立專案空資料夾(如 mylaravel)

  2. 新增composer.json,執行composer install

{
    "name": "laravel/laravel",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "type": "project",
    "autoload": {
        "classmap": [
        ],
        "psr-4": {
            "App\": "app/"
        }
    }
}

目的是為了學習、利用composer的自動載入。

  1. 檔案路徑如下所示

  • mylaravel

    • app

      • Traveller

        • Traveller.php

      • Visit

        • Leg.php

        • Visit.php

      • Container.php

      • Index.php

    • vendor

    • composer.json

  1. 簡化版Container.php

<?php
namespace App;

Class Container
{
    protected $bindings = [];

    /**
     * 繫結介面和生成相應例項的回撥函式
     * @param $abstract 服務名稱
     * @param null $concreate 回撥函式或名稱
     * @param bool $shared 是否為單例
     *
     */
    public function bind($abstract, $concrete = null, $shared = false)
    {
        if ( ! $concrete instanceof Closure) {
            // 如果提供的引數不是回撥函式,則產生預設的回撥函式
            $concrete = $this->getClosure($abstract, $concrete);
        }
    
        $this->bindings[$abstract] = compact(`concrete`, `shared`);
    
    }
    //預設的回撥函式
    protected function getClosure($abstract, $concrete)
    {
        //生成例項的回撥函式, $c一般為ioc容器物件
        return function($c) use ($abstract, $concrete)
        {
            $method = ($abstract == $concrete) ? `build` : `make`;
            return $c->$method($concrete);
        };
    }
    //生成例項物件,首先解決介面和要例項化類之間的依賴
    public function make($abstract)
    {
        $concrete = $this->getConcrete($abstract);
        if ($this->isBuildable($concrete, $abstract)) {
            $object = $this->build($concrete);
        } else {
            $object = $this->make($concrete);
        }
        return $object;
    }
    protected function isBuildable($concrete, $abstract)
    {
        return $concrete === $abstract || $concrete instanceof Closure;
    }
    //獲取繫結的回撥函式
    protected function getConcrete($abstract)
    {
        if ( ! isset($this->bindings[$abstract]))
        {
            return $abstract;
        }
        return $this->bindings[$abstract][`concrete`];
    }
    //例項化物件
    public function build($concrete)
    {
        if ($concrete instanceof Closure) {
            return $concrete($this);
        }
        $reflector = new ReflectionClass($concrete);
        if ( ! $reflector->isInstantiable()) {
            echo $message = "Target [$concrete] is not instantiable.";
        }
        $constructor = $reflector->getConstructor();
        if (is_null($constructor)) {
            return new $concrete;
        }
        $dependencies = $constructor->getParameters();
        $instances = $this->getDependencies($dependencies);
        return $reflector->newInstanceArgs($instances);
    }
    protected function getDependencies($parameters)
    {
        $dependencies = [];
        foreach ($parameters as $parameter)
        {
            $denpendency = $parameter->getClass();
            if (is_null($denpendency)) {
                $dependencies[] = NULL;
            } else {
                $dependencies[] = $this->resolveClass($parameter);
            }
        }
        return (array) $dependencies;
    }
    protected function resolveClass(ReflectionParameter $parameter)
    {
        return $this->make($parameter->getClass()->name);
    }
}

Visit.php介面

<?php
namespace AppVisit;

interface Visit
{
    public function go();
}

Leg.php介面的一種實現

<?php
namespace AppVisit;

Class Leg implements Visit
{
    public function go()
    {
        echo "walk to tibet!";
    }
}

Traveller.php相當於Controller下的方法

<?php
namespace AppTraveller;

use AppVisitVisit;

Class Traveller
{
    protected $trafficTool;
    //Visit是抽象類,index中將leg注入到了容器,取代了Visit
    public function __construct(Visit $visit)
    {
        $this->trafficTool = $visit;
    }
    public function visitTibet()
    {
        $this->trafficTool->go();
    }
}

Index.php

<?php 
namespace App;

//呼叫composer的自動載入
require `../vendor/autoload.php`;

Class Index
{
    public function index()
    {
            //例項化ioc容器
            $app = new Container();
            //容器填充
            $app->bind(`AppVisitVisit`, `AppVisitLeg`);
            $app->bind(`Traveller`, `AppTravellerTraveller`);
            //通過容器實現依賴注入,完成類的例項化
            $tra = $app->make("AppTravellerTraveller");
            $tra->visitTibet();
    }
}

$b = new Index;
$b->index();

相關文章