1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Basic util functions. |
4
|
|
|
* |
5
|
|
|
* @author Greg Sherwood <[email protected]> |
6
|
|
|
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) |
7
|
|
|
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace PHP_CodeSniffer\Util; |
11
|
|
|
|
12
|
|
|
use PHP_CodeSniffer\Config; |
13
|
|
|
use PHP_CodeSniffer\Exceptions\RuntimeException; |
14
|
|
|
|
15
|
|
|
class Common |
16
|
|
|
{ |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* An array of variable types for param/var we will check. |
20
|
|
|
* |
21
|
|
|
* @var string[] |
22
|
|
|
*/ |
23
|
|
|
public static $allowedTypes = array( |
24
|
|
|
'array', |
25
|
|
|
'boolean', |
26
|
|
|
'float', |
27
|
|
|
'integer', |
28
|
|
|
'mixed', |
29
|
|
|
'object', |
30
|
|
|
'string', |
31
|
|
|
'resource', |
32
|
|
|
'callable', |
33
|
|
|
); |
34
|
|
|
|
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* CodeSniffer alternative for realpath. |
38
|
|
|
* |
39
|
|
|
* Allows for PHAR support. |
40
|
|
|
* |
41
|
|
|
* @param string $path The path to use. |
42
|
|
|
* |
43
|
|
|
* @return mixed |
44
|
|
|
*/ |
45
|
|
|
public static function realpath($path) |
46
|
|
|
{ |
47
|
|
|
// Support the path replacement of ~ with the user's home directory. |
48
|
|
|
if (substr($path, 0, 2) === '~/') { |
49
|
|
|
$homeDir = getenv('HOME'); |
50
|
|
|
if ($homeDir !== false) { |
51
|
|
|
$path = $homeDir.substr($path, 1); |
52
|
|
|
} |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
// No extra work needed if this is not a phar file. |
56
|
|
|
return realpath($path); |
57
|
|
|
|
58
|
|
|
}//end realpath() |
59
|
|
|
|
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Removes a base path from the front of a file path. |
63
|
|
|
* |
64
|
|
|
* @param string $path The path of the file. |
65
|
|
|
* @param string $basepath The base path to remove. This should not end |
66
|
|
|
* with a directory separator. |
67
|
|
|
* |
68
|
|
|
* @return string |
69
|
|
|
*/ |
70
|
|
|
public static function stripBasepath($path, $basepath) |
71
|
|
|
{ |
72
|
|
|
if (empty($basepath) === true) { |
73
|
|
|
return $path; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
$basepathLen = strlen($basepath); |
77
|
|
|
if (substr($path, 0, $basepathLen) === $basepath) { |
78
|
|
|
$path = substr($path, $basepathLen); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
$path = ltrim($path, DIRECTORY_SEPARATOR); |
82
|
|
|
if ($path === '') { |
83
|
|
|
$path = '.'; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
return $path; |
87
|
|
|
|
88
|
|
|
}//end stripBasepath() |
89
|
|
|
|
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Detects the EOL character being used in a string. |
93
|
|
|
* |
94
|
|
|
* @param string $contents The contents to check. |
95
|
|
|
* |
96
|
|
|
* @return string |
97
|
|
|
*/ |
98
|
|
|
public static function detectLineEndings($contents) |
99
|
|
|
{ |
100
|
|
|
if (preg_match("/\r\n?|\n/", $contents, $matches) !== 1) { |
101
|
|
|
// Assume there are no newlines. |
102
|
|
|
$eolChar = "\n"; |
103
|
|
|
} else { |
104
|
|
|
$eolChar = $matches[0]; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
return $eolChar; |
108
|
|
|
|
109
|
|
|
}//end detectLineEndings() |
110
|
|
|
|
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Prepares token content for output to screen. |
114
|
|
|
* |
115
|
|
|
* Replaces invisible characters so they are visible. On non-Windows |
116
|
|
|
* OSes it will also colour the invisible characters. |
117
|
|
|
* |
118
|
|
|
* @param string $content The content to prepare. |
119
|
|
|
* @param string[] $exclude A list of characters to leave invisible. |
120
|
|
|
* Can contain \r, \n, \t and a space. |
121
|
|
|
* |
122
|
|
|
* @return string |
123
|
|
|
*/ |
124
|
|
|
public static function prepareForOutput($content, $exclude=array()) |
125
|
|
|
{ |
126
|
|
|
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { |
127
|
|
|
if (in_array("\r", $exclude) === false) { |
128
|
|
|
$content = str_replace("\r", '\r', $content); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
if (in_array("\n", $exclude) === false) { |
132
|
|
|
$content = str_replace("\n", '\n', $content); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
if (in_array("\t", $exclude) === false) { |
136
|
|
|
$content = str_replace("\t", '\t', $content); |
137
|
|
|
} |
138
|
|
|
} else { |
139
|
|
|
if (in_array("\r", $exclude) === false) { |
140
|
|
|
$content = str_replace("\r", "\033[30;1m\\r\033[0m", $content); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
if (in_array("\n", $exclude) === false) { |
144
|
|
|
$content = str_replace("\n", "\033[30;1m\\n\033[0m", $content); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
if (in_array("\t", $exclude) === false) { |
148
|
|
|
$content = str_replace("\t", "\033[30;1m\\t\033[0m", $content); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
if (in_array(' ', $exclude) === false) { |
152
|
|
|
$content = str_replace(' ', "\033[30;1m·\033[0m", $content); |
153
|
|
|
} |
154
|
|
|
}//end if |
155
|
|
|
|
156
|
|
|
return $content; |
157
|
|
|
|
158
|
|
|
}//end prepareForOutput() |
159
|
|
|
|
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Returns true if the specified string is in the camel caps format. |
163
|
|
|
* |
164
|
|
|
* @param string $string The string the verify. |
165
|
|
|
* @param boolean $classFormat If true, check to see if the string is in the |
166
|
|
|
* class format. Class format strings must start |
167
|
|
|
* with a capital letter and contain no |
168
|
|
|
* underscores. |
169
|
|
|
* @param boolean $public If true, the first character in the string |
170
|
|
|
* must be an a-z character. If false, the |
171
|
|
|
* character must be an underscore. This |
172
|
|
|
* argument is only applicable if $classFormat |
173
|
|
|
* is false. |
174
|
|
|
* @param boolean $strict If true, the string must not have two capital |
175
|
|
|
* letters next to each other. If false, a |
176
|
|
|
* relaxed camel caps policy is used to allow |
177
|
|
|
* for acronyms. |
178
|
|
|
* |
179
|
|
|
* @return boolean |
180
|
|
|
*/ |
181
|
|
|
public static function isCamelCaps( |
182
|
|
|
$string, |
183
|
|
|
$classFormat=false, |
184
|
|
|
$public=true, |
185
|
|
|
$strict=true |
186
|
|
|
) { |
187
|
|
|
// Check the first character first. |
188
|
|
|
if ($classFormat === false) { |
189
|
|
|
$legalFirstChar = ''; |
190
|
|
|
if ($public === false) { |
191
|
|
|
$legalFirstChar = '[_]'; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
if ($strict === false) { |
195
|
|
|
// Can either start with a lowercase letter, or multiple uppercase |
196
|
|
|
// in a row, representing an acronym. |
197
|
|
|
$legalFirstChar .= '([A-Z]{2,}|[a-z])'; |
198
|
|
|
} else { |
199
|
|
|
$legalFirstChar .= '[a-z]'; |
200
|
|
|
} |
201
|
|
|
} else { |
202
|
|
|
$legalFirstChar = '[A-Z]'; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
if (preg_match("/^$legalFirstChar/", $string) === 0) { |
206
|
|
|
return false; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
// Check that the name only contains legal characters. |
210
|
|
|
$legalChars = 'a-zA-Z0-9'; |
211
|
|
|
if (preg_match("|[^$legalChars]|", substr($string, 1)) > 0) { |
212
|
|
|
return false; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
if ($strict === true) { |
216
|
|
|
// Check that there are not two capital letters next to each other. |
217
|
|
|
$length = strlen($string); |
218
|
|
|
$lastCharWasCaps = $classFormat; |
219
|
|
|
|
220
|
|
|
for ($i = 1; $i < $length; $i++) { |
221
|
|
|
$ascii = ord($string{$i}); |
222
|
|
|
if ($ascii >= 48 && $ascii <= 57) { |
223
|
|
|
// The character is a number, so it cant be a capital. |
224
|
|
|
$isCaps = false; |
225
|
|
|
} else { |
226
|
|
|
if (strtoupper($string{$i}) === $string{$i}) { |
227
|
|
|
$isCaps = true; |
228
|
|
|
} else { |
229
|
|
|
$isCaps = false; |
230
|
|
|
} |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
if ($isCaps === true && $lastCharWasCaps === true) { |
234
|
|
|
return false; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
$lastCharWasCaps = $isCaps; |
238
|
|
|
} |
239
|
|
|
}//end if |
240
|
|
|
|
241
|
|
|
return true; |
242
|
|
|
|
243
|
|
|
}//end isCamelCaps() |
244
|
|
|
|
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Returns true if the specified string is in the underscore caps format. |
248
|
|
|
* |
249
|
|
|
* @param string $string The string to verify. |
250
|
|
|
* |
251
|
|
|
* @return boolean |
252
|
|
|
*/ |
253
|
|
|
public static function isUnderscoreName($string) |
254
|
|
|
{ |
255
|
|
|
// If there are space in the name, it can't be valid. |
256
|
|
|
if (strpos($string, ' ') !== false) { |
257
|
|
|
return false; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
$validName = true; |
261
|
|
|
$nameBits = explode('_', $string); |
262
|
|
|
|
263
|
|
|
if (preg_match('|^[A-Z]|', $string) === 0) { |
264
|
|
|
// Name does not begin with a capital letter. |
265
|
|
|
$validName = false; |
266
|
|
|
} else { |
267
|
|
|
foreach ($nameBits as $bit) { |
268
|
|
|
if ($bit === '') { |
269
|
|
|
continue; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
if ($bit{0} !== strtoupper($bit{0})) { |
273
|
|
|
$validName = false; |
274
|
|
|
break; |
275
|
|
|
} |
276
|
|
|
} |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
return $validName; |
280
|
|
|
|
281
|
|
|
}//end isUnderscoreName() |
282
|
|
|
|
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* Returns a valid variable type for param/var tag. |
286
|
|
|
* |
287
|
|
|
* If type is not one of the standard type, it must be a custom type. |
288
|
|
|
* Returns the correct type name suggestion if type name is invalid. |
289
|
|
|
* |
290
|
|
|
* @param string $varType The variable type to process. |
291
|
|
|
* |
292
|
|
|
* @return string |
293
|
|
|
*/ |
294
|
|
|
public static function suggestType($varType) |
295
|
|
|
{ |
296
|
|
|
if ($varType === '') { |
297
|
|
|
return ''; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
if (in_array($varType, self::$allowedTypes) === true) { |
301
|
|
|
return $varType; |
302
|
|
|
} else { |
303
|
|
|
$lowerVarType = strtolower($varType); |
304
|
|
|
switch ($lowerVarType) { |
305
|
|
|
case 'bool': |
306
|
|
|
return 'boolean'; |
307
|
|
|
case 'double': |
308
|
|
|
case 'real': |
309
|
|
|
return 'float'; |
310
|
|
|
case 'int': |
311
|
|
|
return 'integer'; |
312
|
|
|
case 'array()': |
313
|
|
|
return 'array'; |
314
|
|
|
}//end switch |
315
|
|
|
|
316
|
|
|
if (strpos($lowerVarType, 'array(') !== false) { |
317
|
|
|
// Valid array declaration: |
318
|
|
|
// array, array(type), array(type1 => type2). |
|
|
|
|
319
|
|
|
$matches = array(); |
320
|
|
|
$pattern = '/^array\(\s*([^\s^=^>]*)(\s*=>\s*(.*))?\s*\)/i'; |
321
|
|
|
if (preg_match($pattern, $varType, $matches) !== 0) { |
322
|
|
|
$type1 = ''; |
323
|
|
|
if (isset($matches[1]) === true) { |
324
|
|
|
$type1 = $matches[1]; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
$type2 = ''; |
328
|
|
|
if (isset($matches[3]) === true) { |
329
|
|
|
$type2 = $matches[3]; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
$type1 = self::suggestType($type1); |
333
|
|
|
$type2 = self::suggestType($type2); |
334
|
|
|
if ($type2 !== '') { |
335
|
|
|
$type2 = ' => '.$type2; |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
return "array($type1$type2)"; |
339
|
|
|
} else { |
340
|
|
|
return 'array'; |
341
|
|
|
}//end if |
342
|
|
|
} else if (in_array($lowerVarType, self::$allowedTypes) === true) { |
343
|
|
|
// A valid type, but not lower cased. |
344
|
|
|
return $lowerVarType; |
345
|
|
|
} else { |
346
|
|
|
// Must be a custom type name. |
347
|
|
|
return $varType; |
348
|
|
|
}//end if |
349
|
|
|
}//end if |
350
|
|
|
|
351
|
|
|
}//end suggestType() |
352
|
|
|
|
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* Given a sniff class name, returns the code for the sniff. |
356
|
|
|
* |
357
|
|
|
* @param string $sniffClass The fully qualified sniff class name. |
358
|
|
|
* |
359
|
|
|
* @return string |
360
|
|
|
*/ |
361
|
|
|
public static function getSniffCode($sniffClass) |
362
|
|
|
{ |
363
|
|
|
$parts = explode('\\', $sniffClass); |
364
|
|
|
$sniff = array_pop($parts); |
365
|
|
|
|
366
|
|
|
if (substr($sniff, -5) === 'Sniff') { |
367
|
|
|
// Sniff class name. |
368
|
|
|
$sniff = substr($sniff, 0, -5); |
369
|
|
|
} else { |
370
|
|
|
// Unit test class name. |
371
|
|
|
$sniff = substr($sniff, 0, -8); |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
$category = array_pop($parts); |
375
|
|
|
$sniffDir = array_pop($parts); |
|
|
|
|
376
|
|
|
$standard = array_pop($parts); |
377
|
|
|
$code = $standard.'.'.$category.'.'.$sniff; |
378
|
|
|
return $code; |
379
|
|
|
|
380
|
|
|
}//end getSniffCode() |
381
|
|
|
|
382
|
|
|
|
383
|
|
|
}//end class |
384
|
|
|
|
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.