Slim容器分析

Slim容器分析

5年前,我還沒什么編程經(jīng)驗,第一次接觸java的spring框架,了解容器容器的概念,立刻被它巧妙的設(shè)計所驚呆,沒錯,就是驚呆...沒想到程序居然可以這么寫!!

不是從上至下的命令式編程,不是分而治之的結(jié)構(gòu)式編程,也不是我當(dāng)時水平所認(rèn)知的自底向上,相互作用的對象式編程,而是可復(fù)用,可替換的組件化編程。

后來一直做PHP Web應(yīng)用開發(fā),也沒機會用Spring做一些應(yīng)用,一直在想PHP什么時候也能有使用容器的框架就好了。

一次偶然機會,在一個技術(shù)qq群,有人推薦一個叫Slim的框架,我隨手打開github,看看這個框架源碼,又驚呆了,Slim里有容器,而且驚嘆現(xiàn)在的PHP框架怎么越來越像Java Web的框架,有容器,有組件,全OOP。

Slim的源代碼地址Github

附Slim資料鏈接:

以日志組件為例,來看看PHP是怎么配置組件,怎么講組件注入容器,怎么實例化組件,以及何時實例化組件和調(diào)用組件的方法?

入口文件

Slim/public/index.php

所有請求都是發(fā)送給入口文件,然后由入口文件分發(fā)請求到相應(yīng)的服務(wù),入口文件很簡單,我截取了和主題相關(guān)的部分。

<?php
// 包含應(yīng)用配置文件
$settings = require __DIR__ . '/../src/settings.php';

// 初始化應(yīng)用
$app = new \Slim\App($settings);

// 注入應(yīng)用所依賴組件
// Set up dependencies
require __DIR__ . '/../src/dependencies.php';

配置組件

Slim/src/settings.php

其中就包含了logger組件的配置信息:
日志組件的名字:slim-app
日志組件的log記錄保存的位置:_DIR_ . '/../logs/app.log',

<?php
return [
    'settings' => [
        'displayErrorDetails' => true, // set to false in production
        'addContentLengthHeader' => false, // Allow the web server to send the content-length header

        // Renderer settings
        'renderer' => [
            'template_path' => __DIR__ . '/../templates/',
        ],

        // Monolog settings
        'logger' => [
            'name' => 'slim-app',
            'path' => __DIR__ . '/../logs/app.log',
        ],
    ],
];

注入組件 - 依賴注入

應(yīng)用初始化之后,開始向容器注入應(yīng)用所依賴的組件。
在Slim/src/dependencies.php里面定義了應(yīng)用所依賴的組件,比如模板組件、日志組件、數(shù)據(jù)庫組件等等。

我們就取其中l(wèi)ogger組件來分析分析

<?php
// monolog
$container['logger'] = function ($container) {
    $settings = $container->get('settings')['logger'];
    $logger = new Monolog\Logger($settings['name']);
    $logger->pushProcessor(new Monolog\Processor\UidProcessor());
    $logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], Monolog\Logger::DEBUG));
    return $logger;
};

這里將一個回調(diào)函數(shù)賦值給一個容器實例的“l(fā)ogger”屬性,

研究一下這個回調(diào)函數(shù):

回調(diào)函數(shù)的參數(shù)是一個容器實例,回調(diào)函數(shù)體通過這個容器實例獲取logger組件的配置信息,根據(jù)配置信息實例化組件,最后返回這個組件實例。

將這樣一個實例化組件的回調(diào)函數(shù)交給容器,就實現(xiàn)logger組件的注入——這種注入,通過回調(diào)函數(shù)注入依賴是依賴注入的一種實現(xiàn)方法。

這也是控制反轉(zhuǎn)的一種實現(xiàn),把原本由應(yīng)用程序?qū)嵗M件,交給了低層容器去做。

實例化組件

那么把實例化得控制權(quán)交給容器,那么容器什么時候?qū)嵗M件呢?

答案是,在第一次調(diào)用組件的時候。

Slim/src/routes.php

<?php
app->get('/[{name}]', function ($request, $response, $args) {

    // Sample log message
    $this->logger->info("Slim-Skeleton '/' route");

    // Render index view
    return $this->renderer->render($response, 'index.phtml', $args);
});

在執(zhí)行下面語句時,如果logger組件沒有實例化,就實例logger組件,將實例保存在容器中,并且返回logger組件實例;如果容器中已經(jīng)有l(wèi)ogger組件的實例,就返回該實例——單例模式。

 <?php
   $this->logger->info("Slim-Skeleton '/' route");

$this指向容器,這里使用了php的魔術(shù)方法__get()去獲取容器的內(nèi)的屬性。

Slim/vendor/slim/slim/Slim/Container.php

<?php
/********************************************************************************
 * Magic methods for convenience
 *******************************************************************************/

public function __get($name)
 {
     return $this->get($name);
 }

最后調(diào)用下面方法放回logger組件的實例。

<?php
public function offsetGet($id)
   {
       if (!isset($this->keys[$id])) {
           throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
       }

       if (
           isset($this->raw[$id])
           || !is_object($this->values[$id])
           || isset($this->protected[$this->values[$id]])
           || !method_exists($this->values[$id], '__invoke')
       ) {
           return $this->values[$id];
       }

       if (isset($this->factories[$this->values[$id]])) {
           return $this->values[$id]($this);
       }

       $raw = $this->values[$id];
       $val = $this->values[$id] = $raw($this);
       $this->raw[$id] = $raw;

       $this->frozen[$id] = true;

       return $val;
   }

其中最關(guān)鍵是這一句

<?php
   $val = $this->values[$id] = $raw($this);

$raw是前面提到的logger的回調(diào)函數(shù),通過$raw($this)去調(diào)用回調(diào)函數(shù),返回logger組件的實例。

緊接著做了兩件事:

一是賦值給$this->values[$id],作為一個單例保存在容器中,之后再次調(diào)用logger組件時,直接返回這個單例。

二是將logger組件實例賦值給$val,作為整個方法的返回值,返回到logger組件的調(diào)用處,也就是回到了之前調(diào)用logger組件的info()方法處,見下面代碼,這樣就能寫日志到app.log文件里了,

Slim/src/routes.php

 <?php
   $this->logger->info("Slim-Skeleton '/' route");

現(xiàn)代制造工業(yè)模式

容器組件化編程,讓我想起現(xiàn)代制造工業(yè)模式,比如汽車制造業(yè)。

最初汽車制造商所有汽車零件都是自己生產(chǎn)組裝。

現(xiàn)代汽車廠商已經(jīng)將汽車零件外包給第三方工廠。

汽車制造商只需要與第三方工廠簽到合同,提供標(biāo)準(zhǔn)。

第三方工廠自行安排具體的零件生產(chǎn)工作。

汽車制造商需要汽車時,就從第三方工廠取貨,組裝汽車。

在這里汽車制造商就是就是容器,汽車就是應(yīng)用程序,汽車零件就是組件。

看來不僅面向?qū)ο缶幊淌菍ΜF(xiàn)實的抽象,軟件設(shè)計思想也是來源現(xiàn)實世界的抽象

最后總結(jié)下來,Slim容器有2個特點:

  • 使用回調(diào)函數(shù)實現(xiàn)依賴注入,達(dá)到控制反轉(zhuǎn)的目的。
  • 在使用組件時,才實例化組件,并單例化。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Composer Repositories Composer源 Firegento - Magento模塊Comp...
    零一間閱讀 4,021評論 1 66
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,150評論 25 708
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,616評論 19 139
  • 當(dāng)你不以自己的喜好交朋友,當(dāng)你會做事,當(dāng)你能從每個人身上發(fā)現(xiàn)自己沒有的優(yōu)點時,恭喜你,你已經(jīng)有很大的進步了!
    本帥閱讀 128評論 0 0
  • 美好的早晨,一覺睡到自然醒,瞬間感覺幸福到飛起啊,伸伸懶腰,打開手機又準(zhǔn)備葛優(yōu)躺一天哈。可是,在我看到手機屏幕上顯...
    青木薔薇閱讀 626評論 1 5

友情鏈接更多精彩內(nèi)容