dsawardekar/encase-php

PHP的轻量级IOC容器

0.2.0 2014-06-06 11:58 UTC

This package is not auto-updated.

Last update: 2024-09-14 15:28:58 UTC


README

PHP的轻量级IOC容器。

Encase是一个用于在PHP应用程序中实现依赖注入的库。它提供了一个轻量级的IOC容器,用于管理类之间的依赖关系。它被编写来帮助在框架之外连接领域对象,以构建更快的测试套件。

Ruby实现在这里:here

特性

  • 存储对象、工厂和单例
  • 声明式语法来指定依赖
  • 简单的API来配置容器
  • 支持嵌套容器
  • 支持延迟初始化
  • 初始化器

用法

考虑一个Worker类,它依赖于一个Logger。我们希望这个依赖在创建Worker时可用。

首先,我们创建一个容器对象并声明依赖。

<?php
use Encase\Container;

$container = new Container();
$container->object('logger', new Logger())
          ->factory('worker', 'Worker');

然后,我们使用needs函数声明Worker依赖于logger。它返回与我们在容器上注册的键相对应的键数组。

<?php
class Worker {

  function needs() {
    return array('logger');
  }

}

就是这样!现在我们可以通过在容器上查找worker键来创建一个新的Worker

<?php
$myWorker = $container->lookup('worker');
$myWorker instanceof Worker; // true

Worker实例会自动获取对logger的引用。

<?php
$myWorker->logger instanceof Logger; // true

容器配置

容器通过函数objectfactorysingleton进行配置。容器项目存储在容器内部的关联数组中,并在稍后用于查找对象。

<?php
$container->object(...);
$container->factory(...);
$container->singleton(...);

以下列出您可以在容器中存储的项目类型。

对象

对象是在系统中已创建的现有值。可以是字符串数字或不需要实例化的任何值。

它们存储在容器中,并且未经任何修改就返回。要存储对象,请调用object函数。并传递要存储的对象的字符串键以及对象本身。

<?php
$container->object('key', existingValue);

工厂

工厂容器项是一个Classpath字符串。在每次查找时,都会使用它创建该Class的新实例,并自动注入其依赖项。

要存储此类,请使用factory函数。并传递要实例化的Class的完整类路径。

<?php
$container->factory('key', 'FactoryClass');

单例

单例类似于工厂。然而,它在第一次查找时缓存创建的实例,并在随后的查找中返回该实例。

使用singleton函数存储单例Class的路径。

<?php
$container->singleton('key', 'SingletonClass');

声明依赖

要指定类的依赖项,您使用needs函数。它必须返回与容器中存储的键对应的字符串数组。

<?php
class Worker {

  function needs() {
    return array('one', 'two', 'three');
  }

}

您可以选择使用INeeds接口来声明需求。

<?php
class Worker implements INeeds {

  function needs() {
    return array('one', 'two', 'three');
  }

}

延迟初始化

Encase允许延迟存储依赖项。如果依赖项在容器配置时不可用,这很有用。但在查找之前将准备好。

通过将closurecallable传递给容器而不是值来执行延迟初始化。在这里,keycallable将在第一次查找之前执行。

<?php
// with $callables
$container->object('key', $callable);

// with an anonymous function
$container->object('key', function($container) {
  return 'value';
});

闭包或可调用对象接受一个等于容器对象本身的参数。您可以使用这个参数根据容器中的其他对象或系统中的其他地方的条件性地解析值。

初始化器

初始化器在处理不使用Encase容器的外部库对象时非常有用。这些对象没有声明它们的needs,但在使用之前仍然需要初始化。

初始化器方法接受要初始化的对象的键和一个用于初始化对象的$callable。可调用对象将接收两个参数,从容器中查找的对象的值和容器本身。

对于objectsingleton类型的项,初始化仅在第一次查找时发生。而factory类型的项,每次从容器中查找新实例时都会运行其初始化器。

下面的代码将Currency对象存储在容器中。为此对象添加了一个初始化器,以确保每次实例化时都会设置格式化程序。

<?php
$container->factory('currency', 'Currency');
$container->factory('formatter', 'NumberFormatter');
$container->initializer('currency', array($this, 'initCurrency'));

function initCurrency($currency, $container) {
  $currency->setFormatter($container->lookup('formatter'));
}

打包器

打包器是方便的辅助工具,可以将一个特性的依赖项作为一个单元进行分组。例如,如果Options特性由OptionsStoreOptionsPageOptionsValidator类组成,每次需要使用此Options特性时,您都需要在容器中声明这些依赖。

使用打包器对象可以将这些依赖添加到容器中。打包器应在下面的onInject函数中设置其依赖项,如下所示:

<?php
class OptionsPackager {

  function onInject($container) {
    $this->container
      ->factory('optionsStore', 'OptionsStore')
      ->factory('optionsValidator', 'OptionsValidator')
      ->factory('optionsPage', 'OptionsPage');
  }

}

现在,当您需要该特性时,只需将打包器添加到容器中即可。

<?php
$container->packager('optionsPackager', 'OptionsPackager')

packager本身将作为单例注册到容器中,可以通过其键查找。

嵌套容器

容器也可以嵌套在其他容器中。这允许在应用程序的不同上下文中对依赖项进行分组。在查找键时,如果子容器中没有找到键,则查询父容器。

<?php
$parent = new Container();
$parent->object('logger', new Logger())
  ->factory('worker', 'Worker');

$child = $parent->child();
$child->factory('worker', 'CustomWorker');

$child->lookup('logger'); // from parent container
$child->lookup('worker'); // from child container

在这里,child将使用CustomWorker来解析worker。而logger将从parent容器中查找。

公共属性

对于每个声明的need,Encase将在注入的对象上创建相应的公共属性。同时,也会将一个container属性注入到类中,以便在运行时查找其他依赖。

<?php
class Worker {

  function needs() {
    return array('one', 'two', 'three');
  }

}

worker = container->lookup('worker');
worker->one;
worker->two;
worker->three;
worker->container; // reference to the container object

生命周期

容器项在其依赖项注入后提供了onInject事件钩子。任何后注入的初始化都可以在这里执行。

<?php
class Worker implements {

  function needs() {
    return array('logger');
  }

  function onInject($container) {
    $this->logger->log('Worker is ready');
  }

}

测试

Encase简化了对象的测试。由于对象存储在容器中,您可以深入到对象的依赖图内部,并用一个模拟对象替换一个昂贵的依赖。

在早期示例中,为了测试日志器确实被worker调用,我们可以将worker注册为一个mock对象。然后验证这个mock是否被适当地调用。

<?php
function test_it_logs_message_to_the_logger() {
  $mock = $this->getMock('Logger', 'log');
  $mock->expects($this->once())
    ->method('log')
    ->with($this->equalTo('something'));

  $container = new Container();
  $container->factory('worker', 'Worker');
  $container->object('logger', $mock);

  $worker = $container->lookup('worker');
  $worker->start();
}

安装

将此行添加到您的composer.json中。

{
  "require": {
    "dsawardekar/encase-php": "~0.1.0"
  }
}

然后执行

$ composer install

系统要求

Encase已测试,可在以下平台上运行。

  • PHP 5.3
  • PHP 5.3.3
  • PHP 5.4
  • PHP 5.5
  • PHP 5.6

贡献

有关Portkey的贡献指南,请参阅贡献指南。

许可

MIT许可证。版权所有©2014 Darshan Sawardekar