Passed
Pull Request — master (#40)
by Alexander
09:55 queued 07:23
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 11
    protected function getAvailableClasses(): iterable
24
    {
25 11
        $files = $this->getFiles();
26
27 11
        foreach ($files as $file) {
28
            try {
29 11
                require_once $file;
30 1
            } catch (\Throwable) {
31
                // Ignore syntax errors
32
            }
33
        }
34
35 11
        foreach (get_declared_classes() as $className) {
36 11
            if ($this->skipClass($className)) {
37 11
                continue;
38
            }
39
40 11
            yield $className;
41
        }
42
    }
43
44
    /**
45
     * @psalm-param class-string $className
46
     */
47 11
    private function skipClass(string $className): bool
48
    {
49 11
        $reflectionClass = self::$reflectionsCache[$className] ??= new ReflectionClass($className);
50
51 11
        if ($reflectionClass->isInternal() || $reflectionClass->isAnonymous()) {
52 11
            return true;
53
        }
54 11
        $directories = $this->directories;
55 11
        $isWindows = DIRECTORY_SEPARATOR === '\\';
56
57 11
        if ($isWindows) {
58
            /**
59
             * @psalm-var string[] $directories
60
             */
61
            // @codeCoverageIgnoreStart
62
            $directories = str_replace('/', '\\', $directories);
63
            // @codeCoverageIgnoreEnd
64
        }
65
66 11
        $matchedDirs = array_filter(
67 11
            $directories,
68 11
            static fn($directory) => str_starts_with($reflectionClass->getFileName(), $directory)
69 11
        );
70
71 11
        if (count($matchedDirs) === 0) {
72 11
            return true;
73
        }
74
75 11
        if (!empty($this->interfaces)) {
76 9
            $interfaces = $reflectionClass->getInterfaces();
77 9
            $interfaces = array_map(static fn(ReflectionClass $class) => $class->getName(), $interfaces);
78
79 9
            if (count(array_intersect($this->interfaces, $interfaces)) !== count($this->interfaces)) {
80 5
                return true;
81
            }
82
        }
83
84 11
        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 11
        return ($this->parentClass !== null) && !is_subclass_of($reflectionClass->getName(), $this->parentClass);
97
    }
98
}
99