Completed
Push — master ( 42d661...12712d )
by Juliette
10s
created

addWarningOnInvalidValue()   C

Complexity

Conditions 7
Paths 20

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 31
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 19
nc 20
nop 3
1
<?php
2
/**
3
 * PHPCompatibility_Sniffs_PHP_NewExecutionDirectivesSniff.
4
 *
5
 * @category  PHP
6
 * @package   PHPCompatibility
7
 * @author    Juliette Reinders Folmer <[email protected]>
8
 */
9
10
/**
11
 * PHPCompatibility_Sniffs_PHP_NewExecutionDirectivesSniff.
12
 *
13
 * @category  PHP
14
 * @package   PHPCompatibility
15
 * @author    Juliette Reinders Folmer <[email protected]>
16
 */
17
class PHPCompatibility_Sniffs_PHP_NewExecutionDirectivesSniff 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...
18
{
19
20
    /**
21
     * A list of new execution directives
22
     *
23
     * The array lists : version number with false (not present) or true (present).
24
     * If the execution order is conditional, add the condition as a string to the version nr.
25
     * If's sufficient to list the first version where the execution directive appears.
26
     *
27
     * @var array(string => array(string => int|string|null))
28
     */
29
    protected $newDirectives = array (
30
                                'ticks' => array(
31
                                    '3.1' => false,
32
                                    '4.0' => true,
33
                                    'valid_value_callback' => 'isNumeric',
34
                                ),
35
                                'encoding' => array(
36
                                    '5.2' => false,
37
                                    '5.3' => '--enable-zend-multibyte', // Directive ignored unless.
38
                                    '5.4' => true,
39
                                    'valid_value_callback' => 'validEncoding',
40
                                ),
41
                                'strict_types' => array(
42
                                    '5.6' => false,
43
                                    '7.0' => true,
44
                                    'valid_values' => array(1),
45
                                ),
46
                               );
47
48
49
    /**
50
     * Tokens to ignore when trying to find the value for the directive.
51
     *
52
     * @var array
53
     */
54
    protected $ignoreTokens = array();
55
56
57
    /**
58
     * Returns an array of tokens this test wants to listen for.
59
     *
60
     * @return array
61
     */
62
    public function register()
63
    {
64
        $this->ignoreTokens          = PHP_CodeSniffer_Tokens::$emptyTokens;
65
        $this->ignoreTokens[T_EQUAL] = T_EQUAL;
66
67
        return array(T_DECLARE);
68
    }//end register()
69
70
71
    /**
72
     * Processes this test, when one of its tokens is encountered.
73
     *
74
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
75
     * @param int                  $stackPtr  The position of the current token in
76
     *                                        the stack passed in $tokens.
77
     *
78
     * @return void
79
     */
80
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
81
    {
82
        $tokens = $phpcsFile->getTokens();
83
84
        if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === true) {
85
	        $openParenthesis  = $tokens[$stackPtr]['parenthesis_opener'];
86
	        $closeParenthesis = $tokens[$stackPtr]['parenthesis_closer'];
87
        }
88
        else {
89
	        if (version_compare(PHP_CodeSniffer::VERSION, '2.0', '>=')) {
90
				return;
91
			}
92
93
			// Deal with PHPCS 1.x which does not set the parenthesis properly for declare statements.
94
			$openParenthesis = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($stackPtr + 1), null, false, null, true);
95 View Code Duplication
			if ($openParenthesis === false || isset($tokens[$openParenthesis]['parenthesis_closer']) === 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...
96
				return;
97
			}
98
			$closeParenthesis = $tokens[$openParenthesis]['parenthesis_closer'];
99
		}
100
101
        $directivePtr = $phpcsFile->findNext(T_STRING, ($openParenthesis + 1), $closeParenthesis, false);
102
        if ($directivePtr === false) {
103
            return;
104
        }
105
106
        $directiveContent = $tokens[$directivePtr]['content'];
107
108
        if (isset($this->newDirectives[$directiveContent]) === false) {
109
            $error = 'Declare can only be used with the directives %s. Found: %s';
110
            $data  = array(
111
                implode(', ', array_keys($this->newDirectives)),
112
                $directiveContent,
113
            );
114
            $phpcsFile->addError($error, $stackPtr, 'InvalidDirectiveFound', $data);
115
        }
116
        else {
117
            // Check for valid directive for version.
118
            $this->maybeAddError($phpcsFile, $stackPtr, $directiveContent);
119
120
            // Check for valid directive value.
121
            $valuePtr = $phpcsFile->findNext($this->ignoreTokens, $directivePtr + 1, $closeParenthesis, true);
122
            if ($valuePtr === false) {
123
                return;
124
            }
125
126
            $this->addWarningOnInvalidValue($phpcsFile, $valuePtr, $directiveContent);
127
        }
128
129
    }//end process()
130
131
132
    /**
133
     * Generates a error or warning for this sniff.
134
     *
135
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
136
     * @param int                  $stackPtr  The position of the declare statement
137
     *                                        in the token array.
138
     * @param string               $directive The directive.
139
     *
140
     * @return void
141
     */
142
    protected function maybeAddError($phpcsFile, $stackPtr, $directive)
143
    {
144
        $isError            = false;
145
        $notInVersion       = '';
146
        $conditionalVersion = '';
147
        foreach ($this->newDirectives[$directive] as $version => $present) {
148
            if (strpos($version, 'valid_') === false && $this->supportsBelow($version)) {
149
                if ($present === false) {
150
                    $isError      = true;
151
                    $notInVersion = $version;
152
                }
153
                else if (is_string($present)) {
154
                    // We cannot test for compilation option (ok, except by scraping the output of phpinfo...).
155
                    $conditionalVersion = $version;
156
                }
157
            }
158
        }
159
        if ($notInVersion !== '' || $conditionalVersion !== '') {
160
            if ($isError === true && $notInVersion !== '') {
161
                $error     = 'Directive %s is not present in PHP version %s or earlier';
162
                $errorCode = $directive . 'Found';
163
                $data      = array(
164
                    $directive,
165
                    $notInVersion,
166
                );
167
168
                $phpcsFile->addError($error, $stackPtr, $errorCode, $data);
169
            }
170
            else if($conditionalVersion !== '') {
171
                $error     = 'Directive %s is present in PHP version %s but will be disregarded unless PHP is compiled with %s';
172
                $errorCode = $directive . 'Found';
173
                $data      = array(
174
                    $directive,
175
                    $conditionalVersion,
176
                    $this->newDirectives[$directive][$conditionalVersion],
177
                );
178
179
                $phpcsFile->addWarning($error, $stackPtr, $errorCode, $data);
180
            }
181
        }
182
183
    }//end maybeAddError()
184
185
186
    /**
187
     * Generates a error or warning for this sniff.
188
     *
189
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
190
     * @param int                  $stackPtr  The position of the execution directive value
191
     *                                        in the token array.
192
     * @param string               $directive The directive.
193
     *
194
     * @return void
195
     */
196
    protected function addWarningOnInvalidValue($phpcsFile, $stackPtr, $directive)
197
    {
198
        $tokens = $phpcsFile->getTokens();
199
200
        $value = $tokens[$stackPtr]['content'];
201
        if ($tokens[$stackPtr]['code'] === T_CONSTANT_ENCAPSED_STRING) {
202
            $value = $this->stripQuotes($value);
203
        }
204
205
        $isError = false;
206
        if (isset($this->newDirectives[$directive]['valid_values'])) {
207
            if (in_array($value, $this->newDirectives[$directive]['valid_values']) === false) {
208
                $isError = true;
209
            }
210
        }
211
        else if (isset($this->newDirectives[$directive]['valid_value_callback'])) {
212
            $valid = call_user_func(array($this, $this->newDirectives[$directive]['valid_value_callback']), $value);
213
            if ($valid === false) {
214
                $isError = true;
215
            }
216
        }
217
218
        if ($isError === true) {
219
            $error = 'The execution directive %s does not seem to have a valid value. Please review. Found: %s';
220
            $data  = array(
221
                $directive,
222
                $value,
223
            );
224
            $phpcsFile->addWarning($error, $stackPtr, 'InvalidDirectiveValueFound', $data);
225
        }
226
    }// addErrorOnInvalidValue()
227
228
229
    /**
230
     * Check whether a value is numeric.
231
     *
232
     * Callback function to test whether the value for an execution directive is valid.
233
     *
234
     * @param mixed $value The value to test.
235
     *
236
     * @return bool
237
     */
238
    protected function isNumeric($value)
239
    {
240
        return is_numeric($value);
241
    }
242
243
244
    /**
245
     * Check whether a value is valid encoding.
246
     *
247
     * Callback function to test whether the value for an execution directive is valid.
248
     *
249
     * @param mixed $value The value to test.
250
     *
251
     * @return bool
252
     */
253
    protected function validEncoding($value)
254
    {
255
        $encodings = array();
256
        if (function_exists('mb_list_encodings')) {
257
            $encodings = mb_list_encodings();
258
        }
259
260
        if (empty($encodings)) {
261
            // If we can't test the encoding, let it pass through.
262
            return true;
263
        }
264
265
        if (in_array($value, $encodings, true)) {
266
            return true;
267
        }
268
        else {
269
            return false;
270
        }
271
    }
272
273
}//end class
274