CompositeCodeElementSniff::run()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
nc 3
nop 2
dl 0
loc 8
ccs 6
cts 6
cp 1
crap 3
rs 10
c 2
b 0
f 0
1
<?php
2
3
namespace Gskema\TypeSniff\Sniffs;
4
5
use Generator;
6
use Gskema\TypeSniff\Core\CodeElement\Element\CodeElementInterface;
7
use Gskema\TypeSniff\Core\CodeElement\Element\FileElement;
8
use Gskema\TypeSniff\Sniffs\CodeElement\FqcnDescriptionSniff;
9
use PHP_CodeSniffer\Files\File;
10
use Gskema\TypeSniff\Core\CodeElement\CodeElementDetector;
11
use Gskema\TypeSniff\Sniffs\CodeElement\CodeElementSniffInterface;
12
use Gskema\TypeSniff\Sniffs\CodeElement\FqcnConstSniff;
13
use Gskema\TypeSniff\Sniffs\CodeElement\FqcnMethodSniff;
14
use Gskema\TypeSniff\Sniffs\CodeElement\FqcnPropSniff;
15
16
/**
17
 * @see CompositeCodeElementSniffTest
18
 */
19
class CompositeCodeElementSniff extends AbstractConfigurableSniff
20
{
21
    protected bool $useReflection = false;
22
23
    /** @var CodeElementSniffInterface[][] */
24
    protected array $sniffs = [];
25
26
    /**
27
     * @inheritDoc
28
     */
29 16
    protected function configure(array $config): void
30
    {
31
        // 0. Global config
32 16
        $globalReportType = $config['reportType'] ?? null;
33 16
        $globalAddViolationId = $config['addViolationId'] ?? false;
34
35
        // 1. CompositeCodeElementSniff configuration
36 16
        $this->useReflection = (bool)($config['useReflection'] ?? false);
37
38
        // 2. CodeElementSniff(s) configuration
39
        // Default sniffs. They can be removed by specifying <property name="FqcnMethodSniff.enabled" value="false"/>
40 16
        $config['sniffs'][] = FqcnMethodSniff::class;
41 16
        $config['sniffs'][] = FqcnPropSniff::class;
42 16
        $config['sniffs'][] = FqcnConstSniff::class;
43 16
        $config['sniffs'][] = FqcnDescriptionSniff::class;
44
45
        // CodeElementSniff(s) are saved by their short name, meaning you can't have 2 instances of same sniff.
46 16
        $rawSniffs = [];
47 16
        foreach ($config['sniffs'] as $class) {
48 16
            $bits = explode('\\', $class);
49 16
            $shortClass = end($bits);
50 16
            if (!isset($rawSniffs[$shortClass])) {
51 16
                $rawSniffs[$shortClass] = ['class' => $class, 'config' => []];
52
            }
53
        }
54
55
        // Property keys for CodeElementSniff(s) are applied by the short class name.
56
        // E.g. FqcnMethodSniff.invalidTags
57 16
        foreach ($config as $key => $val) {
58 16
            if ('sniffs' !== $key && false !== strpos($key, '.')) {
59 5
                [$shortClass, $cfgKey] = explode('.', $key, 2);
60 5
                if (isset($rawSniffs[$shortClass])) {
61 5
                    $rawSniffs[$shortClass]['config'][$cfgKey] = $val;
62
                }
63
            }
64
        }
65
66 16
        foreach ($rawSniffs as $rawSniff) {
67 16
            $enabled = $rawSniff['config']['enabled'] ?? true;
68 16
            if (!$enabled) {
69 1
                continue;
70
            }
71
72
            // Modify individual sniff configs with global config values
73 16
            $rawSniff['config']['reportType'] = $rawSniff['config']['reportType'] ?? $globalReportType ?? null;
74 16
            $rawSniff['config']['addViolationId'] = $globalAddViolationId;
75
76
            /** @var CodeElementSniffInterface $sniff */
77 16
            $sniff = new $rawSniff['class']();
78 16
            $sniff->configure($rawSniff['config']);
79
80 16
            $codeElementClasses = $sniff->register();
81 16
            foreach ($codeElementClasses as $codeElementClass) {
82 16
                $this->sniffs[$codeElementClass][] = $sniff;
83
            }
84
        }
85 16
    }
86
87
    /**
88
     * @inheritDoc
89
     */
90 38
    public function register()
91
    {
92
        return [
93 38
            T_OPEN_TAG,
94
        ];
95
    }
96
97
    /**
98
     * @inheritDoc
99
     */
100 16
    protected function run(File $file, int $openTagPtr): void
101
    {
102 16
        $fileElement = CodeElementDetector::detectFromTokens($file, $this->useReflection);
103
104 16
        foreach ($this->getArgIterator($fileElement) as [$element, $parentElement]) {
105 16
            $className = get_class($element);
106 16
            foreach ($this->sniffs[$className] ?? [] as $sniff) {
107 16
                $sniff->process($file, $element, $parentElement);
108
            }
109
        }
110 16
    }
111
112
    /**
113
     * @param FileElement $file
114
     *
115
     * @return Generator|CodeElementInterface[][]
116
     */
117 16
    protected function getArgIterator(FileElement $file): Generator
118
    {
119
        // world's most complicated iterator
120 16
        yield [$file, $file];
121 16
        foreach ($file->getConstants() as $constant) {
122 2
            yield [$constant, $file];
123
        }
124 16
        foreach ($file->getFunctions() as $function) {
125 2
            yield [$function, $file];
126
        }
127 16
        foreach ($file->getClasses() as $class) {
128 13
            yield [$class, $file];
129 13
            foreach ($class->getConstants() as $constant) {
130 5
                yield [$constant, $class];
131
            }
132 13
            foreach ($class->getProperties() as $prop) {
133 11
                yield [$prop, $class];
134
            }
135 13
            foreach ($class->getMethods() as $method) {
136 11
                yield [$method, $class];
137
            }
138
        }
139 16
        foreach ($file->getTraits() as $trait) {
140 1
            yield [$trait, $file];
141 1
            foreach ($trait->getProperties() as $prop) {
142 1
                yield [$prop, $trait];
143
            }
144 1
            foreach ($trait->getMethods() as $method) {
145 1
                yield [$method, $trait];
146
            }
147
        }
148 16
        foreach ($file->getInterfaces() as $interface) {
149 2
            yield [$interface, $file];
150 2
            foreach ($interface->getConstants() as $constant) {
151 2
                yield [$constant, $interface];
152
            }
153 2
            foreach ($interface->getMethods() as $method) {
154 2
                yield [$method, $interface];
155
            }
156
        }
157 16
    }
158
}
159