Passed
Push — master ( f9855b...d6b3b1 )
by
unknown
02:16 queued 11s
created

ShortVariable::isInitializedInLoop()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 1
dl 0
loc 19
ccs 9
cts 9
cp 1
crap 4
rs 9.6333
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of PHP Mess Detector.
4
 *
5
 * Copyright (c) Manuel Pichler <[email protected]>.
6
 * All rights reserved.
7
 *
8
 * Licensed under BSD License
9
 * For full copyright and license information, please see the LICENSE file.
10
 * Redistributions of files must retain the above copyright notice.
11
 *
12
 * @author Manuel Pichler <[email protected]>
13
 * @copyright Manuel Pichler. All rights reserved.
14
 * @license https://opensource.org/licenses/bsd-license.php BSD License
15
 * @link http://phpmd.org/
16
 */
17
18
namespace PHPMD\Rule\Naming;
19
20
use PHPMD\AbstractNode;
21
use PHPMD\AbstractRule;
22
use PHPMD\Rule\ClassAware;
23
use PHPMD\Rule\FunctionAware;
24
use PHPMD\Rule\MethodAware;
25
26
/**
27
 * This rule class will detect variables, parameters and properties with short
28
 * names.
29
 */
30
class ShortVariable extends AbstractRule implements ClassAware, MethodAware, FunctionAware
31
{
32
    /**
33
     * Temporary map holding variables that were already processed in the
34
     * current context.
35
     *
36
     * @var array(string=>boolean)
37
     */
38
    protected $processedVariables = array();
39
40
    /**
41
     * Extracts all variable and variable declarator nodes from the given node
42
     *
43
     * Checks the variable name length against the configured minimum
44
     * length.
45
     *
46
     * @param \PHPMD\AbstractNode $node
47
     * @return void
48
     */
49 21
    public function apply(AbstractNode $node)
50
    {
51 21
        $this->resetProcessed();
52
53 21
        if ($node->getType() === 'class') {
54 11
            $this->applyClass($node);
55 11
56
            return;
57
        }
58 14
59 14
        $this->applyNonClass($node);
60
    }
61
62
    /**
63
     * Extracts all variable and variable declarator nodes from the given class node
64
     *
65
     * Checks the variable name length against the configured minimum
66
     * length.
67
     *
68
     * @param AbstractNode $node
69
     * @return void
70 11
     */
71 View Code Duplication
    protected function applyClass(AbstractNode $node)
72 11
    {
73 11
        $fields = $node->findChildrenOfType('FieldDeclaration');
74 5
        foreach ($fields as $field) {
75 5
            $declarators = $field->findChildrenOfType('VariableDeclarator');
76 5
            foreach ($declarators as $declarator) {
77
                $this->checkNodeImage($declarator);
78
            }
79 11
        }
80 11
        $this->resetProcessed();
81
    }
82
83
    /**
84
     * Extracts all variable and variable declarator nodes from the given non-class node
85
     *
86
     * Checks the variable name length against the configured minimum
87
     * length.
88
     *
89
     * @param AbstractNode $node
90
     * @return void
91 14
     */
92 View Code Duplication
    protected function applyNonClass(AbstractNode $node)
93 14
    {
94 14
        $declarators = $node->findChildrenOfType('VariableDeclarator');
95 4
        foreach ($declarators as $declarator) {
96
            $this->checkNodeImage($declarator);
97
        }
98 14
99 14
        $variables = $node->findChildrenOfType('Variable');
100 10
        foreach ($variables as $variable) {
101
            $this->checkNodeImage($variable);
102 14
        }
103 14
        $this->resetProcessed();
104
    }
105
106
    /**
107
     * Checks if the variable name of the given node is greater/equal to the
108
     * configured threshold or if the given node is an allowed context.
109
     *
110
     * @param \PHPMD\AbstractNode $node
111
     * @return void
112 18
     */
113
    protected function checkNodeImage(AbstractNode $node)
114 18
    {
115 18
        if ($this->isNotProcessed($node)) {
116 18
            $this->addProcessed($node);
117
            $this->checkMinimumLength($node);
118 18
        }
119
    }
120
121
    /**
122
     * Template method that performs the real node image check.
123
     *
124
     * @param \PHPMD\AbstractNode $node
125
     * @return void
126 18
     */
127
    protected function checkMinimumLength(AbstractNode $node)
128 18
    {
129
        $threshold = $this->getIntProperty('minimum');
130 18
131 6
        if ($threshold <= strlen($node->getImage()) - 1) {
132
            return;
133
        }
134 13
135 4
        if ($this->isNameAllowedInContext($node)) {
136
            return;
137
        }
138 9
139
        $exceptions = $this->getExceptionsList();
140 9
141 1
        if (in_array(substr($node->getImage(), 1), $exceptions)) {
142
            return;
143
        }
144 8
145 8
        $this->addViolation($node, array($node->getImage(), $threshold));
146
    }
147
148
    /**
149
     * Gets array of exceptions from property
150
     *
151
     * @return array
152 9
     */
153 View Code Duplication
    protected function getExceptionsList()
154
    {
155 9
        try {
156
            $exceptions = $this->getStringProperty('exceptions');
157
        } catch (\OutOfBoundsException $e) {
158
            $exceptions = '';
159
        }
160 9
161
        return explode(',', $exceptions);
162
    }
163
164
    /**
165
     * Checks if a short name is acceptable in the current context. For the
166
     * moment these contexts are the init section of a for-loop and short
167
     * variable names in catch-statements.
168
     *
169
     * @param \PHPMD\AbstractNode $node
170
     * @return boolean
171 13
     */
172
    protected function isNameAllowedInContext(AbstractNode $node)
173 13
    {
174 12
        if ($this->isChildOf($node, 'ForeachStatement')) {
175 11
            return $this->isInitializedInLoop($node);
176 13
        }
177
178
        return $this->isChildOf($node, 'CatchStatement')
179
            || $this->isChildOf($node, 'ForInit')
180
            || $this->isChildOf($node, 'MemberPrimaryPrefix');
181
    }
182
183
    /**
184
     * Checks if a short name is initialized within a foreach loop statement
185
     *
186
     * @param \PHPMD\AbstractNode $node
187 13
     * @return boolean
188
     */
189 13
    protected function isInitializedInLoop(AbstractNode $node)
190 13
    {
191 13
        if (!$this->getBooleanProperty('allow-short-variables-in-loop', true)) {
192 4
            return false;
193
        }
194 12
195
        $exceptionVariables = array();
196 12
197
        $parentForeaches = $this->getParentsOfType($node, 'ForeachStatement');
198
        foreach ($parentForeaches as $foreach) {
199
            foreach ($foreach->getChildren() as $foreachChild) {
200
                $exceptionVariables[] = $foreachChild->getImage();
201
            }
202
        }
203
204 21
        $exceptionVariables = array_filter(array_unique($exceptionVariables));
205
206 21
        return in_array($node->getImage(), $exceptionVariables, true);
207 21
    }
208
209
    /**
210
     * Returns an array of parent nodes of the specified type
211
     *
212
     * @param \PHPMD\AbstractNode $node
213
     * @return array
214
     */
215 18
    protected function getParentsOfType(AbstractNode $node, $type)
216
    {
217 18
        $parents = array();
218 18
219
        $parent = $node->getParent();
220
221
        while (is_object($parent)) {
222
            if ($parent->isInstanceOf($type)) {
223
                $parents[] = $parent;
224
            }
225
            $parent = $parent->getParent();
226 18
        }
227
228 18
        return $parents;
229
    }
230
231
    /**
232
     * Checks if the given node is a direct or indirect child of a node with
233
     * the given type.
234
     *
235
     * @param \PHPMD\AbstractNode $node
236
     * @param string $type
237
     * @return boolean
238
     */
239 View Code Duplication
    protected function isChildOf(AbstractNode $node, $type)
240
    {
241
        $parent = $node->getParent();
242
        while (is_object($parent)) {
243
            if ($parent->isInstanceOf($type)) {
244
                return true;
245
            }
246
            $parent = $parent->getParent();
247
        }
248
249
        return false;
250
    }
251
252
    /**
253
     * Resets the already processed nodes.
254
     *
255
     * @return void
256
     */
257
    protected function resetProcessed()
258
    {
259
        $this->processedVariables = array();
260
    }
261
262
    /**
263
     * Flags the given node as already processed.
264
     *
265
     * @param \PHPMD\AbstractNode $node
266
     * @return void
267
     */
268
    protected function addProcessed(AbstractNode $node)
269
    {
270
        $this->processedVariables[$node->getImage()] = true;
271
    }
272
273
    /**
274
     * Checks if the given node was already processed.
275
     *
276
     * @param \PHPMD\AbstractNode $node
277
     * @return boolean
278
     */
279
    protected function isNotProcessed(AbstractNode $node)
280
    {
281
        return !isset($this->processedVariables[$node->getImage()]);
282
    }
283
}
284