Passed
Push — master ( 4bec7e...6acb29 )
by Stanislau
01:26 queued 12s
created

Locator::getPluginClasses()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 32
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 6.105

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 6
eloc 21
c 2
b 0
f 0
nc 5
nop 1
dl 0
loc 32
ccs 18
cts 21
cp 0.8571
crap 6.105
rs 8.9617
1
<?php
2
3
/*
4
 *  This file is part of the Micro framework package.
5
 *
6
 *  (c) Stanislau Komar <[email protected]>
7
 *
8
 *  For the full copyright and license information, please view the LICENSE
9
 *  file that was distributed with this source code.
10
 */
11
12
namespace Micro\Plugin\Locator\Locator;
13
14
use Micro\Framework\Kernel\KernelInterface;
15
use Symfony\Component\Finder\Finder;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Finder\Finder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
17
class Locator implements LocatorInterface
18
{
19
    /**
20
     * @var array<class-string>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string>.
Loading history...
21
     */
22
    private array $locatedClasses;
23
24 1
    public function __construct(
25
        private readonly KernelInterface $kernel
26
    ) {
27 1
        $this->locatedClasses = [];
28
    }
29
30 1
    public function lookup(string $classOrInterfaceName): iterable
31
    {
32 1
        $isInterface = interface_exists($classOrInterfaceName);
33
34 1
        if (!$isInterface && !class_exists($classOrInterfaceName)) {
35 1
            return;
36
        }
37
38 1
        foreach ($this->kernel->plugins() as $plugin) {
39 1
            $reflection = $this->createReflectionClass($plugin);
40 1
            foreach ($this->getPluginClasses($reflection) as $pluginInternalClassName) {
41 1
                $pluginClassReflection = $this->createReflectionClass($pluginInternalClassName);
42
43 1
                if ($isInterface) {
44 1
                    if (!\in_array($classOrInterfaceName, $pluginClassReflection->getInterfaceNames())) {
45 1
                        continue;
46
                    }
47
                    /*
48
                     * @phpstan-ignore-next-line
49
                     */
50 1
                    yield $pluginInternalClassName;
51
52 1
                    continue;
53
                }
54
55
                if (!$pluginClassReflection->isSubclassOf($classOrInterfaceName)) {
56
                    continue;
57
                }
58
59
                /*
60
                 * @phpstan-ignore-next-line
61
                 */
62
                yield $pluginInternalClassName;
63
            }
64
        }
65
66 1
        $this->locatedClasses = [];
67
    }
68
69
    /**
70
     * @template T of object
71
     *
72
     * @param \ReflectionClass<T> $reflection
73
     *
74
     * @return \Generator<class-string>
75
     */
76 1
    protected function getPluginClasses(\ReflectionClass $reflection): \Generator
77
    {
78 1
        $filename = $reflection->getFileName();
79 1
        if (!$filename) {
80 1
            return;
81
        }
82
83 1
        $pluginBasePath = \dirname($filename);
84 1
        $pluginNamespace = $reflection->getNamespaceName();
85 1
        $finder = new Finder();
86
        try {
87 1
            $finder
88 1
                ->in($pluginBasePath.'/**')
89 1
                ->name('*.php')
90 1
                ->notName('*Interface.php');
91
        } catch (\Throwable $exception) {
92
            return;
93
        }
94
95 1
        foreach ($finder as $splFileInfo) {
96 1
            $relative = str_replace($pluginBasePath, '', (string) $splFileInfo);
97 1
            $namespaceRelative = str_replace('/', '\\', $relative);
98
99 1
            $file = str_replace('.php', '', $pluginNamespace.$namespaceRelative);
100
101 1
            if (\in_array($file, $this->locatedClasses) || !class_exists($file)) {
102
                continue;
103
            }
104
105 1
            $this->locatedClasses[] = $file;
106
107 1
            yield $file;
108
        }
109
    }
110
111
    /**
112
     * @template T of Object
113
     *
114
     * @psalm-param  class-string<T>|T $objectOrClass
115
     *
116
     * @return \ReflectionClass<T>
117
     *
118
     * @throws \ReflectionException
119
     */
120 1
    protected function createReflectionClass(string|object $objectOrClass): \ReflectionClass
121
    {
122 1
        return new \ReflectionClass($objectOrClass);
123
    }
124
}
125