Laravel标记所有实现接口的类



我正在使用Laravel 8,我想获得实现接口X的所有类。

几个月前,我用DI:用symfony4做了这件事

services.yml

_instanceof:
AppCalculatorBudgetBudgetCalculatorInterface:
tags: ['app.budget_calculator']
AppHandlerCalculatorBudgetHandler:
arguments: [!tagged app.budget_calculator]

然后在我的课堂CalculatorBudgetHandler.php

private $calculatorList = [];
public function __construct(iterable $calculatorList)
{
$this->calculatorList = $calculatorList;
}
public function __construct(iterable $calculatorList)
{
$this->calculatorList = $calculatorList;
}
public function calculate(array $data): float
{
foreach ($this->calculatorList as $calculator) {
if ($calculator->supports($data)) {
return $calculator->calculate($data);
}
}

}

但我不明白如何与拉拉威尔合作。我想我必须在绑定或标记中通过我所有的类:

$this->app->tag([CpuReport::class, MemoryReport::class], 'reports');

这意味着,如果我有一个实现X的新类,我必须将其添加到bind/tag中?我想自动完成。

thx!

我也需要这个。找了更长的时间,我基本上找到了解决方案。这样做的糟糕之处在于,在PHP中,当您没有use时,类实际上并没有被声明。因此,您必须扫描整个项目中的类并测试每个类,以找到实现接口的类,或者(更好(使用composer自动加载类映射。在那里,您可能会将类的搜索范围限制为一个子命名空间。

一个以这种方式工作的小但很酷的包是这样的:https://gitlab.com/hpierce1102/ClassFinder-基本上,它使用了composer PSR4类映射,总体性能良好。

这是我找到的解决方案:

// Add to service provider
private function tagByInterface(string $interfaceName, string $tagName, string $rootNamespace)
{
foreach (ClassFinder::getClassesInNamespace($rootNamespace, ClassFinder::RECURSIVE_MODE) as $className) {
$class = new ReflectionClass($className);
if ($class->isAbstract() || $class->isInterface()) {
continue;
}
if ($class->implementsInterface($interfaceName)) {
$this->app->tag($className, $tagName);
}
}
}

然后可以像这样在register():中使用

$this->tagByInterface(SomeInterface::class, 'some-tag', 'AppDomainSomething');
$this->app->when(SomeClass::class)->needs('$myServices')->giveTagged('some-tag');

由于类是使用反射加载的,如果根命名空间设置不正确或太宽,则此操作可能仍需要时间。反射很快(据我所知,比从缓存加载信息更快(,但您仍然应该考虑为任务使用延迟的提供程序,以便只有在实际需要时才触发对实现类的搜索。

几个月后更新

这个解决方案是有效的,但如果项目变得很大,可能会对性能造成巨大的消耗。我现在正在缓存标记的类。类似这样的东西:

use HaydenPierceClassFinderClassFinder as HPClassFinder;
use IlluminateContractsCacheRepository;
class InheritanceClassFinder
{
public function __construct(private ?Repository $cache = null)
{
}
public function findClassesImplementingOrExtending(string $interfaceOrClass, string $rootNamespace): array
{
if ($this->cache) {
return $this->cache->rememberForever(
'inheriting-classes-'.$interfaceOrClass,
fn () => $this->findClassesInheriting($interfaceOrClass, $rootNamespace));
}
return $this->findClassesInheriting($interfaceOrClass, $rootNamespace);
}
private function findClassesInheriting(string $interfaceOrClass, string $rootNamespace): array
{
$classes = [];
foreach (HPClassFinder::getClassesInNamespace($rootNamespace, HPClassFinder::RECURSIVE_MODE) as $className) {
if (!is_subclass_of($className, $interfaceOrClass)
|| ($class = new ReflectionClass($className))->isAbstract() || $class->isInterface()) {
continue;
}
$classes[] = $className;
}
return $classes;
}
}

这意味着只要注入缓存,就会加载一次内容,然后从缓存中取出。我只在生产中注入缓存,所以在本地它有点慢,但总是最新的。在生产中,我每次部署都会丢弃缓存,所以每次部署后我都会得到一次新的负载。

最新更新