Passed
Push — master ( 902a34...c826a7 )
by Kyle
53s queued 11s
created

src/main/php/PHPMD/Rule/UnusedFormalParameter.php (1 issue)

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;
19
20
use PHPMD\AbstractNode;
21
use PHPMD\Node\ASTNode;
22
use PHPMD\Node\MethodNode;
23
24
/**
25
 * This rule collects all formal parameters of a given function or method that
26
 * are not used in a statement of the artifact's body.
27
 */
28
class UnusedFormalParameter extends AbstractLocalVariable implements FunctionAware, MethodAware
29
{
30
    /**
31
     * Collected ast nodes.
32
     *
33
     * @var \PHPMD\Node\ASTNode[]
34
     */
35
    protected $nodes = array();
36
37
    /**
38
     * This method checks that all parameters of a given function or method are
39
     * used at least one time within the artifacts body.
40
     *
41
     * @param \PHPMD\AbstractNode $node
42
     * @return void
43
     */
44 33
    public function apply(AbstractNode $node)
45
    {
46 33
        if ($this->isAbstractMethod($node)) {
47 2
            return;
48
        }
49
50
        // Magic methods should be ignored as invalid declarations are picked up by PHP.
51 31
        if ($this->isMagicMethod($node)) {
52 1
            return;
53
        }
54
55 30
        if ($this->isInheritedSignature($node)) {
56 2
            return;
57
        }
58
59 28
        if ($this->isNotDeclaration($node)) {
60 3
            return;
61
        }
62
63 25
        $this->nodes = array();
64
65 25
        $this->collectParameters($node);
66 25
        $this->removeUsedParameters($node);
67
68 25
        foreach ($this->nodes as $node) {
69 7
            $this->addViolation($node, array($node->getImage()));
70
        }
71 25
    }
72
73
    /**
74
     * Returns <b>true</b> when the given node is an abstract method.
75
     *
76
     * @param \PHPMD\AbstractNode $node
77
     * @return boolean
78
     */
79 33
    protected function isAbstractMethod(AbstractNode $node)
80
    {
81 33
        if ($node instanceof MethodNode) {
82 30
            return $node->isAbstract();
83
        }
84 3
85
        return false;
86
    }
87
88
    /**
89
     * Returns <b>true</b> when the given node is method with signature declared as inherited using
90
     * {@inheritdoc} annotation.
91
     *
92
     * @param \PHPMD\AbstractNode $node
93
     * @return boolean
94 30
     */
95
    protected function isInheritedSignature(AbstractNode $node)
96 30
    {
97 27
        if ($node instanceof MethodNode) {
98
            return preg_match('/@inheritdoc/i', $node->getDocComment()) === 1;
0 ignored issues
show
Documentation Bug introduced by
The method getDocComment does not exist on object<PHPMD\Node\MethodNode>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
99 3
        }
100
101
        return false;
102
    }
103
104
    /**
105
     * Returns <b>true</b> when the given node is a magic method signature
106
     *
107 31
     * @param AbstractNode $node
108
     * @return boolean
109 31
     */
110
    protected function isMagicMethod(AbstractNode $node)
111
    {
112
        if (!($node instanceof MethodNode)) {
113
            return false;
114
        }
115
116
        static $magicMethodRegExp = null;
117
118
        if ($magicMethodRegExp === null) {
119 31
            $magicMethodRegExp = '/__(?:' . implode("|", array(
120 28
                    'call',
121
                    'callStatic',
122 3
                    'get',
123
                    'set',
124
                    'isset',
125
                    'unset',
126
                    'set_state',
127
                )) . ')/i';
128
        }
129
130
        return preg_match($magicMethodRegExp, $node->getName()) === 1;
131
    }
132
133 28
    /**
134
     * Tests if the given <b>$node</b> is a method and if this method is also
135 28
     * the initial declaration.
136 25
     *
137
     * @param \PHPMD\AbstractNode $node
138 3
     * @return boolean
139
     * @since 1.2.1
140
     */
141
    protected function isNotDeclaration(AbstractNode $node)
142
    {
143
        if ($node instanceof MethodNode) {
144
            return !$node->isDeclaration();
145
        }
146
147
        return false;
148 25
    }
149
150
    /**
151 25
     * This method extracts all parameters for the given function or method node
152
     * and it stores the parameter images in the <b>$_images</b> property.
153
     *
154 25
     * @param \PHPMD\AbstractNode $node
155
     * @return void
156 25
     */
157 25
    protected function collectParameters(AbstractNode $node)
158
    {
159 25
        // First collect the formal parameters containers
160
        foreach ($node->findChildrenOfType('FormalParameters') as $parameters) {
161
            // Now get all declarators in the formal parameters container
162
            $declarators = $parameters->findChildrenOfType('VariableDeclarator');
163
164
            foreach ($declarators as $declarator) {
165
                $this->nodes[$declarator->getImage()] = $declarator;
166
            }
167
        }
168
    }
169 25
170
    /**
171 25
     * This method collects all local variables in the body of the currently
172 25
     * analyzed method or function and removes those parameters that are
173 25
     * referenced by one of the collected variables.
174 25
     *
175
     * @param \PHPMD\AbstractNode $node
176
     * @return void
177
     */
178
    protected function removeUsedParameters(AbstractNode $node)
179
    {
180
        $this->removeRegularVariables($node);
181
        $this->removeCompoundVariables($node);
182 25
        $this->removeVariablesUsedByFuncGetArgs($node);
183
    }
184 25
185 25
    /**
186
     * Removes all the regular variables from a given node
187 15
     *
188 14
     * @param \PHPMD\AbstractNode $node The node to remove the regular variables from.
189
     * @return void
190
     */
191 25
    protected function removeRegularVariables(AbstractNode $node)
192
    {
193
        $variables = $node->findChildrenOfType('Variable');
194
195
        foreach ($variables as $variable) {
196
            /** @var $variable ASTNode */
197
            if ($this->isRegularVariable($variable)) {
198
                unset($this->nodes[$variable->getImage()]);
199
            }
200
        }
201
    }
202
203
    /**
204
     * Removes all the compound variables from a given node
205
     *
206
     * Such as
207
     *
208
     * <code>
209
     * // ------
210
     * Foo::${BAR}();
211 25
     * // ------
212
     *
213 25
     * // ------
214 25
     * Foo::$${BAR}();
215 5
     * // ------
216
     * </code>
217 5
     *
218 5
     * @param \PHPMD\AbstractNode $node The node to remove the compound variables from.
219
     * @return void
220 5
     */
221 2
    protected function removeCompoundVariables(AbstractNode $node)
222
    {
223
        $compoundVariables = $node->findChildrenOfType('CompoundVariable');
224
225 25
        foreach ($compoundVariables as $compoundVariable) {
226
            $variablePrefix = $compoundVariable->getImage();
227
228
            foreach ($compoundVariable->findChildrenOfType('Expression') as $child) {
229
                $variableImage = $variablePrefix . $child->getImage();
230
231
                if (isset($this->nodes[$variableImage])) {
232
                    unset($this->nodes[$variableImage]);
233
                }
234
            }
235 25
        }
236
    }
237 25
238 25
    /**
239 8
     * Removes all the variables from a given node, if func_get_args() is called within
240 2
     *
241
     * If the given method calls func_get_args() then all parameters are automatically referenced.
242
     *
243 8
     * @param \PHPMD\AbstractNode $node The node to remove the referneced variables from.
244 6
     * @return void
245 6
     */
246
    protected function removeVariablesUsedByFuncGetArgs(AbstractNode $node)
247
    {
248
        $functionCalls = $node->findChildrenOfType('FunctionPostfix');
249 25
250
        foreach ($functionCalls as $functionCall) {
251
            if ($this->isFunctionNameEqual($functionCall, 'func_get_args')) {
252
                $this->nodes = array();
253
            }
254
255
            if ($this->isFunctionNameEndingWith($functionCall, 'compact')) {
256
                foreach ($functionCall->findChildrenOfType('Literal') as $literal) {
257
                    unset($this->nodes['$' . trim($literal->getImage(), '"\'')]);
258
                }
259
            }
260
        }
261
    }
262
}
263