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

NativeClassifier::skipClass()   B

Complexity

Conditions 10
Paths 25

Size

Total Lines 50
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 10

Importance

Changes 0
Metric Value
cc 10
eloc 25
nc 25
nop 1
dl 0
loc 50
ccs 27
cts 27
cp 1
crap 10
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
            try {
29 10
                require_once $file;
30 1
            } catch (\Throwable) {
31
                // Ignore syntax errors
32
            }
33
        }
34
35 10
        foreach (get_declared_classes() as $className) {
36 10
            if ($this->skipClass($className)) {
37 10
                continue;
38
            }
39
40 10
            yield $className;
41
        }
42
    }
43
44
    /**
45
     * @psalm-param class-string $className
46
     */
47 10
    private function skipClass(string $className): bool
48
    {
49 10
        $reflectionClass = self::$reflectionsCache[$className] ??= new ReflectionClass($className);
50
51 10
        if ($reflectionClass->isInternal() || $reflectionClass->isAnonymous()) {
52 10
            return true;
53
        }
54 10
        $directories = $this->directories;
55 10
        $isWindows = DIRECTORY_SEPARATOR === '\\';
56
57 10
        if ($isWindows) {
58
            /**
59
             * @psalm-var string[] $directories
60
             */
61
            // @codeCoverageIgnoreStart
62
            $directories = str_replace('/', '\\', $directories);
63
            // @codeCoverageIgnoreEnd
64
        }
65
66 10
        $matchedDirs = array_filter(
67 10
            $directories,
68 10
            static fn($directory) => str_starts_with($reflectionClass->getFileName(), $directory)
69 10
        );
70
71 10
        if (count($matchedDirs) === 0) {
72 10
            return true;
73
        }
74
75 10
        if (!empty($this->interfaces)) {
76 8
            $interfaces = $reflectionClass->getInterfaces();
77 8
            $interfaces = array_map(static fn(ReflectionClass $class) => $class->getName(), $interfaces);
78
79 8
            if (count(array_intersect($this->interfaces, $interfaces)) !== count($this->interfaces)) {
80 5
                return true;
81
            }
82
        }
83
84 10
        if (!empty($this->attributes)) {
85 2
            $attributes = $reflectionClass->getAttributes();
86 2
            $attributes = array_map(
87 2
                static fn(ReflectionAttribute $attribute) => $attribute->getName(),
88 2
                $attributes
89 2
            );
90
91 2
            if (count(array_intersect($this->attributes, $attributes)) !== count($this->attributes)) {
92 2
                return true;
93
            }
94
        }
95
96 10
        return ($this->parentClass !== null) && !is_subclass_of($reflectionClass->getName(), $this->parentClass);
97
    }
98
}
99