Passed
Pull Request — master (#38)
by Rustam
02:25
created

NativeClassifier   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 80
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 34
dl 0
loc 80
ccs 35
cts 35
cp 1
rs 10
c 0
b 0
f 0
wmc 14

2 Methods

Rating   Name   Duplication   Size   Complexity  
A getAvailableClasses() 0 14 4
B skipClass() 0 50 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Classifier;
6
7
use ReflectionAttribute;
8
use ReflectionClass;
9
10
/**
11
 * `NativeClassifier` is a classifier that finds classes using PHP's native function {@see get_declared_classes()}.
12
 */
13
final class NativeClassifier extends AbstractClassifier
14
{
15
    /**
16
     * @psalm-var array<class-string, ReflectionClass>
17
     */
18
    private static array $reflectionsCache = [];
19
20
    /**
21
     * @psalm-suppress UnresolvableInclude
22
     */
23 10
    protected function getAvailableClasses(): iterable
24
    {
25 10
        $files = $this->getFiles();
26
27 10
        foreach ($files as $file) {
28 10
            require_once $file;
29
        }
30
31 10
        foreach (get_declared_classes() as $className) {
32 10
            if ($this->skipClass($className)) {
33 10
                continue;
34
            }
35
36 10
            yield $className;
37
        }
38
    }
39
40
    /**
41
     * @psalm-param class-string $className
42
     */
43 10
    private function skipClass(string $className): bool
44
    {
45 10
        $reflectionClass = self::$reflectionsCache[$className] ??= new ReflectionClass($className);
46
47 10
        if ($reflectionClass->isInternal() || $reflectionClass->isAnonymous()) {
48 10
            return true;
49
        }
50 10
        $directories = $this->directories;
51 10
        $isWindows = DIRECTORY_SEPARATOR === '\\';
52
53 10
        if ($isWindows) {
54
            /**
55
             * @psalm-var string[] $directories
56
             */
57
            // @codeCoverageIgnoreStart
58
            $directories = str_replace('/', '\\', $directories);
59
            // @codeCoverageIgnoreEnd
60
        }
61
62 10
        $matchedDirs = array_filter(
63 10
            $directories,
64 10
            static fn($directory) => str_starts_with($reflectionClass->getFileName(), $directory)
65 10
        );
66
67 10
        if (count($matchedDirs) === 0) {
68 10
            return true;
69
        }
70
71 10
        if (!empty($this->interfaces)) {
72 8
            $interfaces = $reflectionClass->getInterfaces();
73 8
            $interfaces = array_map(static fn(ReflectionClass $class) => $class->getName(), $interfaces);
74
75 8
            if (count(array_intersect($this->interfaces, $interfaces)) !== count($this->interfaces)) {
76 5
                return true;
77
            }
78
        }
79
80 10
        if (!empty($this->attributes)) {
81 2
            $attributes = $reflectionClass->getAttributes();
82 2
            $attributes = array_map(
83 2
                static fn(ReflectionAttribute $attribute) => $attribute->getName(),
84 2
                $attributes
85 2
            );
86
87 2
            if (count(array_intersect($this->attributes, $attributes)) !== count($this->attributes)) {
88 2
                return true;
89
            }
90
        }
91
92 10
        return ($this->parentClass !== null) && !is_subclass_of($reflectionClass->getName(), $this->parentClass);
93
    }
94
}
95