Slim容器分析

Slim容器分析

5年前,我还没什么编程经验,第一次接触java的spring框架,了解容器容器的概念,立刻被它巧妙的设计所惊呆,没错,就是惊呆...没想到程序居然可以这么写!!

不是从上至下的命令式编程,不是分而治之的结构式编程,也不是我当时水平所认知的自底向上,相互作用的对象式编程,而是可复用,可替换的组件化编程。

后来一直做PHP Web应用开发,也没机会用Spring做一些应用,一直在想PHP什么时候也能有使用容器的框架就好了。

一次偶然机会,在一个技术qq群,有人推荐一个叫Slim的框架,我随手打开github,看看这个框架源码,又惊呆了,Slim里有容器,而且惊叹现在的PHP框架怎么越来越像Java Web的框架,有容器,有组件,全OOP。

Slim的源代码地址Github

附Slim资料链接:

以日志组件为例,来看看PHP是怎么配置组件,怎么讲组件注入容器,怎么实例化组件,以及何时实例化组件和调用组件的方法?

入口文件

Slim/public/index.php

所有请求都是发送给入口文件,然后由入口文件分发请求到相应的服务,入口文件很简单,我截取了和主题相关的部分。

<?php
// 包含应用配置文件
$settings = require __DIR__ . '/../src/settings.php';

// 初始化应用
$app = new \Slim\App($settings);

// 注入应用所依赖组件
// 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',
        ],
    ],
];

注入组件 - 依赖注入

应用初始化之后,开始向容器注入应用所依赖的组件。
在Slim/src/dependencies.php里面定义了应用所依赖的组件,比如模板组件、日志组件、数据库组件等等。

我们就取其中logger组件来分析分析

<?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;
};

这里将一个回调函数赋值给一个容器实例的“logger”属性,

研究一下这个回调函数:

回调函数的参数是一个容器实例,回调函数体通过这个容器实例获取logger组件的配置信息,根据配置信息实例化组件,最后返回这个组件实例。

将这样一个实例化组件的回调函数交给容器,就实现logger组件的注入——这种注入,通过回调函数注入依赖是依赖注入的一种实现方法。

这也是控制反转的一种实现,把原本由应用程序实例化组件,交给了低层容器去做。

实例化组件

那么把实例化得控制权交给容器,那么容器什么时候实例化组件呢?

答案是,在第一次调用组件的时候。

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);
});

在执行下面语句时,如果logger组件没有实例化,就实例logger组件,将实例保存在容器中,并且返回logger组件实例;如果容器中已经有logger组件的实例,就返回该实例——单例模式。

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

$this指向容器,这里使用了php的魔术方法__get()去获取容器的内的属性。

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

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

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

最后调用下面方法放回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;
   }

其中最关键是这一句

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

$raw是前面提到的logger的回调函数,通过$raw($this)去调用回调函数,返回logger组件的实例。

紧接着做了两件事:

一是赋值给$this->values[$id],作为一个单例保存在容器中,之后再次调用logger组件时,直接返回这个单例。

二是将logger组件实例赋值给$val,作为整个方法的返回值,返回到logger组件的调用处,也就是回到了之前调用logger组件的info()方法处,见下面代码,这样就能写日志到app.log文件里了,

Slim/src/routes.php

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

现代制造工业模式

容器组件化编程,让我想起现代制造工业模式,比如汽车制造业。

最初汽车制造商所有汽车零件都是自己生产组装。

现代汽车厂商已经将汽车零件外包给第三方工厂。

汽车制造商只需要与第三方工厂签到合同,提供标准。

第三方工厂自行安排具体的零件生产工作。

汽车制造商需要汽车时,就从第三方工厂取货,组装汽车。

在这里汽车制造商就是就是容器,汽车就是应用程序,汽车零件就是组件。

看来不仅面向对象编程是对现实的抽象,软件设计思想也是来源现实世界的抽象

最后总结下来,Slim容器有2个特点:

  • 使用回调函数实现依赖注入,达到控制反转的目的。
  • 在使用组件时,才实例化组件,并单例化。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Composer Repositories Composer源 Firegento - Magento模块Comp...
    零一间阅读 3,956评论 1 66
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,395评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 当你不以自己的喜好交朋友,当你会做事,当你能从每个人身上发现自己没有的优点时,恭喜你,你已经有很大的进步了!
    本帅阅读 111评论 0 0
  • 美好的早晨,一觉睡到自然醒,瞬间感觉幸福到飞起啊,伸伸懒腰,打开手机又准备葛优躺一天哈。可是,在我看到手机屏幕上显...
    青木蔷薇阅读 513评论 1 5