• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPCSStandards / PHP_CodeSniffer / 8939618108

03 May 2024 01:17PM UTC coverage: 75.009% (+0.6%) from 74.408%
8939618108

Pull #455

github

web-flow
Merge eeb3c2fad into bb267bb2c
Pull Request #455: Generic/DisallowLongArraySyntax: improve code coverage

4 of 4 new or added lines in 1 file covered. (100.0%)

710 existing lines in 7 files now uncovered.

23078 of 30767 relevant lines covered (75.01%)

59.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

37.98
/src/Util/Common.php
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/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8
 */
9

10
namespace PHP_CodeSniffer\Util;
11

12
use Phar;
13

14
class Common
15
{
16

17
    /**
18
     * An array of variable types for param/var we will check.
19
     *
20
     * @var string[]
21
     */
22
    public static $allowedTypes = [
23
        'array',
24
        'boolean',
25
        'float',
26
        'integer',
27
        'mixed',
28
        'object',
29
        'string',
30
        'resource',
31
        'callable',
32
    ];
33

34

35
    /**
36
     * Return TRUE if the path is a PHAR file.
37
     *
38
     * @param string $path The path to use.
39
     *
40
     * @return bool
41
     */
42
    public static function isPharFile($path)
×
43
    {
44
        if (strpos($path, 'phar://') === 0) {
×
45
            return true;
×
46
        }
47

48
        return false;
×
49

50
    }//end isPharFile()
51

52

53
    /**
54
     * Checks if a file is readable.
55
     *
56
     * Addresses PHP bug related to reading files from network drives on Windows.
57
     * e.g. when using WSL2.
58
     *
59
     * @param string $path The path to the file.
60
     *
61
     * @return boolean
62
     */
63
    public static function isReadable($path)
×
64
    {
65
        if (@is_readable($path) === true) {
×
66
            return true;
×
67
        }
68

69
        if (@file_exists($path) === true && @is_file($path) === true) {
×
70
            $f = @fopen($path, 'rb');
×
71
            if (fclose($f) === true) {
×
72
                return true;
×
73
            }
74
        }
75

76
        return false;
×
77

78
    }//end isReadable()
79

80

81
    /**
82
     * CodeSniffer alternative for realpath.
83
     *
84
     * Allows for PHAR support.
85
     *
86
     * @param string $path The path to use.
87
     *
88
     * @return string|false
89
     */
90
    public static function realpath($path)
×
91
    {
92
        // Support the path replacement of ~ with the user's home directory.
93
        if (substr($path, 0, 2) === '~/') {
×
94
            $homeDir = getenv('HOME');
×
95
            if ($homeDir !== false) {
×
96
                $path = $homeDir.substr($path, 1);
×
97
            }
98
        }
99

100
        // Check for process substitution.
101
        if (strpos($path, '/dev/fd') === 0) {
×
102
            return str_replace('/dev/fd', 'php://fd', $path);
×
103
        }
104

105
        // No extra work needed if this is not a phar file.
106
        if (self::isPharFile($path) === false) {
×
107
            return realpath($path);
×
108
        }
109

110
        // Before trying to break down the file path,
111
        // check if it exists first because it will mostly not
112
        // change after running the below code.
113
        if (file_exists($path) === true) {
×
114
            return $path;
×
115
        }
116

117
        $phar  = Phar::running(false);
×
118
        $extra = str_replace('phar://'.$phar, '', $path);
×
119
        $path  = realpath($phar);
×
120
        if ($path === false) {
×
121
            return false;
×
122
        }
123

124
        $path = 'phar://'.$path.$extra;
×
125
        if (file_exists($path) === true) {
×
126
            return $path;
×
127
        }
128

129
        return false;
×
130

131
    }//end realpath()
132

133

134
    /**
135
     * Removes a base path from the front of a file path.
136
     *
137
     * @param string $path     The path of the file.
138
     * @param string $basepath The base path to remove. This should not end
139
     *                         with a directory separator.
140
     *
141
     * @return string
142
     */
143
    public static function stripBasepath($path, $basepath)
×
144
    {
145
        if (empty($basepath) === true) {
×
146
            return $path;
×
147
        }
148

149
        $basepathLen = strlen($basepath);
×
150
        if (substr($path, 0, $basepathLen) === $basepath) {
×
151
            $path = substr($path, $basepathLen);
×
152
        }
153

154
        $path = ltrim($path, DIRECTORY_SEPARATOR);
×
155
        if ($path === '') {
×
156
            $path = '.';
×
157
        }
158

159
        return $path;
×
160

161
    }//end stripBasepath()
162

163

164
    /**
165
     * Detects the EOL character being used in a string.
166
     *
167
     * @param string $contents The contents to check.
168
     *
169
     * @return string
170
     */
171
    public static function detectLineEndings($contents)
×
172
    {
173
        if (preg_match("/\r\n?|\n/", $contents, $matches) !== 1) {
×
174
            // Assume there are no newlines.
175
            $eolChar = "\n";
×
176
        } else {
177
            $eolChar = $matches[0];
×
178
        }
179

180
        return $eolChar;
×
181

182
    }//end detectLineEndings()
183

184

185
    /**
186
     * Check if STDIN is a TTY.
187
     *
188
     * @return boolean
189
     */
190
    public static function isStdinATTY()
×
191
    {
192
        // The check is slow (especially calling `tty`) so we static
193
        // cache the result.
194
        static $isTTY = null;
×
195

196
        if ($isTTY !== null) {
×
197
            return $isTTY;
×
198
        }
199

200
        if (defined('STDIN') === false) {
×
201
            return false;
×
202
        }
203

204
        // If PHP has the POSIX extensions we will use them.
205
        if (function_exists('posix_isatty') === true) {
×
206
            $isTTY = (posix_isatty(STDIN) === true);
×
207
            return $isTTY;
×
208
        }
209

210
        // Next try is detecting whether we have `tty` installed and use that.
211
        if (defined('PHP_WINDOWS_VERSION_PLATFORM') === true) {
×
212
            $devnull = 'NUL';
×
213
            $which   = 'where';
×
214
        } else {
215
            $devnull = '/dev/null';
×
216
            $which   = 'which';
×
217
        }
218

219
        $tty = trim(shell_exec("$which tty 2> $devnull"));
×
220
        if (empty($tty) === false) {
×
221
            exec("tty -s 2> $devnull", $output, $returnValue);
×
222
            $isTTY = ($returnValue === 0);
×
223
            return $isTTY;
×
224
        }
225

226
        // Finally we will use fstat.  The solution borrowed from
227
        // https://stackoverflow.com/questions/11327367/detect-if-a-php-script-is-being-run-interactively-or-not
228
        // This doesn't work on Mingw/Cygwin/... using Mintty but they
229
        // have `tty` installed.
230
        $type = [
231
            'S_IFMT'  => 0170000,
×
232
            'S_IFIFO' => 0010000,
233
        ];
234

235
        $stat  = fstat(STDIN);
×
236
        $mode  = ($stat['mode'] & $type['S_IFMT']);
×
237
        $isTTY = ($mode !== $type['S_IFIFO']);
×
238

239
        return $isTTY;
×
240

241
    }//end isStdinATTY()
242

243

244
    /**
245
     * Escape a path to a system command.
246
     *
247
     * @param string $cmd The path to the system command.
248
     *
249
     * @return string
250
     */
251
    public static function escapeshellcmd($cmd)
×
252
    {
253
        $cmd = escapeshellcmd($cmd);
×
254

255
        if (stripos(PHP_OS, 'WIN') === 0) {
×
256
            // Spaces are not escaped by escapeshellcmd on Windows, but need to be
257
            // for the command to be able to execute.
258
            $cmd = preg_replace('`(?<!^) `', '^ ', $cmd);
×
259
        }
260

261
        return $cmd;
×
262

263
    }//end escapeshellcmd()
264

265

266
    /**
267
     * Prepares token content for output to screen.
268
     *
269
     * Replaces invisible characters so they are visible. On non-Windows
270
     * operating systems it will also colour the invisible characters.
271
     *
272
     * @param string   $content The content to prepare.
273
     * @param string[] $exclude A list of characters to leave invisible.
274
     *                          Can contain \r, \n, \t and a space.
275
     *
276
     * @return string
277
     */
278
    public static function prepareForOutput($content, $exclude=[])
×
279
    {
280
        if (stripos(PHP_OS, 'WIN') === 0) {
×
281
            if (in_array("\r", $exclude, true) === false) {
×
282
                $content = str_replace("\r", '\r', $content);
×
283
            }
284

285
            if (in_array("\n", $exclude, true) === false) {
×
286
                $content = str_replace("\n", '\n', $content);
×
287
            }
288

289
            if (in_array("\t", $exclude, true) === false) {
×
290
                $content = str_replace("\t", '\t', $content);
×
291
            }
292
        } else {
293
            if (in_array("\r", $exclude, true) === false) {
×
294
                $content = str_replace("\r", "\033[30;1m\\r\033[0m", $content);
×
295
            }
296

297
            if (in_array("\n", $exclude, true) === false) {
×
298
                $content = str_replace("\n", "\033[30;1m\\n\033[0m", $content);
×
299
            }
300

301
            if (in_array("\t", $exclude, true) === false) {
×
302
                $content = str_replace("\t", "\033[30;1m\\t\033[0m", $content);
×
303
            }
304

305
            if (in_array(' ', $exclude, true) === false) {
×
306
                $content = str_replace(' ', "\033[30;1m·\033[0m", $content);
×
307
            }
308
        }//end if
309

310
        return $content;
×
311

312
    }//end prepareForOutput()
313

314

315
    /**
316
     * Strip colors from a text for output to screen.
317
     *
318
     * @param string $text The text to process.
319
     *
320
     * @return string
321
     */
322
    public static function stripColors($text)
27✔
323
    {
324
        return preg_replace('`\033\[[0-9;]+m`', '', $text);
27✔
325

326
    }//end stripColors()
327

328

329
    /**
330
     * Returns true if the specified string is in the camel caps format.
331
     *
332
     * @param string  $string      The string the verify.
333
     * @param boolean $classFormat If true, check to see if the string is in the
334
     *                             class format. Class format strings must start
335
     *                             with a capital letter and contain no
336
     *                             underscores.
337
     * @param boolean $public      If true, the first character in the string
338
     *                             must be an a-z character. If false, the
339
     *                             character must be an underscore. This
340
     *                             argument is only applicable if $classFormat
341
     *                             is false.
342
     * @param boolean $strict      If true, the string must not have two capital
343
     *                             letters next to each other. If false, a
344
     *                             relaxed camel caps policy is used to allow
345
     *                             for acronyms.
346
     *
347
     * @return boolean
348
     */
349
    public static function isCamelCaps(
21✔
350
        $string,
351
        $classFormat=false,
352
        $public=true,
353
        $strict=true
354
    ) {
355
        // Check the first character first.
356
        if ($classFormat === false) {
21✔
357
            $legalFirstChar = '';
12✔
358
            if ($public === false) {
12✔
359
                $legalFirstChar = '[_]';
6✔
360
            }
2✔
361

362
            if ($strict === false) {
12✔
363
                // Can either start with a lowercase letter, or multiple uppercase
364
                // in a row, representing an acronym.
365
                $legalFirstChar .= '([A-Z]{2,}|[a-z])';
9✔
366
            } else {
3✔
367
                $legalFirstChar .= '[a-z]';
12✔
368
            }
369
        } else {
4✔
370
            $legalFirstChar = '[A-Z]';
9✔
371
        }
372

373
        if (preg_match("/^$legalFirstChar/", $string) === 0) {
21✔
374
            return false;
12✔
375
        }
376

377
        // Check that the name only contains legal characters.
378
        $legalChars = 'a-zA-Z0-9';
18✔
379
        if (preg_match("|[^$legalChars]|", substr($string, 1)) > 0) {
18✔
380
            return false;
9✔
381
        }
382

383
        if ($strict === true) {
15✔
384
            // Check that there are not two capital letters next to each other.
385
            $length          = strlen($string);
15✔
386
            $lastCharWasCaps = $classFormat;
15✔
387

388
            for ($i = 1; $i < $length; $i++) {
15✔
389
                $ascii = ord($string[$i]);
15✔
390
                if ($ascii >= 48 && $ascii <= 57) {
15✔
391
                    // The character is a number, so it can't be a capital.
392
                    $isCaps = false;
3✔
393
                } else {
1✔
394
                    if (strtoupper($string[$i]) === $string[$i]) {
15✔
395
                        $isCaps = true;
15✔
396
                    } else {
5✔
397
                        $isCaps = false;
15✔
398
                    }
399
                }
400

401
                if ($isCaps === true && $lastCharWasCaps === true) {
15✔
402
                    return false;
6✔
403
                }
404

405
                $lastCharWasCaps = $isCaps;
15✔
406
            }
5✔
407
        }//end if
3✔
408

409
        return true;
9✔
410

411
    }//end isCamelCaps()
412

413

414
    /**
415
     * Returns true if the specified string is in the underscore caps format.
416
     *
417
     * @param string $string The string to verify.
418
     *
419
     * @return boolean
420
     */
421
    public static function isUnderscoreName($string)
×
422
    {
423
        // If there are space in the name, it can't be valid.
UNCOV
424
        if (strpos($string, ' ') !== false) {
×
UNCOV
425
            return false;
×
426
        }
427

428
        $validName = true;
×
UNCOV
429
        $nameBits  = explode('_', $string);
×
430

UNCOV
431
        if (preg_match('|^[A-Z]|', $string) === 0) {
×
432
            // Name does not begin with a capital letter.
433
            $validName = false;
×
434
        } else {
UNCOV
435
            foreach ($nameBits as $bit) {
×
UNCOV
436
                if ($bit === '') {
×
UNCOV
437
                    continue;
×
438
                }
439

UNCOV
440
                if ($bit[0] !== strtoupper($bit[0])) {
×
UNCOV
441
                    $validName = false;
×
UNCOV
442
                    break;
×
443
                }
444
            }
445
        }
446

UNCOV
447
        return $validName;
×
448

449
    }//end isUnderscoreName()
450

451

452
    /**
453
     * Returns a valid variable type for param/var tags.
454
     *
455
     * If type is not one of the standard types, it must be a custom type.
456
     * Returns the correct type name suggestion if type name is invalid.
457
     *
458
     * @param string $varType The variable type to process.
459
     *
460
     * @return string
461
     */
462
    public static function suggestType($varType)
135✔
463
    {
464
        if ($varType === '') {
135✔
465
            return '';
6✔
466
        }
467

468
        if (in_array($varType, self::$allowedTypes, true) === true) {
132✔
469
            return $varType;
36✔
470
        } else {
471
            $lowerVarType = strtolower($varType);
105✔
472
            switch ($lowerVarType) {
35✔
473
            case 'bool':
105✔
474
            case 'boolean':
101✔
475
                return 'boolean';
15✔
476
            case 'double':
93✔
477
            case 'real':
89✔
478
            case 'float':
87✔
479
                return 'float';
21✔
480
            case 'int':
78✔
481
            case 'integer':
74✔
482
                return 'integer';
15✔
483
            case 'array()':
66✔
484
            case 'array':
64✔
485
                return 'array';
9✔
486
            }//end switch
487

488
            if (strpos($lowerVarType, 'array(') !== false) {
57✔
489
                // Valid array declaration:
490
                // array, array(type), array(type1 => type2).
491
                $matches = [];
18✔
492
                $pattern = '/^array\(\s*([^\s^=^>]*)(\s*=>\s*(.*))?\s*\)/i';
18✔
493
                if (preg_match($pattern, $varType, $matches) !== 0) {
18✔
494
                    $type1 = '';
15✔
495
                    if (isset($matches[1]) === true) {
15✔
496
                        $type1 = $matches[1];
15✔
497
                    }
5✔
498

499
                    $type2 = '';
15✔
500
                    if (isset($matches[3]) === true) {
15✔
501
                        $type2 = $matches[3];
12✔
502
                    }
4✔
503

504
                    $type1 = self::suggestType($type1);
15✔
505
                    $type2 = self::suggestType($type2);
15✔
506
                    if ($type2 !== '') {
15✔
507
                        $type2 = ' => '.$type2;
12✔
508
                    }
4✔
509

510
                    return "array($type1$type2)";
15✔
511
                } else {
512
                    return 'array';
3✔
513
                }//end if
514
            } else if (in_array($lowerVarType, self::$allowedTypes, true) === true) {
39✔
515
                // A valid type, but not lower cased.
516
                return $lowerVarType;
30✔
517
            } else {
518
                // Must be a custom type name.
519
                return $varType;
9✔
520
            }//end if
521
        }//end if
522

523
    }//end suggestType()
524

525

526
    /**
527
     * Given a sniff class name, returns the code for the sniff.
528
     *
529
     * @param string $sniffClass The fully qualified sniff class name.
530
     *
531
     * @return string
532
     */
533
    public static function getSniffCode($sniffClass)
×
534
    {
535
        $parts = explode('\\', $sniffClass);
×
536
        $sniff = array_pop($parts);
×
537

UNCOV
538
        if (substr($sniff, -5) === 'Sniff') {
×
539
            // Sniff class name.
UNCOV
540
            $sniff = substr($sniff, 0, -5);
×
541
        } else {
542
            // Unit test class name.
UNCOV
543
            $sniff = substr($sniff, 0, -8);
×
544
        }
545

UNCOV
546
        $category = array_pop($parts);
×
UNCOV
547
        $sniffDir = array_pop($parts);
×
548
        $standard = array_pop($parts);
×
UNCOV
549
        $code     = $standard.'.'.$category.'.'.$sniff;
×
550
        return $code;
×
551

552
    }//end getSniffCode()
553

554

555
    /**
556
     * Removes project-specific information from a sniff class name.
557
     *
558
     * @param string $sniffClass The fully qualified sniff class name.
559
     *
560
     * @return string
561
     */
UNCOV
562
    public static function cleanSniffClass($sniffClass)
×
563
    {
UNCOV
564
        $newName = strtolower($sniffClass);
×
565

566
        $sniffPos = strrpos($newName, '\sniffs\\');
×
567
        if ($sniffPos === false) {
×
568
            // Nothing we can do as it isn't in a known format.
UNCOV
569
            return $newName;
×
570
        }
571

UNCOV
572
        $end   = (strlen($newName) - $sniffPos + 1);
×
UNCOV
573
        $start = strrpos($newName, '\\', ($end * -1));
×
574

UNCOV
575
        if ($start === false) {
×
576
            // Nothing needs to be cleaned.
UNCOV
577
            return $newName;
×
578
        }
579

UNCOV
580
        $newName = substr($newName, ($start + 1));
×
UNCOV
581
        return $newName;
×
582

583
    }//end cleanSniffClass()
584

585

586
}//end class
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc