supportsAbove('7.0') === false) { return; } $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr]['code'] === \T_OPEN_SHORT_ARRAY && $this->isShortList($phpcsFile, $stackPtr) === false ) { // Short array, not short list. return; } if ($tokens[$stackPtr]['code'] === \T_LIST) { $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS || isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false ) { // Parse error or live coding. return; } $opener = $nextNonEmpty; $closer = $tokens[$nextNonEmpty]['parenthesis_closer']; } else { // Short list syntax. $opener = $stackPtr; if (isset($tokens[$stackPtr]['bracket_closer'])) { $closer = $tokens[$stackPtr]['bracket_closer']; } } if (isset($opener, $closer) === false) { return; } /* * OK, so we have the opener & closer, now we need to check all the variables in the * list() to see if there are duplicates as that's the problem. */ $hasVars = $phpcsFile->findNext(array(\T_VARIABLE, \T_DOLLAR), ($opener + 1), $closer); if ($hasVars === false) { // Empty list, not our concern. return ($closer + 1); } // Set the variable delimiters based on the list type being examined. $stopPoints = array(\T_COMMA); if ($tokens[$stackPtr]['code'] === \T_OPEN_SHORT_ARRAY) { $stopPoints[] = \T_CLOSE_SHORT_ARRAY; } else { $stopPoints[] = \T_CLOSE_PARENTHESIS; } $listVars = array(); $lastStopPoint = $opener; /* * Create a list of all variables used within the `list()` construct. * We're not concerned with whether these are nested or not, as any duplicate * variable name used will be problematic, independent of nesting. */ do { $nextStopPoint = $phpcsFile->findNext($stopPoints, ($lastStopPoint + 1), $closer); if ($nextStopPoint === false) { $nextStopPoint = $closer; } // Also detect this in PHP 7.1 keyed lists. $hasDoubleArrow = $phpcsFile->findNext(\T_DOUBLE_ARROW, ($lastStopPoint + 1), $nextStopPoint); if ($hasDoubleArrow !== false) { $lastStopPoint = $hasDoubleArrow; } // Find the start of the variable, allowing for variable variables. $nextStartPoint = $phpcsFile->findNext(array(\T_VARIABLE, \T_DOLLAR), ($lastStopPoint + 1), $nextStopPoint); if ($nextStartPoint === false) { // Skip past empty bits in the list, i.e. `list( $a, , ,)`. $lastStopPoint = $nextStopPoint; continue; } /* * Gather the content of all non-empty tokens to determine the "variable name". * Variable name in this context includes array or object property syntaxes, such * as `$a['name']` and `$b->property`. */ $varContent = ''; for ($i = $nextStartPoint; $i < $nextStopPoint; $i++) { if (isset(Tokens::$emptyTokens[$tokens[$i]['code']])) { continue; } $varContent .= $tokens[$i]['content']; } if ($varContent !== '') { $listVars[] = $varContent; } $lastStopPoint = $nextStopPoint; } while ($lastStopPoint < $closer); if (empty($listVars)) { // Shouldn't be possible, but just in case. return ($closer + 1); } // Verify that all variables used in the list() construct are unique. if (\count($listVars) !== \count(array_unique($listVars))) { $phpcsFile->addError( 'list() will assign variable from left-to-right since PHP 7.0. Ensure all variables in list() are unique to prevent unexpected results.', $stackPtr, 'Affected' ); } return ($closer + 1); } }