带依赖注入、插件、首选项等高级对象管理器,不再需要创建新的Obj()!
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个参数
- ...
之后
用于修改返回值。在插件中将after
前缀添加到方法名。例如,afterDivide
。
参数
- 主题(它是插件的类的一个对象)
- 实际函数的返回值
周围
用于改变整个方法流程。在这里,方法本身不会自动执行。相反,它被转换为可调用的,并作为参数传递给插件。在插件中将around
前缀添加到方法名。例如,aroundDivide
。
参数
- 主题(它是插件的类的一个对象)
- 可调用的(指向实际方法的可调用方法)
- 实际方法的第1个参数
- 实际方法的第2个参数
- ...