mindplay/stockpile

此包已被废弃且不再维护。作者建议使用 mindplay/unbox 包。

强类型、独立的PHP配置/服务容器

3.2.1 2015-06-17 16:38 UTC

This package is auto-updated.

Last update: 2021-06-05 13:26:56 UTC


README

https://github.com/mindplay-dk/stockpile

Build Status

Code Coverage

Stockpile提供了一个基类,可以轻松实现服务定位器模式,并提供简单的实现简单、高效的依赖注入的方法。

⚠️ 服务定位器通常被认为是一种反模式 - 如果您正在寻找一个不鼓励或优化该模式,但仍然提供出色IDE支持的现代DI容器,请查看 Unbox

已测试并设计适用于PHP 5.3及以上版本。

请参阅根目录中的 "example.php",了解如何使用此类的示例。

概述

Container基类将解析您的类上的@property注释 - 这些提供了设计时IDE支持,同时您的类级别doc-block中的类型提示和属性名也将被基类拾取并解析,然后能够提供运行时类型检查和额外安全性。

API概述

Container类的生命周期有两个阶段:最初是开放注册和配置,然后通过seal()方法进行密封(防止进一步修改)。换句话说,最初是只写,然后变为只读。

在调用seal()之前可用的配置方法

register(string $name, Closure $init)   # register component creation function
unregister(string $name)                # unregister a component
configure(Closure|Closure[] $config)    # configure a registered component
shutdown(Closure $function)             # dispose of components after use
load(string $path)                      # load an external configuration file

始终可用的其他方法

getRootPath()                           # get configuration files root path
invoke(callable $function, $params)     # invoke a function with components as arguments
isActive(string $name)                  # check if a component has been initialized
isDefined(string $name)                 # check if a component has been defined                 
isRegistered(string $name)              # check if a component has been registered

一个“已定义”组件是使用@property注释定义的容器属性。一个“已注册”组件已通过register()方法注册,或者已通过直接设置属性初始化。一个“活动”组件已初始化,例如,通过在容器密封后访问属性。

使用方法

通过将此类用作您应用程序或模块的全局入口点的基类,您的容器将获得适当的IDE支持和运行时类型检查,例如服务接口和配置值。

一个基本的容器类可能看起来像这样

use mindplay\stockpile\Container;

/**
 * @property string $db_username
 * @property string $db_password
 *
 * @property-read PDO $db application-wide database connection
 */
class MyApp extends Container
{
    ...
}

类的使用可能如下所示

$container = new MyApp(__DIR__ . '/config');

$container->load('default.php'); // load and execute "config/default.php"

请注意,故意不支持通过嵌套数组、XML/JSON/YAML数据文件或其他无模式配置方式进行配置 - 这些增加了复杂性,它们在现代IDE中不提供设计时检查的支持,它们是不必要的,并且没有提供明确的好处。

配置文件

示例中加载的 config/default.php 只是一个PHP脚本,可能看起来像这样

/** @var MyApp $this */

$this->db_username = 'foo';
$this->db_password = 'bar';

注意@var类型提示,它提供了设计时IDE支持。

容器密封后,当第一次访问 $db 属性时,将调用注册的创建函数。此函数的参数对应于属性名 - 在可能的情况下,您应为此函数添加类型提示以支持IDE;在本例中,这两个属性都是字符串。

依赖解析

通过闭包的参数请求所需组件,使得容器能够级联初始化依赖项(其他组件)。例如,假设有多个不同的组件依赖于缓存组件,下面是一个将视图引擎与缓存组件注册的示例

$container->register(
    'view',
    function (FileCache $cache) {
        // cache argument injected via $container->cache

        return new ViewEngine($cache, ...);
    }
);

分层配置

当配置发生在层中(例如,不同环境的多个配置文件)时,您可以通过使用额外的匿名函数进一步配置命名组件,并添加类型提示以支持IDE。

例如,当 $db 组件初始化时,要向MySQL发送 set names utf8 查询,您可能需要添加以下内容

$container->configure(
    function (PDO $db) {
        $db->exec("set names utf8");
    }
);

密封

完成容器配置后,在您可以使用组件之前,需要将其密封 - 这将防止意外进行任何更改,并验证每个已定义组件的配置是否完整。

$container->db_username = '...';
$container->db_password = '...';

$container->seal(); // prevent further changes (exception if incomplete)

注意,如果您有故意缺失的组件,您必须将这些组件显式设置为null - 这迫使您积极做出决定,并导致代码更具自说明性。

立即加载与延迟加载

您不会找到切换组件立即加载/延迟加载的选项 - 假设您希望尽可能晚地初始化所有内容。如果确实有一个立即可用的组件,只需直接注入该组件即可 - 例如

$container->logger = new Logger(...); // eager construction, vs lazy register()

缓存

对于非常大的应用程序(具有许多容器和大量属性),缓存可能有益 - 包含的基准测试显示了这种益处,表明性能提高了约3.5倍,但不要高估这种差异的影响;对于大多数应用程序,实践中可能最多只有几毫秒的差异,因为即使没有缓存,它也已经非常快。

要配置缓存,您需要重写受保护的 getCache() 方法 - 例如,您可以使用容器根路径下的子文件夹

class MyContainer extends Container
{
    /**
     * @return CacheProvider
     */
    protected function getCache()
    {
        return new FileCache($this->getRootPath() . '/cache');
    }
}

请注意,此文件夹必须可由Web服务器的用户账户写入。

高级用法

对于高级用法,例如构建具有专用行为的容器(例如,通过除解析@property注释之外的其他方式定义组件)的情况下,有一个抽象基类 AbstractContainer,它提供了一组更多的受保护API方法。如有需要,请自行探索。