annotate vendor/drupal/coder/coder_sniffer/Drupal/Sniffs/Array/ArraySniff.php @ 12:7a779792577d

Update Drupal core to v8.4.5 (via Composer)
author Chris Cannam
date Fri, 23 Feb 2018 15:52:07 +0000
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2 /**
Chris@0 3 * Drupal_Sniffs_Array_ArraySniff.
Chris@0 4 *
Chris@0 5 * @category PHP
Chris@0 6 * @package PHP_CodeSniffer
Chris@0 7 * @link http://pear.php.net/package/PHP_CodeSniffer
Chris@0 8 */
Chris@0 9
Chris@0 10 /**
Chris@0 11 * Drupal_Sniffs_Array_ArraySniff.
Chris@0 12 *
Chris@0 13 * Checks if the array's are styled in the Drupal way.
Chris@0 14 * - Comma after the last array element
Chris@0 15 * - Indentation is 2 spaces for multi line array definitions
Chris@0 16 *
Chris@0 17 * @category PHP
Chris@0 18 * @package PHP_CodeSniffer
Chris@0 19 * @link http://pear.php.net/package/PHP_CodeSniffer
Chris@0 20 */
Chris@0 21 class Drupal_Sniffs_Array_ArraySniff implements PHP_CodeSniffer_Sniff
Chris@0 22 {
Chris@0 23
Chris@0 24
Chris@0 25 /**
Chris@0 26 * Returns an array of tokens this test wants to listen for.
Chris@0 27 *
Chris@0 28 * @return array
Chris@0 29 */
Chris@0 30 public function register()
Chris@0 31 {
Chris@0 32 return array(
Chris@0 33 T_ARRAY,
Chris@0 34 T_OPEN_SHORT_ARRAY,
Chris@0 35 );
Chris@0 36
Chris@0 37 }//end register()
Chris@0 38
Chris@0 39
Chris@0 40 /**
Chris@0 41 * Processes this test, when one of its tokens is encountered.
Chris@0 42 *
Chris@0 43 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
Chris@0 44 * @param int $stackPtr The position of the current token in
Chris@0 45 * the stack passed in $tokens.
Chris@0 46 *
Chris@0 47 * @return void
Chris@0 48 */
Chris@0 49 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
Chris@0 50 {
Chris@0 51 $tokens = $phpcsFile->getTokens();
Chris@0 52
Chris@0 53 // Support long and short syntax.
Chris@0 54 $parenthesis_opener = 'parenthesis_opener';
Chris@0 55 $parenthesis_closer = 'parenthesis_closer';
Chris@0 56 if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY) {
Chris@0 57 $parenthesis_opener = 'bracket_opener';
Chris@0 58 $parenthesis_closer = 'bracket_closer';
Chris@0 59 }
Chris@0 60
Chris@0 61 // Sanity check: this can sometimes be NULL if the array was not correctly
Chris@0 62 // parsed.
Chris@0 63 if ($tokens[$stackPtr][$parenthesis_closer] === null) {
Chris@0 64 return;
Chris@0 65 }
Chris@0 66
Chris@0 67 $lastItem = $phpcsFile->findPrevious(
Chris@0 68 PHP_CodeSniffer_Tokens::$emptyTokens,
Chris@0 69 ($tokens[$stackPtr][$parenthesis_closer] - 1),
Chris@0 70 $stackPtr,
Chris@0 71 true
Chris@0 72 );
Chris@0 73
Chris@0 74 // Empty array.
Chris@0 75 if ($lastItem === $tokens[$stackPtr][$parenthesis_opener]) {
Chris@0 76 return;
Chris@0 77 }
Chris@0 78
Chris@0 79 // Inline array.
Chris@0 80 $isInlineArray = $tokens[$tokens[$stackPtr][$parenthesis_opener]]['line'] === $tokens[$tokens[$stackPtr][$parenthesis_closer]]['line'];
Chris@0 81
Chris@0 82 // Check if the last item in a multiline array has a "closing" comma.
Chris@0 83 if ($tokens[$lastItem]['code'] !== T_COMMA && $isInlineArray === false
Chris@0 84 && $tokens[($lastItem + 1)]['code'] !== T_CLOSE_PARENTHESIS
Chris@0 85 && $tokens[($lastItem + 1)]['code'] !== T_CLOSE_SHORT_ARRAY
Chris@0 86 && isset(PHP_CodeSniffer_Tokens::$heredocTokens[$tokens[$lastItem]['code']]) === false
Chris@0 87 ) {
Chris@0 88 $data = array($tokens[$lastItem]['content']);
Chris@0 89 $fix = $phpcsFile->addFixableWarning('A comma should follow the last multiline array item. Found: %s', $lastItem, 'CommaLastItem', $data);
Chris@0 90 if ($fix === true) {
Chris@0 91 $phpcsFile->fixer->addContent($lastItem, ',');
Chris@0 92 }
Chris@0 93
Chris@0 94 return;
Chris@0 95 }
Chris@0 96
Chris@0 97 if ($isInlineArray === true) {
Chris@0 98 // Check if this array contains at least 3 elements and exceeds the 80
Chris@0 99 // character line length.
Chris@0 100 if ($tokens[$tokens[$stackPtr][$parenthesis_closer]]['column'] > 80) {
Chris@0 101 $comma1 = $phpcsFile->findNext(T_COMMA, ($stackPtr + 1), $tokens[$stackPtr][$parenthesis_closer]);
Chris@0 102 if ($comma1 !== false) {
Chris@0 103 $comma2 = $phpcsFile->findNext(T_COMMA, ($comma1 + 1), $tokens[$stackPtr][$parenthesis_closer]);
Chris@0 104 if ($comma2 !== false) {
Chris@0 105 $error = 'If the line declaring an array spans longer than 80 characters, each element should be broken into its own line';
Chris@0 106 $phpcsFile->addError($error, $stackPtr, 'LongLineDeclaration');
Chris@0 107 }
Chris@0 108 }
Chris@0 109 }
Chris@0 110
Chris@0 111 // Only continue for multi line arrays.
Chris@0 112 return;
Chris@0 113 }
Chris@0 114
Chris@0 115 // Find the first token on this line.
Chris@0 116 $firstLineColumn = $tokens[$stackPtr]['column'];
Chris@0 117 for ($i = $stackPtr; $i >= 0; $i--) {
Chris@0 118 // If there is a PHP open tag then this must be a template file where we
Chris@0 119 // don't check indentation.
Chris@0 120 if ($tokens[$i]['code'] === T_OPEN_TAG) {
Chris@0 121 return;
Chris@0 122 }
Chris@0 123
Chris@0 124 // Record the first code token on the line.
Chris@0 125 if ($tokens[$i]['code'] !== T_WHITESPACE) {
Chris@0 126 $firstLineColumn = $tokens[$i]['column'];
Chris@0 127 // This could be a multi line string or comment beginning with white
Chris@0 128 // spaces.
Chris@0 129 $trimmed = ltrim($tokens[$i]['content']);
Chris@0 130 if ($trimmed !== $tokens[$i]['content']) {
Chris@0 131 $firstLineColumn = ($firstLineColumn + strpos($tokens[$i]['content'], $trimmed));
Chris@0 132 }
Chris@0 133 }
Chris@0 134
Chris@0 135 // It's the start of the line, so we've found our first php token.
Chris@0 136 if ($tokens[$i]['column'] === 1) {
Chris@0 137 break;
Chris@0 138 }
Chris@0 139 }//end for
Chris@0 140
Chris@0 141 $lineStart = $stackPtr;
Chris@0 142 // Iterate over all lines of this array.
Chris@0 143 while ($lineStart < $tokens[$stackPtr][$parenthesis_closer]) {
Chris@0 144 // Find next line start.
Chris@0 145 $newLineStart = $lineStart;
Chris@0 146 $current_line = $tokens[$newLineStart]['line'];
Chris@0 147 while ($current_line >= $tokens[$newLineStart]['line']) {
Chris@0 148 $newLineStart = $phpcsFile->findNext(
Chris@0 149 PHP_CodeSniffer_Tokens::$emptyTokens,
Chris@0 150 ($newLineStart + 1),
Chris@0 151 ($tokens[$stackPtr][$parenthesis_closer] + 1),
Chris@0 152 true
Chris@0 153 );
Chris@0 154
Chris@0 155 if ($newLineStart === false) {
Chris@0 156 break 2;
Chris@0 157 }
Chris@0 158
Chris@0 159 // Long array syntax: Skip nested arrays, they are checked in a next
Chris@0 160 // run.
Chris@0 161 if ($tokens[$newLineStart]['code'] === T_ARRAY) {
Chris@0 162 $newLineStart = $tokens[$newLineStart]['parenthesis_closer'];
Chris@0 163 $current_line = $tokens[$newLineStart]['line'];
Chris@0 164 }
Chris@0 165
Chris@0 166 // Short array syntax: Skip nested arrays, they are checked in a next
Chris@0 167 // run.
Chris@0 168 if ($tokens[$newLineStart]['code'] === T_OPEN_SHORT_ARRAY) {
Chris@0 169 $newLineStart = $tokens[$newLineStart]['bracket_closer'];
Chris@0 170 $current_line = $tokens[$newLineStart]['line'];
Chris@0 171 }
Chris@0 172
Chris@0 173 // Nested structures such as closures: skip those, they are checked
Chris@0 174 // in other sniffs. If the conditions of a token are different it
Chris@0 175 // means that it is in a different nesting level.
Chris@0 176 if ($tokens[$newLineStart]['conditions'] !== $tokens[$stackPtr]['conditions']) {
Chris@0 177 $current_line++;
Chris@0 178 }
Chris@0 179 }//end while
Chris@0 180
Chris@0 181 if ($newLineStart === $tokens[$stackPtr][$parenthesis_closer]) {
Chris@0 182 // End of the array reached.
Chris@0 183 if ($tokens[$newLineStart]['column'] !== $firstLineColumn) {
Chris@0 184 $error = 'Array closing indentation error, expected %s spaces but found %s';
Chris@0 185 $data = array(
Chris@0 186 $firstLineColumn - 1,
Chris@0 187 $tokens[$newLineStart]['column'] - 1,
Chris@0 188 );
Chris@0 189 $fix = $phpcsFile->addFixableError($error, $newLineStart, 'ArrayClosingIndentation', $data);
Chris@0 190 if ($fix === true) {
Chris@0 191 if ($tokens[$newLineStart]['column'] === 1) {
Chris@0 192 $phpcsFile->fixer->addContentBefore($newLineStart, str_repeat(' ', ($firstLineColumn - 1)));
Chris@0 193 } else {
Chris@0 194 $phpcsFile->fixer->replaceToken(($newLineStart - 1), str_repeat(' ', ($firstLineColumn - 1)));
Chris@0 195 }
Chris@0 196 }
Chris@0 197 }
Chris@0 198
Chris@0 199 break;
Chris@0 200 }
Chris@0 201
Chris@0 202 $expectedColumn = ($firstLineColumn + 2);
Chris@0 203 // If the line starts with "->" then we assume an additional level of
Chris@0 204 // indentation.
Chris@0 205 if ($tokens[$newLineStart]['code'] === T_OBJECT_OPERATOR) {
Chris@0 206 $expectedColumn += 2;
Chris@0 207 }
Chris@0 208
Chris@0 209 if ($tokens[$newLineStart]['column'] !== $expectedColumn) {
Chris@0 210 // Skip lines in nested structures such as a function call within an
Chris@0 211 // array, no defined coding standard for those.
Chris@0 212 $innerNesting = empty($tokens[$newLineStart]['nested_parenthesis']) === false
Chris@0 213 && end($tokens[$newLineStart]['nested_parenthesis']) < $tokens[$stackPtr][$parenthesis_closer];
Chris@0 214 // Skip lines that are part of a multi-line string.
Chris@0 215 $isMultiLineString = $tokens[($newLineStart - 1)]['code'] === T_CONSTANT_ENCAPSED_STRING
Chris@0 216 && substr($tokens[($newLineStart - 1)]['content'], -1) === $phpcsFile->eolChar;
Chris@0 217 // Skip NOWDOC or HEREDOC lines.
Chris@0 218 $nowDoc = isset(PHP_CodeSniffer_Tokens::$heredocTokens[$tokens[$newLineStart]['code']]);
Chris@0 219 if ($innerNesting === false && $isMultiLineString === false && $nowDoc === false) {
Chris@0 220 $error = 'Array indentation error, expected %s spaces but found %s';
Chris@0 221 $data = array(
Chris@0 222 $expectedColumn - 1,
Chris@0 223 $tokens[$newLineStart]['column'] - 1,
Chris@0 224 );
Chris@0 225 $fix = $phpcsFile->addFixableError($error, $newLineStart, 'ArrayIndentation', $data);
Chris@0 226 if ($fix === true) {
Chris@0 227 if ($tokens[$newLineStart]['column'] === 1) {
Chris@0 228 $phpcsFile->fixer->addContentBefore($newLineStart, str_repeat(' ', ($expectedColumn - 1)));
Chris@0 229 } else {
Chris@0 230 $phpcsFile->fixer->replaceToken(($newLineStart - 1), str_repeat(' ', ($expectedColumn - 1)));
Chris@0 231 }
Chris@0 232 }
Chris@0 233 }
Chris@0 234 }//end if
Chris@0 235
Chris@0 236 $lineStart = $newLineStart;
Chris@0 237 }//end while
Chris@0 238
Chris@0 239 }//end process()
Chris@0 240
Chris@0 241
Chris@0 242 }//end class