UnusedFormalParameter::removeUsedParameters()   B
last analyzed

Complexity

Conditions 10
Paths 84

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 10

Importance

Changes 0
Metric Value
cc 10
nc 84
nop 1
dl 0
loc 38
ccs 20
cts 20
cp 1
crap 10
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
    private $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)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->isMagicMethod($node) of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
52 1
            return;
53
        }
54
55 30
        if ($this->isInheritedSignature($node)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->isInheritedSignature($node) of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
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
        $exceptions = $this->getExceptionsList();
69
70 25
        foreach ($this->nodes as $node) {
71 7
            if (in_array(substr($node->getImage(), 1), $exceptions)) {
72
                continue;
73
            }
74 7
            $this->addViolation($node, array($node->getImage()));
75
        }
76 25
    }
77
78
    /**
79
     * Gets array of exceptions from property
80
     *
81
     * @return array
82
     */
83 25 View Code Duplication
    private function getExceptionsList()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
84
    {
85
        try {
86 25
            $exceptions = $this->getStringProperty('exceptions');
87 23
        } catch (\OutOfBoundsException $e) {
88 23
            $exceptions = '';
89
        }
90
91 25
        return explode(',', $exceptions);
92
    }
93
94
    /**
95
     * Returns <b>true</b> when the given node is an abstract method.
96
     *
97
     * @param \PHPMD\AbstractNode $node
98
     * @return boolean
99
     */
100 33
    private function isAbstractMethod(AbstractNode $node)
101
    {
102 33
        if ($node instanceof MethodNode) {
103 30
            return $node->isAbstract();
104
        }
105 3
        return false;
106
    }
107
108
    /**
109
     * Returns <b>true</b> when the given node is method with signature declared as inherited using
110
     * {@inheritdoc} annotation.
111
     *
112
     * @param \PHPMD\AbstractNode $node
113
     * @return boolean
114
     */
115 30
    private function isInheritedSignature(AbstractNode $node)
116
    {
117 30
        if ($node instanceof MethodNode) {
118 27
            return preg_match('/\@inheritdoc/i', $node->getDocComment());
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...
119
        }
120 3
        return false;
121
    }
122
123
    /**
124
     * Returns <b>true</b> when the given node is a magic method signature
125
     * @param AbstractNode $node
126
     * @return boolean
127
     */
128 31
    private function isMagicMethod(AbstractNode $node)
129
    {
130 31
        static $names = array(
131
                'call',
132
                'callStatic',
133
                'get',
134
                'set',
135
                'isset',
136
                'unset',
137
                'set_state'
138
        );
139
140 31
        if ($node instanceof MethodNode) {
141 28
            return preg_match('/\__(?:' . implode("|", $names) . ')/i', $node->getName());
142
        }
143 3
        return false;
144
    }
145
146
    /**
147
     * Tests if the given <b>$node</b> is a method and if this method is also
148
     * the initial declaration.
149
     *
150
     * @param \PHPMD\AbstractNode $node
151
     * @return boolean
152
     * @since 1.2.1
153
     */
154 28
    private function isNotDeclaration(AbstractNode $node)
155
    {
156 28
        if ($node instanceof MethodNode) {
157 25
            return !$node->isDeclaration();
158
        }
159 3
        return false;
160
    }
161
162
    /**
163
     * This method extracts all parameters for the given function or method node
164
     * and it stores the parameter images in the <b>$_images</b> property.
165
     *
166
     * @param \PHPMD\AbstractNode $node
167
     * @return void
168
     */
169 25
    private function collectParameters(AbstractNode $node)
170
    {
171
        // First collect the formal parameters container
172 25
        $parameters = $node->getFirstChildOfType('FormalParameters');
173
174
        // Now get all declarators in the formal parameters container
175 25
        $declarators = $parameters->findChildrenOfType('VariableDeclarator');
176
177 25
        foreach ($declarators as $declarator) {
178 25
            $this->nodes[$declarator->getImage()] = $declarator;
179
        }
180 25
    }
181
182
    /**
183
     * This method collects all local variables in the body of the currently
184
     * analyzed method or function and removes those parameters that are
185
     * referenced by one of the collected variables.
186
     *
187
     * @param \PHPMD\AbstractNode $node
188
     * @return void
189
     */
190 25
    private function removeUsedParameters(AbstractNode $node)
191
    {
192 25
        $variables = $node->findChildrenOfType('Variable');
193 25
        foreach ($variables as $variable) {
194
            /** @var $variable ASTNode */
195 15
            if ($this->isRegularVariable($variable)) {
196 15
                unset($this->nodes[$variable->getImage()]);
197
            }
198
        }
199
200 25
        $compoundVariables = $node->findChildrenOfType('CompoundVariable');
201 25
        foreach ($compoundVariables as $compoundVariable) {
202 5
            $variablePrefix = $compoundVariable->getImage();
203
204 5
            foreach ($compoundVariable->findChildrenOfType('Expression') as $child) {
205 5
                $variableImage = $variablePrefix . $child->getImage();
206
207 5
                if (isset($this->nodes[$variableImage])) {
208 5
                    unset($this->nodes[$variableImage]);
209
                }
210
            }
211
        }
212
213
        /* If the method calls func_get_args() then all parameters are
214
         * automatically referenced */
215 25
        $functionCalls = $node->findChildrenOfType('FunctionPostfix');
216 25
        foreach ($functionCalls as $functionCall) {
217 8
            if ($this->isFunctionNameEqual($functionCall, 'func_get_args')) {
218 2
                $this->nodes = array();
219
            }
220
221 8
            if ($this->isFunctionNameEndingWith($functionCall, 'compact')) {
222 6
                foreach ($functionCall->findChildrenOfType('Literal') as $literal) {
223 8
                    unset($this->nodes['$' . trim($literal->getImage(), '"\'')]);
224
                }
225
            }
226
        }
227 25
    }
228
}
229