annotate vendor/squizlabs/php_codesniffer/src/Fixer.php @ 5:12f9dff5fda9 tip

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