annotate vendor/squizlabs/php_codesniffer/src/Fixer.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 * A helper class for fixing errors.
Chris@17 4 *
Chris@17 5 * Provides helper functions that act upon a token array and modify the file
Chris@17 6 * content.
Chris@17 7 *
Chris@17 8 * @author Greg Sherwood <gsherwood@squiz.net>
Chris@17 9 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
Chris@17 10 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
Chris@17 11 */
Chris@17 12
Chris@17 13 namespace PHP_CodeSniffer;
Chris@17 14
Chris@17 15 use PHP_CodeSniffer\Files\File;
Chris@17 16 use PHP_CodeSniffer\Util\Common;
Chris@17 17
Chris@17 18 class Fixer
Chris@17 19 {
Chris@17 20
Chris@17 21 /**
Chris@17 22 * Is the fixer enabled and fixing a file?
Chris@17 23 *
Chris@17 24 * Sniffs should check this value to ensure they are not
Chris@17 25 * doing extra processing to prepare for a fix when fixing is
Chris@17 26 * not required.
Chris@17 27 *
Chris@17 28 * @var boolean
Chris@17 29 */
Chris@17 30 public $enabled = false;
Chris@17 31
Chris@17 32 /**
Chris@17 33 * The number of times we have looped over a file.
Chris@17 34 *
Chris@17 35 * @var integer
Chris@17 36 */
Chris@17 37 public $loops = 0;
Chris@17 38
Chris@17 39 /**
Chris@17 40 * The file being fixed.
Chris@17 41 *
Chris@17 42 * @var \PHP_CodeSniffer\Files\File
Chris@17 43 */
Chris@17 44 private $currentFile = null;
Chris@17 45
Chris@17 46 /**
Chris@17 47 * The list of tokens that make up the file contents.
Chris@17 48 *
Chris@17 49 * This is a simplified list which just contains the token content and nothing
Chris@17 50 * else. This is the array that is updated as fixes are made, not the file's
Chris@17 51 * token array. Imploding this array will give you the file content back.
Chris@17 52 *
Chris@17 53 * @var array<int, string>
Chris@17 54 */
Chris@17 55 private $tokens = [];
Chris@17 56
Chris@17 57 /**
Chris@17 58 * A list of tokens that have already been fixed.
Chris@17 59 *
Chris@17 60 * We don't allow the same token to be fixed more than once each time
Chris@17 61 * through a file as this can easily cause conflicts between sniffs.
Chris@17 62 *
Chris@17 63 * @var int[]
Chris@17 64 */
Chris@17 65 private $fixedTokens = [];
Chris@17 66
Chris@17 67 /**
Chris@17 68 * The last value of each fixed token.
Chris@17 69 *
Chris@17 70 * If a token is being "fixed" back to its last value, the fix is
Chris@17 71 * probably conflicting with another.
Chris@17 72 *
Chris@17 73 * @var array<int, string>
Chris@17 74 */
Chris@17 75 private $oldTokenValues = [];
Chris@17 76
Chris@17 77 /**
Chris@17 78 * A list of tokens that have been fixed during a changeset.
Chris@17 79 *
Chris@17 80 * All changes in changeset must be able to be applied, or else
Chris@17 81 * the entire changeset is rejected.
Chris@17 82 *
Chris@17 83 * @var array
Chris@17 84 */
Chris@17 85 private $changeset = [];
Chris@17 86
Chris@17 87 /**
Chris@17 88 * Is there an open changeset.
Chris@17 89 *
Chris@17 90 * @var boolean
Chris@17 91 */
Chris@17 92 private $inChangeset = false;
Chris@17 93
Chris@17 94 /**
Chris@17 95 * Is the current fixing loop in conflict?
Chris@17 96 *
Chris@17 97 * @var boolean
Chris@17 98 */
Chris@17 99 private $inConflict = false;
Chris@17 100
Chris@17 101 /**
Chris@17 102 * The number of fixes that have been performed.
Chris@17 103 *
Chris@17 104 * @var integer
Chris@17 105 */
Chris@17 106 private $numFixes = 0;
Chris@17 107
Chris@17 108
Chris@17 109 /**
Chris@17 110 * Starts fixing a new file.
Chris@17 111 *
Chris@17 112 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being fixed.
Chris@17 113 *
Chris@17 114 * @return void
Chris@17 115 */
Chris@17 116 public function startFile(File $phpcsFile)
Chris@17 117 {
Chris@17 118 $this->currentFile = $phpcsFile;
Chris@17 119 $this->numFixes = 0;
Chris@17 120 $this->fixedTokens = [];
Chris@17 121
Chris@17 122 $tokens = $phpcsFile->getTokens();
Chris@17 123 $this->tokens = [];
Chris@17 124 foreach ($tokens as $index => $token) {
Chris@17 125 if (isset($token['orig_content']) === true) {
Chris@17 126 $this->tokens[$index] = $token['orig_content'];
Chris@17 127 } else {
Chris@17 128 $this->tokens[$index] = $token['content'];
Chris@17 129 }
Chris@17 130 }
Chris@17 131
Chris@17 132 }//end startFile()
Chris@17 133
Chris@17 134
Chris@17 135 /**
Chris@17 136 * Attempt to fix the file by processing it until no fixes are made.
Chris@17 137 *
Chris@17 138 * @return boolean
Chris@17 139 */
Chris@17 140 public function fixFile()
Chris@17 141 {
Chris@17 142 $fixable = $this->currentFile->getFixableCount();
Chris@17 143 if ($fixable === 0) {
Chris@17 144 // Nothing to fix.
Chris@17 145 return false;
Chris@17 146 }
Chris@17 147
Chris@17 148 $this->enabled = true;
Chris@17 149
Chris@17 150 $this->loops = 0;
Chris@17 151 while ($this->loops < 50) {
Chris@17 152 ob_start();
Chris@17 153
Chris@17 154 // Only needed once file content has changed.
Chris@17 155 $contents = $this->getContents();
Chris@17 156
Chris@17 157 if (PHP_CODESNIFFER_VERBOSITY > 2) {
Chris@17 158 @ob_end_clean();
Chris@17 159 echo '---START FILE CONTENT---'.PHP_EOL;
Chris@17 160 $lines = explode($this->currentFile->eolChar, $contents);
Chris@17 161 $max = strlen(count($lines));
Chris@17 162 foreach ($lines as $lineNum => $line) {
Chris@17 163 $lineNum++;
Chris@17 164 echo str_pad($lineNum, $max, ' ', STR_PAD_LEFT).'|'.$line.PHP_EOL;
Chris@17 165 }
Chris@17 166
Chris@17 167 echo '--- END FILE CONTENT ---'.PHP_EOL;
Chris@17 168 ob_start();
Chris@17 169 }
Chris@17 170
Chris@17 171 $this->inConflict = false;
Chris@17 172 $this->currentFile->ruleset->populateTokenListeners();
Chris@17 173 $this->currentFile->setContent($contents);
Chris@17 174 $this->currentFile->process();
Chris@17 175 ob_end_clean();
Chris@17 176
Chris@17 177 $this->loops++;
Chris@17 178
Chris@17 179 if (PHP_CODESNIFFER_CBF === true && PHP_CODESNIFFER_VERBOSITY > 0) {
Chris@17 180 echo "\r".str_repeat(' ', 80)."\r";
Chris@17 181 echo "\t=> Fixing file: $this->numFixes/$fixable violations remaining [made $this->loops pass";
Chris@17 182 if ($this->loops > 1) {
Chris@17 183 echo 'es';
Chris@17 184 }
Chris@17 185
Chris@17 186 echo ']... ';
Chris@17 187 }
Chris@17 188
Chris@17 189 if ($this->numFixes === 0 && $this->inConflict === false) {
Chris@17 190 // Nothing left to do.
Chris@17 191 break;
Chris@17 192 } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 193 echo "\t* fixed $this->numFixes violations, starting loop ".($this->loops + 1).' *'.PHP_EOL;
Chris@17 194 }
Chris@17 195 }//end while
Chris@17 196
Chris@17 197 $this->enabled = false;
Chris@17 198
Chris@17 199 if ($this->numFixes > 0) {
Chris@17 200 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 201 if (ob_get_level() > 0) {
Chris@17 202 ob_end_clean();
Chris@17 203 }
Chris@17 204
Chris@17 205 echo "\t*** Reached maximum number of loops with $this->numFixes violations left unfixed ***".PHP_EOL;
Chris@17 206 ob_start();
Chris@17 207 }
Chris@17 208
Chris@17 209 return false;
Chris@17 210 }
Chris@17 211
Chris@17 212 return true;
Chris@17 213
Chris@17 214 }//end fixFile()
Chris@17 215
Chris@17 216
Chris@17 217 /**
Chris@17 218 * Generates a text diff of the original file and the new content.
Chris@17 219 *
Chris@17 220 * @param string $filePath Optional file path to diff the file against.
Chris@17 221 * If not specified, the original version of the
Chris@17 222 * file will be used.
Chris@17 223 * @param boolean $colors Print colored output or not.
Chris@17 224 *
Chris@17 225 * @return string
Chris@17 226 */
Chris@17 227 public function generateDiff($filePath=null, $colors=true)
Chris@17 228 {
Chris@17 229 if ($filePath === null) {
Chris@17 230 $filePath = $this->currentFile->getFilename();
Chris@17 231 }
Chris@17 232
Chris@17 233 $cwd = getcwd().DIRECTORY_SEPARATOR;
Chris@17 234 if (strpos($filePath, $cwd) === 0) {
Chris@17 235 $filename = substr($filePath, strlen($cwd));
Chris@17 236 } else {
Chris@17 237 $filename = $filePath;
Chris@17 238 }
Chris@17 239
Chris@17 240 $contents = $this->getContents();
Chris@17 241
Chris@17 242 $tempName = tempnam(sys_get_temp_dir(), 'phpcs-fixer');
Chris@17 243 $fixedFile = fopen($tempName, 'w');
Chris@17 244 fwrite($fixedFile, $contents);
Chris@17 245
Chris@17 246 // We must use something like shell_exec() because whitespace at the end
Chris@17 247 // of lines is critical to diff files.
Chris@17 248 $filename = escapeshellarg($filename);
Chris@17 249 $cmd = "diff -u -L$filename -LPHP_CodeSniffer $filename \"$tempName\"";
Chris@17 250
Chris@17 251 $diff = shell_exec($cmd);
Chris@17 252
Chris@17 253 fclose($fixedFile);
Chris@17 254 if (is_file($tempName) === true) {
Chris@17 255 unlink($tempName);
Chris@17 256 }
Chris@17 257
Chris@17 258 if ($colors === false) {
Chris@17 259 return $diff;
Chris@17 260 }
Chris@17 261
Chris@17 262 $diffLines = explode(PHP_EOL, $diff);
Chris@17 263 if (count($diffLines) === 1) {
Chris@17 264 // Seems to be required for cygwin.
Chris@17 265 $diffLines = explode("\n", $diff);
Chris@17 266 }
Chris@17 267
Chris@17 268 $diff = [];
Chris@17 269 foreach ($diffLines as $line) {
Chris@17 270 if (isset($line[0]) === true) {
Chris@17 271 switch ($line[0]) {
Chris@17 272 case '-':
Chris@17 273 $diff[] = "\033[31m$line\033[0m";
Chris@17 274 break;
Chris@17 275 case '+':
Chris@17 276 $diff[] = "\033[32m$line\033[0m";
Chris@17 277 break;
Chris@17 278 default:
Chris@17 279 $diff[] = $line;
Chris@17 280 }
Chris@17 281 }
Chris@17 282 }
Chris@17 283
Chris@17 284 $diff = implode(PHP_EOL, $diff);
Chris@17 285
Chris@17 286 return $diff;
Chris@17 287
Chris@17 288 }//end generateDiff()
Chris@17 289
Chris@17 290
Chris@17 291 /**
Chris@17 292 * Get a count of fixes that have been performed on the file.
Chris@17 293 *
Chris@17 294 * This value is reset every time a new file is started, or an existing
Chris@17 295 * file is restarted.
Chris@17 296 *
Chris@17 297 * @return int
Chris@17 298 */
Chris@17 299 public function getFixCount()
Chris@17 300 {
Chris@17 301 return $this->numFixes;
Chris@17 302
Chris@17 303 }//end getFixCount()
Chris@17 304
Chris@17 305
Chris@17 306 /**
Chris@17 307 * Get the current content of the file, as a string.
Chris@17 308 *
Chris@17 309 * @return string
Chris@17 310 */
Chris@17 311 public function getContents()
Chris@17 312 {
Chris@17 313 $contents = implode($this->tokens);
Chris@17 314 return $contents;
Chris@17 315
Chris@17 316 }//end getContents()
Chris@17 317
Chris@17 318
Chris@17 319 /**
Chris@17 320 * Get the current fixed content of a token.
Chris@17 321 *
Chris@17 322 * This function takes changesets into account so should be used
Chris@17 323 * instead of directly accessing the token array.
Chris@17 324 *
Chris@17 325 * @param int $stackPtr The position of the token in the token stack.
Chris@17 326 *
Chris@17 327 * @return string
Chris@17 328 */
Chris@17 329 public function getTokenContent($stackPtr)
Chris@17 330 {
Chris@17 331 if ($this->inChangeset === true
Chris@17 332 && isset($this->changeset[$stackPtr]) === true
Chris@17 333 ) {
Chris@17 334 return $this->changeset[$stackPtr];
Chris@17 335 } else {
Chris@17 336 return $this->tokens[$stackPtr];
Chris@17 337 }
Chris@17 338
Chris@17 339 }//end getTokenContent()
Chris@17 340
Chris@17 341
Chris@17 342 /**
Chris@17 343 * Start recording actions for a changeset.
Chris@17 344 *
Chris@17 345 * @return void
Chris@17 346 */
Chris@17 347 public function beginChangeset()
Chris@17 348 {
Chris@17 349 if ($this->inConflict === true) {
Chris@17 350 return false;
Chris@17 351 }
Chris@17 352
Chris@17 353 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 354 $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
Chris@17 355 $sniff = $bt[1]['class'];
Chris@17 356 $line = $bt[0]['line'];
Chris@17 357
Chris@17 358 @ob_end_clean();
Chris@17 359 echo "\t=> Changeset started by $sniff (line $line)".PHP_EOL;
Chris@17 360 ob_start();
Chris@17 361 }
Chris@17 362
Chris@17 363 $this->changeset = [];
Chris@17 364 $this->inChangeset = true;
Chris@17 365
Chris@17 366 }//end beginChangeset()
Chris@17 367
Chris@17 368
Chris@17 369 /**
Chris@17 370 * Stop recording actions for a changeset, and apply logged changes.
Chris@17 371 *
Chris@17 372 * @return boolean
Chris@17 373 */
Chris@17 374 public function endChangeset()
Chris@17 375 {
Chris@17 376 if ($this->inConflict === true) {
Chris@17 377 return false;
Chris@17 378 }
Chris@17 379
Chris@17 380 $this->inChangeset = false;
Chris@17 381
Chris@17 382 $success = true;
Chris@17 383 $applied = [];
Chris@17 384 foreach ($this->changeset as $stackPtr => $content) {
Chris@17 385 $success = $this->replaceToken($stackPtr, $content);
Chris@17 386 if ($success === false) {
Chris@17 387 break;
Chris@17 388 } else {
Chris@17 389 $applied[] = $stackPtr;
Chris@17 390 }
Chris@17 391 }
Chris@17 392
Chris@17 393 if ($success === false) {
Chris@17 394 // Rolling back all changes.
Chris@17 395 foreach ($applied as $stackPtr) {
Chris@17 396 $this->revertToken($stackPtr);
Chris@17 397 }
Chris@17 398
Chris@17 399 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 400 @ob_end_clean();
Chris@17 401 echo "\t=> Changeset failed to apply".PHP_EOL;
Chris@17 402 ob_start();
Chris@17 403 }
Chris@17 404 } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 405 $fixes = count($this->changeset);
Chris@17 406 @ob_end_clean();
Chris@17 407 echo "\t=> Changeset ended: $fixes changes applied".PHP_EOL;
Chris@17 408 ob_start();
Chris@17 409 }
Chris@17 410
Chris@17 411 $this->changeset = [];
Chris@17 412
Chris@17 413 }//end endChangeset()
Chris@17 414
Chris@17 415
Chris@17 416 /**
Chris@17 417 * Stop recording actions for a changeset, and discard logged changes.
Chris@17 418 *
Chris@17 419 * @return void
Chris@17 420 */
Chris@17 421 public function rollbackChangeset()
Chris@17 422 {
Chris@17 423 $this->inChangeset = false;
Chris@17 424 $this->inConflict = false;
Chris@17 425
Chris@17 426 if (empty($this->changeset) === false) {
Chris@17 427 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 428 $bt = debug_backtrace();
Chris@17 429 if ($bt[1]['class'] === 'PHP_CodeSniffer\Fixer') {
Chris@17 430 $sniff = $bt[2]['class'];
Chris@17 431 $line = $bt[1]['line'];
Chris@17 432 } else {
Chris@17 433 $sniff = $bt[1]['class'];
Chris@17 434 $line = $bt[0]['line'];
Chris@17 435 }
Chris@17 436
Chris@17 437 $numChanges = count($this->changeset);
Chris@17 438
Chris@17 439 @ob_end_clean();
Chris@17 440 echo "\t\tR: $sniff (line $line) rolled back the changeset ($numChanges changes)".PHP_EOL;
Chris@17 441 echo "\t=> Changeset rolled back".PHP_EOL;
Chris@17 442 ob_start();
Chris@17 443 }
Chris@17 444
Chris@17 445 $this->changeset = [];
Chris@17 446 }//end if
Chris@17 447
Chris@17 448 }//end rollbackChangeset()
Chris@17 449
Chris@17 450
Chris@17 451 /**
Chris@17 452 * Replace the entire contents of a token.
Chris@17 453 *
Chris@17 454 * @param int $stackPtr The position of the token in the token stack.
Chris@17 455 * @param string $content The new content of the token.
Chris@17 456 *
Chris@17 457 * @return bool If the change was accepted.
Chris@17 458 */
Chris@17 459 public function replaceToken($stackPtr, $content)
Chris@17 460 {
Chris@17 461 if ($this->inConflict === true) {
Chris@17 462 return false;
Chris@17 463 }
Chris@17 464
Chris@17 465 if ($this->inChangeset === false
Chris@17 466 && isset($this->fixedTokens[$stackPtr]) === true
Chris@17 467 ) {
Chris@17 468 $indent = "\t";
Chris@17 469 if (empty($this->changeset) === false) {
Chris@17 470 $indent .= "\t";
Chris@17 471 }
Chris@17 472
Chris@17 473 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 474 @ob_end_clean();
Chris@17 475 echo "$indent* token $stackPtr has already been modified, skipping *".PHP_EOL;
Chris@17 476 ob_start();
Chris@17 477 }
Chris@17 478
Chris@17 479 return false;
Chris@17 480 }
Chris@17 481
Chris@17 482 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 483 $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
Chris@17 484 if ($bt[1]['class'] === 'PHP_CodeSniffer\Fixer') {
Chris@17 485 $sniff = $bt[2]['class'];
Chris@17 486 $line = $bt[1]['line'];
Chris@17 487 } else {
Chris@17 488 $sniff = $bt[1]['class'];
Chris@17 489 $line = $bt[0]['line'];
Chris@17 490 }
Chris@17 491
Chris@17 492 $tokens = $this->currentFile->getTokens();
Chris@17 493 $type = $tokens[$stackPtr]['type'];
Chris@17 494 $oldContent = Common::prepareForOutput($this->tokens[$stackPtr]);
Chris@17 495 $newContent = Common::prepareForOutput($content);
Chris@17 496 if (trim($this->tokens[$stackPtr]) === '' && isset($this->tokens[($stackPtr + 1)]) === true) {
Chris@17 497 // Add some context for whitespace only changes.
Chris@17 498 $append = Common::prepareForOutput($this->tokens[($stackPtr + 1)]);
Chris@17 499 $oldContent .= $append;
Chris@17 500 $newContent .= $append;
Chris@17 501 }
Chris@17 502 }//end if
Chris@17 503
Chris@17 504 if ($this->inChangeset === true) {
Chris@17 505 $this->changeset[$stackPtr] = $content;
Chris@17 506
Chris@17 507 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 508 @ob_end_clean();
Chris@17 509 echo "\t\tQ: $sniff (line $line) replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL;
Chris@17 510 ob_start();
Chris@17 511 }
Chris@17 512
Chris@17 513 return true;
Chris@17 514 }
Chris@17 515
Chris@17 516 if (isset($this->oldTokenValues[$stackPtr]) === false) {
Chris@17 517 $this->oldTokenValues[$stackPtr] = [
Chris@17 518 'curr' => $content,
Chris@17 519 'prev' => $this->tokens[$stackPtr],
Chris@17 520 'loop' => $this->loops,
Chris@17 521 ];
Chris@17 522 } else {
Chris@17 523 if ($this->oldTokenValues[$stackPtr]['prev'] === $content
Chris@17 524 && $this->oldTokenValues[$stackPtr]['loop'] === ($this->loops - 1)
Chris@17 525 ) {
Chris@17 526 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 527 $indent = "\t";
Chris@17 528 if (empty($this->changeset) === false) {
Chris@17 529 $indent .= "\t";
Chris@17 530 }
Chris@17 531
Chris@17 532 $loop = $this->oldTokenValues[$stackPtr]['loop'];
Chris@17 533
Chris@17 534 @ob_end_clean();
Chris@17 535 echo "$indent**** $sniff (line $line) has possible conflict with another sniff on loop $loop; caused by the following change ****".PHP_EOL;
Chris@17 536 echo "$indent**** replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\" ****".PHP_EOL;
Chris@17 537 }
Chris@17 538
Chris@17 539 if ($this->oldTokenValues[$stackPtr]['loop'] >= ($this->loops - 1)) {
Chris@17 540 $this->inConflict = true;
Chris@17 541 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 542 echo "$indent**** ignoring all changes until next loop ****".PHP_EOL;
Chris@17 543 }
Chris@17 544 }
Chris@17 545
Chris@17 546 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 547 ob_start();
Chris@17 548 }
Chris@17 549
Chris@17 550 return false;
Chris@17 551 }//end if
Chris@17 552
Chris@17 553 $this->oldTokenValues[$stackPtr]['prev'] = $this->oldTokenValues[$stackPtr]['curr'];
Chris@17 554 $this->oldTokenValues[$stackPtr]['curr'] = $content;
Chris@17 555 $this->oldTokenValues[$stackPtr]['loop'] = $this->loops;
Chris@17 556 }//end if
Chris@17 557
Chris@17 558 $this->fixedTokens[$stackPtr] = $this->tokens[$stackPtr];
Chris@17 559 $this->tokens[$stackPtr] = $content;
Chris@17 560 $this->numFixes++;
Chris@17 561
Chris@17 562 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 563 $indent = "\t";
Chris@17 564 if (empty($this->changeset) === false) {
Chris@17 565 $indent .= "\tA: ";
Chris@17 566 }
Chris@17 567
Chris@17 568 if (ob_get_level() > 0) {
Chris@17 569 ob_end_clean();
Chris@17 570 }
Chris@17 571
Chris@17 572 echo "$indent$sniff (line $line) replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL;
Chris@17 573 ob_start();
Chris@17 574 }
Chris@17 575
Chris@17 576 return true;
Chris@17 577
Chris@17 578 }//end replaceToken()
Chris@17 579
Chris@17 580
Chris@17 581 /**
Chris@17 582 * Reverts the previous fix made to a token.
Chris@17 583 *
Chris@17 584 * @param int $stackPtr The position of the token in the token stack.
Chris@17 585 *
Chris@17 586 * @return bool If a change was reverted.
Chris@17 587 */
Chris@17 588 public function revertToken($stackPtr)
Chris@17 589 {
Chris@17 590 if (isset($this->fixedTokens[$stackPtr]) === false) {
Chris@17 591 return false;
Chris@17 592 }
Chris@17 593
Chris@17 594 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 595 $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
Chris@17 596 if ($bt[1]['class'] === 'PHP_CodeSniffer\Fixer') {
Chris@17 597 $sniff = $bt[2]['class'];
Chris@17 598 $line = $bt[1]['line'];
Chris@17 599 } else {
Chris@17 600 $sniff = $bt[1]['class'];
Chris@17 601 $line = $bt[0]['line'];
Chris@17 602 }
Chris@17 603
Chris@17 604 $tokens = $this->currentFile->getTokens();
Chris@17 605 $type = $tokens[$stackPtr]['type'];
Chris@17 606 $oldContent = Common::prepareForOutput($this->tokens[$stackPtr]);
Chris@17 607 $newContent = Common::prepareForOutput($this->fixedTokens[$stackPtr]);
Chris@17 608 if (trim($this->tokens[$stackPtr]) === '' && isset($tokens[($stackPtr + 1)]) === true) {
Chris@17 609 // Add some context for whitespace only changes.
Chris@17 610 $append = Common::prepareForOutput($this->tokens[($stackPtr + 1)]);
Chris@17 611 $oldContent .= $append;
Chris@17 612 $newContent .= $append;
Chris@17 613 }
Chris@17 614 }//end if
Chris@17 615
Chris@17 616 $this->tokens[$stackPtr] = $this->fixedTokens[$stackPtr];
Chris@17 617 unset($this->fixedTokens[$stackPtr]);
Chris@17 618 $this->numFixes--;
Chris@17 619
Chris@17 620 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 621 $indent = "\t";
Chris@17 622 if (empty($this->changeset) === false) {
Chris@17 623 $indent .= "\tR: ";
Chris@17 624 }
Chris@17 625
Chris@17 626 @ob_end_clean();
Chris@17 627 echo "$indent$sniff (line $line) reverted token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL;
Chris@17 628 ob_start();
Chris@17 629 }
Chris@17 630
Chris@17 631 return true;
Chris@17 632
Chris@17 633 }//end revertToken()
Chris@17 634
Chris@17 635
Chris@17 636 /**
Chris@17 637 * Replace the content of a token with a part of its current content.
Chris@17 638 *
Chris@17 639 * @param int $stackPtr The position of the token in the token stack.
Chris@17 640 * @param int $start The first character to keep.
Chris@18 641 * @param int $length The number of characters to keep. If NULL, the content of
Chris@17 642 * the token from $start to the end of the content is kept.
Chris@17 643 *
Chris@17 644 * @return bool If the change was accepted.
Chris@17 645 */
Chris@17 646 public function substrToken($stackPtr, $start, $length=null)
Chris@17 647 {
Chris@17 648 $current = $this->getTokenContent($stackPtr);
Chris@17 649
Chris@17 650 if ($length === null) {
Chris@17 651 $newContent = substr($current, $start);
Chris@17 652 } else {
Chris@17 653 $newContent = substr($current, $start, $length);
Chris@17 654 }
Chris@17 655
Chris@17 656 return $this->replaceToken($stackPtr, $newContent);
Chris@17 657
Chris@17 658 }//end substrToken()
Chris@17 659
Chris@17 660
Chris@17 661 /**
Chris@17 662 * Adds a newline to end of a token's content.
Chris@17 663 *
Chris@17 664 * @param int $stackPtr The position of the token in the token stack.
Chris@17 665 *
Chris@17 666 * @return bool If the change was accepted.
Chris@17 667 */
Chris@17 668 public function addNewline($stackPtr)
Chris@17 669 {
Chris@17 670 $current = $this->getTokenContent($stackPtr);
Chris@17 671 return $this->replaceToken($stackPtr, $current.$this->currentFile->eolChar);
Chris@17 672
Chris@17 673 }//end addNewline()
Chris@17 674
Chris@17 675
Chris@17 676 /**
Chris@17 677 * Adds a newline to the start of a token's content.
Chris@17 678 *
Chris@17 679 * @param int $stackPtr The position of the token in the token stack.
Chris@17 680 *
Chris@17 681 * @return bool If the change was accepted.
Chris@17 682 */
Chris@17 683 public function addNewlineBefore($stackPtr)
Chris@17 684 {
Chris@17 685 $current = $this->getTokenContent($stackPtr);
Chris@17 686 return $this->replaceToken($stackPtr, $this->currentFile->eolChar.$current);
Chris@17 687
Chris@17 688 }//end addNewlineBefore()
Chris@17 689
Chris@17 690
Chris@17 691 /**
Chris@17 692 * Adds content to the end of a token's current content.
Chris@17 693 *
Chris@17 694 * @param int $stackPtr The position of the token in the token stack.
Chris@17 695 * @param string $content The content to add.
Chris@17 696 *
Chris@17 697 * @return bool If the change was accepted.
Chris@17 698 */
Chris@17 699 public function addContent($stackPtr, $content)
Chris@17 700 {
Chris@17 701 $current = $this->getTokenContent($stackPtr);
Chris@17 702 return $this->replaceToken($stackPtr, $current.$content);
Chris@17 703
Chris@17 704 }//end addContent()
Chris@17 705
Chris@17 706
Chris@17 707 /**
Chris@17 708 * Adds content to the start of a token's current content.
Chris@17 709 *
Chris@17 710 * @param int $stackPtr The position of the token in the token stack.
Chris@17 711 * @param string $content The content to add.
Chris@17 712 *
Chris@17 713 * @return bool If the change was accepted.
Chris@17 714 */
Chris@17 715 public function addContentBefore($stackPtr, $content)
Chris@17 716 {
Chris@17 717 $current = $this->getTokenContent($stackPtr);
Chris@17 718 return $this->replaceToken($stackPtr, $content.$current);
Chris@17 719
Chris@17 720 }//end addContentBefore()
Chris@17 721
Chris@17 722
Chris@17 723 }//end class