comparison vendor/squizlabs/php_codesniffer/src/Util/Common.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents
children af1871eacc83
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
1 <?php
2 /**
3 * Basic util functions.
4 *
5 * @author Greg Sherwood <gsherwood@squiz.net>
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 class Common
13 {
14
15 /**
16 * An array of variable types for param/var we will check.
17 *
18 * @var string[]
19 */
20 public static $allowedTypes = [
21 'array',
22 'boolean',
23 'float',
24 'integer',
25 'mixed',
26 'object',
27 'string',
28 'resource',
29 'callable',
30 ];
31
32
33 /**
34 * Return TRUE if the path is a PHAR file.
35 *
36 * @param string $path The path to use.
37 *
38 * @return mixed
39 */
40 public static function isPharFile($path)
41 {
42 if (strpos($path, 'phar://') === 0) {
43 return true;
44 }
45
46 return false;
47
48 }//end isPharFile()
49
50
51 /**
52 * CodeSniffer alternative for realpath.
53 *
54 * Allows for PHAR support.
55 *
56 * @param string $path The path to use.
57 *
58 * @return mixed
59 */
60 public static function realpath($path)
61 {
62 // Support the path replacement of ~ with the user's home directory.
63 if (substr($path, 0, 2) === '~/') {
64 $homeDir = getenv('HOME');
65 if ($homeDir !== false) {
66 $path = $homeDir.substr($path, 1);
67 }
68 }
69
70 // Check for process substitution.
71 if (strpos($path, '/dev/fd') === 0) {
72 return str_replace('/dev/fd', 'php://fd', $path);
73 }
74
75 // No extra work needed if this is not a phar file.
76 if (self::isPharFile($path) === false) {
77 return realpath($path);
78 }
79
80 // Before trying to break down the file path,
81 // check if it exists first because it will mostly not
82 // change after running the below code.
83 if (file_exists($path) === true) {
84 return $path;
85 }
86
87 $phar = \Phar::running(false);
88 $extra = str_replace('phar://'.$phar, '', $path);
89 $path = realpath($phar);
90 if ($path === false) {
91 return false;
92 }
93
94 $path = 'phar://'.$path.$extra;
95 if (file_exists($path) === true) {
96 return $path;
97 }
98
99 return false;
100
101 }//end realpath()
102
103
104 /**
105 * Removes a base path from the front of a file path.
106 *
107 * @param string $path The path of the file.
108 * @param string $basepath The base path to remove. This should not end
109 * with a directory separator.
110 *
111 * @return string
112 */
113 public static function stripBasepath($path, $basepath)
114 {
115 if (empty($basepath) === true) {
116 return $path;
117 }
118
119 $basepathLen = strlen($basepath);
120 if (substr($path, 0, $basepathLen) === $basepath) {
121 $path = substr($path, $basepathLen);
122 }
123
124 $path = ltrim($path, DIRECTORY_SEPARATOR);
125 if ($path === '') {
126 $path = '.';
127 }
128
129 return $path;
130
131 }//end stripBasepath()
132
133
134 /**
135 * Detects the EOL character being used in a string.
136 *
137 * @param string $contents The contents to check.
138 *
139 * @return string
140 */
141 public static function detectLineEndings($contents)
142 {
143 if (preg_match("/\r\n?|\n/", $contents, $matches) !== 1) {
144 // Assume there are no newlines.
145 $eolChar = "\n";
146 } else {
147 $eolChar = $matches[0];
148 }
149
150 return $eolChar;
151
152 }//end detectLineEndings()
153
154
155 /**
156 * Check if STDIN is a TTY.
157 *
158 * @return boolean
159 */
160 public static function isStdinATTY()
161 {
162 // The check is slow (especially calling `tty`) so we static
163 // cache the result.
164 static $isTTY = null;
165
166 if ($isTTY !== null) {
167 return $isTTY;
168 }
169
170 if (defined('STDIN') === false) {
171 return false;
172 }
173
174 // If PHP has the POSIX extensions we will use them.
175 if (function_exists('posix_isatty') === true) {
176 $isTTY = (posix_isatty(STDIN) === true);
177 return $isTTY;
178 }
179
180 // Next try is detecting whether we have `tty` installed and use that.
181 if (defined('PHP_WINDOWS_VERSION_PLATFORM') === true) {
182 $devnull = 'NUL';
183 $which = 'where';
184 } else {
185 $devnull = '/dev/null';
186 $which = 'which';
187 }
188
189 $tty = trim(shell_exec("$which tty 2> $devnull"));
190 if (empty($tty) === false) {
191 exec("tty -s 2> $devnull", $output, $returnValue);
192 $isTTY = ($returnValue === 0);
193 return $isTTY;
194 }
195
196 // Finally we will use fstat. The solution borrowed from
197 // https://stackoverflow.com/questions/11327367/detect-if-a-php-script-is-being-run-interactively-or-not
198 // This doesn't work on Mingw/Cygwin/... using Mintty but they
199 // have `tty` installed.
200 $type = [
201 'S_IFMT' => 0170000,
202 'S_IFIFO' => 0010000,
203 ];
204
205 $stat = fstat(STDIN);
206 $mode = ($stat['mode'] & $type['S_IFMT']);
207 $isTTY = ($mode !== $type['S_IFIFO']);
208
209 return $isTTY;
210
211 }//end isStdinATTY()
212
213
214 /**
215 * Prepares token content for output to screen.
216 *
217 * Replaces invisible characters so they are visible. On non-Windows
218 * OSes it will also colour the invisible characters.
219 *
220 * @param string $content The content to prepare.
221 * @param string[] $exclude A list of characters to leave invisible.
222 * Can contain \r, \n, \t and a space.
223 *
224 * @return string
225 */
226 public static function prepareForOutput($content, $exclude=[])
227 {
228 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
229 if (in_array("\r", $exclude) === false) {
230 $content = str_replace("\r", '\r', $content);
231 }
232
233 if (in_array("\n", $exclude) === false) {
234 $content = str_replace("\n", '\n', $content);
235 }
236
237 if (in_array("\t", $exclude) === false) {
238 $content = str_replace("\t", '\t', $content);
239 }
240 } else {
241 if (in_array("\r", $exclude) === false) {
242 $content = str_replace("\r", "\033[30;1m\\r\033[0m", $content);
243 }
244
245 if (in_array("\n", $exclude) === false) {
246 $content = str_replace("\n", "\033[30;1m\\n\033[0m", $content);
247 }
248
249 if (in_array("\t", $exclude) === false) {
250 $content = str_replace("\t", "\033[30;1m\\t\033[0m", $content);
251 }
252
253 if (in_array(' ', $exclude) === false) {
254 $content = str_replace(' ', "\033[30;1m·\033[0m", $content);
255 }
256 }//end if
257
258 return $content;
259
260 }//end prepareForOutput()
261
262
263 /**
264 * Returns true if the specified string is in the camel caps format.
265 *
266 * @param string $string The string the verify.
267 * @param boolean $classFormat If true, check to see if the string is in the
268 * class format. Class format strings must start
269 * with a capital letter and contain no
270 * underscores.
271 * @param boolean $public If true, the first character in the string
272 * must be an a-z character. If false, the
273 * character must be an underscore. This
274 * argument is only applicable if $classFormat
275 * is false.
276 * @param boolean $strict If true, the string must not have two capital
277 * letters next to each other. If false, a
278 * relaxed camel caps policy is used to allow
279 * for acronyms.
280 *
281 * @return boolean
282 */
283 public static function isCamelCaps(
284 $string,
285 $classFormat=false,
286 $public=true,
287 $strict=true
288 ) {
289 // Check the first character first.
290 if ($classFormat === false) {
291 $legalFirstChar = '';
292 if ($public === false) {
293 $legalFirstChar = '[_]';
294 }
295
296 if ($strict === false) {
297 // Can either start with a lowercase letter, or multiple uppercase
298 // in a row, representing an acronym.
299 $legalFirstChar .= '([A-Z]{2,}|[a-z])';
300 } else {
301 $legalFirstChar .= '[a-z]';
302 }
303 } else {
304 $legalFirstChar = '[A-Z]';
305 }
306
307 if (preg_match("/^$legalFirstChar/", $string) === 0) {
308 return false;
309 }
310
311 // Check that the name only contains legal characters.
312 $legalChars = 'a-zA-Z0-9';
313 if (preg_match("|[^$legalChars]|", substr($string, 1)) > 0) {
314 return false;
315 }
316
317 if ($strict === true) {
318 // Check that there are not two capital letters next to each other.
319 $length = strlen($string);
320 $lastCharWasCaps = $classFormat;
321
322 for ($i = 1; $i < $length; $i++) {
323 $ascii = ord($string{$i});
324 if ($ascii >= 48 && $ascii <= 57) {
325 // The character is a number, so it cant be a capital.
326 $isCaps = false;
327 } else {
328 if (strtoupper($string{$i}) === $string{$i}) {
329 $isCaps = true;
330 } else {
331 $isCaps = false;
332 }
333 }
334
335 if ($isCaps === true && $lastCharWasCaps === true) {
336 return false;
337 }
338
339 $lastCharWasCaps = $isCaps;
340 }
341 }//end if
342
343 return true;
344
345 }//end isCamelCaps()
346
347
348 /**
349 * Returns true if the specified string is in the underscore caps format.
350 *
351 * @param string $string The string to verify.
352 *
353 * @return boolean
354 */
355 public static function isUnderscoreName($string)
356 {
357 // If there are space in the name, it can't be valid.
358 if (strpos($string, ' ') !== false) {
359 return false;
360 }
361
362 $validName = true;
363 $nameBits = explode('_', $string);
364
365 if (preg_match('|^[A-Z]|', $string) === 0) {
366 // Name does not begin with a capital letter.
367 $validName = false;
368 } else {
369 foreach ($nameBits as $bit) {
370 if ($bit === '') {
371 continue;
372 }
373
374 if ($bit{0} !== strtoupper($bit{0})) {
375 $validName = false;
376 break;
377 }
378 }
379 }
380
381 return $validName;
382
383 }//end isUnderscoreName()
384
385
386 /**
387 * Returns a valid variable type for param/var tag.
388 *
389 * If type is not one of the standard type, it must be a custom type.
390 * Returns the correct type name suggestion if type name is invalid.
391 *
392 * @param string $varType The variable type to process.
393 *
394 * @return string
395 */
396 public static function suggestType($varType)
397 {
398 if ($varType === '') {
399 return '';
400 }
401
402 if (in_array($varType, self::$allowedTypes) === true) {
403 return $varType;
404 } else {
405 $lowerVarType = strtolower($varType);
406 switch ($lowerVarType) {
407 case 'bool':
408 case 'boolean':
409 return 'boolean';
410 case 'double':
411 case 'real':
412 case 'float':
413 return 'float';
414 case 'int':
415 case 'integer':
416 return 'integer';
417 case 'array()':
418 case 'array':
419 return 'array';
420 }//end switch
421
422 if (strpos($lowerVarType, 'array(') !== false) {
423 // Valid array declaration:
424 // array, array(type), array(type1 => type2).
425 $matches = [];
426 $pattern = '/^array\(\s*([^\s^=^>]*)(\s*=>\s*(.*))?\s*\)/i';
427 if (preg_match($pattern, $varType, $matches) !== 0) {
428 $type1 = '';
429 if (isset($matches[1]) === true) {
430 $type1 = $matches[1];
431 }
432
433 $type2 = '';
434 if (isset($matches[3]) === true) {
435 $type2 = $matches[3];
436 }
437
438 $type1 = self::suggestType($type1);
439 $type2 = self::suggestType($type2);
440 if ($type2 !== '') {
441 $type2 = ' => '.$type2;
442 }
443
444 return "array($type1$type2)";
445 } else {
446 return 'array';
447 }//end if
448 } else if (in_array($lowerVarType, self::$allowedTypes) === true) {
449 // A valid type, but not lower cased.
450 return $lowerVarType;
451 } else {
452 // Must be a custom type name.
453 return $varType;
454 }//end if
455 }//end if
456
457 }//end suggestType()
458
459
460 /**
461 * Given a sniff class name, returns the code for the sniff.
462 *
463 * @param string $sniffClass The fully qualified sniff class name.
464 *
465 * @return string
466 */
467 public static function getSniffCode($sniffClass)
468 {
469 $parts = explode('\\', $sniffClass);
470 $sniff = array_pop($parts);
471
472 if (substr($sniff, -5) === 'Sniff') {
473 // Sniff class name.
474 $sniff = substr($sniff, 0, -5);
475 } else {
476 // Unit test class name.
477 $sniff = substr($sniff, 0, -8);
478 }
479
480 $category = array_pop($parts);
481 $sniffDir = array_pop($parts);
482 $standard = array_pop($parts);
483 $code = $standard.'.'.$category.'.'.$sniff;
484 return $code;
485
486 }//end getSniffCode()
487
488
489 /**
490 * Removes project-specific information from a sniff class name.
491 *
492 * @param string $sniffClass The fully qualified sniff class name.
493 *
494 * @return string
495 */
496 public static function cleanSniffClass($sniffClass)
497 {
498 $newName = strtolower($sniffClass);
499
500 $sniffPos = strrpos($newName, '\sniffs\\');
501 if ($sniffPos === false) {
502 // Nothing we can do as it isn't in a known format.
503 return $newName;
504 }
505
506 $end = (strlen($newName) - $sniffPos + 1);
507 $start = strrpos($newName, '\\', ($end * -1));
508
509 if ($start === false) {
510 // Nothing needs to be cleaned.
511 return $newName;
512 }
513
514 $newName = substr($newName, ($start + 1));
515 return $newName;
516
517 }//end cleanSniffClass()
518
519
520 }//end class