mrclay/props-dic

Props 是一个简单的依赖注入容器,允许通过自定义属性和方法名称来检索值

4.0.0 2024-01-04 04:27 UTC

This package is auto-updated.

Last update: 2024-09-04 05:49:59 UTC


README

大多数 依赖注入 容器都有获取操作,如 $di->get('foo')$di['foo'],这不会让你的 IDE 知道接收到的值的类型,也不会提供任何帮助来记住/输入键名。

使用 Props,你可以通过自定义属性读取 $di->foo 或方法调用 $di->new_foo() 来访问值。这允许你子类化容器并提供 @property 和/或 @method PHPDoc 声明,为你的 IDE 和静态分析工具提供有价值的运行时类型信息。

一个例子将有所帮助

/**
 * @property-read Foo $foo
 * @method        Foo new_foo()
 */
class MyContainer extends \Props\Container {
    public function __construct() {
        $this->foo = function (MyContainer $c) {
            return new Foo();
        };
    }
}

$c = new MyContainer();

$foo1 = $c->foo; // your IDE knows this is a Foo instance

$foo2 = $c->new_foo(); // A fresh Foo instance

$foo3 = $c->foo; // same as $foo1

这是一个更复杂的例子

/**
 * @property-read string $style
 * @property-read Dough  $dough
 * @property-read Cheese $cheese
 * @property-read Pizza  $pizza
 * @method        Slice  new_slice()
 */
class PizzaServices extends \Props\Container {
    public function __construct() {
        $this->style = 'deluxe';

        $this->dough = function (PizzaServices $c) {
            return new Dough();
        };

        $this->setFactory('cheese', 'CheeseFactory::getCheese');

        $this->pizza = function (PizzaServices $c) {
            $pizza = new Pizza($c->style, $c->cheese);
            $pizza->setDough($c->dough);
            return $pizza;
        };

        $this->slice = function (PizzaServices $c) {
            return $c->pizza->getSlice();
        };
    }
}

$c = new PizzaServices;

$c->pizza; // This first resolves and caches the cheese and dough.

$c->pizza; // The same pizza instance as above (no factories called).

由于 "slice" 设置了工厂函数,我们可以调用 new_slice() 从它那里获取新的实例

$c->new_slice(); // a new Slice instance
$c->new_slice(); // a new Slice instance

你的 IDE 将容器视为一个具有类型属性的普通旧类,允许它提供可用的属性建议、自动完成名称以及自动完成返回的对象。它在提供静态分析和自动化重构时提供了更多功能。

兼容性

Props\Container 实现 ContainerInterface

概述

你可以通过直接设置来指定依赖关系

$c->aaa = new AAA();

你可以通过设置一个 Closure 或使用 setFactory() 方法来指定工厂。这些在功能上是等效的

$c->bbb = function ($c) {
    return BBB::factory($c);
};

$c->setFactory('bbb', 'BBB::factory');

已解析的依赖关系被缓存,返回相同的实例

$c->bbb === $c->bbb; // true

使用工厂

如果你不希望使用缓存值,请使用 new_PROPERTYNAME() 总是获取一个新的实例

$c->new_bbb() === $c->new_bbb(); // false

常规值集不存储工厂,因此在使用 new_PROPERTYNAME() 之前,你可能需要检查 hasFactory()

// store a value
$c->ccc = new CCC();
$c->hasFactory('ccc'); // false

// store a factory
$c->ccc = function () {
    return new CCC();
};
$c->hasFactory('ccc'); // true

你也可以获取到设置工厂的访问权限

$callable = $c->getFactory('ccc');

扩展工厂

使用 extend 在返回值之前进行过滤

$c->foo = function ($c) {
    return new Foo($c->bar);
};

$c->extend('foo', function ($value, Container $c) {
    return array($value, $c->bing);
});

$c->foo; // [Foo, "bing"]

$c->new_foo(); // re-call original foo factory and re-extend output (`bar` and `bing` will be re-read)

带有属性访问的 Pimple

如果你习惯了 Pimple API,尝试 Props\Pimple,它只添加了属性访问。这样你就可以添加 @property 声明并得到相同的类型优势。

你可以看到一个 例子,它与 Pimple 文档相似。

要求

  • PHP 8.1

许可 (MIT)

查看 LICENSE