tonybogdanov / mockable-annotations
Mockable Doctrine 注解
Requires
- php-64bit: ^7.3
- doctrine/annotations: ^1.10
Requires (Dev)
- ext-pcov: ^1.0
- jaschilz/php-coverage-badger: ^2.0
- phpunit/phpunit: ^9.1
This package is auto-updated.
Last update: 2024-09-03 21:07:40 UTC
README
介绍
Doctrine Annotations 是一个强大的工具,允许以干净、语义化和可预测的方式为类、方法和属性指定所有类型的元信息。
默认注解读取器中缺少的一个特性是可模拟性,或是在运行时注入注解的能力。
当被注解的类是第三方库的一部分,而你无法控制它时,这特别有用。在这种情况下,默认的策略可能是扩展或重写整个类,但这并不总是可行,也肯定不是可维护的。
此包引入了一个可模拟的注解读取器,支持多种方式来“指导”它如何读取注解。它支持重写和合并类、方法和属性级别的注解,并且还公开了大量接口,以便你引入自己的功能。
它作为一个包装器来包装你的选择注解读取器实例,因此你仍然可以以最小的努力从中受益(例如缓存)。
安装
composer require tonybogdanov/mockable-annotations
* 默认情况下,读取器仅依赖于 doctrine/annotations
包,所以如果你打算使用 Doctrine 的缓存读取器,请确保也将 doctrine/cache
添加到你的依赖项中。
提供者
包含的 MockableAnnotationReader
使用注解提供者模拟类、方法级别和属性级别的注解。使用任何 [clear|get|set|add][Class|Method|Property]MockProvider(s)
方法来注册你的提供者。这些方法期望实例化相应的接口,因此如果你需要,你甚至可以构建自己的提供者。
该包提供了 3 个默认提供者:ClassMockProvider
、MethodMockProvider
和 PropertyMockProvider
,支持覆盖和合并策略,可选过滤和优先级(优先级较高的值将在最后执行)。
示例
以下类定义
/** * @TestAnnotation("hello") */ class TestClass {}
可以模拟其类注解,因此读取器实际上看到的是以下内容
/** * @AnotherTestAnnotation("world") */ class TestClass {}
使用以下示例代码
$newAnnotation = new AnotherTestAnnotation(); $newAnnotation->value = "world"; $reader = new MockableAnnotationReader( new AnnotationReader() ); $reader->addClassMockProvider( new ClassMockProvider( [ $newAnnotation ], new OverrideStrategy(), new ClassNameFilter( TestClass::class ) ) ); $reader->getClassAnnotations( TestClass::class ); // ...
这里我们使用的是 OverrideStrategy
,它忽略原始注解并使用传递给 ClassMockProvider
的自定义注解替换它们。您也可以使用 MergeStrategy
,它将合并原始注解与新的注解。当然,您也可以实现并传递自己的策略。
此外,我们使用了一个 ClassNameFilter
,这样提供者只有在读取的类是 TestClass
的实例时才会应用。
* 如果您需要过滤器检查被读取的类是否是 TestClass
的 精确实例 而不是继承自 TestClass
的类的实例,可以将 true
作为第二个参数传递给 ClassNameFilter
过滤器。
模拟方法和属性遵循相同的原理,具有非常相似的 API。
请参阅源代码以获取参考。
便利方法
注册提供者可能是一个相当冗长的任务,因此对于大多数常见场景,读取器公开了一些便利方法。
-
[覆盖|合并][类|方法|属性]注解
- 接受一个类名、(一个方法或属性名)、一个注解数组和一个可选的优先级,注册一个简单的类/方法/属性注解提供者,仅当正在读取的类/方法/属性与指定的相匹配时适用。 -
[覆盖|合并]别名[类|方法|属性]注解
- 接受两个类名对(以及方法或属性名),其中第一对目标为正在读取的类/方法/属性,第二对目标为相应类/方法/属性的注解将覆盖/合并到源注解中。
这在您不想以编程方式指定注解并希望从“别名”或“哑”类/方法/属性中获取注解时非常有用。 -
[覆盖|合并]别名注解
- 可能是最方便的,此方法接受源类名和目标类名,然后将目标类中所有类/方法 & 属性注解覆盖/合并到源类中。
请注意,只有源类中的方法和属性将被检查并与目标类匹配。目标(别名)类中的注解(源类中不存在),将被忽略。
模拟与继承
设计上,Doctrine 注解是不可继承的。这意味着如果您有类 A 继承自类 B,并在 B 上定义了一些类级注解,那么检索 A 的注解时将不会包括它们。
* 请注意,上述内容仅适用于注解。父类的特性和属性仍然可见,并且其注解将在子类中进行检查。
上述内容反映到模拟中,意味着您理论上需要单独模拟子类和父类,因为读取器会分别遍历它们。
MockableReader
可以帮助在一定程度上缓解这个问题,因为它尊重继承。当您模拟类 B 时,当读取器检查从 B 继承的任何类时,所有类级注解都将应用(除非在 ClassNameFilter
上将 strict
标志设置为 TRUE
),即使您没有明确模拟它。
如果您确实明确模拟了它,那么继承的注解将被忽略。