Chris@17: Chris@17: * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) Chris@17: * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence Chris@17: */ Chris@17: Chris@17: namespace PHP_CodeSniffer; Chris@17: Chris@17: use PHP_CodeSniffer\Files\File; Chris@17: use PHP_CodeSniffer\Util\Common; Chris@17: Chris@17: class Fixer Chris@17: { Chris@17: Chris@17: /** Chris@17: * Is the fixer enabled and fixing a file? Chris@17: * Chris@17: * Sniffs should check this value to ensure they are not Chris@17: * doing extra processing to prepare for a fix when fixing is Chris@17: * not required. Chris@17: * Chris@17: * @var boolean Chris@17: */ Chris@17: public $enabled = false; Chris@17: Chris@17: /** Chris@17: * The number of times we have looped over a file. Chris@17: * Chris@17: * @var integer Chris@17: */ Chris@17: public $loops = 0; Chris@17: Chris@17: /** Chris@17: * The file being fixed. Chris@17: * Chris@17: * @var \PHP_CodeSniffer\Files\File Chris@17: */ Chris@17: private $currentFile = null; Chris@17: Chris@17: /** Chris@17: * The list of tokens that make up the file contents. Chris@17: * Chris@17: * This is a simplified list which just contains the token content and nothing Chris@17: * else. This is the array that is updated as fixes are made, not the file's Chris@17: * token array. Imploding this array will give you the file content back. Chris@17: * Chris@17: * @var array Chris@17: */ Chris@17: private $tokens = []; Chris@17: Chris@17: /** Chris@17: * A list of tokens that have already been fixed. Chris@17: * Chris@17: * We don't allow the same token to be fixed more than once each time Chris@17: * through a file as this can easily cause conflicts between sniffs. Chris@17: * Chris@17: * @var int[] Chris@17: */ Chris@17: private $fixedTokens = []; Chris@17: Chris@17: /** Chris@17: * The last value of each fixed token. Chris@17: * Chris@17: * If a token is being "fixed" back to its last value, the fix is Chris@17: * probably conflicting with another. Chris@17: * Chris@17: * @var array Chris@17: */ Chris@17: private $oldTokenValues = []; Chris@17: Chris@17: /** Chris@17: * A list of tokens that have been fixed during a changeset. Chris@17: * Chris@17: * All changes in changeset must be able to be applied, or else Chris@17: * the entire changeset is rejected. Chris@17: * Chris@17: * @var array Chris@17: */ Chris@17: private $changeset = []; Chris@17: Chris@17: /** Chris@17: * Is there an open changeset. Chris@17: * Chris@17: * @var boolean Chris@17: */ Chris@17: private $inChangeset = false; Chris@17: Chris@17: /** Chris@17: * Is the current fixing loop in conflict? Chris@17: * Chris@17: * @var boolean Chris@17: */ Chris@17: private $inConflict = false; Chris@17: Chris@17: /** Chris@17: * The number of fixes that have been performed. Chris@17: * Chris@17: * @var integer Chris@17: */ Chris@17: private $numFixes = 0; Chris@17: Chris@17: Chris@17: /** Chris@17: * Starts fixing a new file. Chris@17: * Chris@17: * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being fixed. Chris@17: * Chris@17: * @return void Chris@17: */ Chris@17: public function startFile(File $phpcsFile) Chris@17: { Chris@17: $this->currentFile = $phpcsFile; Chris@17: $this->numFixes = 0; Chris@17: $this->fixedTokens = []; Chris@17: Chris@17: $tokens = $phpcsFile->getTokens(); Chris@17: $this->tokens = []; Chris@17: foreach ($tokens as $index => $token) { Chris@17: if (isset($token['orig_content']) === true) { Chris@17: $this->tokens[$index] = $token['orig_content']; Chris@17: } else { Chris@17: $this->tokens[$index] = $token['content']; Chris@17: } Chris@17: } Chris@17: Chris@17: }//end startFile() Chris@17: Chris@17: Chris@17: /** Chris@17: * Attempt to fix the file by processing it until no fixes are made. Chris@17: * Chris@17: * @return boolean Chris@17: */ Chris@17: public function fixFile() Chris@17: { Chris@17: $fixable = $this->currentFile->getFixableCount(); Chris@17: if ($fixable === 0) { Chris@17: // Nothing to fix. Chris@17: return false; Chris@17: } Chris@17: Chris@17: $this->enabled = true; Chris@17: Chris@17: $this->loops = 0; Chris@17: while ($this->loops < 50) { Chris@17: ob_start(); Chris@17: Chris@17: // Only needed once file content has changed. Chris@17: $contents = $this->getContents(); Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 2) { Chris@17: @ob_end_clean(); Chris@17: echo '---START FILE CONTENT---'.PHP_EOL; Chris@17: $lines = explode($this->currentFile->eolChar, $contents); Chris@17: $max = strlen(count($lines)); Chris@17: foreach ($lines as $lineNum => $line) { Chris@17: $lineNum++; Chris@17: echo str_pad($lineNum, $max, ' ', STR_PAD_LEFT).'|'.$line.PHP_EOL; Chris@17: } Chris@17: Chris@17: echo '--- END FILE CONTENT ---'.PHP_EOL; Chris@17: ob_start(); Chris@17: } Chris@17: Chris@17: $this->inConflict = false; Chris@17: $this->currentFile->ruleset->populateTokenListeners(); Chris@17: $this->currentFile->setContent($contents); Chris@17: $this->currentFile->process(); Chris@17: ob_end_clean(); Chris@17: Chris@17: $this->loops++; Chris@17: Chris@17: if (PHP_CODESNIFFER_CBF === true && PHP_CODESNIFFER_VERBOSITY > 0) { Chris@17: echo "\r".str_repeat(' ', 80)."\r"; Chris@17: echo "\t=> Fixing file: $this->numFixes/$fixable violations remaining [made $this->loops pass"; Chris@17: if ($this->loops > 1) { Chris@17: echo 'es'; Chris@17: } Chris@17: Chris@17: echo ']... '; Chris@17: } Chris@17: Chris@17: if ($this->numFixes === 0 && $this->inConflict === false) { Chris@17: // Nothing left to do. Chris@17: break; Chris@17: } else if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: echo "\t* fixed $this->numFixes violations, starting loop ".($this->loops + 1).' *'.PHP_EOL; Chris@17: } Chris@17: }//end while Chris@17: Chris@17: $this->enabled = false; Chris@17: Chris@17: if ($this->numFixes > 0) { Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: if (ob_get_level() > 0) { Chris@17: ob_end_clean(); Chris@17: } Chris@17: Chris@17: echo "\t*** Reached maximum number of loops with $this->numFixes violations left unfixed ***".PHP_EOL; Chris@17: ob_start(); Chris@17: } Chris@17: Chris@17: return false; Chris@17: } Chris@17: Chris@17: return true; Chris@17: Chris@17: }//end fixFile() Chris@17: Chris@17: Chris@17: /** Chris@17: * Generates a text diff of the original file and the new content. Chris@17: * Chris@17: * @param string $filePath Optional file path to diff the file against. Chris@17: * If not specified, the original version of the Chris@17: * file will be used. Chris@17: * @param boolean $colors Print colored output or not. Chris@17: * Chris@17: * @return string Chris@17: */ Chris@17: public function generateDiff($filePath=null, $colors=true) Chris@17: { Chris@17: if ($filePath === null) { Chris@17: $filePath = $this->currentFile->getFilename(); Chris@17: } Chris@17: Chris@17: $cwd = getcwd().DIRECTORY_SEPARATOR; Chris@17: if (strpos($filePath, $cwd) === 0) { Chris@17: $filename = substr($filePath, strlen($cwd)); Chris@17: } else { Chris@17: $filename = $filePath; Chris@17: } Chris@17: Chris@17: $contents = $this->getContents(); Chris@17: Chris@17: $tempName = tempnam(sys_get_temp_dir(), 'phpcs-fixer'); Chris@17: $fixedFile = fopen($tempName, 'w'); Chris@17: fwrite($fixedFile, $contents); Chris@17: Chris@17: // We must use something like shell_exec() because whitespace at the end Chris@17: // of lines is critical to diff files. Chris@17: $filename = escapeshellarg($filename); Chris@17: $cmd = "diff -u -L$filename -LPHP_CodeSniffer $filename \"$tempName\""; Chris@17: Chris@17: $diff = shell_exec($cmd); Chris@17: Chris@17: fclose($fixedFile); Chris@17: if (is_file($tempName) === true) { Chris@17: unlink($tempName); Chris@17: } Chris@17: Chris@17: if ($colors === false) { Chris@17: return $diff; Chris@17: } Chris@17: Chris@17: $diffLines = explode(PHP_EOL, $diff); Chris@17: if (count($diffLines) === 1) { Chris@17: // Seems to be required for cygwin. Chris@17: $diffLines = explode("\n", $diff); Chris@17: } Chris@17: Chris@17: $diff = []; Chris@17: foreach ($diffLines as $line) { Chris@17: if (isset($line[0]) === true) { Chris@17: switch ($line[0]) { Chris@17: case '-': Chris@17: $diff[] = "\033[31m$line\033[0m"; Chris@17: break; Chris@17: case '+': Chris@17: $diff[] = "\033[32m$line\033[0m"; Chris@17: break; Chris@17: default: Chris@17: $diff[] = $line; Chris@17: } Chris@17: } Chris@17: } Chris@17: Chris@17: $diff = implode(PHP_EOL, $diff); Chris@17: Chris@17: return $diff; Chris@17: Chris@17: }//end generateDiff() Chris@17: Chris@17: Chris@17: /** Chris@17: * Get a count of fixes that have been performed on the file. Chris@17: * Chris@17: * This value is reset every time a new file is started, or an existing Chris@17: * file is restarted. Chris@17: * Chris@17: * @return int Chris@17: */ Chris@17: public function getFixCount() Chris@17: { Chris@17: return $this->numFixes; Chris@17: Chris@17: }//end getFixCount() Chris@17: Chris@17: Chris@17: /** Chris@17: * Get the current content of the file, as a string. Chris@17: * Chris@17: * @return string Chris@17: */ Chris@17: public function getContents() Chris@17: { Chris@17: $contents = implode($this->tokens); Chris@17: return $contents; Chris@17: Chris@17: }//end getContents() Chris@17: Chris@17: Chris@17: /** Chris@17: * Get the current fixed content of a token. Chris@17: * Chris@17: * This function takes changesets into account so should be used Chris@17: * instead of directly accessing the token array. Chris@17: * Chris@17: * @param int $stackPtr The position of the token in the token stack. Chris@17: * Chris@17: * @return string Chris@17: */ Chris@17: public function getTokenContent($stackPtr) Chris@17: { Chris@17: if ($this->inChangeset === true Chris@17: && isset($this->changeset[$stackPtr]) === true Chris@17: ) { Chris@17: return $this->changeset[$stackPtr]; Chris@17: } else { Chris@17: return $this->tokens[$stackPtr]; Chris@17: } Chris@17: Chris@17: }//end getTokenContent() Chris@17: Chris@17: Chris@17: /** Chris@17: * Start recording actions for a changeset. Chris@17: * Chris@17: * @return void Chris@17: */ Chris@17: public function beginChangeset() Chris@17: { Chris@17: if ($this->inConflict === true) { Chris@17: return false; Chris@17: } Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); Chris@17: $sniff = $bt[1]['class']; Chris@17: $line = $bt[0]['line']; Chris@17: Chris@17: @ob_end_clean(); Chris@17: echo "\t=> Changeset started by $sniff (line $line)".PHP_EOL; Chris@17: ob_start(); Chris@17: } Chris@17: Chris@17: $this->changeset = []; Chris@17: $this->inChangeset = true; Chris@17: Chris@17: }//end beginChangeset() Chris@17: Chris@17: Chris@17: /** Chris@17: * Stop recording actions for a changeset, and apply logged changes. Chris@17: * Chris@17: * @return boolean Chris@17: */ Chris@17: public function endChangeset() Chris@17: { Chris@17: if ($this->inConflict === true) { Chris@17: return false; Chris@17: } Chris@17: Chris@17: $this->inChangeset = false; Chris@17: Chris@17: $success = true; Chris@17: $applied = []; Chris@17: foreach ($this->changeset as $stackPtr => $content) { Chris@17: $success = $this->replaceToken($stackPtr, $content); Chris@17: if ($success === false) { Chris@17: break; Chris@17: } else { Chris@17: $applied[] = $stackPtr; Chris@17: } Chris@17: } Chris@17: Chris@17: if ($success === false) { Chris@17: // Rolling back all changes. Chris@17: foreach ($applied as $stackPtr) { Chris@17: $this->revertToken($stackPtr); Chris@17: } Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: @ob_end_clean(); Chris@17: echo "\t=> Changeset failed to apply".PHP_EOL; Chris@17: ob_start(); Chris@17: } Chris@17: } else if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: $fixes = count($this->changeset); Chris@17: @ob_end_clean(); Chris@17: echo "\t=> Changeset ended: $fixes changes applied".PHP_EOL; Chris@17: ob_start(); Chris@17: } Chris@17: Chris@17: $this->changeset = []; Chris@17: Chris@17: }//end endChangeset() Chris@17: Chris@17: Chris@17: /** Chris@17: * Stop recording actions for a changeset, and discard logged changes. Chris@17: * Chris@17: * @return void Chris@17: */ Chris@17: public function rollbackChangeset() Chris@17: { Chris@17: $this->inChangeset = false; Chris@17: $this->inConflict = false; Chris@17: Chris@17: if (empty($this->changeset) === false) { Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: $bt = debug_backtrace(); Chris@17: if ($bt[1]['class'] === 'PHP_CodeSniffer\Fixer') { Chris@17: $sniff = $bt[2]['class']; Chris@17: $line = $bt[1]['line']; Chris@17: } else { Chris@17: $sniff = $bt[1]['class']; Chris@17: $line = $bt[0]['line']; Chris@17: } Chris@17: Chris@17: $numChanges = count($this->changeset); Chris@17: Chris@17: @ob_end_clean(); Chris@17: echo "\t\tR: $sniff (line $line) rolled back the changeset ($numChanges changes)".PHP_EOL; Chris@17: echo "\t=> Changeset rolled back".PHP_EOL; Chris@17: ob_start(); Chris@17: } Chris@17: Chris@17: $this->changeset = []; Chris@17: }//end if Chris@17: Chris@17: }//end rollbackChangeset() Chris@17: Chris@17: Chris@17: /** Chris@17: * Replace the entire contents of a token. Chris@17: * Chris@17: * @param int $stackPtr The position of the token in the token stack. Chris@17: * @param string $content The new content of the token. Chris@17: * Chris@17: * @return bool If the change was accepted. Chris@17: */ Chris@17: public function replaceToken($stackPtr, $content) Chris@17: { Chris@17: if ($this->inConflict === true) { Chris@17: return false; Chris@17: } Chris@17: Chris@17: if ($this->inChangeset === false Chris@17: && isset($this->fixedTokens[$stackPtr]) === true Chris@17: ) { Chris@17: $indent = "\t"; Chris@17: if (empty($this->changeset) === false) { Chris@17: $indent .= "\t"; Chris@17: } Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: @ob_end_clean(); Chris@17: echo "$indent* token $stackPtr has already been modified, skipping *".PHP_EOL; Chris@17: ob_start(); Chris@17: } Chris@17: Chris@17: return false; Chris@17: } Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); Chris@17: if ($bt[1]['class'] === 'PHP_CodeSniffer\Fixer') { Chris@17: $sniff = $bt[2]['class']; Chris@17: $line = $bt[1]['line']; Chris@17: } else { Chris@17: $sniff = $bt[1]['class']; Chris@17: $line = $bt[0]['line']; Chris@17: } Chris@17: Chris@17: $tokens = $this->currentFile->getTokens(); Chris@17: $type = $tokens[$stackPtr]['type']; Chris@17: $oldContent = Common::prepareForOutput($this->tokens[$stackPtr]); Chris@17: $newContent = Common::prepareForOutput($content); Chris@17: if (trim($this->tokens[$stackPtr]) === '' && isset($this->tokens[($stackPtr + 1)]) === true) { Chris@17: // Add some context for whitespace only changes. Chris@17: $append = Common::prepareForOutput($this->tokens[($stackPtr + 1)]); Chris@17: $oldContent .= $append; Chris@17: $newContent .= $append; Chris@17: } Chris@17: }//end if Chris@17: Chris@17: if ($this->inChangeset === true) { Chris@17: $this->changeset[$stackPtr] = $content; Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: @ob_end_clean(); Chris@17: echo "\t\tQ: $sniff (line $line) replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL; Chris@17: ob_start(); Chris@17: } Chris@17: Chris@17: return true; Chris@17: } Chris@17: Chris@17: if (isset($this->oldTokenValues[$stackPtr]) === false) { Chris@17: $this->oldTokenValues[$stackPtr] = [ Chris@17: 'curr' => $content, Chris@17: 'prev' => $this->tokens[$stackPtr], Chris@17: 'loop' => $this->loops, Chris@17: ]; Chris@17: } else { Chris@17: if ($this->oldTokenValues[$stackPtr]['prev'] === $content Chris@17: && $this->oldTokenValues[$stackPtr]['loop'] === ($this->loops - 1) Chris@17: ) { Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: $indent = "\t"; Chris@17: if (empty($this->changeset) === false) { Chris@17: $indent .= "\t"; Chris@17: } Chris@17: Chris@17: $loop = $this->oldTokenValues[$stackPtr]['loop']; Chris@17: Chris@17: @ob_end_clean(); Chris@17: echo "$indent**** $sniff (line $line) has possible conflict with another sniff on loop $loop; caused by the following change ****".PHP_EOL; Chris@17: echo "$indent**** replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\" ****".PHP_EOL; Chris@17: } Chris@17: Chris@17: if ($this->oldTokenValues[$stackPtr]['loop'] >= ($this->loops - 1)) { Chris@17: $this->inConflict = true; Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: echo "$indent**** ignoring all changes until next loop ****".PHP_EOL; Chris@17: } Chris@17: } Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: ob_start(); Chris@17: } Chris@17: Chris@17: return false; Chris@17: }//end if Chris@17: Chris@17: $this->oldTokenValues[$stackPtr]['prev'] = $this->oldTokenValues[$stackPtr]['curr']; Chris@17: $this->oldTokenValues[$stackPtr]['curr'] = $content; Chris@17: $this->oldTokenValues[$stackPtr]['loop'] = $this->loops; Chris@17: }//end if Chris@17: Chris@17: $this->fixedTokens[$stackPtr] = $this->tokens[$stackPtr]; Chris@17: $this->tokens[$stackPtr] = $content; Chris@17: $this->numFixes++; Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: $indent = "\t"; Chris@17: if (empty($this->changeset) === false) { Chris@17: $indent .= "\tA: "; Chris@17: } Chris@17: Chris@17: if (ob_get_level() > 0) { Chris@17: ob_end_clean(); Chris@17: } Chris@17: Chris@17: echo "$indent$sniff (line $line) replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL; Chris@17: ob_start(); Chris@17: } Chris@17: Chris@17: return true; Chris@17: Chris@17: }//end replaceToken() Chris@17: Chris@17: Chris@17: /** Chris@17: * Reverts the previous fix made to a token. Chris@17: * Chris@17: * @param int $stackPtr The position of the token in the token stack. Chris@17: * Chris@17: * @return bool If a change was reverted. Chris@17: */ Chris@17: public function revertToken($stackPtr) Chris@17: { Chris@17: if (isset($this->fixedTokens[$stackPtr]) === false) { Chris@17: return false; Chris@17: } Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); Chris@17: if ($bt[1]['class'] === 'PHP_CodeSniffer\Fixer') { Chris@17: $sniff = $bt[2]['class']; Chris@17: $line = $bt[1]['line']; Chris@17: } else { Chris@17: $sniff = $bt[1]['class']; Chris@17: $line = $bt[0]['line']; Chris@17: } Chris@17: Chris@17: $tokens = $this->currentFile->getTokens(); Chris@17: $type = $tokens[$stackPtr]['type']; Chris@17: $oldContent = Common::prepareForOutput($this->tokens[$stackPtr]); Chris@17: $newContent = Common::prepareForOutput($this->fixedTokens[$stackPtr]); Chris@17: if (trim($this->tokens[$stackPtr]) === '' && isset($tokens[($stackPtr + 1)]) === true) { Chris@17: // Add some context for whitespace only changes. Chris@17: $append = Common::prepareForOutput($this->tokens[($stackPtr + 1)]); Chris@17: $oldContent .= $append; Chris@17: $newContent .= $append; Chris@17: } Chris@17: }//end if Chris@17: Chris@17: $this->tokens[$stackPtr] = $this->fixedTokens[$stackPtr]; Chris@17: unset($this->fixedTokens[$stackPtr]); Chris@17: $this->numFixes--; Chris@17: Chris@17: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@17: $indent = "\t"; Chris@17: if (empty($this->changeset) === false) { Chris@17: $indent .= "\tR: "; Chris@17: } Chris@17: Chris@17: @ob_end_clean(); Chris@17: echo "$indent$sniff (line $line) reverted token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL; Chris@17: ob_start(); Chris@17: } Chris@17: Chris@17: return true; Chris@17: Chris@17: }//end revertToken() Chris@17: Chris@17: Chris@17: /** Chris@17: * Replace the content of a token with a part of its current content. Chris@17: * Chris@17: * @param int $stackPtr The position of the token in the token stack. Chris@17: * @param int $start The first character to keep. Chris@18: * @param int $length The number of characters to keep. If NULL, the content of Chris@17: * the token from $start to the end of the content is kept. Chris@17: * Chris@17: * @return bool If the change was accepted. Chris@17: */ Chris@17: public function substrToken($stackPtr, $start, $length=null) Chris@17: { Chris@17: $current = $this->getTokenContent($stackPtr); Chris@17: Chris@17: if ($length === null) { Chris@17: $newContent = substr($current, $start); Chris@17: } else { Chris@17: $newContent = substr($current, $start, $length); Chris@17: } Chris@17: Chris@17: return $this->replaceToken($stackPtr, $newContent); Chris@17: Chris@17: }//end substrToken() Chris@17: Chris@17: Chris@17: /** Chris@17: * Adds a newline to end of a token's content. Chris@17: * Chris@17: * @param int $stackPtr The position of the token in the token stack. Chris@17: * Chris@17: * @return bool If the change was accepted. Chris@17: */ Chris@17: public function addNewline($stackPtr) Chris@17: { Chris@17: $current = $this->getTokenContent($stackPtr); Chris@17: return $this->replaceToken($stackPtr, $current.$this->currentFile->eolChar); Chris@17: Chris@17: }//end addNewline() Chris@17: Chris@17: Chris@17: /** Chris@17: * Adds a newline to the start of a token's content. Chris@17: * Chris@17: * @param int $stackPtr The position of the token in the token stack. Chris@17: * Chris@17: * @return bool If the change was accepted. Chris@17: */ Chris@17: public function addNewlineBefore($stackPtr) Chris@17: { Chris@17: $current = $this->getTokenContent($stackPtr); Chris@17: return $this->replaceToken($stackPtr, $this->currentFile->eolChar.$current); Chris@17: Chris@17: }//end addNewlineBefore() Chris@17: Chris@17: Chris@17: /** Chris@17: * Adds content to the end of a token's current content. Chris@17: * Chris@17: * @param int $stackPtr The position of the token in the token stack. Chris@17: * @param string $content The content to add. Chris@17: * Chris@17: * @return bool If the change was accepted. Chris@17: */ Chris@17: public function addContent($stackPtr, $content) Chris@17: { Chris@17: $current = $this->getTokenContent($stackPtr); Chris@17: return $this->replaceToken($stackPtr, $current.$content); Chris@17: Chris@17: }//end addContent() Chris@17: Chris@17: Chris@17: /** Chris@17: * Adds content to the start of a token's current content. Chris@17: * Chris@17: * @param int $stackPtr The position of the token in the token stack. Chris@17: * @param string $content The content to add. Chris@17: * Chris@17: * @return bool If the change was accepted. Chris@17: */ Chris@17: public function addContentBefore($stackPtr, $content) Chris@17: { Chris@17: $current = $this->getTokenContent($stackPtr); Chris@17: return $this->replaceToken($stackPtr, $content.$current); Chris@17: Chris@17: }//end addContentBefore() Chris@17: Chris@17: Chris@17: }//end class