<?php
/**
* Checks the length of all lines in a file.
*
* Checks all lines in the file, and throws warnings if they are over 80
* characters in length and errors if they are over 100. Both these
* figures can be changed in a ruleset.xml file.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class LineLengthSniff implements Sniff
{
/**
* The limit that the length of a line should not exceed.
*
* @var integer
*/
public $lineLimit = 80;
/**
* The limit that the length of a line must not exceed.
*
* Set to zero (0) to disable.
*
* @var integer
*/
public $absoluteLineLimit = 100;
/**
* Whether or not to ignore trailing comments.
*
* This has the effect of also ignoring all lines
* that only contain comments.
*
* @var boolean
*/
public $ignoreComments = false;
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_OPEN_TAG];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token in
* the stack passed in $tokens.
*
* @return int
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
for ($i = 1; $i < $phpcsFile->numTokens; $i++) {
if ($tokens[$i]['column'] === 1) {
$this->checkLineLength($phpcsFile, $tokens, $i);
}
}
$this->checkLineLength($phpcsFile, $tokens, $i);
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
/**
* Checks if a line is too long.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param array $tokens The token stack.
* @param int $stackPtr The first token on the next line.
*
* @return void
*/
protected function checkLineLength($phpcsFile, $tokens, $stackPtr)
{
// The passed token is the first on the line.
$stackPtr--;
if ($tokens[$stackPtr]['column'] === 1
&& $tokens[$stackPtr]['length'] === 0
) {
// Blank line.
return;
}
if ($tokens[$stackPtr]['column'] !== 1
&& $tokens[$stackPtr]['content'] === $phpcsFile->eolChar
) {
$stackPtr--;
}
$onlyComment = false;
if (isset(Tokens::$commentTokens[$tokens[$stackPtr]['code']]) === true) {
$prevNonWhiteSpace = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($tokens[$stackPtr]['line'] !== $tokens[$prevNonWhiteSpace]['line']) {
$onlyComment = true;
}
}
if ($onlyComment === true
&& isset(Tokens::$phpcsCommentTokens[$tokens[$stackPtr]['code']]) === true
) {
// Ignore PHPCS annotation comments that are on a line by themselves.
return;
}
$lineLength = ($tokens[$stackPtr]['column'] + $tokens[$stackPtr]['length'] - 1);
if ($this->ignoreComments === true
&& isset(Tokens::$commentTokens[$tokens[$stackPtr]['code']]) === true
) {
// Trailing comments are being ignored in line length calculations.
if ($onlyComment === true) {
// The comment is the only thing on the line, so no need to check length.
return;
}
$lineLength -= $tokens[$stackPtr]['length'];
}
// Record metrics for common line length groupings.
if ($lineLength <= 80) {
$phpcsFile->recordMetric($stackPtr, 'Line length', '80 or less');
} else if ($lineLength <= 120) {
$phpcsFile->recordMetric($stackPtr, 'Line length', '81-120');
} else if ($lineLength <= 150) {
$phpcsFile->recordMetric($stackPtr, 'Line length', '121-150');
} else {
$phpcsFile->recordMetric($stackPtr, 'Line length', '151 or more');
}
if ($onlyComment === true) {
// If this is a long comment, check if it can be broken up onto multiple lines.
// Some comments contain unbreakable strings like URLs and so it makes sense
// to ignore the line length in these cases if the URL would be longer than the max
// line length once you indent it to the correct level.
if ($lineLength > $this->lineLimit) {
$oldLength = strlen($tokens[$stackPtr]['content']);
$newLength = strlen(ltrim($tokens[$stackPtr]['content'], "/#\t "));
$indent = (($tokens[$stackPtr]['column'] - 1) + ($oldLength - $newLength));
$nonBreakingLength = $tokens[$stackPtr]['length'];
$space = strrpos($tokens[$stackPtr]['content'], ' ');
if ($space !== false) {
$nonBreakingLength -= ($space + 1);
}
if (($nonBreakingLength + $indent) > $this->lineLimit) {
return;
}
}
}//end if
if ($this->absoluteLineLimit > 0
&& $lineLength > $this->absoluteLineLimit
) {
$data = [
$this->absoluteLineLimit,
$lineLength,
];
$error = 'Line exceeds maximum limit of %s characters; contains %s characters';
$phpcsFile->addError($error, $stackPtr, 'MaxExceeded', $data);
} else if ($lineLength > $this->lineLimit) {
$data = [
$this->lineLimit,
$lineLength,
];
$warning = 'Line exceeds %s characters; contains %s characters';
$phpcsFile->addWarning($warning, $stackPtr, 'TooLong', $data);
}
}//end checkLineLength()
}//end class