带依赖注入、插件、首选项等高级对象管理器,不再需要创建新的Obj()!

安装: 22

依赖项: 0

建议者: 0

安全性: 0

星标: 0

关注者: 2

分支: 1

开放问题: 0

类型:php-extension

1.1.1 2023-07-23 19:46 UTC

This package is auto-updated.

Last update: 2024-09-23 22:32:06 UTC


README

带依赖注入、插件、首选项等高级对象管理器,不再需要创建新的Obj()!

不再让项目充斥着不必要的对象创建。现在在构造函数中注入您想要的任何对象,让OM来处理。就这么简单!

本项目深受Magento 2使用对象管理器和依赖注入的启发。

安装

composer require jayankaghosh/om

特性

OM(对象管理器)通过强大的特性如

  • 依赖注入
  • 构造函数参数注入(类似Magento)
  • 插件(类似Magento)
  • 首选项(类似Magento)
  • 参数注入(类似Magento)

如何使用

1. 简单用法

// Get object manager instance from object manager factory Like this
$objectManagerFactory = new \Om\OmFactory();
$objectManager = $objectManagerFactory->getInstance();

// And then create the initial object of your project 
// using $objectManager instead of "new"
// $objectManager::get method takes 2 parameters
// 1. the class name as string
// 2. an associative array of constructor arguments of the class
$app = $objectManager->get(\My\Project\App::class, []);
$app->run();

// After that we are open to a new world of possibilities 
// that will change the way programming was done forever!!! 
// Too much? Okay too much 😐

2. 构造函数参数

使用OM,我们不需要担心构造函数参数。如果参数类型是对象,它将自动(作为单例)传递给构造函数

class A {...}
class B {...}
class C {
    public function __construct(A $a, B $b) {...}
    ...
}

// $c = new C(); // Will throw error. Since C required A and B to run
$c = $objectManager->get(C::class); // Works like a charm!

3. DI配置

记得我们创建了一个OmFactory的对象吗?

$objectManagerFactory = new \Om\OmFactory();

实际上,我们还可以向它传递一个配置对象和参数。这个配置包含关于对象管理器如何处理依赖注入的信息。我们可以使用配置对象传递有关 首选项类型参数插件 的配置。

配置是Om\DiConfig\Config对象。我们可以使用类内部提供的函数来制作配置,或者使用关联数组来制作。在这个例子中,我们将使用后者

$config = \Om\DiConfig\Config::fromArray([
    'preferences' => [
        [
            'for' => 'MyCalculator',
            'type' => Calculator::class
        ]
    ],
    'types' => [
        [
            'name' => Calculator::class,
            'arguments' => [
                [
                    'name' => 'logger',
                    'type' => 'object',
                    'value' => \Monolog\Logger::class
                ]
            ],
            'plugins' => [
                [
                    'name' => 'divide-by-zero-check',
                    'type' => CalculatorPlugin::class
                ]
            ]
        ]
    ]
]);
$objectManagerFactory = new \Om\OmFactory($config);
$objectManager = $objectManagerFactory->getInstance();

您也可以使用\Om\DiConfig\Config::fromXml方法从XML字符串创建DI配置,如下所示,

$config = Om\DiConfig\Config::fromXml(
    \file_get_contents(__DIR__  . '/di.xml')
);

以下是一个示例,说明di.xml文件应该如何看起来

<?xml version="1.0" encoding="utf-8" ?>
<config>
    <preference for="MyCalculator" type="Calculator" />
    <type name="Calculator">
        <arguments>
            <argument name="logger" xsi:type="object">Monolog\Logger</argument>
        </arguments>
        <plugin name="divide-by-zero-check" type="CalculatorPlugin" />
    </type>
</config>

4. 首选项

首选项是一种为对象创建别名或代理的方式。将'B'作为'A'的首选项就像说"每次我想创建'A'类的对象时,请创建'B'类的对象代替"

首选项使用DI配置定义

$config = \Om\DiConfig\Config::fromArray([
    'preferences' => [
        [
            'for' => 'MyCalculatorAlias',
            'type' => Calculator::class
        ]
    ]
]);
$objectManagerFactory = new \Om\OmFactory($config);
$objectManager = $objectManagerFactory->getInstance();

// There is not class with the name 'MyCalculatorAlias',
// but still it doesn't throw any errors because we had already
// mentioned that Calculator::class is the preference for 'MyCalculatorAlias'
$calculator = $objectManager->get('MyCalculatorAlias');
echo get_class($calculator); // Calculator::class

5. 构造函数参数

假设我们有一个名为A的类,它有两个构造函数参数

class A {
    public function __constructor(
        Logger $logger,
        string $message
    ) {
        var_dump($message);
    }
}

现在,如果我们想使用对象管理器创建A类的对象

$objectManager->get('A');

它将抛出一个错误,说参数传递太少。但为什么?我以为OM已经处理了所有构造函数参数呢:(

实际上并不是这样。《OM只能传递类型为对象的参数》。如果类型是其他东西(在这个例子中,$message是一个 字符串)。因此,我们需要自己传递"message"参数。怎么办?

$a = $objectManager->get('A', ['message' => 'Hello OM']);

通过将其作为关联数组的第二参数的一部分传递。我们可以传递任意多的构造函数参数,就像这样。我们甚至可以传递类型为对象的参数,这将覆盖默认参数。

但每次我们想要创建A类的对象时都必须传递message的值,这不是很烦人吗?因此,还有一种方法可以将全局参数传递给类。那就是通过DI配置

$config = \Om\DiConfig\Config::fromArray([
    'types' => [
        [
            'name' => 'A',
            'arguments' => [
                [
                    'name' => 'message',
                    'type' => 'string',
                    'value' => 'Hello Global OM'
                ]
            ]
        ]
    ]
]);
$objectManagerFactory = new \Om\OmFactory($config);
$objectManager = $objectManagerFactory->getInstance();

$objectManager->get('A'); // doesn't throw error anymore

6. 单例

默认情况下,使用\Om\ObjectManager\ObjectManager::get方法创建的所有对象都是单例。这意味着,如果类的对象已经创建,它将返回该同一对象,或者如果它是第一次,则创建一个新的对象。

有时我们可能不希望这种行为,而想要创建一个新的对象。在这些情况下,我们使用\Om\ObjectManager\ObjectManager::create方法。

$aSingleton = $objectManager->get('A'); // singleton
$aNew = $objectManager->create('A'); // new object

7. 生成类

为了提供某些超级花哨的特性,OM可能需要在您的系统中生成一些文件(不是虫子。发誓!)为此,我们需要提供一个OM可以生成文件的目录的绝对路径。

$config = [...];
$writableDirectoryPath = __DIR__ . '/var/generated/';
$objectManagerFactory = new \Om\OmFactory($config, $writableDirectoryPath);
$objectManager = $objectManagerFactory->getInstance();

8. 工厂模式

需要生成类

一旦我们为OM提供了生成文件的可写目录,我们就可以使用OM的工厂模式来生成新类,而不是单例。只需在类名后添加单词Factory即可。

class A {
    public function __construct(
        \My\Useful\HelperClass $helper,
        \My\Useful\HelperClassFactory $helperFactory
    ) {
        $this->helper = $helper; // singleton
        $this->helper = $helperFactory->create(); // new object
    }
}

9. 插件

需要生成类

插件是实现应用程序完全可扩展性的优雅方式。插件使我们能够拦截任何公共方法的流程,在方法执行之前、之后或周围进行操作。

插件也在DI 配置中定义

$config = \Om\DiConfig\Config::fromArray([
    'types' => [
        [
            'name' => 'Calculator',
            'plugins' => [
                [
                    'name' => 'divide-by-zero-check',
                    'type' => 'CalculatorPlugin'
                ]
            ]
        ]
    ]
]);
$objectManagerFactory = new \Om\OmFactory($config);
$objectManager = $objectManagerFactory->getInstance();

class Calculator {
    public function divide($a, $b)
    {
        return $a / $b;    
    }
}


class CalculatorPlugin {
    public function beforeDivide(Calculator $subject, $a, $b)
    {
        if ($b === 0) {
            $b = 1;
            echo '$b changed to 1 because it was 0';
        }
        return [$a, $b];
    }
}

$calculator = $objectManager->get('Calculator');

如您所见,我们修改了Calculator类中divide方法的行为,甚至没有触摸Calculator类。这有多么酷?!

以下是插件的一个实用指南

之前

用于修改参数。在插件中将before前缀添加到方法名。例如,beforeDivide

参数

  1. 主题(它是插件的类的一个对象)
  2. 实际方法的第1个参数
  3. 实际方法的第2个参数
  4. ...

之后

用于修改返回值。在插件中将after前缀添加到方法名。例如,afterDivide

参数

  1. 主题(它是插件的类的一个对象)
  2. 实际函数的返回值

周围

用于改变整个方法流程。在这里,方法本身不会自动执行。相反,它被转换为可调用的,并作为参数传递给插件。在插件中将around前缀添加到方法名。例如,aroundDivide

参数

  1. 主题(它是插件的类的一个对象)
  2. 可调用的(指向实际方法的可调用方法)
  3. 实际方法的第1个参数
  4. 实际方法的第2个参数
  5. ...

示例可以在/examples目录中找到,以帮助您入门