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

php/PHPMD/Rule/CleanCode/DuplicatedArrayKey.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\CleanCode;
19
20
use PDepend\Source\AST\AbstractASTNode;
21
use PDepend\Source\AST\ASTLiteral;
22
use PDepend\Source\AST\ASTNode as PDependASTNode;
23
use PHPMD\AbstractNode;
24
use PHPMD\AbstractRule;
25
use PHPMD\Node\ASTNode;
26
use PHPMD\Rule\FunctionAware;
27
use PHPMD\Rule\MethodAware;
28
29
/**
30
 * Duplicated Array Key Rule
31
 *
32
 * This rule detects duplicated array keys.
33
 *
34
 * @author Rafał Wrzeszcz <[email protected]>
35
 * @author Kamil Szymanaski <[email protected]>
36
 */
37
class DuplicatedArrayKey extends AbstractRule implements MethodAware, FunctionAware
38
{
39
    /**
40
     * Retrieves all arrays from single node and performs comparison logic on it
41
     *
42
     * @param AbstractNode $node
43
     * @return void
44
     */
45
    public function apply(AbstractNode $node)
46 17
    {
47
        foreach ($node->findChildrenOfType('Array') as $arrayNode) {
48 17
            /** @var ASTNode $arrayNode */
49
            $this->checkForDuplicatedArrayKeys($arrayNode);
50 15
        }
51
    }
52 17
53
    /**
54
     * This method checks if a given function or method contains an array literal
55
     * with duplicated entries for any key and emits a rule violation if so.
56
     *
57
     * @param ASTNode $node Array node.
58
     * @return void
59
     */
60
    protected function checkForDuplicatedArrayKeys(ASTNode $node)
61 15
    {
62
        $keys = array();
63 15
        /** @var ASTArrayElement $arrayElement */
64
        foreach ($node->getChildren() as $index => $arrayElement) {
0 ignored issues
show
Documentation Bug introduced by
The method getChildren 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 15
            $arrayElement = $this->normalizeKey($arrayElement, $index);
66 15
            if (null === $arrayElement) {
67 15
                // skip everything that can't be resolved easily
68
                continue;
69 4
            }
70
71
            $key = $arrayElement->getImage();
72 13
            if (isset($keys[$key])) {
73 13
                $this->addViolation($node, array($key, $arrayElement->getStartLine()));
74 11
                continue;
75 11
            }
76
            $keys[$key] = $arrayElement;
77 13
        }
78
    }
79 15
80
    /**
81
     * Changes key name to its string format.
82
     *
83
     * To compare keys, we have to cast them to string.
84
     * Non-associative keys have to use index as its key,
85
     * while boolean and nulls have to be casted respectively.
86
     * As current logic doesn't evaluate expressions nor constants,
87
     * statics, globals, etc. we simply skip them.
88
     *
89
     * @param AbstractASTNode $node Array key to evaluate.
90
     * @param int $index Fallback in case of non-associative arrays
91
     * @return AbstractASTNode Key name
92
     */
93
    protected function normalizeKey(AbstractASTNode $node, $index)
94 15
    {
95
        $childCount = count($node->getChildren());
96 15
        // Skip, if there is no array key, just an array value
97
        if ($childCount === 1) {
98 15
            return null;
99 4
        }
100
        // non-associative - key name equals to its index
101
        if ($childCount === 0) {
102 13
            $node->setImage((string)$index);
103
104
            return $node;
105
        }
106
107 13
        $node = $node->getChild(0);
108 13
        if (!($node instanceof ASTLiteral)) {
109
            // skip expressions, method calls, globals and constants
110 1
            return null;
111
        }
112 13
        $node->setImage($this->castStringFromLiteral($node));
113
114 13
        return $node;
115
    }
116
117
    /**
118
     * Cleans string literals and casts boolean and null values as PHP engine does
119
     *
120
     * @param PDependASTNode $key
121
     * @return string
122
     */
123 13
    protected function castStringFromLiteral(PDependASTNode $key)
124
    {
125 13
        $value = $key->getImage();
126 13
        switch ($value) {
127 13
            case 'false':
128 1
                return '0';
129 13
            case 'true':
130 1
                return '1';
131 13
            case 'null':
132 1
                return '';
133
            default:
134 13
                return trim($value, '\'""');
135
        }
136
    }
137
}
138