isWrappedByIndexExpression()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
ccs 3
cts 3
cp 1
crap 2
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\AbstractRule;
22
use PHPMD\Node\ASTNode;
23
24
/**
25
 * Base class for rules that rely on local variables.
26
 *
27
 * @since     0.2.6
28
 */
29
abstract class AbstractLocalVariable extends AbstractRule
30
{
31
    /**
32
     * PHP super globals that are available in all php scopes, so that they
33
     * can never be unused local variables.
34
     *
35
     * @var array(string=>boolean)
36
     * @link http://php.net/manual/en/reserved.variables.php
37
     */
38
    private static $superGlobals = array(
39
        '$argc' => true,
40
        '$argv' => true,
41
        '$_COOKIE' => true,
42
        '$_ENV' => true,
43
        '$_FILES' => true,
44
        '$_GET' => true,
45
        '$_POST' => true,
46
        '$_REQUEST' => true,
47
        '$_SERVER' => true,
48
        '$_SESSION' => true,
49
        '$GLOBALS' => true,
50
        '$HTTP_RAW_POST_DATA' => true,
51
        '$php_errormsg' => true,
52
        '$http_response_header' => true,
53
    );
54
55
    /**
56
     * Tests if the given variable node represents a local variable or if it is
57
     * a static object property or something similar.
58
     *
59
     * @param \PHPMD\Node\ASTNode $variable The variable to check.
60
     * @return boolean
61
     */
62 43
    protected function isLocal(ASTNode $variable)
63
    {
64 43
        return (false === $variable->isThis()
0 ignored issues
show
Documentation Bug introduced by
The method isThis does not exist on object<PHPMD\Node\ASTNode>? 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...
65 43
            && $this->isNotSuperGlobal($variable)
66 43
            && $this->isRegularVariable($variable)
67
        );
68
    }
69
70
    /**
71
     * Tests if the given variable represents one of the PHP super globals
72
     * that are available in scopes.
73
     *
74
     * @param \PHPMD\AbstractNode $variable
75
     * @return boolean
76
     */
77 42
    protected function isNotSuperGlobal(AbstractNode $variable)
78
    {
79 42
        return !isset(self::$superGlobals[$variable->getImage()]);
80
    }
81
82
    /**
83
     * Tests if the given variable node is a regular variable an not property
84
     * or method postfix.
85
     *
86
     * @param \PHPMD\Node\ASTNode $variable
87
     * @return boolean
88
     */
89 43
    protected function isRegularVariable(ASTNode $variable)
90
    {
91 43
        $node   = $this->stripWrappedIndexExpression($variable);
92 43
        $parent = $node->getParent();
93
94 43
        if ($parent->isInstanceOf('PropertyPostfix')) {
95 17
            $primaryPrefix = $parent->getParent();
96 17
            if ($primaryPrefix->getParent()->isInstanceOf('MemberPrimaryPrefix')) {
97 1
                return !$primaryPrefix->getParent()->isStatic();
98
            }
99 16
            return ($parent->getChild(0)->getNode() !== $node->getNode()
100 16
                || !$primaryPrefix->isStatic()
101
            );
102
        }
103 39
        return true;
104
    }
105
106
    /**
107
     * Removes all index expressions that are wrapped around the given node
108
     * instance.
109
     *
110
     * @param \PHPMD\Node\ASTNode $node
111
     * @return \PHPMD\Node\ASTNode
112
     */
113 43
    protected function stripWrappedIndexExpression(ASTNode $node)
114
    {
115 43
        if (false === $this->isWrappedByIndexExpression($node)) {
116 43
            return $node;
117
        }
118
119 14
        $parent = $node->getParent();
120 14
        if ($parent->getChild(0)->getNode() === $node->getNode()) {
121 14
            return $this->stripWrappedIndexExpression($parent);
0 ignored issues
show
Bug introduced by
It seems like $parent defined by $node->getParent() on line 119 can be null; however, PHPMD\Rule\AbstractLocal...rappedIndexExpression() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
122
        }
123 10
        return $node;
124
    }
125
126
    /**
127
     * Tests if the given variable node os part of an index expression.
128
     *
129
     * @param \PHPMD\Node\ASTNode $node
130
     * @return boolean
131
     */
132 43
    protected function isWrappedByIndexExpression(ASTNode $node)
133
    {
134 43
        return ($node->getParent()->isInstanceOf('ArrayIndexExpression')
135 43
            || $node->getParent()->isInstanceOf('StringIndexExpression')
136
        );
137
    }
138
139
    /**
140
     * PHP is case insensitive so we should compare function names case
141
     * insensitive.
142
     *
143
     * @param \PHPMD\AbstractNode $node
144
     * @param string $name
145
     * @return boolean
146
     */
147 8
    protected function isFunctionNameEqual(AbstractNode $node, $name)
148
    {
149 8
        return (0 === strcasecmp(trim($node->getImage(), '\\'), $name));
150
    }
151
152
    /**
153
     * AST puts namespace prefix to global functions called from a namespace.
154
     * This method checks if the last part of function fully qualified name is equal to $name
155
     *
156
     * @param \PHPMD\AbstractNode $node
157
     * @param string $name
158
     * @return boolean
159
     */
160 12
    protected function isFunctionNameEndingWith(AbstractNode $node, $name)
161
    {
162 12
        $parts = explode('\\', trim($node->getImage(), '\\'));
163
164 12
        return (0 === strcasecmp(array_pop($parts), $name));
165
    }
166
}
167