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\Sniffs; Chris@17: Chris@17: use PHP_CodeSniffer\Files\File; Chris@17: use PHP_CodeSniffer\Util\Tokens; Chris@17: Chris@17: abstract class AbstractArraySniff implements Sniff Chris@17: { Chris@17: Chris@17: Chris@17: /** Chris@17: * Returns an array of tokens this test wants to listen for. Chris@17: * Chris@17: * @return array Chris@17: */ Chris@17: final public function register() Chris@17: { Chris@17: return [ Chris@17: T_ARRAY, Chris@17: T_OPEN_SHORT_ARRAY, Chris@17: ]; Chris@17: Chris@17: }//end register() Chris@17: Chris@17: Chris@17: /** Chris@17: * Processes this sniff, when one of its tokens is encountered. Chris@17: * Chris@17: * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked. Chris@17: * @param int $stackPtr The position of the current token in Chris@17: * the stack passed in $tokens. Chris@17: * Chris@17: * @return void Chris@17: */ Chris@17: public function process(File $phpcsFile, $stackPtr) Chris@17: { Chris@17: $tokens = $phpcsFile->getTokens(); Chris@17: Chris@17: if ($tokens[$stackPtr]['code'] === T_ARRAY) { Chris@17: $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'no'); Chris@17: Chris@17: $arrayStart = $tokens[$stackPtr]['parenthesis_opener']; Chris@17: if (isset($tokens[$arrayStart]['parenthesis_closer']) === false) { Chris@17: // Incomplete array. Chris@17: return; Chris@17: } Chris@17: Chris@17: $arrayEnd = $tokens[$arrayStart]['parenthesis_closer']; Chris@17: } else { Chris@17: $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes'); Chris@17: $arrayStart = $stackPtr; Chris@17: $arrayEnd = $tokens[$stackPtr]['bracket_closer']; Chris@17: } Chris@17: Chris@17: $lastContent = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($arrayEnd - 1), null, true); Chris@17: if ($tokens[$lastContent]['code'] === T_COMMA) { Chris@17: // Last array item ends with a comma. Chris@17: $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'yes'); Chris@17: $lastArrayToken = $lastContent; Chris@17: } else { Chris@17: $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'no'); Chris@17: $lastArrayToken = $arrayEnd; Chris@17: } Chris@17: Chris@17: if ($tokens[$stackPtr]['code'] === T_ARRAY) { Chris@17: $lastToken = $tokens[$stackPtr]['parenthesis_opener']; Chris@17: } else { Chris@17: $lastToken = $stackPtr; Chris@17: } Chris@17: Chris@17: $keyUsed = false; Chris@17: $indices = []; Chris@17: Chris@17: for ($checkToken = ($stackPtr + 1); $checkToken <= $lastArrayToken; $checkToken++) { Chris@17: // Skip bracketed statements, like function calls. Chris@17: if ($tokens[$checkToken]['code'] === T_OPEN_PARENTHESIS Chris@17: && (isset($tokens[$checkToken]['parenthesis_owner']) === false Chris@17: || $tokens[$checkToken]['parenthesis_owner'] !== $stackPtr) Chris@17: ) { Chris@17: $checkToken = $tokens[$checkToken]['parenthesis_closer']; Chris@17: continue; Chris@17: } Chris@17: Chris@17: if ($tokens[$checkToken]['code'] === T_ARRAY Chris@17: || $tokens[$checkToken]['code'] === T_OPEN_SHORT_ARRAY Chris@17: || $tokens[$checkToken]['code'] === T_CLOSURE Chris@17: ) { Chris@17: // Let subsequent calls of this test handle nested arrays. Chris@17: if ($tokens[$lastToken]['code'] !== T_DOUBLE_ARROW) { Chris@17: $indices[] = ['value_start' => $checkToken]; Chris@17: $lastToken = $checkToken; Chris@17: } Chris@17: Chris@17: if ($tokens[$checkToken]['code'] === T_ARRAY) { Chris@17: $checkToken = $tokens[$tokens[$checkToken]['parenthesis_opener']]['parenthesis_closer']; Chris@17: } else if ($tokens[$checkToken]['code'] === T_OPEN_SHORT_ARRAY) { Chris@17: $checkToken = $tokens[$checkToken]['bracket_closer']; Chris@17: } else { Chris@17: // T_CLOSURE. Chris@17: $checkToken = $tokens[$checkToken]['scope_closer']; Chris@17: } Chris@17: Chris@17: $checkToken = $phpcsFile->findNext(T_WHITESPACE, ($checkToken + 1), null, true); Chris@17: $lastToken = $checkToken; Chris@17: if ($tokens[$checkToken]['code'] !== T_COMMA) { Chris@17: $checkToken--; Chris@17: } Chris@17: Chris@17: continue; Chris@17: }//end if Chris@17: Chris@17: if ($tokens[$checkToken]['code'] !== T_DOUBLE_ARROW Chris@17: && $tokens[$checkToken]['code'] !== T_COMMA Chris@17: && $checkToken !== $arrayEnd Chris@17: ) { Chris@17: continue; Chris@17: } Chris@17: Chris@17: if ($tokens[$checkToken]['code'] === T_COMMA Chris@17: || $checkToken === $arrayEnd Chris@17: ) { Chris@17: $stackPtrCount = 0; Chris@17: if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) { Chris@17: $stackPtrCount = count($tokens[$stackPtr]['nested_parenthesis']); Chris@17: } Chris@17: Chris@17: $commaCount = 0; Chris@17: if (isset($tokens[$checkToken]['nested_parenthesis']) === true) { Chris@17: $commaCount = count($tokens[$checkToken]['nested_parenthesis']); Chris@17: if ($tokens[$stackPtr]['code'] === T_ARRAY) { Chris@17: // Remove parenthesis that are used to define the array. Chris@17: $commaCount--; Chris@17: } Chris@17: } Chris@17: Chris@17: if ($commaCount > $stackPtrCount) { Chris@17: // This comma is inside more parenthesis than the ARRAY keyword, Chris@17: // so it is actually a comma used to do things like Chris@17: // separate arguments in a function call. Chris@17: continue; Chris@17: } Chris@17: Chris@17: if ($keyUsed === false) { Chris@17: $valueContent = $phpcsFile->findNext( Chris@17: Tokens::$emptyTokens, Chris@17: ($lastToken + 1), Chris@17: $checkToken, Chris@17: true Chris@17: ); Chris@17: Chris@17: $indices[] = ['value_start' => $valueContent]; Chris@17: } Chris@17: Chris@17: $lastToken = $checkToken; Chris@17: $keyUsed = false; Chris@17: continue; Chris@17: }//end if Chris@17: Chris@17: if ($tokens[$checkToken]['code'] === T_DOUBLE_ARROW) { Chris@17: $keyUsed = true; Chris@17: Chris@17: // Find the start of index that uses this double arrow. Chris@17: $indexEnd = $phpcsFile->findPrevious(T_WHITESPACE, ($checkToken - 1), $arrayStart, true); Chris@17: $indexStart = $phpcsFile->findStartOfStatement($indexEnd); Chris@17: Chris@17: // Find the value of this index. Chris@17: $nextContent = $phpcsFile->findNext( Chris@17: Tokens::$emptyTokens, Chris@17: ($checkToken + 1), Chris@17: $arrayEnd, Chris@17: true Chris@17: ); Chris@17: Chris@17: $indices[] = [ Chris@17: 'index_start' => $indexStart, Chris@17: 'index_end' => $indexEnd, Chris@17: 'arrow' => $checkToken, Chris@17: 'value_start' => $nextContent, Chris@17: ]; Chris@17: Chris@17: $lastToken = $checkToken; Chris@17: }//end if Chris@17: }//end for Chris@17: Chris@17: if ($tokens[$arrayStart]['line'] === $tokens[$arrayEnd]['line']) { Chris@17: $this->processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices); Chris@17: } else { Chris@17: $this->processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices); Chris@17: } Chris@17: Chris@17: }//end process() Chris@17: Chris@17: Chris@17: /** Chris@17: * Processes a single-line array definition. Chris@17: * Chris@17: * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked. Chris@17: * @param int $stackPtr The position of the current token Chris@17: * in the stack passed in $tokens. Chris@17: * @param int $arrayStart The token that starts the array definition. Chris@17: * @param int $arrayEnd The token that ends the array definition. Chris@17: * @param array $indices An array of token positions for the array keys, Chris@17: * double arrows, and values. Chris@17: * Chris@17: * @return void Chris@17: */ Chris@17: abstract protected function processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices); Chris@17: Chris@17: Chris@17: /** Chris@17: * Processes a multi-line array definition. Chris@17: * Chris@17: * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked. Chris@17: * @param int $stackPtr The position of the current token Chris@17: * in the stack passed in $tokens. Chris@17: * @param int $arrayStart The token that starts the array definition. Chris@17: * @param int $arrayEnd The token that ends the array definition. Chris@17: * @param array $indices An array of token positions for the array keys, Chris@17: * double arrows, and values. Chris@17: * Chris@17: * @return void Chris@17: */ Chris@17: abstract protected function processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices); Chris@17: Chris@17: Chris@17: }//end class