Passed
Pull Request — master (#3)
by Stanislau
13:30
created

Locator   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 109
Duplicated Lines 0 %

Test Coverage

Coverage 84.09%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 43
c 2
b 0
f 0
dl 0
loc 109
ccs 37
cts 44
cp 0.8409
rs 10
wmc 16

4 Methods

Rating   Name   Duplication   Size   Complexity  
A createReflectionClass() 0 3 1
A __construct() 0 4 1
B getPluginClasses() 0 35 6
B lookup() 0 37 8
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 1
            if (!class_exists($file)) {
101
                continue;
102
            }
103
104 1
            if (\in_array($file, $this->locatedClasses)) {
105
                continue;
106
            }
107
108 1
            $this->locatedClasses[] = $file;
109
110 1
            yield $file;
111
        }
112
    }
113
114
    /**
115
     * @template T of Object
116
     *
117
     * @psalm-param  class-string<T>|T $objectOrClass
118
     *
119
     * @return \ReflectionClass<T>
120
     *
121
     * @throws \ReflectionException
122
     */
123 1
    protected function createReflectionClass(string|object $objectOrClass): \ReflectionClass
124
    {
125 1
        return new \ReflectionClass($objectOrClass);
126
    }
127
}
128