Failed Conditions
Push — test-scrutinizer-coverage ( 1fb662...215aeb )
by Juliette
03:50
created

NewLanguageConstructsSniff   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 280
Duplicated Lines 3.93 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 50.82%

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 2
dl 11
loc 280
ccs 31
cts 61
cp 0.5082
rs 9.6
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getItemArray() 0 4 1
A getNonVersionArrayKeys() 0 4 1
A getErrorInfo() 0 8 1
A filterErrorData() 0 5 1
A register() 0 12 4
C process() 0 34 11
B isTCoalesceEqual() 4 18 7
B isTCoalesce() 7 16 6

How to fix   Duplicated Code   

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:

1
<?php
2
/**
3
 * PHPCompatibility_Sniffs_PHP_NewLanguageConstructsSniff.
4
 *
5
 * @category  PHP
6
 * @package   PHPCompatibility
7
 * @author    Wim Godden <[email protected]>
8
 * @copyright 2013 Cu.be Solutions bvba
9
 */
10
11
/**
12
 * PHPCompatibility_Sniffs_PHP_NewLanguageConstructsSniff.
13
 *
14
 * @category  PHP
15
 * @package   PHPCompatibility
16
 * @author    Wim Godden <[email protected]>
17
 * @copyright 2013 Cu.be Solutions bvba
18
 */
19
class PHPCompatibility_Sniffs_PHP_NewLanguageConstructsSniff extends PHPCompatibility_AbstractNewFeatureSniff
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
20
{
21
22
    /**
23
     * A list of new language constructs, not present in older versions.
24
     *
25
     * The array lists : version number with false (not present) or true (present).
26
     * If's sufficient to list the first version where the keyword appears.
27
     *
28
     * @var array(string => array(string => int|string|null))
29
     */
30
    protected $newConstructs = array(
31
        'T_NS_SEPARATOR' => array(
32
            '5.2' => false,
33
            '5.3' => true,
34
            'description' => 'the \ operator (for namespaces)',
35
        ),
36
        'T_POW' => array(
37
            '5.5' => false,
38
            '5.6' => true,
39
            'description' => 'power operator (**)',
40
        ), // Identified in PHPCS 1.5 as T_MULTIPLY + T_MULTIPLY.
41
        'T_POW_EQUAL' => array(
42
            '5.5' => false,
43
            '5.6' => true,
44
            'description' => 'power assignment operator (**=)',
45
        ), // Identified in PHPCS 1.5 as T_MULTIPLY + T_MUL_EQUAL.
46
        'T_ELLIPSIS' => array(
47
            '5.5' => false,
48
            '5.6' => true,
49
            'description' => 'variadic functions using ...',
50
        ),
51
        'T_SPACESHIP' => array(
52
            '5.6' => false,
53
            '7.0' => true,
54
            'description' => 'spaceship operator (<=>)',
55
        ), // Identified in PHPCS 1.5 as T_IS_SMALLER_OR_EQUAL + T_GREATER_THAN.
56
        'T_COALESCE' => array(
57
            '5.6' => false,
58
            '7.0' => true,
59
            'description' => 'null coalescing operator (??)',
60
        ), // Identified in PHPCS 1.5 as T_INLINE_THEN + T_INLINE_THEN.
61
        'T_COALESCE_EQUAL' => array(
62
            '7.1' => false,
63
            '7.2' => true,
64
            'description' => 'null coalesce equal operator (??=)',
65
        ), // Identified in PHPCS 1.5 as T_INLINE_THEN + T_INLINE_THEN + T_EQUAL and pre-PHPCS 2.8.1 as T_COALESCE + T_EQUAL.
66
    );
67
68
69
    /**
70
     * A list of new language constructs which are not recognized in PHPCS 1.x.
71
     *
72
     * The array lists an alternative token to listen for.
73
     *
74
     * @var array(string => int)
75
     */
76
    protected $newConstructsPHPCSCompat = array(
77
        'T_POW'            => T_MULTIPLY,
78
        'T_POW_EQUAL'      => T_MUL_EQUAL,
79
        'T_SPACESHIP'      => T_GREATER_THAN,
80
        'T_COALESCE'       => T_INLINE_THEN,
81
        'T_COALESCE_EQUAL' => T_EQUAL,
82
    );
83
84
    /**
85
     * Translation table for PHPCS 1.x and older 2.x tokens.
86
     *
87
     * The 'before' index lists the token which would have to be directly before the
88
     * token found for it to be one of the new language constructs.
89
     * The 'real_token' index indicates which language construct was found in that case.
90
     *
91
     * If the token combination has multi-layer complexity, such as is the case
92
     * with T_COALESCE(_EQUAL), a 'callback' index is added instead pointing to a
93
     * separate function which can determine whether this is the targetted token across
94
     * PHP and PHPCS versions.
95
     *
96
     * {@internal 'before' was chosen rather than 'after' as that allowed for a 1-on-1
97
     * translation list with the current tokens.}}
98
     *
99
     * @var array(string => array(string => string))
100
     */
101
    protected $PHPCSCompatTranslate = array(
102
        'T_MULTIPLY' => array(
103
            'before' => 'T_MULTIPLY',
104
            'real_token' => 'T_POW',
105
        ),
106
        'T_MUL_EQUAL' => array(
107
            'before' => 'T_MULTIPLY',
108
            'real_token' => 'T_POW_EQUAL',
109
        ),
110
        'T_GREATER_THAN' => array(
111
            'before' => 'T_IS_SMALLER_OR_EQUAL',
112
            'real_token' => 'T_SPACESHIP',
113
        ),
114
        'T_INLINE_THEN' => array(
115
            'callback' => 'isTCoalesce',
116
            'real_token' => 'T_COALESCE',
117
        ),
118
        'T_EQUAL' => array(
119
            'callback' => 'isTCoalesceEqual',
120
            'real_token' => 'T_COALESCE_EQUAL',
121
        ),
122
    );
123
124
    /**
125
     * Returns an array of tokens this test wants to listen for.
126
     *
127
     * @return array
128
     */
129 8
    public function register()
130
    {
131 8
        $tokens = array();
132 8
        foreach ($this->newConstructs as $token => $versions) {
133 8
            if (defined($token)) {
134 8
                $tokens[] = constant($token);
135 8
            } elseif (isset($this->newConstructsPHPCSCompat[$token])) {
136
                $tokens[] = $this->newConstructsPHPCSCompat[$token];
137
            }
138 8
        }
139 8
        return $tokens;
140
    }//end register()
141
142
143
    /**
144
     * Processes this test, when one of its tokens is encountered.
145
     *
146
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
147
     * @param int                  $stackPtr  The position of the current token in
148
     *                                        the stack passed in $tokens.
149
     *
150
     * @return void
151
     */
152 5
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
153
    {
154 5
        $tokens    = $phpcsFile->getTokens();
155 5
        $tokenType = $tokens[$stackPtr]['type'];
156
157
        // Translate older PHPCS token combis for new constructs to the actual construct.
158 5
        if (isset($this->newConstructs[$tokenType]) === false) {
159
            if (isset($this->PHPCSCompatTranslate[$tokenType])
160
                && ((isset($this->PHPCSCompatTranslate[$tokenType]['before']) === true
161
                    && $tokens[$stackPtr - 1]['type'] === $this->PHPCSCompatTranslate[$tokenType]['before'])
162
                || (isset($this->PHPCSCompatTranslate[$tokenType]['callback']) === true
163
                    && call_user_func(array($this, $this->PHPCSCompatTranslate[$tokenType]['callback']), $tokens, $stackPtr) === true))
164
            ) {
165
                $tokenType = $this->PHPCSCompatTranslate[$tokenType]['real_token'];
166
            }
167 5
        } elseif ($tokenType === 'T_COALESCE') {
168
            // Make sure that T_COALESCE is not confused with T_COALESCE_EQUAL.
169 5
            if (isset($tokens[($stackPtr + 1)]) !== false && $tokens[($stackPtr + 1)]['code'] === T_EQUAL) {
170
                // Ignore as will be dealt with via the T_EQUAL token.
171
                return;
172
            }
173 5
        }
174
175
        // If the translation did not yield one of the tokens we are looking for, bow out.
176 5
        if (isset($this->newConstructs[$tokenType]) === false) {
177
            return;
178
        }
179
180
        $itemInfo = array(
181 5
            'name'   => $tokenType,
182 5
        );
183 5
        $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
184
185 5
    }//end process()
186
187
188
    /**
189
     * Get the relevant sub-array for a specific item from a multi-dimensional array.
190
     *
191
     * @param array $itemInfo Base information about the item.
192
     *
193
     * @return array Version and other information about the item.
194
     */
195 5
    public function getItemArray(array $itemInfo)
196
    {
197 5
        return $this->newConstructs[$itemInfo['name']];
198
    }
199
200
201
    /**
202
     * Get an array of the non-PHP-version array keys used in a sub-array.
203
     *
204
     * @return array
205
     */
206 5
    protected function getNonVersionArrayKeys()
207
    {
208 5
        return array('description');
209
    }
210
211
212
    /**
213
     * Retrieve the relevant detail (version) information for use in an error message.
214
     *
215
     * @param array $itemArray Version and other information about the item.
216
     * @param array $itemInfo  Base information about the item.
217
     *
218
     * @return array
219
     */
220 5
    public function getErrorInfo(array $itemArray, array $itemInfo)
221
    {
222 5
        $errorInfo = parent::getErrorInfo($itemArray, $itemInfo);
223 5
        $errorInfo['description'] = $itemArray['description'];
224
225 5
        return $errorInfo;
226
227
    }
228
229
230
    /**
231
     * Allow for concrete child classes to filter the error data before it's passed to PHPCS.
232
     *
233
     * @param array $data      The error data array which was created.
234
     * @param array $itemInfo  Base information about the item this error message applied to.
235
     * @param array $errorInfo Detail information about an item this error message applied to.
236
     *
237
     * @return array
238
     */
239 4
    protected function filterErrorData(array $data, array $itemInfo, array $errorInfo)
240
    {
241 4
        $data[0] = $errorInfo['description'];
242 4
        return $data;
243
    }
244
245
246
    /**
247
     * Callback function to determine whether a T_EQUAL token is really a T_COALESCE_EQUAL token.
248
     *
249
     * @param array $tokens   The token stack.
250
     * @param int   $stackPtr The current position in the token stack.
251
     *
252
     * @return bool
253
     */
254
    private function isTCoalesceEqual($tokens, $stackPtr)
255
    {
256 View Code Duplication
        if ($tokens[$stackPtr]['code'] !== T_EQUAL || isset($tokens[($stackPtr - 1)]) === false) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across 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...
257
            // Function called for wrong token or token has no predecesor.
258
            return false;
259
        }
260
261
        if ($tokens[($stackPtr - 1)]['type'] === 'T_COALESCE') {
262
            return true;
263
        }
264
        if ($tokens[($stackPtr - 1)]['type'] === 'T_INLINE_THEN'
265
            && ( isset($tokens[($stackPtr - 2)]) && $tokens[($stackPtr - 2)]['type'] === 'T_INLINE_THEN')
266
        ) {
267
            return true;
268
        }
269
270
        return false;
271
    }
272
273
    /**
274
     * Callback function to determine whether a T_INLINE_THEN token is really a T_COALESCE token.
275
     *
276
     * @param array $tokens   The token stack.
277
     * @param int   $stackPtr The current position in the token stack.
278
     *
279
     * @return bool
280
     */
281
    private function isTCoalesce($tokens, $stackPtr)
282
    {
283 View Code Duplication
        if ($tokens[$stackPtr]['code'] !== T_INLINE_THEN || isset($tokens[($stackPtr - 1)]) === false) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across 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...
284
            // Function called for wrong token or token has no predecesor.
285
            return false;
286
        }
287
288
        if ($tokens[($stackPtr - 1)]['code'] === T_INLINE_THEN) {
289
            // Make sure not to confuse it with the T_COALESCE_EQUAL token.
290 View Code Duplication
            if (isset($tokens[($stackPtr + 1)]) === false || $tokens[($stackPtr + 1)]['code'] !== T_EQUAL) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across 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...
291
                return true;
292
            }
293
        }
294
295
        return false;
296
    }
297
298
}//end class
299