annotate vendor/squizlabs/php_codesniffer/src/Util/Common.php @ 19:fa3358dc1485 tip

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