Completed
Branch master (f7eb77)
by Tomáš
03:26
created

RulesetBuilder::sortSniffs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
3
/*
4
 * This file is part of Symplify
5
 * Copyright (c) 2016 Tomas Votruba (http://tomasvotruba.cz).
6
 */
7
8
namespace Symplify\PHP7_CodeSniffer\Ruleset;
9
10
use SimpleXMLElement;
11
use Symplify\PHP7_CodeSniffer\Ruleset\Rule\ReferenceNormalizer;
12
use Symplify\PHP7_CodeSniffer\Sniff\Finder\SniffFinder;
13
use Symplify\PHP7_CodeSniffer\Standard\StandardFinder;
14
15
final class RulesetBuilder
16
{
17
    /**
18
     * @var SniffFinder
19
     */
20
    private $sniffFinder;
21
22
    /**
23
     * @var ReferenceNormalizer
24
     */
25
    private $ruleReferenceNormalizer;
26
27
    /**
28
     * @var array
29
     */
30
    private $ruleset = [];
31
32
    /**
33
     * @var array
34
     */
35
    private $includedSniffs = [];
36
37
    /**
38
     * @var array
39
     */
40
    private $excludedSniffs = [];
41
42
    /**
43
     * @var StandardFinder
44
     */
45
    private $standardFinder;
46
47 5
    public function __construct(
48
        SniffFinder $sniffFinder,
49
        StandardFinder $standardFinder,
50
        ReferenceNormalizer $ruleReferenceNormalizer
51
    ) {
52 5
        $this->sniffFinder = $sniffFinder;
53 5
        $this->standardFinder = $standardFinder;
54 5
        $this->ruleReferenceNormalizer = $ruleReferenceNormalizer;
55 5
    }
56
57 4
    public function buildFromRulesetXml(string $rulesetXmlFile) : array
58
    {
59 4
        $this->cleanCache();
60
61 4
        $rulesetXml = simplexml_load_file($rulesetXmlFile);
62 4
        foreach ($rulesetXml->rule as $rule) {
63 4
            if (isset($rule['ref']) === false) {
64
                continue;
65
            }
66
67 4
            $expandedSniffs = $this->normalizeReference($rule['ref']);
68 4
            $newSniffs = array_diff($expandedSniffs, $this->includedSniffs);
69
70 4
            $this->includedSniffs = array_merge($this->includedSniffs, $expandedSniffs);
71
72 4
            $this->processExcludedRules($rule);
73
74 4
            $this->processRule($rule, $newSniffs);
75
        }
76
77 4
        $ownSniffs = $this->getOwnSniffsFromRuleset($rulesetXmlFile);
78
79 4
        $this->includedSniffs = array_unique(array_merge($ownSniffs, $this->includedSniffs));
80 4
        $this->excludedSniffs = array_unique($this->excludedSniffs);
81
82 4
        $sniffs = $this->filterOutExcludedSniffs();
83 4
        return $this->sortSniffs($sniffs);
84
    }
85
86
    public function getRuleset() : array
87
    {
88
        return $this->ruleset;
89
    }
90
91
    /**
92
     * Processes a rule from a ruleset XML file, overriding built-in defaults.
93
     */
94 4
    private function processRule(SimpleXMLElement $rule, array $newSniffs)
95
    {
96 4
        $ref  = (string) $rule['ref'];
97 4
        $todo = [$ref];
98
99 4
        $parts = explode('.', $ref);
100 4
        if (count($parts) <= 2) {
101
            // We are processing a standard or a category of sniffs.
102 4
            foreach ($newSniffs as $sniffFile) {
103
                $parts = explode(DIRECTORY_SEPARATOR, $sniffFile);
104
                $sniffName = array_pop($parts);
105
                $sniffCategory = array_pop($parts);
106
                array_pop($parts);
107
                $sniffStandard = array_pop($parts);
108
                $todo[] = $sniffStandard.'.'.$sniffCategory.'.'.substr($sniffName, 0, -9);
109
            }
110
        }
111
112 4
        foreach ($todo as $code) {
113
            // Custom properties.
114 4
            if (isset($rule->properties) === true) {
115 4
                foreach ($rule->properties->property as $prop) {
116 4
                    if (isset($this->ruleset[$code]) === false) {
117 4
                        $this->ruleset[$code] = [
118 4
                            'properties' => [],
119
                        ];
120 3
                    } else if (isset($this->ruleset[$code]['properties']) === false) {
121
                        $this->ruleset[$code]['properties'] = [];
122
                    }
123
124 4
                    $name = (string) $prop['name'];
125 4
                    if (isset($prop['type']) === true
126 4
                        && (string) $prop['type'] === 'array'
127
                    ) {
128 3
                        $value  = (string) $prop['value'];
129 3
                        $values = [];
130 3
                        foreach (explode(',', $value) as $val) {
131 3
                            $v = '';
132
133 3
                            list($k,$v) = explode('=>', $val.'=>');
134 3
                            if ($v !== '') {
135
                                $values[$k] = $v;
136
                            } else {
137 3
                                $values[] = $k;
138
                            }
139
                        }
140
141 3
                        $this->ruleset[$code]['properties'][$name] = $values;
142
                    } else {
143 4
                        $this->ruleset[$code]['properties'][$name] = (string) $prop['value'];
144
                    }
145
                }
146
            }
147
        }
148 4
    }
149
150 4
    private function normalizeReference(string $reference)
151
    {
152 4
        if ($this->ruleReferenceNormalizer->isRulesetReference($reference)) {
153
            return $this->buildFromRulesetXml($reference);
154
        }
155
156 4
        if ($this->ruleReferenceNormalizer->isStandardReference($reference)) {
157 4
            $ruleset = $this->standardFinder->getRulesetPathForStandardName($reference);
158 4
            return $this->buildFromRulesetXml($ruleset);
159
        }
160
161 4
        return $this->ruleReferenceNormalizer->normalize($reference);
162
    }
163
164 4
    private function cleanCache()
165
    {
166 4
        $this->includedSniffs = [];
167 4
        $this->excludedSniffs = [];
168 4
    }
169
170
    /**
171
     * @return string[]
172
     */
173 4
    private function getOwnSniffsFromRuleset(string $rulesetXml) : array
174
    {
175 4
        $rulesetDir = dirname($rulesetXml);
176 4
        $sniffDir = $rulesetDir.DIRECTORY_SEPARATOR.'Sniffs';
177 4
        if (is_dir($sniffDir)) {
178 4
            return $this->sniffFinder->findAllSniffClassesInDirectory($sniffDir);
179
        }
180
181 1
        return [];
182
    }
183
184 4
    private function processExcludedRules(SimpleXMLElement $rule)
185
    {
186 4
        if (isset($rule->exclude)) {
187
            foreach ($rule->exclude as $exclude) {
188
                $this->excludedSniffs = array_merge(
189
                    $this->excludedSniffs,
190
                    $this->normalizeReference($exclude['name'])
191
                );
192
            }
193
        }
194 4
    }
195
196 4
    private function filterOutExcludedSniffs() : array
197
    {
198 4
        $sniffs = [];
199 4
        foreach ($this->includedSniffs as $sniffCode => $sniffClass) {
200 4
            if (!in_array($sniffCode, $this->excludedSniffs)) {
201 4
                $sniffs[$sniffCode] = $sniffClass;
202
            }
203
        }
204
205 4
        return $sniffs;
206
    }
207
208 4
    private function sortSniffs(array $sniffs) : array
209
    {
210 4
        ksort($sniffs);
211 4
        return $sniffs;
212
    }
213
}
214