function Tokenizer::createPositionMap
Sets token position information.
Can also convert tabs into spaces. Each tab can represent between 1 and $width spaces, so this cannot be a straight string replace.
Return value
void
1 call to Tokenizer::createPositionMap()
- Tokenizer::__construct in vendor/
squizlabs/ php_codesniffer/ src/ Tokenizers/ Tokenizer.php - Initialise and run the tokenizer.
File
-
vendor/
squizlabs/ php_codesniffer/ src/ Tokenizers/ Tokenizer.php, line 171
Class
Namespace
PHP_CodeSniffer\TokenizersCode
private function createPositionMap() {
$currColumn = 1;
$lineNumber = 1;
$eolLen = strlen($this->eolChar);
$ignoring = null;
$inTests = defined('PHP_CODESNIFFER_IN_TESTS');
$checkEncoding = false;
if (function_exists('iconv_strlen') === true) {
$checkEncoding = true;
}
$checkAnnotations = $this->config->annotations;
$encoding = $this->config->encoding;
$tabWidth = $this->config->tabWidth;
$tokensWithTabs = [
T_WHITESPACE => true,
T_COMMENT => true,
T_DOC_COMMENT => true,
T_DOC_COMMENT_WHITESPACE => true,
T_DOC_COMMENT_STRING => true,
T_CONSTANT_ENCAPSED_STRING => true,
T_DOUBLE_QUOTED_STRING => true,
T_START_HEREDOC => true,
T_START_NOWDOC => true,
T_HEREDOC => true,
T_NOWDOC => true,
T_END_HEREDOC => true,
T_END_NOWDOC => true,
T_INLINE_HTML => true,
T_YIELD_FROM => true,
];
$this->numTokens = count($this->tokens);
for ($i = 0; $i < $this->numTokens; $i++) {
$this->tokens[$i]['line'] = $lineNumber;
$this->tokens[$i]['column'] = $currColumn;
if (isset($this->knownLengths[$this->tokens[$i]['code']]) === true) {
// There are no tabs in the tokens we know the length of.
$length = $this->knownLengths[$this->tokens[$i]['code']];
$currColumn += $length;
}
else {
if ($tabWidth === 0 || isset($tokensWithTabs[$this->tokens[$i]['code']]) === false || strpos($this->tokens[$i]['content'], "\t") === false) {
// There are no tabs in this content, or we aren't replacing them.
if ($checkEncoding === true) {
// Not using the default encoding, so take a bit more care.
$oldLevel = error_reporting();
error_reporting(0);
$length = iconv_strlen($this->tokens[$i]['content'], $encoding);
error_reporting($oldLevel);
if ($length === false) {
// String contained invalid characters, so revert to default.
$length = strlen($this->tokens[$i]['content']);
}
}
else {
$length = strlen($this->tokens[$i]['content']);
}
$currColumn += $length;
}
else {
$this->replaceTabsInToken($this->tokens[$i]);
$length = $this->tokens[$i]['length'];
$currColumn += $length;
}
}
//end if
$this->tokens[$i]['length'] = $length;
if (isset($this->knownLengths[$this->tokens[$i]['code']]) === false && strpos($this->tokens[$i]['content'], $this->eolChar) !== false) {
$lineNumber++;
$currColumn = 1;
// Newline chars are not counted in the token length.
$this->tokens[$i]['length'] -= $eolLen;
}
if ($this->tokens[$i]['code'] === T_COMMENT || $this->tokens[$i]['code'] === T_DOC_COMMENT_STRING || $this->tokens[$i]['code'] === T_DOC_COMMENT_TAG || $inTests === true && $this->tokens[$i]['code'] === T_INLINE_HTML) {
$commentText = ltrim($this->tokens[$i]['content'], " \t/*#");
$commentText = rtrim($commentText, " */\t\r\n");
$commentTextLower = strtolower($commentText);
if (strpos($commentText, '@codingStandards') !== false) {
// If this comment is the only thing on the line, it tells us
// to ignore the following line. If the line contains other content
// then we are just ignoring this one single line.
$ownLine = false;
if ($i > 0) {
for ($prev = $i - 1; $prev >= 0; $prev--) {
if ($this->tokens[$prev]['code'] === T_WHITESPACE) {
continue;
}
break;
}
if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {
$ownLine = true;
}
}
if ($ignoring === null && strpos($commentText, '@codingStandardsIgnoreStart') !== false) {
$ignoring = [
'.all' => true,
];
if ($ownLine === true) {
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
}
}
else {
if ($ignoring !== null && strpos($commentText, '@codingStandardsIgnoreEnd') !== false) {
if ($ownLine === true) {
$this->ignoredLines[$this->tokens[$i]['line']] = [
'.all' => true,
];
}
else {
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
}
$ignoring = null;
}
else {
if ($ignoring === null && strpos($commentText, '@codingStandardsIgnoreLine') !== false) {
$ignoring = [
'.all' => true,
];
if ($ownLine === true) {
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
$this->ignoredLines[$this->tokens[$i]['line'] + 1] = $ignoring;
}
else {
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
}
$ignoring = null;
}
}
}
//end if
}
else {
if (substr($commentTextLower, 0, 6) === 'phpcs:' || substr($commentTextLower, 0, 7) === '@phpcs:') {
// If the @phpcs: syntax is being used, strip the @ to make
// comparisons easier.
if ($commentText[0] === '@') {
$commentText = substr($commentText, 1);
$commentTextLower = strtolower($commentText);
}
// If there is a comment on the end, strip it off.
$commentStart = strpos($commentTextLower, ' --');
if ($commentStart !== false) {
$commentText = substr($commentText, 0, $commentStart);
$commentTextLower = strtolower($commentText);
}
// If this comment is the only thing on the line, it tells us
// to ignore the following line. If the line contains other content
// then we are just ignoring this one single line.
$lineHasOtherContent = false;
$lineHasOtherTokens = false;
if ($i > 0) {
for ($prev = $i - 1; $prev > 0; $prev--) {
if ($this->tokens[$prev]['line'] !== $this->tokens[$i]['line']) {
// Changed lines.
break;
}
if ($this->tokens[$prev]['code'] === T_WHITESPACE || $this->tokens[$prev]['code'] === T_DOC_COMMENT_WHITESPACE || $this->tokens[$prev]['code'] === T_INLINE_HTML && trim($this->tokens[$prev]['content']) === '') {
continue;
}
$lineHasOtherTokens = true;
if ($this->tokens[$prev]['code'] === T_OPEN_TAG || $this->tokens[$prev]['code'] === T_DOC_COMMENT_STAR) {
continue;
}
$lineHasOtherContent = true;
break;
}
//end for
$changedLines = false;
for ($next = $i; $next < $this->numTokens; $next++) {
if ($changedLines === true) {
// Changed lines.
break;
}
if (isset($this->knownLengths[$this->tokens[$next]['code']]) === false && strpos($this->tokens[$next]['content'], $this->eolChar) !== false) {
// Last token on the current line.
$changedLines = true;
}
if ($next === $i) {
continue;
}
if ($this->tokens[$next]['code'] === T_WHITESPACE || $this->tokens[$next]['code'] === T_DOC_COMMENT_WHITESPACE || $this->tokens[$next]['code'] === T_INLINE_HTML && trim($this->tokens[$next]['content']) === '') {
continue;
}
$lineHasOtherTokens = true;
if ($this->tokens[$next]['code'] === T_CLOSE_TAG) {
continue;
}
$lineHasOtherContent = true;
break;
}
//end for
}
//end if
if (substr($commentTextLower, 0, 9) === 'phpcs:set') {
// Ignore standards for complete lines that change sniff settings.
if ($lineHasOtherTokens === false) {
$this->ignoredLines[$this->tokens[$i]['line']] = [
'.all' => true,
];
}
// Need to maintain case here, to get the correct sniff code.
$parts = explode(' ', substr($commentText, 10));
if (count($parts) >= 2) {
$sniffParts = explode('.', $parts[0]);
if (count($sniffParts) >= 3) {
$this->tokens[$i]['sniffCode'] = array_shift($parts);
$this->tokens[$i]['sniffProperty'] = array_shift($parts);
$this->tokens[$i]['sniffPropertyValue'] = rtrim(implode(' ', $parts), " */\r\n");
}
}
$this->tokens[$i]['code'] = T_PHPCS_SET;
$this->tokens[$i]['type'] = 'T_PHPCS_SET';
}
else {
if (substr($commentTextLower, 0, 16) === 'phpcs:ignorefile') {
// The whole file will be ignored, but at least set the correct token.
$this->tokens[$i]['code'] = T_PHPCS_IGNORE_FILE;
$this->tokens[$i]['type'] = 'T_PHPCS_IGNORE_FILE';
}
else {
if (substr($commentTextLower, 0, 13) === 'phpcs:disable') {
if ($lineHasOtherContent === false) {
// Completely ignore the comment line.
$this->ignoredLines[$this->tokens[$i]['line']] = [
'.all' => true,
];
}
if ($ignoring === null) {
$ignoring = [];
}
$disabledSniffs = [];
$additionalText = substr($commentText, 14);
if (empty($additionalText) === true) {
$ignoring = [
'.all' => true,
];
}
else {
$parts = explode(',', $additionalText);
foreach ($parts as $sniffCode) {
$sniffCode = trim($sniffCode);
$disabledSniffs[$sniffCode] = true;
$ignoring[$sniffCode] = true;
// This newly disabled sniff might be disabling an existing
// enabled exception that we are tracking.
if (isset($ignoring['.except']) === true) {
foreach (array_keys($ignoring['.except']) as $ignoredSniffCode) {
if ($ignoredSniffCode === $sniffCode || strpos($ignoredSniffCode, $sniffCode . '.') === 0) {
unset($ignoring['.except'][$ignoredSniffCode]);
}
}
if (empty($ignoring['.except']) === true) {
unset($ignoring['.except']);
}
}
}
//end foreach
}
//end if
$this->tokens[$i]['code'] = T_PHPCS_DISABLE;
$this->tokens[$i]['type'] = 'T_PHPCS_DISABLE';
$this->tokens[$i]['sniffCodes'] = $disabledSniffs;
}
else {
if (substr($commentTextLower, 0, 12) === 'phpcs:enable') {
if ($ignoring !== null) {
$enabledSniffs = [];
$additionalText = substr($commentText, 13);
if (empty($additionalText) === true) {
$ignoring = null;
}
else {
$parts = explode(',', $additionalText);
foreach ($parts as $sniffCode) {
$sniffCode = trim($sniffCode);
$enabledSniffs[$sniffCode] = true;
// This new enabled sniff might remove previously disabled
// sniffs if it is actually a standard or category of sniffs.
foreach (array_keys($ignoring) as $ignoredSniffCode) {
if ($ignoredSniffCode === $sniffCode || strpos($ignoredSniffCode, $sniffCode . '.') === 0) {
unset($ignoring[$ignoredSniffCode]);
}
}
// This new enabled sniff might be able to clear up
// previously enabled sniffs if it is actually a standard or
// category of sniffs.
if (isset($ignoring['.except']) === true) {
foreach (array_keys($ignoring['.except']) as $ignoredSniffCode) {
if ($ignoredSniffCode === $sniffCode || strpos($ignoredSniffCode, $sniffCode . '.') === 0) {
unset($ignoring['.except'][$ignoredSniffCode]);
}
}
}
}
//end foreach
if (empty($ignoring) === true) {
$ignoring = null;
}
else {
if (isset($ignoring['.except']) === true) {
$ignoring['.except'] += $enabledSniffs;
}
else {
$ignoring['.except'] = $enabledSniffs;
}
}
}
//end if
if ($lineHasOtherContent === false) {
// Completely ignore the comment line.
$this->ignoredLines[$this->tokens[$i]['line']] = [
'.all' => true,
];
}
else {
// The comment is on the same line as the code it is ignoring,
// so respect the new ignore rules.
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
}
$this->tokens[$i]['sniffCodes'] = $enabledSniffs;
}
//end if
$this->tokens[$i]['code'] = T_PHPCS_ENABLE;
$this->tokens[$i]['type'] = 'T_PHPCS_ENABLE';
}
else {
if (substr($commentTextLower, 0, 12) === 'phpcs:ignore') {
$ignoreRules = [];
$additionalText = substr($commentText, 13);
if (empty($additionalText) === true) {
$ignoreRules = [
'.all' => true,
];
}
else {
$parts = explode(',', $additionalText);
foreach ($parts as $sniffCode) {
$ignoreRules[trim($sniffCode)] = true;
}
}
$this->tokens[$i]['code'] = T_PHPCS_IGNORE;
$this->tokens[$i]['type'] = 'T_PHPCS_IGNORE';
$this->tokens[$i]['sniffCodes'] = $ignoreRules;
if ($ignoring !== null) {
$ignoreRules += $ignoring;
}
if ($lineHasOtherContent === false) {
// Completely ignore the comment line, and set the following
// line to include the ignore rules we've set.
$this->ignoredLines[$this->tokens[$i]['line']] = [
'.all' => true,
];
$this->ignoredLines[$this->tokens[$i]['line'] + 1] = $ignoreRules;
}
else {
// The comment is on the same line as the code it is ignoring,
// so respect the ignore rules it set.
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoreRules;
}
}
}
}
}
}
//end if
}
}
//end if
}
//end if
if ($ignoring !== null && isset($this->ignoredLines[$this->tokens[$i]['line']]) === false) {
$this->ignoredLines[$this->tokens[$i]['line']] = $ignoring;
}
}
//end for
// If annotations are being ignored, we clear out all the ignore rules
// but leave the annotations tokenized as normal.
if ($checkAnnotations === false) {
$this->ignoredLines = [];
}
}