Passed
Push — master ( 2ae08f...9f5502 )
by Kyle
05:15 queued 02:52
created

php/PHPMD/Rule/CleanCode/UndefinedVariable.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\CleanCode;
19
20
use PDepend\Source\AST\ASTArray;
21
use PDepend\Source\AST\ASTClass;
22
use PDepend\Source\AST\ASTPropertyPostfix;
23
use PDepend\Source\AST\ASTUnaryExpression;
24
use PDepend\Source\AST\ASTVariable;
25
use PDepend\Source\AST\ASTVariableDeclarator;
26
use PDepend\Source\AST\State;
27
use PHPMD\AbstractNode;
28
use PHPMD\Node\AbstractCallableNode;
29
use PHPMD\Node\ASTNode;
30
use PHPMD\Node\MethodNode;
31
use PHPMD\Rule\AbstractLocalVariable;
32
use PHPMD\Rule\FunctionAware;
33
use PHPMD\Rule\MethodAware;
34
35
/**
36
 * This rule collects all undefined variables within a given function or method
37
 * that are used by any code in the analyzed source artifact.
38
 */
39
class UndefinedVariable extends AbstractLocalVariable implements FunctionAware, MethodAware
40
{
41
    /**
42
     * Found variable images within a single method or function.
43
     *
44
     * @var array(string)
45
     */
46
    protected $images = array();
47
48
    /**
49
     * This method checks that all local variables within the given function or
50 6
     * method are used at least one time.
51
     *
52 6
     * @param \PHPMD\AbstractNode $node
53
     * @return void
54 6
     */
55 6
    public function apply(AbstractNode $node)
56 6
    {
57 6
        $this->images = array();
58 6
59 6
        if ($node instanceof MethodNode) {
60 6
            $this->collectProperties($this->getNode($node->getNode()->getParent()));
61 6
        }
62
63 6
        $this->collect($node);
64 6
65 1
        foreach ($node->findChildrenOfType('Class') as $class) {
66
            /** @var ASTClass $class */
67 6
68 4
            $this->collectProperties($class);
69
70
            foreach ($class->getMethods() as $method) {
71 6
                $this->collect(new MethodNode($method));
0 ignored issues
show
$method of type object<PDepend\Source\AST\ASTArtifact> is not a sub-type of object<PDepend\Source\AST\ASTMethod>. It seems like you assume a concrete implementation of the interface PDepend\Source\AST\ASTArtifact to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
72
            }
73
        }
74
75
        foreach ($node->findChildrenOfTypeVariable() as $variable) {
76
            if ($this->isSuperGlobal($variable) || $this->isPassedByReference($variable)) {
77
                $this->addVariableDefinition($variable);
78
            } elseif (!$this->checkVariableDefined($variable, $node)) {
79 6
                $this->addViolation($variable, array($this->getVariableImage($variable)));
80
            }
81 6
        }
82
    }
83 6
84
    /**
85
     * Collect variables defined inside a PHPMD entry node (such as MethodNode).
86
     *
87
     * @param AbstractNode $node
88 6
     */
89
    protected function collect(AbstractNode $node)
90
    {
91
        $this->collectPropertyPostfix($node);
92
        $this->collectClosureParameters($node);
93
        $this->collectForeachStatements($node);
94
        $this->collectListExpressions($node);
95
        $this->collectAssignments($node);
96 6
        $this->collectParameters($node);
97
        $this->collectExceptionCatches($node);
98 6
        $this->collectGlobalStatements($node);
99
    }
100 6
101
    protected function collectProperties($node)
102
    {
103
        if (!($node instanceof ASTClass)) {
104
            return;
105
        }
106
107 6
        foreach ($node->getProperties() as $property) {
108
            if ($property->isStatic()) {
109
                $this->images['::'.$property->getName()] = $property;
110
            }
111
        }
112
    }
113
114
    /**
115 6
     * Stores the given literal node in an global of found variables.
116
     *
117 6
     * @param \PHPMD\Node\AbstractNode $node
118
     * @return void
119 6
     */
120
    protected function collectGlobalStatements(AbstractNode $node)
121
    {
122
        $globalStatements = $node->findChildrenOfType('GlobalStatement');
123
124 6
        foreach ($globalStatements as $globalStatement) {
125
            foreach ($globalStatement->getChildren() as $variable) {
126
                $this->addVariableDefinition($variable);
127
            }
128
        }
129
    }
130
131
    /**
132
     * Stores the given literal node in an catch of found variables.
133 6
     *
134
     * @param \PHPMD\Node\AbstractCallableNode $node
135 6
     * @return void
136
     */
137 6 View Code Duplication
    protected function collectExceptionCatches(AbstractCallableNode $node)
138
    {
139
        $catchStatements = $node->findChildrenOfType('CatchStatement');
140
141
        foreach ($catchStatements as $catchStatement) {
142
            foreach ($catchStatement->getChildren() as $children) {
143
                if ($children instanceof ASTVariable) {
144 6
                    $this->addVariableDefinition($children);
145
                }
146
            }
147
        }
148
    }
149
150
    /**
151
     * Stores the given literal node in an internal list of found variables.
152 6
     *
153
     * @param \PHPMD\Node\AbstractCallableNode $node
154 6
     * @return void
155
     */
156 6 View Code Duplication
    protected function collectListExpressions(AbstractCallableNode $node)
157
    {
158
        $lists = $node->findChildrenOfType('ListExpression');
159 6
160
        foreach ($lists as $listExpression) {
161
            foreach ($listExpression->getChildren() as $variable) {
162
                $this->addVariableDefinition($variable);
163
            }
164
        }
165
    }
166
167
    /**
168 6
     * Stores the given literal node in an internal foreach of found variables.
169
     *
170 6
     * @param \PHPMD\Node\AbstractCallableNode $node
171
     * @return void
172
     */
173
    protected function collectForeachStatements(AbstractCallableNode $node)
174
    {
175
        $foreachStatements = $node->findChildrenOfType('ForeachStatement');
176
177
        foreach ($foreachStatements as $foreachStatement) {
178
            foreach ($foreachStatement->getChildren() as $children) {
179 6
                if ($children instanceof ASTVariable) {
180
                    $this->addVariableDefinition($children);
181
                } elseif ($children instanceof ASTUnaryExpression) {
182 6
                    foreach ($children->getChildren() as $refChildren) {
183
                        if ($refChildren instanceof ASTVariable) {
184
                            $this->addVariableDefinition($refChildren);
185 6
                        }
186
                    }
187 6
                }
188
            }
189
        }
190 6
    }
191
192
    /**
193
     * Stores the given literal node in an internal closure of found variables.
194
     *
195
     * @param \PHPMD\Node\AbstractCallableNode $node
196
     * @return void
197
     */
198 6
    protected function collectClosureParameters(AbstractCallableNode $node)
199
    {
200 6
        $closures = $node->findChildrenOfType('Closure');
201 3
202
        foreach ($closures as $closure) {
203 3
            $this->collectParameters($closure);
204
        }
205 6
    }
206
207
    /**
208
     * Check if the given variable was defined in the current context before usage.
209
     *
210
     * @param \PHPMD\Node\ASTNode $variable
211
     * @param \PHPMD\Node\AbstractCallableNode $parentNode
212
     * @return bool
213 6
     */
214
    protected function checkVariableDefined(ASTNode $variable, AbstractCallableNode $parentNode)
215 6
    {
216
        $image = $this->getVariableImage($variable);
217 6
218 1
        return isset($this->images[$image]) || $this->isNameAllowedInContext($parentNode, $variable);
219 1
    }
220
221
    /**
222
     * Collect parameter names of method/function.
223
     *
224 6
     * @param \PHPMD\Node\AbstractNode $node
225
     * @return void
226
     */
227
    protected function collectParameters(AbstractNode $node)
228
    {
229
        // Get formal parameter container
230
        $parameters = $node->getFirstChildOfType('FormalParameters');
231
232 4
        // Now get all declarators in the formal parameters container
233
        $declarators = $parameters->findChildrenOfType('VariableDeclarator');
234 4
235 4
        foreach ($declarators as $declarator) {
236
            $this->addVariableDefinition($declarator);
237 4
        }
238
    }
239
240
    /**
241
     * Collect assignments of variables.
242
     *
243
     * @param \PHPMD\Node\AbstractCallableNode $node
244
     * @return void
245
     */
246
    protected function collectAssignments(AbstractCallableNode $node)
247 5
    {
248
        foreach ($node->findChildrenOfType('AssignmentExpression') as $assignment) {
249
            $variable = $assignment->getChild(0);
250 5
251 5
            if ($variable->getNode() instanceof ASTArray) {
252 5
                foreach ($variable->findChildrenOfTypeVariable() as $unpackedVariable) {
253
                    $this->addVariableDefinition($unpackedVariable);
254
                }
255
256
                continue;
257
            }
258
259
            $this->addVariableDefinition($variable);
260
        }
261
    }
262
263
    /**
264
     * Collect postfix property.
265
     *
266
     * @param \PHPMD\Node\AbstractNode $node
267
     * @return void
268
     */
269 View Code Duplication
    protected function collectPropertyPostfix(AbstractNode $node)
270
    {
271
        $properties = $node->findChildrenOfType('PropertyPostfix');
272
273
        foreach ($properties as $property) {
274
            foreach ($property->getChildren() as $children) {
275
                if ($children instanceof ASTVariable) {
276
                    $this->addVariableDefinition($children);
277
                }
278
            }
279
        }
280
    }
281
282
    /**
283
     * Add the variable to images.
284
     *
285
     * @param ASTVariable|ASTPropertyPostfix|ASTVariableDeclarator $variable
286
     * @return void
287
     */
288
    protected function addVariableDefinition($variable)
289
    {
290
        $image = $this->getVariableImage($variable);
291
292
        if (!isset($this->images[$image])) {
293
            $this->images[$image] = $variable;
294
        }
295
    }
296
297
    /**
298
     * Checks if a short name is acceptable in the current context.
299
     *
300
     * @param \PHPMD\Node\AbstractCallableNode $node
301
     * @param \PHPMD\Node\ASTNode $variable
302
     *
303
     * @return boolean
304
     */
305
    protected function isNameAllowedInContext(AbstractCallableNode $node, ASTNode $variable)
306
    {
307
        return (
308
            $node instanceof MethodNode &&
309
            $variable->getImage() === '$this' &&
310
            ($node->getModifiers() & State::IS_STATIC) === 0
311
        );
312
    }
313
}
314