/**
* Processes multi-line calls.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param int $openBracket The position of the opening bracket
* in the stack passed in $tokens.
* @param array $tokens The stack of tokens that make up
* the file.
*
* @return void
*/
public function processMultiLineCall(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $openBracket, $tokens)
{
// We need to work out how far indented the function
// call itself is, so we can work out how far to
// indent the arguments.
$start = $phpcsFile->findStartOfStatement($stackPtr);
foreach (array('stackPtr', 'start') as $checkToken) {
$x = ${$checkToken};
for ($i = $x - 1; $i >= 0; $i--) {
if ($tokens[$i]['line'] !== $tokens[$x]['line']) {
$i++;
break;
}
}
if ($i <= 0) {
$functionIndent = 0;
} else {
if ($tokens[$i]['code'] === T_WHITESPACE) {
$functionIndent = strlen($tokens[$i]['content']);
} else {
if ($tokens[$i]['code'] === T_CONSTANT_ENCAPSED_STRING) {
$functionIndent = 0;
} else {
$trimmed = ltrim($tokens[$i]['content']);
if ($trimmed === '') {
if ($tokens[$i]['code'] === T_INLINE_HTML) {
$functionIndent = strlen($tokens[$i]['content']);
} else {
$functionIndent = $tokens[$i]['column'] - 1;
}
} else {
$functionIndent = strlen($tokens[$i]['content']) - strlen($trimmed);
}
}
}
}
$varName = $checkToken . 'Indent';
${$varName} = $functionIndent;
}
//end foreach
$functionIndent = max($startIndent, $stackPtrIndent);
$next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $openBracket + 1, null, true);
if ($tokens[$next]['line'] === $tokens[$openBracket]['line']) {
$error = 'Opening parenthesis of a multi-line function call must be the last content on the line';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'ContentAfterOpenBracket');
if ($fix === true) {
$phpcsFile->fixer->addContent($openBracket, $phpcsFile->eolChar . str_repeat(' ', $functionIndent + $this->indent));
}
}
$closeBracket = $tokens[$openBracket]['parenthesis_closer'];
$prev = $phpcsFile->findPrevious(T_WHITESPACE, $closeBracket - 1, null, true);
if ($tokens[$prev]['line'] === $tokens[$closeBracket]['line']) {
$error = 'Closing parenthesis of a multi-line function call must be on a line by itself';
$fix = $phpcsFile->addFixableError($error, $closeBracket, 'CloseBracketLine');
if ($fix === true) {
$phpcsFile->fixer->addContentBefore($closeBracket, $phpcsFile->eolChar . str_repeat(' ', $functionIndent + $this->indent));
}
}
// Each line between the parenthesis should be indented n spaces.
$lastLine = $tokens[$openBracket]['line'];
$argStart = null;
$argEnd = null;
$inArg = false;
for ($i = $openBracket + 1; $i < $closeBracket; $i++) {
if ($i > $argStart && $i < $argEnd) {
$inArg = true;
} else {
$inArg = false;
}
if ($tokens[$i]['line'] !== $lastLine) {
$lastLine = $tokens[$i]['line'];
// Ignore heredoc indentation.
if (isset(PHP_CodeSniffer_Tokens::$heredocTokens[$tokens[$i]['code']]) === true) {
continue;
}
// Ignore multi-line string indentation.
if (isset(PHP_CodeSniffer_Tokens::$stringTokens[$tokens[$i]['code']]) === true && $tokens[$i]['code'] === $tokens[$i - 1]['code']) {
continue;
}
// Ignore inline HTML.
if ($tokens[$i]['code'] === T_INLINE_HTML) {
continue;
}
// We changed lines, so this should be a whitespace indent token, but first make
// sure it isn't a blank line because we don't need to check indent unless there
// is actually some code to indent.
if ($tokens[$i]['code'] === T_WHITESPACE) {
$nextCode = $phpcsFile->findNext(T_WHITESPACE, $i + 1, $closeBracket + 1, true);
if ($tokens[$nextCode]['line'] !== $lastLine) {
if ($inArg === false) {
$error = 'Empty lines are not allowed in multi-line function calls';
$fix = $phpcsFile->addFixableError($error, $i, 'EmptyLine');
if ($fix === true) {
$phpcsFile->fixer->replaceToken($i, '');
}
}
continue;
}
} else {
$nextCode = $i;
}
if ($tokens[$nextCode]['line'] === $tokens[$closeBracket]['line']) {
// Closing brace needs to be indented to the same level
// as the function call.
$inArg = false;
$expectedIndent = $functionIndent;
} else {
$expectedIndent = $functionIndent + $this->indent;
}
if ($tokens[$i]['code'] !== T_WHITESPACE && $tokens[$i]['code'] !== T_DOC_COMMENT_WHITESPACE) {
// Just check if it is a multi-line block comment. If so, we can
// calculate the indent from the whitespace before the content.
if ($tokens[$i]['code'] === T_COMMENT && $tokens[$i - 1]['code'] === T_COMMENT) {
$trimmed = ltrim($tokens[$i]['content']);
$foundIndent = strlen($tokens[$i]['content']) - strlen($trimmed);
} else {
$foundIndent = 0;
}
} else {
$foundIndent = strlen($tokens[$i]['content']);
}
if ($foundIndent < $expectedIndent || $inArg === false && $expectedIndent !== $foundIndent) {
$error = 'Multi-line function call not indented correctly; expected %s spaces but found %s';
$data = array($expectedIndent, $foundIndent);
$fix = $phpcsFile->addFixableError($error, $i, 'Indent', $data);
if ($fix === true) {
$padding = str_repeat(' ', $expectedIndent);
if ($foundIndent === 0) {
$phpcsFile->fixer->addContentBefore($i, $padding);
} else {
if ($tokens[$i]['code'] === T_COMMENT) {
$comment = $padding . ltrim($tokens[$i]['content']);
$phpcsFile->fixer->replaceToken($i, $comment);
} else {
$phpcsFile->fixer->replaceToken($i, $padding);
}
}
}
}
//end if
if ($inArg === false) {
$argStart = $nextCode;
$argEnd = $phpcsFile->findEndOfStatement($nextCode);
}
}
//end if
// If we are within an argument we should be ignoring commas
// as these are not signaling the end of an argument.
if ($inArg === false && $tokens[$i]['code'] === T_COMMA) {
$next = $phpcsFile->findNext(array(T_WHITESPACE, T_COMMENT), $i + 1, $closeBracket, true);
if ($next === false) {
continue;
}
if ($this->allowMultipleArguments === false) {
// Comma has to be the last token on the line.
if ($tokens[$i]['line'] === $tokens[$next]['line']) {
$error = 'Only one argument is allowed per line in a multi-line function call';
$fix = $phpcsFile->addFixableError($error, $next, 'MultipleArguments');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($x = $next - 1; $x > $i; $x--) {
if ($tokens[$x]['code'] !== T_WHITESPACE) {
break;
}
$phpcsFile->fixer->replaceToken($x, '');
}
$phpcsFile->fixer->addContentBefore($next, $phpcsFile->eolChar . str_repeat(' ', $functionIndent + $this->indent));
$phpcsFile->fixer->endChangeset();
}
}
}
//end if
$argStart = $next;
$argEnd = $phpcsFile->findEndOfStatement($next);
}
//end if
}
//end for
}