Completed
Pull Request — master (#86)
by Scott
01:51
created

DetectorVisitor::checkNameContainsLanguage()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
cc 4
nc 5
nop 2
1
<?php
2
3
namespace Povils\PHPMND\Visitor;
4
5
use PhpParser\Node;
6
use PhpParser\Node\Const_;
7
use PhpParser\Node\Expr\UnaryMinus;
8
use PhpParser\Node\Expr\UnaryPlus;
9
use PhpParser\Node\Scalar;
10
use PhpParser\Node\Scalar\DNumber;
11
use PhpParser\Node\Scalar\LNumber;
12
use PhpParser\Node\Scalar\String_;
13
use PhpParser\NodeTraverser;
14
use PhpParser\NodeVisitorAbstract;
15
use Povils\PHPMND\Console\Option;
16
use Povils\PHPMND\Extension\ArrayAwareExtension;
17
use Povils\PHPMND\Extension\Extension;
18
use Povils\PHPMND\Extension\FunctionAwareExtension;
19
use Povils\PHPMND\FileReport;
20
21
/**
22
 * Class DetectorVisitor
23
 *
24
 * @package Povils\PHPMND
25
 */
26
class DetectorVisitor extends NodeVisitorAbstract
27
{
28
    /**
29
     * @var FileReport
30
     */
31
    private $fileReport;
32
33
    /**
34
     * @var Option
35
     */
36
    private $option;
37
38
    public function __construct(FileReport $fileReport, Option $option)
39
    {
40
        $this->fileReport = $fileReport;
41
        $this->option = $option;
42
    }
43
44
    public function enterNode(Node $node): ?int
45
    {
46
        if ($this->isIgnoreableConst($node)) {
47
            if ($this->checkNameContainsLanguage(
48
                $node->name->name,
0 ignored issues
show
Bug introduced by
Accessing name on the interface PhpParser\Node suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
49
                $node->value->value ?? 0
0 ignored issues
show
Bug introduced by
Accessing value on the interface PhpParser\Node suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
50
            )) {
51
                $this->fileReport->addEntry($node->getLine(), $node->value->value);
52
            }
53
54
            return NodeTraverser::DONT_TRAVERSE_CHILDREN;
55
        }
56
57
        /** @var LNumber|DNumber|String_ $scalar */
58
        $scalar = $node;
59
        if ($this->hasSign($scalar) && $this->isNumber($scalar)) {
60
            $node = $scalar->getAttribute('parent');
61
            if ($this->isMinus($node)) {
62
                $scalar->value = -$scalar->value;
63
            }
64
        }
65
66
        if ($this->isNumber($scalar) || $this->isString($scalar)) {
67
68
            if ($this->checkNameContainsLanguage(
69
                $scalar->getAttribute('parent')->var->name ?? '',
70
                $scalar->value
71
            )) {
72
                $this->fileReport->addEntry($node->getLine(), $scalar->value);
73
            }
74
75
            foreach ($this->option->getExtensions() as $extension) {
76
                $extension->setOption($this->option);
77
                if ($extension->extend($node)) {
78
                    $this->fileReport->addEntry($scalar->getLine(), $scalar->value);
79
80
                    return null;
81
                }
82
            }
83
        }
84
85
        return null;
86
    }
87
88
    private function isIgnoreableConst(Node $node): bool
89
    {
90
        return $node instanceof Const_ &&
91
            ($this->isNumber($node->value) || $this->isString($node->value));
92
    }
93
94
    private function isNumber(Node $node): bool
95
    {
96
        $isNumber = (
97
            $node instanceof LNumber ||
98
            $node instanceof DNumber ||
99
            $this->isValidNumeric($node)
100
        );
101
102
        return $isNumber && false === $this->ignoreNumber($node);
103
    }
104
105
    private function isString(Node $node): bool
106
    {
107
        return $this->option->includeStrings() && $node instanceof String_ && false === $this->ignoreString($node);
108
    }
109
110
    private function ignoreNumber(Node $node): bool
111
    {
112
        return in_array($node->value, $this->option->getIgnoreNumbers(), true);
113
    }
114
115
    private function ignoreString(Node $node): bool
116
    {
117
        return in_array($node->value, $this->option->getIgnoreStrings(), true);
118
    }
119
120
    private function hasSign(Node $node): bool
121
    {
122
        return $node->getAttribute('parent') instanceof UnaryMinus
123
            || $node->getAttribute('parent') instanceof UnaryPlus;
124
    }
125
126
    private function isMinus(Node $node): bool
127
    {
128
        return $node instanceof UnaryMinus;
129
    }
130
131
    private function isValidNumeric(Node $node): bool
132
    {
133
        return $this->option->includeNumericStrings() &&
134
        isset($node->value) &&
0 ignored issues
show
Bug introduced by
Accessing value on the interface PhpParser\Node suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
135
        is_numeric($node->value) &&
0 ignored issues
show
Bug introduced by
Accessing value on the interface PhpParser\Node suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
136
        false === $this->ignoreString($node);
137
    }
138
139
    /**
140
     * @param string $name
141
     * @param string|int $value
142
     * @return bool
143
     */
144
    private function checkNameContainsLanguage(string $name, $value): bool
145
    {
146
        foreach ($this->option->checkNaming() as $language) {
147
            $generatedNumbers = $language->parse($value);
148
149
            $regex = '/(?<name>';
150
            foreach ($generatedNumbers as $word) {
151
                $regex .= "(?:{$word}[\s_-]*)?";
152
            }
153
154
            $regex .= ')/i';
155
            preg_match($regex, $name, $matches);
156
157
            if (strlen($matches['name']) > 0) {
158
                return true;
159
            }
160
        }
161
162
        return false;
163
    }
164
}
165