Completed
Pull Request — master (#300)
by Juliette
01:45
created

PregReplaceEModifierSniff::process()   D

Complexity

Conditions 9
Paths 7

Size

Total Lines 40
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 40
rs 4.909
c 0
b 0
f 0
cc 9
eloc 22
nc 7
nop 2
1
<?php
2
/**
3
 * PHPCompatibility_Sniffs_PHP_PregReplaceEModifierSniff.
4
 *
5
 * PHP version 5.6
6
 *
7
 * @category  PHP
8
 * @package   PHPCompatibility
9
 * @author    Wim Godden <[email protected]>
10
 * @copyright 2014 Cu.be Solutions bvba
11
 */
12
13
/**
14
 * PHPCompatibility_Sniffs_PHP_PregReplaceEModifierSniff.
15
 *
16
 * @category  PHP
17
 * @package   PHPCompatibility
18
 * @author    Wim Godden <[email protected]>
19
 * @version   1.1.0
20
 * @copyright 2014 Cu.be Solutions bvba
21
 */
22
class PHPCompatibility_Sniffs_PHP_PregReplaceEModifierSniff extends PHPCompatibility_Sniff
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...
23
{
24
25
    /**
26
     * Functions to check for.
27
     *
28
     * @var array
29
     */
30
    protected $functions = array(
31
        'preg_replace' => true,
32
        'preg_filter'  => true,
33
    );
34
35
    /**
36
     * Regex bracket delimiters.
37
     *
38
     * @var array
39
     */
40
    protected $doublesSeparators = array(
41
        '{' => '}',
42
        '[' => ']',
43
        '(' => ')',
44
        '<' => '>',
45
    );
46
47
    /**
48
     * Returns an array of tokens this test wants to listen for.
49
     *
50
     * @return array
51
     */
52
    public function register()
53
    {
54
        return array(T_STRING);
55
    }//end register()
56
57
    /**
58
     * Processes this test, when one of its tokens is encountered.
59
     *
60
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
61
     * @param int                  $stackPtr  The position of the current token in the
62
     *                                        stack passed in $tokens.
63
     *
64
     * @return void
65
     */
66
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
67
    {
68
        if ($this->supportsAbove('5.5') === false) {
69
            return;
70
        }
71
72
        $tokens         = $phpcsFile->getTokens();
73
        $functionName   = $tokens[$stackPtr]['content'];
74
        $functionNameLc = strtolower($functionName);
75
76
        // Bow out if not one of the functions we're targetting.
77
        if ( isset($this->functions[$functionNameLc]) === false ) {
78
            return;
79
        }
80
81
        // Get the first parameter in the function call as that should contain the regex(es).
82
        $firstParam = $this->getFunctionCallParameter($phpcsFile, $stackPtr, 1);
83
        if ($firstParam === false) {
84
            return;
85
        }
86
87
        // Differentiate between an array of patterns passed and a single pattern.
88
        $nextNonEmpty = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $firstParam['start'], ($firstParam['end'] +1), true);
89
        if ($nextNonEmpty !== false && ($tokens[$nextNonEmpty]['code'] === T_ARRAY || $tokens[$nextNonEmpty]['code'] === T_OPEN_SHORT_ARRAY)) {
90
            $arrayValues = $this->getFunctionCallParameters($phpcsFile, $nextNonEmpty);
91
            foreach ($arrayValues as $value) {
92
                $hasKey = $phpcsFile->findNext(T_DOUBLE_ARROW, $value['start'], ($value['end'] + 1));
93
                if ($hasKey !== false) {
94
                    $value['start'] = ($hasKey + 1);
95
                    $value['raw']   = trim($phpcsFile->getTokensAsString($value['start'], (($value['end'] + 1) - $value['start'])));
96
                }
97
98
                $this->processRegexPattern($value, $phpcsFile, $value['end'], $functionName);
99
            }
100
        }
101
        else {
102
            $this->processRegexPattern($firstParam, $phpcsFile, $stackPtr, $functionName);
103
        }
104
105
    }//end process()
106
107
108
	/**
109
     * Analyse a potential regex pattern for usage of the /e modifier.
110
     *
111
     * @param array                $pattern      Array containing the start and end token
112
	 *                                           pointer of the potential regex pattern and
113
	 *                                           the raw string value of the pattern.
114
     * @param PHP_CodeSniffer_File $phpcsFile    The file being scanned.
115
     * @param int                  $stackPtr     The position of the current token in the
116
     *                                           stack passed in $tokens.
117
     * @param string               $functionName The function which contained the pattern.
118
     *
119
     * @return void
120
	 */
121
    protected function processRegexPattern($pattern, $phpcsFile, $stackPtr, $functionName)
122
    {
123
        $tokens = $phpcsFile->getTokens();
124
125
        /*
126
         * The pattern might be build up of a combination of strings, variables
127
         * and function calls. We are only concerned with the strings.
128
         */
129
        $regex = '';
130
        for ($i = $pattern['start']; $i <= $pattern['end']; $i++ ) {
131
            if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$stringTokens, true) === true) {
132
                $regex .= $this->stripQuotes($tokens[$i]['content']);
133
            }
134
        }
135
        // Deal with multi-line regexes which were broken up in several string tokens.
136
        $regex = $this->stripQuotes($regex);
137
138
        if ($regex === '') {
139
            // No string token found in the first parameter, so skip it (e.g. if variable passed in).
140
            return;
141
        }
142
143
        $regexFirstChar = substr($regex, 0, 1);
144
145
        // Make sure that the character identified as the delimiter is valid.
146
        // Otherwise, it is a false positive caused by the string concatenation.
147
        if (preg_match('`[a-z0-9\\\\ ]`i', $regexFirstChar) === 1) {
148
            return;
149
        }
150
151
        if (isset($this->doublesSeparators[$regexFirstChar])) {
152
            $regexEndPos = strrpos($regex, $this->doublesSeparators[$regexFirstChar]);
153
        }
154
        else {
155
            $regexEndPos = strrpos($regex, $regexFirstChar);
156
        }
157
158
        if($regexEndPos) {
159
            $modifiers = substr($regex, $regexEndPos + 1);
160
161
            if (strpos($modifiers, 'e') !== false) {
162
                $error     = '%s() - /e modifier is deprecated since PHP 5.5';
163
                $isError   = false;
164
                $errorCode = 'Deprecated';
165
                $data      = array($functionName);
166
167
                if ($this->supportsAbove('7.0')) {
168
                    $error    .= ' and removed since PHP 7.0';
169
                    $isError   = true;
170
                    $errorCode = 'Removed';
171
                }
172
173
                $this->addMessage($phpcsFile, $error, $stackPtr, $isError, $errorCode, $data);
174
            }
175
        }
176
    }//end processRegexPattern()
177
178
}//end class
179