Chris@17: getTokens(); Chris@17: Chris@17: // Support long and short syntax. Chris@17: $parenthesis_opener = 'parenthesis_opener'; Chris@17: $parenthesis_closer = 'parenthesis_closer'; Chris@17: if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY) { Chris@17: $parenthesis_opener = 'bracket_opener'; Chris@17: $parenthesis_closer = 'bracket_closer'; Chris@17: } Chris@17: Chris@17: // Sanity check: this can sometimes be NULL if the array was not correctly Chris@17: // parsed. Chris@17: if ($tokens[$stackPtr][$parenthesis_closer] === null) { Chris@17: return; Chris@17: } Chris@17: Chris@17: $lastItem = $phpcsFile->findPrevious( Chris@17: Tokens::$emptyTokens, Chris@17: ($tokens[$stackPtr][$parenthesis_closer] - 1), Chris@17: $stackPtr, Chris@17: true Chris@17: ); Chris@17: Chris@17: // Empty array. Chris@17: if ($lastItem === $tokens[$stackPtr][$parenthesis_opener]) { Chris@17: return; Chris@17: } Chris@17: Chris@17: // Inline array. Chris@17: $isInlineArray = $tokens[$tokens[$stackPtr][$parenthesis_opener]]['line'] === $tokens[$tokens[$stackPtr][$parenthesis_closer]]['line']; Chris@17: Chris@17: // Check if the last item in a multiline array has a "closing" comma. Chris@17: if ($tokens[$lastItem]['code'] !== T_COMMA && $isInlineArray === false Chris@17: && $tokens[($lastItem + 1)]['code'] !== T_CLOSE_PARENTHESIS Chris@17: && $tokens[($lastItem + 1)]['code'] !== T_CLOSE_SHORT_ARRAY Chris@17: && isset(Tokens::$heredocTokens[$tokens[$lastItem]['code']]) === false Chris@17: ) { Chris@17: $data = array($tokens[$lastItem]['content']); Chris@17: $fix = $phpcsFile->addFixableWarning('A comma should follow the last multiline array item. Found: %s', $lastItem, 'CommaLastItem', $data); Chris@17: if ($fix === true) { Chris@17: $phpcsFile->fixer->addContent($lastItem, ','); Chris@17: } Chris@17: Chris@17: return; Chris@17: } Chris@17: Chris@17: if ($isInlineArray === true) { Chris@17: // Check if this array contains at least 3 elements and exceeds the 80 Chris@17: // character line length. Chris@17: if ($tokens[$tokens[$stackPtr][$parenthesis_closer]]['column'] > 80) { Chris@17: $comma1 = $phpcsFile->findNext(T_COMMA, ($stackPtr + 1), $tokens[$stackPtr][$parenthesis_closer]); Chris@17: if ($comma1 !== false) { Chris@17: $comma2 = $phpcsFile->findNext(T_COMMA, ($comma1 + 1), $tokens[$stackPtr][$parenthesis_closer]); Chris@17: if ($comma2 !== false) { Chris@17: $error = 'If the line declaring an array spans longer than 80 characters, each element should be broken into its own line'; Chris@17: $phpcsFile->addError($error, $stackPtr, 'LongLineDeclaration'); Chris@17: } Chris@17: } Chris@17: } Chris@17: Chris@17: // Only continue for multi line arrays. Chris@17: return; Chris@17: } Chris@17: Chris@17: // Find the first token on this line. Chris@17: $firstLineColumn = $tokens[$stackPtr]['column']; Chris@17: for ($i = $stackPtr; $i >= 0; $i--) { Chris@17: // If there is a PHP open tag then this must be a template file where we Chris@17: // don't check indentation. Chris@17: if ($tokens[$i]['code'] === T_OPEN_TAG) { Chris@17: return; Chris@17: } Chris@17: Chris@17: // Record the first code token on the line. Chris@17: if ($tokens[$i]['code'] !== T_WHITESPACE) { Chris@17: $firstLineColumn = $tokens[$i]['column']; Chris@17: // This could be a multi line string or comment beginning with white Chris@17: // spaces. Chris@17: $trimmed = ltrim($tokens[$i]['content']); Chris@17: if ($trimmed !== $tokens[$i]['content']) { Chris@17: $firstLineColumn = ($firstLineColumn + strpos($tokens[$i]['content'], $trimmed)); Chris@17: } Chris@17: } Chris@17: Chris@17: // It's the start of the line, so we've found our first php token. Chris@17: if ($tokens[$i]['column'] === 1) { Chris@17: break; Chris@17: } Chris@17: }//end for Chris@17: Chris@17: $lineStart = $stackPtr; Chris@17: // Iterate over all lines of this array. Chris@17: while ($lineStart < $tokens[$stackPtr][$parenthesis_closer]) { Chris@17: // Find next line start. Chris@17: $newLineStart = $lineStart; Chris@17: $current_line = $tokens[$newLineStart]['line']; Chris@17: while ($current_line >= $tokens[$newLineStart]['line']) { Chris@17: $newLineStart = $phpcsFile->findNext( Chris@17: Tokens::$emptyTokens, Chris@17: ($newLineStart + 1), Chris@17: ($tokens[$stackPtr][$parenthesis_closer] + 1), Chris@17: true Chris@17: ); Chris@17: Chris@17: if ($newLineStart === false) { Chris@17: break 2; Chris@17: } Chris@17: Chris@17: // Long array syntax: Skip nested arrays, they are checked in a next Chris@17: // run. Chris@17: if ($tokens[$newLineStart]['code'] === T_ARRAY) { Chris@17: $newLineStart = $tokens[$newLineStart]['parenthesis_closer']; Chris@17: $current_line = $tokens[$newLineStart]['line']; Chris@17: } Chris@17: Chris@17: // Short array syntax: Skip nested arrays, they are checked in a next Chris@17: // run. Chris@17: if ($tokens[$newLineStart]['code'] === T_OPEN_SHORT_ARRAY) { Chris@17: $newLineStart = $tokens[$newLineStart]['bracket_closer']; Chris@17: $current_line = $tokens[$newLineStart]['line']; Chris@17: } Chris@17: Chris@17: // Nested structures such as closures: skip those, they are checked Chris@17: // in other sniffs. If the conditions of a token are different it Chris@17: // means that it is in a different nesting level. Chris@17: if ($tokens[$newLineStart]['conditions'] !== $tokens[$stackPtr]['conditions']) { Chris@17: $current_line++; Chris@17: } Chris@17: }//end while Chris@17: Chris@17: if ($newLineStart === $tokens[$stackPtr][$parenthesis_closer]) { Chris@17: // End of the array reached. Chris@17: if ($tokens[$newLineStart]['column'] !== $firstLineColumn) { Chris@17: $error = 'Array closing indentation error, expected %s spaces but found %s'; Chris@17: $data = array( Chris@17: $firstLineColumn - 1, Chris@17: $tokens[$newLineStart]['column'] - 1, Chris@17: ); Chris@17: $fix = $phpcsFile->addFixableError($error, $newLineStart, 'ArrayClosingIndentation', $data); Chris@17: if ($fix === true) { Chris@17: if ($tokens[$newLineStart]['column'] === 1) { Chris@17: $phpcsFile->fixer->addContentBefore($newLineStart, str_repeat(' ', ($firstLineColumn - 1))); Chris@17: } else { Chris@17: $phpcsFile->fixer->replaceToken(($newLineStart - 1), str_repeat(' ', ($firstLineColumn - 1))); Chris@17: } Chris@17: } Chris@17: } Chris@17: Chris@17: break; Chris@17: } Chris@17: Chris@17: $expectedColumn = ($firstLineColumn + 2); Chris@17: // If the line starts with "->" then we assume an additional level of Chris@17: // indentation. Chris@17: if ($tokens[$newLineStart]['code'] === T_OBJECT_OPERATOR) { Chris@17: $expectedColumn += 2; Chris@17: } Chris@17: Chris@17: if ($tokens[$newLineStart]['column'] !== $expectedColumn) { Chris@17: // Skip lines in nested structures such as a function call within an Chris@17: // array, no defined coding standard for those. Chris@17: $innerNesting = empty($tokens[$newLineStart]['nested_parenthesis']) === false Chris@17: && end($tokens[$newLineStart]['nested_parenthesis']) < $tokens[$stackPtr][$parenthesis_closer]; Chris@17: // Skip lines that are part of a multi-line string. Chris@17: $isMultiLineString = $tokens[($newLineStart - 1)]['code'] === T_CONSTANT_ENCAPSED_STRING Chris@17: && substr($tokens[($newLineStart - 1)]['content'], -1) === $phpcsFile->eolChar; Chris@17: // Skip NOWDOC or HEREDOC lines. Chris@17: $nowDoc = isset(Tokens::$heredocTokens[$tokens[$newLineStart]['code']]); Chris@17: if ($innerNesting === false && $isMultiLineString === false && $nowDoc === false) { Chris@17: $error = 'Array indentation error, expected %s spaces but found %s'; Chris@17: $data = array( Chris@17: $expectedColumn - 1, Chris@17: $tokens[$newLineStart]['column'] - 1, Chris@17: ); Chris@17: $fix = $phpcsFile->addFixableError($error, $newLineStart, 'ArrayIndentation', $data); Chris@17: if ($fix === true) { Chris@17: if ($tokens[$newLineStart]['column'] === 1) { Chris@17: $phpcsFile->fixer->addContentBefore($newLineStart, str_repeat(' ', ($expectedColumn - 1))); Chris@17: } else { Chris@17: $phpcsFile->fixer->replaceToken(($newLineStart - 1), str_repeat(' ', ($expectedColumn - 1))); Chris@17: } Chris@17: } Chris@17: } Chris@17: }//end if Chris@17: Chris@17: $lineStart = $newLineStart; Chris@17: }//end while Chris@17: Chris@17: }//end process() Chris@17: Chris@17: Chris@17: }//end class