1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* \PHPCompatibility\Sniffs\PHP\NewExtensionsSniff. |
4
|
|
|
* |
5
|
|
|
* @category PHP |
6
|
|
|
* @package PHPCompatibility |
7
|
|
|
* @author Juliette Reinders Folmer <[email protected]> |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace PHPCompatibility\Sniffs\PHP; |
11
|
|
|
|
12
|
|
|
use PHPCompatibility\AbstractNewFeatureSniff; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* \PHPCompatibility\Sniffs\PHP\NewExtensionsSniff. |
16
|
|
|
* |
17
|
|
|
* Detect use of PHP extensions which were not available in older PHP versions. |
18
|
|
|
* |
19
|
|
|
* @category PHP |
20
|
|
|
* @package PHPCompatibility |
21
|
|
|
* @author Juliette Reinders Folmer <[email protected]> |
22
|
|
|
*/ |
23
|
|
|
class NewExtensionsSniff extends AbstractNewFeatureSniff |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* A list of functions to whitelist, if any. |
27
|
|
|
* |
28
|
|
|
* This is intended for projects using functions which start with the same |
29
|
|
|
* prefix as one of the removed extensions. |
30
|
|
|
* |
31
|
|
|
* This property can be set from the ruleset, like so: |
32
|
|
|
* <rule ref="PHPCompatibility.PHP.RemovedExtensions"> |
33
|
|
|
* <properties> |
34
|
|
|
* <property name="functionWhitelist" type="array" value="mysql_to_rfc3339,mysql_another_function" /> |
35
|
|
|
* </properties> |
36
|
|
|
* </rule> |
37
|
|
|
* |
38
|
|
|
* @var array |
39
|
|
|
*/ |
40
|
|
|
public $functionWhitelist; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* A list of new PHP extensions. |
44
|
|
|
* |
45
|
|
|
* The array lists : version number with false (available in PECL) and true (shipped with PHP). |
46
|
|
|
* If's sufficient to list the first version where the extension was introduced. |
47
|
|
|
* |
48
|
|
|
* @var array(string|null) |
49
|
|
|
*/ |
50
|
|
|
protected $newExtensions = array( |
51
|
|
|
'csprng' => array( |
52
|
|
|
'7.0' => true, |
53
|
|
|
'prefixes' => array( |
54
|
|
|
// Function prefix all functions: verified. |
55
|
|
|
'random_', // In practice: only random_bytes, random_int |
56
|
|
|
// NO ini settings |
57
|
|
|
// NO constants |
58
|
|
|
), |
59
|
|
|
), |
60
|
|
|
'fileinfo' => array( |
61
|
|
|
'5.3' => true, |
62
|
|
|
'prefixes' => array( |
63
|
|
|
// Function prefix all functions: verified. |
64
|
|
|
'finfo_' |
65
|
|
|
// Also function: mime_content_type() |
66
|
|
|
// Class: finfo |
67
|
|
|
// NO ini settings |
68
|
|
|
// Constants prefix: FILEINFO_ |
69
|
|
|
), |
70
|
|
|
), |
71
|
|
|
'hash' => array( |
72
|
|
|
'5.1.2' => true, |
73
|
|
|
'prefixes' => array( |
74
|
|
|
// Function prefix: verified. |
75
|
|
|
'hash_' |
76
|
|
|
// Function `hash()` also exists!!!! |
|
|
|
|
77
|
|
|
// NO ini settings |
78
|
|
|
// 1 constant: HASH_HMAC |
79
|
|
|
), |
80
|
|
|
), |
81
|
|
|
'opcache' => array( |
82
|
|
|
'5.2' => false, |
83
|
|
|
'5.5' => true, |
84
|
|
|
'prefixes' => array( |
85
|
|
|
// Function prefix all functions: verified. |
86
|
|
|
'opcache_' |
87
|
|
|
// ini prefix: `opcache.` |
88
|
|
|
// NO constants |
89
|
|
|
), |
90
|
|
|
), |
91
|
|
|
'password' => array( |
92
|
|
|
'5.5' => true, |
93
|
|
|
'prefixes' => array( |
94
|
|
|
// Function prefix all functions: verified. |
95
|
|
|
'password_' |
96
|
|
|
// NO ini settings |
97
|
|
|
// 2 constants: PASSWORD_BCRYPT, PASSWORD_DEFAULT |
98
|
|
|
), |
99
|
|
|
), |
100
|
|
|
'phar' => array( |
101
|
|
|
'5.3' => true, |
102
|
|
|
'prefixes' => array( |
103
|
|
|
// NO functions, only classes. |
104
|
|
|
// ini prefix: `phar.` |
105
|
|
|
// constants - all class constants: `Phar::` |
106
|
|
|
// Classes: Phar, PharData, PharFileInfo, PharException |
107
|
|
|
), |
108
|
|
|
), |
109
|
|
|
|
110
|
|
|
/* |
|
|
|
|
111
|
|
|
'activescript' => array( |
112
|
|
|
'5.1' => true, |
113
|
|
|
'alternative' => 'pecl/activescript', |
114
|
|
|
), |
115
|
|
|
'cpdf' => array( |
116
|
|
|
'5.1' => true, |
117
|
|
|
'alternative' => 'pecl/pdflib', |
118
|
|
|
), |
119
|
|
|
'dbase' => array( |
120
|
|
|
'5.3' => true, |
121
|
|
|
'alternative' => null, |
122
|
|
|
'separator' => '_', // Verified: all functions use separator. |
123
|
|
|
), |
124
|
|
|
'dbx' => array( |
125
|
|
|
'5.1' => true, |
126
|
|
|
'alternative' => 'pecl/dbx', |
127
|
|
|
'separator' => '_', // Verified: all functions use separator. |
128
|
|
|
), |
129
|
|
|
'dio' => array( |
130
|
|
|
'5.1' => true, |
131
|
|
|
'alternative' => 'pecl/dio', |
132
|
|
|
'separator' => '_', // Verified: all functions use separator. |
133
|
|
|
), |
134
|
|
|
'ereg' => array( |
135
|
|
|
'5.3' => false, |
136
|
|
|
'7.0' => true, |
137
|
|
|
'alternative' => 'pcre', |
138
|
|
|
), |
139
|
|
|
'fam' => array( |
140
|
|
|
'5.1' => true, |
141
|
|
|
'alternative' => null, |
142
|
|
|
'separator' => '_', // Verified: all functions use separator. |
143
|
|
|
), |
144
|
|
|
'fbsql' => array( |
145
|
|
|
'5.3' => true, |
146
|
|
|
'alternative' => null, |
147
|
|
|
'separator' => '_', // Verified: all functions use separator. |
148
|
|
|
), |
149
|
|
|
'fdf' => array( |
150
|
|
|
'5.3' => true, |
151
|
|
|
'alternative' => 'pecl/fdf', |
152
|
|
|
'separator' => '_', // Verified: all functions use separator. |
153
|
|
|
), |
154
|
|
|
'filepro' => array( |
155
|
|
|
'5.2' => true, |
156
|
|
|
'alternative' => null, |
157
|
|
|
'separator' => '_', // Verified: function 'filepro' exists - all other functions use separator. |
158
|
|
|
), |
159
|
|
|
'hw_api' => array( |
160
|
|
|
'5.2' => true, |
161
|
|
|
'alternative' => null, |
162
|
|
|
), |
163
|
|
|
'ingres' => array( |
164
|
|
|
'5.1' => true, |
165
|
|
|
'alternative' => 'pecl/ingres', |
166
|
|
|
'separator' => '_', // Verified: all functions use separator. |
167
|
|
|
), |
168
|
|
|
'ircg' => array( |
169
|
|
|
'5.1' => true, |
170
|
|
|
'alternative' => null, |
171
|
|
|
), |
172
|
|
|
'mcrypt' => array( |
173
|
|
|
'7.1' => false, |
174
|
|
|
'alternative' => 'openssl (preferred) or pecl/mcrypt once available', |
175
|
|
|
'separator' => '_', // Verified: all functions use separator, though there is also the mdecrypt_generic function. |
176
|
|
|
), |
177
|
|
|
'mcve' => array( |
178
|
|
|
'5.1' => true, |
179
|
|
|
'alternative' => 'pecl/mvce', |
180
|
|
|
), |
181
|
|
|
'ming' => array( |
182
|
|
|
'5.3' => true, |
183
|
|
|
'alternative' => 'pecl/ming', |
184
|
|
|
), |
185
|
|
|
'mnogosearch' => array( |
186
|
|
|
'5.1' => true, |
187
|
|
|
'alternative' => null, |
188
|
|
|
'prefix' => 'udm', |
189
|
|
|
'separator' => '_', // Verified: all functions use separator. |
190
|
|
|
), |
191
|
|
|
'msql' => array( |
192
|
|
|
'5.3' => true, |
193
|
|
|
'alternative' => null, |
194
|
|
|
'separator' => '_', // Verified: function 'msql' exists - all other functions use separator. |
195
|
|
|
), |
196
|
|
|
'mssql' => array( |
197
|
|
|
'7.0' => true, |
198
|
|
|
'alternative' => null, |
199
|
|
|
'separator' => '_', // Verified: all functions use separator. |
200
|
|
|
), |
201
|
|
|
'mysql_' => array( |
202
|
|
|
'5.5' => false, |
203
|
|
|
'7.0' => true, |
204
|
|
|
'alternative' => 'mysqli', |
205
|
|
|
'separator' => '_', // Verified: all functions use separator. |
206
|
|
|
), |
207
|
|
|
'ncurses' => array( |
208
|
|
|
'5.3' => true, |
209
|
|
|
'alternative' => 'pecl/ncurses', |
210
|
|
|
'separator' => '_', // Verified: all functions use separator. |
211
|
|
|
), |
212
|
|
|
'oracle' => array( |
213
|
|
|
'5.1' => true, |
214
|
|
|
'alternative' => 'oci8 or pdo_oci', |
215
|
|
|
), |
216
|
|
|
'ovrimos' => array( |
217
|
|
|
'5.1' => true, |
218
|
|
|
'alternative' => null, |
219
|
|
|
'separator' => '_', // Verified: all functions use separator. |
220
|
|
|
), |
221
|
|
|
'pfpro' => array( |
222
|
|
|
'5.3' => true, |
223
|
|
|
'alternative' => null, |
224
|
|
|
), |
225
|
|
|
'sqlite' => array( |
226
|
|
|
'5.4' => true, |
227
|
|
|
'alternative' => null, |
228
|
|
|
'separator' => '_', // Verified: all functions use separator. |
229
|
|
|
), |
230
|
|
|
// Has to be before `sybase` as otherwise it will never match. |
231
|
|
|
'sybase_ct' => array( |
232
|
|
|
'7.0' => true, |
233
|
|
|
'alternative' => null, |
234
|
|
|
), |
235
|
|
|
'sybase' => array( |
236
|
|
|
'5.3' => true, |
237
|
|
|
'alternative' => 'sybase_ct', |
238
|
|
|
'separator' => '_', // Verified: all functions use separator. |
239
|
|
|
), |
240
|
|
|
'w32api' => array( |
241
|
|
|
'5.1' => true, |
242
|
|
|
'alternative' => 'pecl/ffi', |
243
|
|
|
'separator' => '_', // Verified: all functions use separator. |
244
|
|
|
), |
245
|
|
|
'yp' => array( |
246
|
|
|
'5.1' => true, |
247
|
|
|
'alternative' => null, |
248
|
|
|
'separator' => '_', // Verified: all functions use separator. |
249
|
|
|
), |
250
|
|
|
*/ |
251
|
|
|
); |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Returns an array of tokens this test wants to listen for. |
255
|
|
|
* |
256
|
|
|
* @return array |
257
|
|
|
*/ |
258
|
|
|
public function register() |
259
|
|
|
{ |
260
|
|
|
// Handle case-insensitivity of function names. |
261
|
|
|
$this->newExtensions = $this->arrayKeysToLowercase($this->newExtensions); |
262
|
|
|
|
263
|
|
|
return array(T_STRING); |
264
|
|
|
|
265
|
|
|
}//end register() |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* Processes this test, when one of its tokens is encountered. |
269
|
|
|
* |
270
|
|
|
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. |
271
|
|
|
* @param int $stackPtr The position of the current token in the |
272
|
|
|
* stack passed in $tokens. |
273
|
|
|
* |
274
|
|
|
* @return void |
275
|
|
|
*/ |
276
|
|
View Code Duplication |
public function process(\PHP_CodeSniffer_File $phpcsFile, $stackPtr) |
277
|
|
|
{ |
278
|
|
|
$tokens = $phpcsFile->getTokens(); |
279
|
|
|
|
280
|
|
|
// Find the next non-empty token. |
281
|
|
|
$openBracket = $phpcsFile->findNext(\PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true); |
282
|
|
|
|
283
|
|
|
if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) { |
284
|
|
|
// Not a function call. |
285
|
|
|
return; |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
if (isset($tokens[$openBracket]['parenthesis_closer']) === false) { |
289
|
|
|
// Not a function call. |
290
|
|
|
return; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
// Find the previous non-empty token. |
294
|
|
|
$search = \PHP_CodeSniffer_Tokens::$emptyTokens; |
295
|
|
|
$search[] = T_BITWISE_AND; |
296
|
|
|
$previous = $phpcsFile->findPrevious($search, ($stackPtr - 1), null, true); |
297
|
|
|
if ($tokens[$previous]['code'] === T_FUNCTION) { |
298
|
|
|
// It's a function definition, not a function call. |
299
|
|
|
return; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
if ($tokens[$previous]['code'] === T_NEW) { |
303
|
|
|
// We are creating an object, not calling a function. |
304
|
|
|
return; |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
if ($tokens[$previous]['code'] === T_OBJECT_OPERATOR) { |
308
|
|
|
// We are calling a method of an object. |
309
|
|
|
return; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
$function = $tokens[$stackPtr]['content']; |
313
|
|
|
$functionLc = strtolower($function); |
314
|
|
|
|
315
|
|
|
if ($this->isWhiteListed($functionLc) === true) { |
316
|
|
|
// Function is whitelisted. |
317
|
|
|
return; |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
foreach ($this->removedExtensions as $extension => $versionList) { |
321
|
|
|
if (strpos($functionLc, $extension) === 0) { |
322
|
|
|
$itemInfo = array( |
323
|
|
|
'name' => $extension, |
324
|
|
|
); |
325
|
|
|
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo); |
326
|
|
|
break; |
327
|
|
|
} |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
}//end process() |
331
|
|
|
|
332
|
|
|
|
333
|
|
|
/** |
334
|
|
|
* Is the current function being checked whitelisted ? |
335
|
|
|
* |
336
|
|
|
* Parsing the list late as it may be provided as a property, but also inline. |
337
|
|
|
* |
338
|
|
|
* @param string $content Content of the current token. |
339
|
|
|
* |
340
|
|
|
* @return bool |
341
|
|
|
*/ |
342
|
|
View Code Duplication |
protected function isWhiteListed($content) |
|
|
|
|
343
|
|
|
{ |
344
|
|
|
if (isset($this->functionWhitelist) === false) { |
345
|
|
|
return false; |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
if (is_string($this->functionWhitelist) === true) { |
349
|
|
|
if (strpos($this->functionWhitelist, ',') !== false) { |
350
|
|
|
$this->functionWhitelist = explode(',', $this->functionWhitelist); |
351
|
|
|
} else { |
352
|
|
|
$this->functionWhitelist = (array) $this->functionWhitelist; |
353
|
|
|
} |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
if (is_array($this->functionWhitelist) === true) { |
357
|
|
|
$this->functionWhitelist = array_map('strtolower', $this->functionWhitelist); |
358
|
|
|
return in_array($content, $this->functionWhitelist, true); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
return false; |
362
|
|
|
|
363
|
|
|
}//end isWhiteListed() |
364
|
|
|
|
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* Get the relevant sub-array for a specific item from a multi-dimensional array. |
368
|
|
|
* |
369
|
|
|
* @param array $itemInfo Base information about the item. |
370
|
|
|
* |
371
|
|
|
* @return array Version and other information about the item. |
372
|
|
|
*/ |
373
|
|
|
public function getItemArray(array $itemInfo) |
374
|
|
|
{ |
375
|
|
|
return $this->removedExtensions[$itemInfo['name']]; |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
|
379
|
|
|
/** |
380
|
|
|
* Get the error message template for this sniff. |
381
|
|
|
* |
382
|
|
|
* @return string |
383
|
|
|
*/ |
384
|
|
|
protected function getErrorMsgTemplate() |
385
|
|
|
{ |
386
|
|
|
return "Extension '%s' is "; |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
|
390
|
|
|
}//end class |
391
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.