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

PHPCSStandards / PHP_CodeSniffer / 14434600573

14 Apr 2025 12:01AM UTC coverage: 78.902%. Remained the same
14434600573

Pull #967

github

web-flow
Merge e2f817c09 into 7925a214c
Pull Request #967: PrintProgressDotsTest: fix up param names in data sets

24963 of 31638 relevant lines covered (78.9%)

68.08 hits per line

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

0.0
/src/Reports/Code.php
1
<?php
2
/**
3
 * Full report for PHP_CodeSniffer.
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\Reports;
11

12
use Exception;
13
use PHP_CodeSniffer\Files\File;
14
use PHP_CodeSniffer\Util\Common;
15
use PHP_CodeSniffer\Util\Timing;
16

17
class Code implements Report
18
{
19

20

21
    /**
22
     * Generate a partial report for a single processed file.
23
     *
24
     * Function should return TRUE if it printed or stored data about the file
25
     * and FALSE if it ignored the file. Returning TRUE indicates that the file and
26
     * its data should be counted in the grand totals.
27
     *
28
     * @param array<string, string|int|array> $report      Prepared report data.
29
     *                                                     See the {@see Report} interface for a detailed specification.
30
     * @param \PHP_CodeSniffer\Files\File     $phpcsFile   The file being reported on.
31
     * @param bool                            $showSources Show sources?
32
     * @param int                             $width       Maximum allowed line width.
33
     *
34
     * @return bool
35
     */
36
    public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
×
37
    {
38
        if ($report['errors'] === 0 && $report['warnings'] === 0) {
×
39
            // Nothing to print.
40
            return false;
×
41
        }
42

43
        // How many lines to show above and below the error line.
44
        $surroundingLines = 2;
×
45

46
        $file   = $report['filename'];
×
47
        $tokens = $phpcsFile->getTokens();
×
48
        if (empty($tokens) === true) {
×
49
            if (PHP_CODESNIFFER_VERBOSITY === 1) {
×
50
                $startTime = microtime(true);
×
51
                echo 'CODE report is parsing '.basename($file).' ';
×
52
            } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
×
53
                echo "CODE report is forcing parse of $file".PHP_EOL;
×
54
            }
55

56
            try {
57
                $phpcsFile->parse();
×
58

59
                // Make sure the fixer is aware of the reparsed file to prevent a race-condition
60
                // with the Diff report also re-parsing the file.
61
                $phpcsFile->fixer->startFile($phpcsFile);
×
62
            } catch (Exception $e) {
×
63
                // This is a second parse, so ignore exceptions.
64
                // They would have been added to the file's error list already.
65
            }
66

67
            if (PHP_CODESNIFFER_VERBOSITY === 1) {
×
68
                $timeTaken = ((microtime(true) - $startTime) * 1000);
×
69
                if ($timeTaken < 1000) {
×
70
                    $timeTaken = round($timeTaken);
×
71
                    echo "DONE in {$timeTaken}ms";
×
72
                } else {
73
                    $timeTaken = round(($timeTaken / 1000), 2);
×
74
                    echo "DONE in $timeTaken secs";
×
75
                }
76

77
                echo PHP_EOL;
×
78
            }
79

80
            $tokens = $phpcsFile->getTokens();
×
81
        }//end if
82

83
        // Create an array that maps lines to the first token on the line.
84
        $lineTokens = [];
×
85
        $lastLine   = 0;
×
86
        $stackPtr   = 0;
×
87
        foreach ($tokens as $stackPtr => $token) {
×
88
            if ($token['line'] !== $lastLine) {
×
89
                if ($lastLine > 0) {
×
90
                    $lineTokens[$lastLine]['end'] = ($stackPtr - 1);
×
91
                }
92

93
                $lastLine++;
×
94
                $lineTokens[$lastLine] = [
×
95
                    'start' => $stackPtr,
×
96
                    'end'   => null,
×
97
                ];
×
98
            }
99
        }
100

101
        // Make sure the last token in the file sits on an imaginary
102
        // last line so it is easier to generate code snippets at the
103
        // end of the file.
104
        $lineTokens[$lastLine]['end'] = $stackPtr;
×
105

106
        // Determine the longest code line we will be showing.
107
        $maxSnippetLength = 0;
×
108
        $eolLen           = strlen($phpcsFile->eolChar);
×
109
        foreach ($report['messages'] as $line => $lineErrors) {
×
110
            $startLine = max(($line - $surroundingLines), 1);
×
111
            $endLine   = min(($line + $surroundingLines), $lastLine);
×
112

113
            $maxLineNumLength = strlen($endLine);
×
114

115
            for ($i = $startLine; $i <= $endLine; $i++) {
×
116
                if ($i === 1) {
×
117
                    continue;
×
118
                }
119

120
                $lineLength       = ($tokens[($lineTokens[$i]['start'] - 1)]['column'] + $tokens[($lineTokens[$i]['start'] - 1)]['length'] - $eolLen);
×
121
                $maxSnippetLength = max($lineLength, $maxSnippetLength);
×
122
            }
123
        }
124

125
        $maxSnippetLength += ($maxLineNumLength + 8);
×
126

127
        // Determine the longest error message we will be showing.
128
        $maxErrorLength = 0;
×
129
        foreach ($report['messages'] as $lineErrors) {
×
130
            foreach ($lineErrors as $colErrors) {
×
131
                foreach ($colErrors as $error) {
×
132
                    $length = strlen($error['message']);
×
133
                    if ($showSources === true) {
×
134
                        $length += (strlen($error['source']) + 3);
×
135
                    }
136

137
                    $maxErrorLength = max($maxErrorLength, ($length + 1));
×
138
                }
139
            }
140
        }
141

142
        // The padding that all lines will require that are printing an error message overflow.
143
        if ($report['warnings'] > 0) {
×
144
            $typeLength = 7;
×
145
        } else {
146
            $typeLength = 5;
×
147
        }
148

149
        $errorPadding  = str_repeat(' ', ($maxLineNumLength + 7));
×
150
        $errorPadding .= str_repeat(' ', $typeLength);
×
151
        $errorPadding .= ' ';
×
152
        if ($report['fixable'] > 0) {
×
153
            $errorPadding .= '    ';
×
154
        }
155

156
        $errorPaddingLength = strlen($errorPadding);
×
157

158
        // The maximum amount of space an error message can use.
159
        $maxErrorSpace = ($width - $errorPaddingLength);
×
160
        if ($showSources === true) {
×
161
            // Account for the chars used to print colors.
162
            $maxErrorSpace += 8;
×
163
        }
164

165
        // Figure out the max report width we need and can use.
166
        $fileLength = strlen($file);
×
167
        $maxWidth   = max(($fileLength + 6), ($maxErrorLength + $errorPaddingLength));
×
168
        $width      = max(min($width, $maxWidth), $maxSnippetLength);
×
169
        if ($width < 70) {
×
170
            $width = 70;
×
171
        }
172

173
        // Print the file header.
174
        echo PHP_EOL."\033[1mFILE: ";
×
175
        if ($fileLength <= ($width - 6)) {
×
176
            echo $file;
×
177
        } else {
178
            echo '...'.substr($file, ($fileLength - ($width - 6)));
×
179
        }
180

181
        echo "\033[0m".PHP_EOL;
×
182
        echo str_repeat('-', $width).PHP_EOL;
×
183

184
        echo "\033[1m".'FOUND '.$report['errors'].' ERROR';
×
185
        if ($report['errors'] !== 1) {
×
186
            echo 'S';
×
187
        }
188

189
        if ($report['warnings'] > 0) {
×
190
            echo ' AND '.$report['warnings'].' WARNING';
×
191
            if ($report['warnings'] !== 1) {
×
192
                echo 'S';
×
193
            }
194
        }
195

196
        echo ' AFFECTING '.count($report['messages']).' LINE';
×
197
        if (count($report['messages']) !== 1) {
×
198
            echo 'S';
×
199
        }
200

201
        echo "\033[0m".PHP_EOL;
×
202

203
        foreach ($report['messages'] as $line => $lineErrors) {
×
204
            $startLine = max(($line - $surroundingLines), 1);
×
205
            $endLine   = min(($line + $surroundingLines), $lastLine);
×
206

207
            $snippet = '';
×
208
            if (isset($lineTokens[$startLine]) === true) {
×
209
                for ($i = $lineTokens[$startLine]['start']; $i <= $lineTokens[$endLine]['end']; $i++) {
×
210
                    $snippetLine = $tokens[$i]['line'];
×
211
                    if ($lineTokens[$snippetLine]['start'] === $i) {
×
212
                        // Starting a new line.
213
                        if ($snippetLine === $line) {
×
214
                            $snippet .= "\033[1m".'>> ';
×
215
                        } else {
216
                            $snippet .= '   ';
×
217
                        }
218

219
                        $snippet .= str_repeat(' ', ($maxLineNumLength - strlen($snippetLine)));
×
220
                        $snippet .= $snippetLine.':  ';
×
221
                        if ($snippetLine === $line) {
×
222
                            $snippet .= "\033[0m";
×
223
                        }
224
                    }
225

226
                    if (isset($tokens[$i]['orig_content']) === true) {
×
227
                        $tokenContent = $tokens[$i]['orig_content'];
×
228
                    } else {
229
                        $tokenContent = $tokens[$i]['content'];
×
230
                    }
231

232
                    if (strpos($tokenContent, "\t") !== false) {
×
233
                        $token            = $tokens[$i];
×
234
                        $token['content'] = $tokenContent;
×
235
                        if (stripos(PHP_OS, 'WIN') === 0) {
×
236
                            $tab = "\000";
×
237
                        } else {
238
                            $tab = "\033[30;1m»\033[0m";
×
239
                        }
240

241
                        $phpcsFile->tokenizer->replaceTabsInToken($token, $tab, "\000");
×
242
                        $tokenContent = $token['content'];
×
243
                    }
244

245
                    $tokenContent = Common::prepareForOutput($tokenContent, ["\r", "\n", "\t"]);
×
246
                    $tokenContent = str_replace("\000", ' ', $tokenContent);
×
247

248
                    $underline = false;
×
249
                    if ($snippetLine === $line && isset($lineErrors[$tokens[$i]['column']]) === true) {
×
250
                        $underline = true;
×
251
                    }
252

253
                    // Underline invisible characters as well.
254
                    if ($underline === true && trim($tokenContent) === '') {
×
255
                        $snippet .= "\033[4m".' '."\033[0m".$tokenContent;
×
256
                    } else {
257
                        if ($underline === true) {
×
258
                            $snippet .= "\033[4m";
×
259
                        }
260

261
                        $snippet .= $tokenContent;
×
262

263
                        if ($underline === true) {
×
264
                            $snippet .= "\033[0m";
×
265
                        }
266
                    }
267
                }//end for
268
            }//end if
269

270
            echo str_repeat('-', $width).PHP_EOL;
×
271

272
            foreach ($lineErrors as $colErrors) {
×
273
                foreach ($colErrors as $error) {
×
274
                    $padding = ($maxLineNumLength - strlen($line));
×
275
                    echo 'LINE '.str_repeat(' ', $padding).$line.': ';
×
276

277
                    if ($error['type'] === 'ERROR') {
×
278
                        echo "\033[31mERROR\033[0m";
×
279
                        if ($report['warnings'] > 0) {
×
280
                            echo '  ';
×
281
                        }
282
                    } else {
283
                        echo "\033[33mWARNING\033[0m";
×
284
                    }
285

286
                    echo ' ';
×
287
                    if ($report['fixable'] > 0) {
×
288
                        echo '[';
×
289
                        if ($error['fixable'] === true) {
×
290
                            echo 'x';
×
291
                        } else {
292
                            echo ' ';
×
293
                        }
294

295
                        echo '] ';
×
296
                    }
297

298
                    $message = $error['message'];
×
299
                    $message = str_replace("\n", "\n".$errorPadding, $message);
×
300
                    if ($showSources === true) {
×
301
                        $message = "\033[1m".$message."\033[0m".' ('.$error['source'].')';
×
302
                    }
303

304
                    $errorMsg = wordwrap(
×
305
                        $message,
×
306
                        $maxErrorSpace,
×
307
                        PHP_EOL.$errorPadding
×
308
                    );
×
309

310
                    echo $errorMsg.PHP_EOL;
×
311
                }//end foreach
312
            }//end foreach
313

314
            echo str_repeat('-', $width).PHP_EOL;
×
315
            echo rtrim($snippet).PHP_EOL;
×
316
        }//end foreach
317

318
        echo str_repeat('-', $width).PHP_EOL;
×
319
        if ($report['fixable'] > 0) {
×
320
            echo "\033[1m".'PHPCBF CAN FIX THE '.$report['fixable'].' MARKED SNIFF VIOLATIONS AUTOMATICALLY'."\033[0m".PHP_EOL;
×
321
            echo str_repeat('-', $width).PHP_EOL;
×
322
        }
323

324
        return true;
×
325

326
    }//end generateFileReport()
327

328

329
    /**
330
     * Prints all errors and warnings for each file processed.
331
     *
332
     * @param string $cachedData    Any partial report data that was returned from
333
     *                              generateFileReport during the run.
334
     * @param int    $totalFiles    Total number of files processed during the run.
335
     * @param int    $totalErrors   Total number of errors found during the run.
336
     * @param int    $totalWarnings Total number of warnings found during the run.
337
     * @param int    $totalFixable  Total number of problems that can be fixed.
338
     * @param bool   $showSources   Show sources?
339
     * @param int    $width         Maximum allowed line width.
340
     * @param bool   $interactive   Are we running in interactive mode?
341
     * @param bool   $toScreen      Is the report being printed to screen?
342
     *
343
     * @return void
344
     */
345
    public function generate(
×
346
        $cachedData,
347
        $totalFiles,
348
        $totalErrors,
349
        $totalWarnings,
350
        $totalFixable,
351
        $showSources=false,
352
        $width=80,
353
        $interactive=false,
354
        $toScreen=true
355
    ) {
356
        if ($cachedData === '') {
×
357
            return;
×
358
        }
359

360
        echo $cachedData;
×
361

362
        if ($toScreen === true && $interactive === false) {
×
363
            Timing::printRunTime();
×
364
        }
365

366
    }//end generate()
367

368

369
}//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

© 2025 Coveralls, Inc