Php54Features   B
last analyzed

Complexity

Total Complexity 48

Size/Duplication

Total Lines 192
Duplicated Lines 9.38 %

Coupling/Cohesion

Components 1
Dependencies 14

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 48
c 3
b 0
f 0
lcom 1
cbo 14
dl 18
loc 192
ccs 118
cts 118
cp 1
rs 8.4864

12 Methods

Rating   Name   Duplication   Size   Complexity  
A enterNode() 0 18 3
A leaveNode() 0 6 2
A addThisInClosureRequirement() 0 4 1
A handleTraitFeatures() 0 19 4
B handleFunctionDereferencing() 0 12 5
B handleCallableType() 15 18 7
A handleInstantClassMemberAccess() 0 9 3
B handleThisInClosure() 0 16 10
A handleBinaryNumberDeclaration() 0 13 4
A handleShortArrayDeclaration() 0 6 3
A handleStaticCallByExpressionSyntax() 3 6 3
A detectShortEchoSyntax() 0 6 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Php54Features often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Php54Features, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Php54Features.php
4
 *
5
 * MIT LICENSE
6
 *
7
 * LICENSE: This source file is subject to the MIT license.
8
 * A copy of the licenses text was distributed alongside this
9
 * file (usually the repository or package root). The text can also
10
 * be obtained through one of the following sources:
11
 * * http://opensource.org/licenses/MIT
12
 * * https://github.com/suralc/pvra/blob/master/LICENSE
13
 *
14
 * @author     suralc <[email protected]>
15
 * @license    http://opensource.org/licenses/MIT  MIT
16
 */
17
namespace Pvra\Analysers;
18
19
20
use PhpParser\Node;
21
use Pvra\AnalyserAwareInterface;
22
use Pvra\Result\Reason;
23
24
/**
25
 * Class Php54Features
26
 *
27
 * This class can be used to detect php 5.4 features.
28
 * Following features are supported:
29
 * * Trait definition using the `trait` keyword
30
 * * Trait using syntax
31
 * * Magic trait constant: `__TRAIT__`
32
 * * Array function dereferencing
33
 * * Callable typhint: `callable`
34
 * * Detection of `$this` in closures
35
 *
36
 * @package Pvra\PhpParser\Analysers
37
 */
38
class Php54Features extends LanguageFeatureAnalyser implements AnalyserAwareInterface
39
{
40
    /**
41
     * Closure nesting level
42
     *
43
     * @var int
44
     */
45
    private $inClosureLevel = 0;
46
47
    /**
48
     * @inheritdoc
49
     */
50 46
    public function enterNode(Node $node)
51
    {
52 46
        if ($node instanceof Node\Expr\Closure) {
53 26
            $this->inClosureLevel++;
54 13
        }
55
56 46
        if ($this->mode & self::MODE_ADDITION) {
57 44
            $this->handleTraitFeatures($node);
58 44
            $this->handleFunctionDereferencing($node);
59 44
            $this->handleCallableType($node);
60 44
            $this->handleInstantClassMemberAccess($node);
61 44
            $this->handleThisInClosure($node);
62 44
            $this->handleBinaryNumberDeclaration($node);
63 42
            $this->handleShortArrayDeclaration($node);
64 42
            $this->handleStaticCallByExpressionSyntax($node);
65 42
            $this->detectShortEchoSyntax($node);
66 21
        }
67 44
    }
68
69
    /**
70
     * Leave node
71
     *
72
     * Currently only used to decrease closure level.
73
     *
74
     * @param \PhpParser\Node $node
75
     * @return void
76
     * @see Php54LanguageFeatureNodeWalker::$inClosureLevel Closure Level
77
     */
78 44
    public function leaveNode(Node $node)
79
    {
80 44
        if ($node instanceof Node\Expr\Closure) {
81 26
            $this->inClosureLevel--;
82 13
        }
83 44
    }
84
85
    /**
86
     * Wrapper for message addition in closure-this context
87
     *
88
     * @param \PhpParser\Node $node
89
     */
90 22
    private function addThisInClosureRequirement(Node $node)
91
    {
92 22
        $this->getResult()->addRequirement(Reason::THIS_IN_CLOSURE, $node->getLine());
93 22
    }
94
95
    /**
96
     * @param \PhpParser\Node $node
97
     */
98 44
    private function handleTraitFeatures(Node $node)
99
    {
100 44
        if ($node instanceof Node\Stmt\Trait_) {
101 20
            $this->getResult()->addRequirement(
102 20
                Reason::TRAIT_DEFINITION,
103 20
                $node->getLine()
104 10
            );
105 23
        } elseif ($node instanceof Node\Stmt\TraitUse) {
106 18
            $this->getResult()->addRequirement(
107 18
                Reason::TRAIT_USE,
108 18
                $node->getLine()
109 9
            );
110 23
        } elseif ($node instanceof Node\Scalar\MagicConst\Trait_) {
111 18
            $this->getResult()->addRequirement(
112 18
                Reason::TRAIT_MAGIC_CONST,
113 18
                $node->getLine()
114 9
            );
115 9
        }
116 44
    }
117
118
    /**
119
     * @param \PhpParser\Node $node
120
     */
121 44
    private function handleFunctionDereferencing(Node $node)
122
    {
123 44
        if ($node instanceof Node\Expr\ArrayDimFetch && ($node->var instanceof Node\Expr\FuncCall
124 30
                || $node->var instanceof Node\Expr\MethodCall
125 37
                || $node->var instanceof Node\Expr\StaticCall)
126 22
        ) {
127 22
            $this->getResult()->addRequirement(
128 22
                Reason::ARRAY_FUNCTION_DEREFERENCING,
129 22
                $node->getLine()
130 11
            );
131 11
        }
132 44
    }
133
134
    /**
135
     * @param \PhpParser\Node $node
136
     */
137 44
    private function handleCallableType(Node $node)
138
    {
139 22 View Code Duplication
        if (($node instanceof Node\Stmt\Function_
140 44
                || $node instanceof Node\Stmt\ClassMethod
141 44
                || $node instanceof Node\Expr\Closure)
142 44
            && !empty($node->params)
143 22
        ) {
144 32
            foreach ($node->params as $param) {
145 32
                if ((string)$param->type === 'callable') {
146 22
                    $this->getResult()->addRequirement(
147 22
                        Reason::TYPEHINT_CALLABLE,
148 27
                        $param->getLine()
149 11
                    );
150 11
                }
151 16
            }
152
153 16
        }
154 44
    }
155
156
    /**
157
     * @param \PhpParser\Node $node
158
     */
159 44
    private function handleInstantClassMemberAccess(Node $node)
160
    {
161 44
        if ($node instanceof Node\Expr\MethodCall && $node->var instanceof Node\Expr\New_) {
162 22
            $this->getResult()->addRequirement(
163 22
                Reason::INSTANT_CLASS_MEMBER_ACCESS,
164 22
                $node->getLine()
165 11
            );
166 11
        }
167 44
    }
168
169
    /**
170
     * @param \PhpParser\Node $node
171
     */
172 44
    private function handleThisInClosure(Node $node)
173
    {
174 44
        if ($this->inClosureLevel > 0) {
175 24
            if (($node instanceof Node\Expr\PropertyFetch || $node instanceof Node\Expr\ArrayDimFetch || $node instanceof Node\Expr\MethodCall)
176 24
                && $node->var instanceof Node\Expr\Variable
177 24
                && $node->var->name === 'this'
178 12
            ) {
179 22
                $this->addThisInClosureRequirement($node->var);
180 23
            } elseif ($node instanceof Node\Expr\FuncCall
181 24
                && $node->name instanceof Node\Expr\Variable
182 24
                && $node->name->name === 'this'
183 12
            ) {
184 2
                $this->addThisInClosureRequirement($node->name);
185 1
            }
186 12
        }
187 44
    }
188
189
    /**
190
     * @param \PhpParser\Node $node
191
     */
192 44
    private function handleBinaryNumberDeclaration(Node $node)
193
    {
194 44
        if ($node instanceof Node\Scalar\LNumber) {
195 32
            if ($node->hasAttribute('originalValue')) {
196 30
                if (stripos($node->getAttribute('originalValue'), '0b') !== false) {
197 24
                    $this->getResult()->addRequirement(Reason::BINARY_NUMBER_DECLARATION, $node->getLine());
198 9
                }
199 15
            } else {
200 2
                throw new \InvalidArgumentException('Node attribute ("originalValue") missing. Only nodes generated by ExtendedEmulativeLexer '
201 2
                    .'or one of its children are supported.');
202
            }
203 15
        }
204 42
    }
205
206
    /**
207
     * @param \PhpParser\Node $node
208
     */
209 42
    private function handleShortArrayDeclaration(Node $node)
210
    {
211 42
        if ($node instanceof Node\Expr\Array_ && $node->getAttribute('traditionalArray', false) !== true) {
212 28
            $this->getResult()->addRequirement(Reason::SHORT_ARRAY_DECLARATION, $node->getLine());
213 14
        }
214 42
    }
215
216 42
    private function handleStaticCallByExpressionSyntax(Node $node)
217
    {
218 42 View Code Duplication
        if ($node instanceof Node\Expr\StaticCall && $node->name instanceof Node\Expr) {
219 18
            $this->getResult()->addRequirement(Reason::STATIC_CALL_BY_EXPRESSION, $node->getLine());
220 9
        }
221 42
    }
222
223 42
    private function detectShortEchoSyntax(Node $node)
224
    {
225 42
        if ($node instanceof Node\Stmt\Echo_ && $node->hasAttribute('isShortEchoTag')) {
226 2
            $this->getResult()->addRequirement(Reason::SHORT_ECHO_TAG, $node->getLine());
227 1
        }
228 42
    }
229
}
230