WordPress/Sniffs/Arrays/ArrayIndentationSniff.php
public function process_token( $stackPtr ) {
		if ( ! isset( $this->tab_width ) ) {
			$this->tab_width = PHPCSHelper::get_tab_width( $this->phpcsFile );
		}

		/*
		 * Determine the array opener & closer.
		 */
		$array_open_close = $this->find_array_open_close( $stackPtr );
		if ( false === $array_open_close ) {
			// Array open/close could not be determined.
			return;
		}

		$opener = $array_open_close['opener'];
		$closer = $array_open_close['closer'];

		if ( $this->tokens[ $opener ]['line'] === $this->tokens[ $closer ]['line'] ) {
			// Not interested in single line arrays.
			return;
		}

		/*
		 * Check the closing bracket is lined up with the start of the content on the line
		 * containing the array opener.
		 */
		$opener_line_spaces = $this->get_indentation_size( $opener );
		$closer_line_spaces = ( $this->tokens[ $closer ]['column'] - 1 );

		if ( $closer_line_spaces !== $opener_line_spaces ) {
			$error      = 'Array closer not aligned correctly; expected %s space(s) but found %s';
			$error_code = 'CloseBraceNotAligned';

			/*
			 * Report & fix the issue if the close brace is on its own line with
			 * nothing or only indentation whitespace before it.
			 */
			if ( 0 === $closer_line_spaces
				|| ( \T_WHITESPACE === $this->tokens[ ( $closer - 1 ) ]['code']
					&& 1 === $this->tokens[ ( $closer - 1 ) ]['column'] )
			) {
				$this->add_array_alignment_error(
					$closer,
					$error,
					$error_code,
					$opener_line_spaces,
					$closer_line_spaces,
					$this->get_indentation_string( $opener_line_spaces )
				);
			} else {
				/*
				 * Otherwise, only report the error, don't try and fix it (yet).
				 *
				 * It will get corrected in a future loop of the fixer once the closer
				 * has been moved to its own line by the `ArrayDeclarationSpacing` sniff.
				 */
				$this->phpcsFile->addError(
					$error,
					$closer,
					$error_code,
					array( $opener_line_spaces, $closer_line_spaces )
				);
			}

			unset( $error, $error_code );
		}

		/*
		 * Verify & correct the array item indentation.
		 */
		$array_items = $this->get_function_call_parameters( $stackPtr );
		if ( empty( $array_items ) ) {
			// Strange, no array items found.
			return;
		}

		$expected_spaces      = ( $opener_line_spaces + $this->tab_width );
		$expected_indent      = $this->get_indentation_string( $expected_spaces );
		$end_of_previous_item = $opener;

		foreach ( $array_items as $item ) {
			$end_of_this_item = ( $item['end'] + 1 );

			// Find the line on which the item starts.
			$first_content = $this->phpcsFile->findNext(
				array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE ),
				$item['start'],
				$end_of_this_item,
				true
			);

			// Deal with trailing comments.
			if ( false !== $first_content
				&& \T_COMMENT === $this->tokens[ $first_content ]['code']
				&& $this->tokens[ $first_content ]['line'] === $this->tokens[ $end_of_previous_item ]['line']
			) {
				$first_content = $this->phpcsFile->findNext(
					array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE, \T_COMMENT ),
					( $first_content + 1 ),
					$end_of_this_item,
					true
				);
			}

			if ( false === $first_content ) {
				$end_of_previous_item = $end_of_this_item;
				continue;
			}

			// Bow out from reporting and fixing mixed multi-line/single-line arrays.
			// That is handled by the ArrayDeclarationSpacingSniff.
			if ( $this->tokens[ $first_content ]['line'] === $this->tokens[ $end_of_previous_item ]['line']
				|| ( 1 !== $this->tokens[ $first_content ]['column']
					&& \T_WHITESPACE !== $this->tokens[ ( $first_content - 1 ) ]['code'] )
			) {
				return $closer;
			}

			$found_spaces = ( $this->tokens[ $first_content ]['column'] - 1 );

			if ( $found_spaces !== $expected_spaces ) {
				$this->add_array_alignment_error(
					$first_content,
					'Array item not aligned correctly; expected %s spaces but found %s',
					'ItemNotAligned',
					$expected_spaces,
					$found_spaces,
					$expected_indent
				);
			}

			// No need for further checking if this is a one-line array item.
			if ( $this->tokens[ $first_content ]['line'] === $this->tokens[ $item['end'] ]['line'] ) {
				$end_of_previous_item = $end_of_this_item;
				continue;
			}

			/*
			 * Multi-line array items.
			 *
			 * Verify & if needed, correct the indentation of subsequent lines.
			 * Subsequent lines may be indented more or less than the mimimum expected indent,
			 * but the "first line after" should be indented - at least - as much as the very first line
			 * of the array item.
			 * Indentation correction for subsequent lines will be based on that diff.
			 */

			// Find first token on second line of the array item.
			// If the second line is a heredoc/nowdoc, continue on until we find a line with a different token.
			// Same for the second line of a multi-line text string.
			for ( $ptr = ( $first_content + 1 ); $ptr <= $item['end']; $ptr++ ) {
				if ( $this->tokens[ $first_content ]['line'] !== $this->tokens[ $ptr ]['line']
					&& 1 === $this->tokens[ $ptr ]['column']
					&& false === $this->ignore_token( $ptr )
				) {
					break;
				}
			}

			$first_content_on_line2 = $this->phpcsFile->findNext(
				array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE ),
				$ptr,
				$end_of_this_item,
				true
			);

			if ( false === $first_content_on_line2 ) {
				/*
				 * Apparently there were only tokens in the ignore list on subsequent lines.
				 *
				 * In that case, the comma after the array item might be on a line by itself,
				 * so check its placement.
				 */
				if ( $this->tokens[ $item['end'] ]['line'] !== $this->tokens[ $end_of_this_item ]['line']
					&& \T_COMMA === $this->tokens[ $end_of_this_item ]['code']
					&& ( $this->tokens[ $end_of_this_item ]['column'] - 1 ) !== $expected_spaces
				) {
					$this->add_array_alignment_error(
						$end_of_this_item,
						'Comma after multi-line array item not aligned correctly; expected %s spaces, but found %s',
						'MultiLineArrayItemCommaNotAligned',
						$expected_spaces,
						( $this->tokens[ $end_of_this_item ]['column'] - 1 ),
						$expected_indent
					);
				}

				$end_of_previous_item = $end_of_this_item;
				continue;
			}

			$found_spaces_on_line2    = $this->get_indentation_size( $first_content_on_line2 );
			$expected_spaces_on_line2 = $expected_spaces;

			if ( $found_spaces < $found_spaces_on_line2 ) {
				$expected_spaces_on_line2 += ( $found_spaces_on_line2 - $found_spaces );
			}

			if ( $found_spaces_on_line2 !== $expected_spaces_on_line2 ) {

				$fix = $this->phpcsFile->addFixableError(
					'Multi-line array item not aligned correctly; expected %s spaces, but found %s',
					$first_content_on_line2,
					'MultiLineArrayItemNotAligned',
					array(
						$expected_spaces_on_line2,
						$found_spaces_on_line2,
					)
				);

				if ( true === $fix ) {
					$expected_indent_on_line2 = $this->get_indentation_string( $expected_spaces_on_line2 );

					$this->phpcsFile->fixer->beginChangeset();

					// Fix second line for the array item.
					if ( 1 === $this->tokens[ $first_content_on_line2 ]['column']
						&& \T_COMMENT === $this->tokens[ $first_content_on_line2 ]['code']
					) {
						$actual_comment = ltrim( $this->tokens[ $first_content_on_line2 ]['content'] );
						$replacement    = $expected_indent_on_line2 . $actual_comment;

						$this->phpcsFile->fixer->replaceToken( $first_content_on_line2, $replacement );

					} else {
						$this->fix_alignment_error( $first_content_on_line2, $expected_indent_on_line2 );
					}

					// Fix subsequent lines.
					for ( $i = ( $first_content_on_line2 + 1 ); $i <= $item['end']; $i++ ) {
						// We're only interested in the first token on each line.
						if ( 1 !== $this->tokens[ $i ]['column'] ) {
							if ( $this->tokens[ $i ]['line'] === $this->tokens[ $item['end'] ]['line'] ) {
								// We might as well quit if we're past the first token on the last line.
								break;
							}
							continue;
						}

						$first_content_on_line = $this->phpcsFile->findNext(
							array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE ),
							$i,
							$end_of_this_item,
							true
						);

						if ( false === $first_content_on_line ) {
							break;
						}

						// Ignore lines with heredoc and nowdoc tokens and subsequent lines in multi-line strings.
						if ( true === $this->ignore_token( $first_content_on_line ) ) {
							$i = $first_content_on_line;
							continue;
						}

						$found_spaces_on_line    = $this->get_indentation_size( $first_content_on_line );
						$expected_spaces_on_line = ( $expected_spaces_on_line2 + ( $found_spaces_on_line - $found_spaces_on_line2 ) );
						$expected_spaces_on_line = max( $expected_spaces_on_line, 0 ); // Can't be below 0.
						$expected_indent_on_line = $this->get_indentation_string( $expected_spaces_on_line );

						if ( $found_spaces_on_line !== $expected_spaces_on_line ) {
							if ( 1 === $this->tokens[ $first_content_on_line ]['column']
								&& \T_COMMENT === $this->tokens[ $first_content_on_line ]['code']
							) {
								$actual_comment = ltrim( $this->tokens[ $first_content_on_line ]['content'] );
								$replacement    = $expected_indent_on_line . $actual_comment;

								$this->phpcsFile->fixer->replaceToken( $first_content_on_line, $replacement );
							} else {
								$this->fix_alignment_error( $first_content_on_line, $expected_indent_on_line );
							}
						}

						// Move past any potential empty lines between the previous non-empty line and this one.
						// No need to do the fixes twice.
						$i = $first_content_on_line;
					}

					/*
					 * Check the placement of the comma after the array item as it might be on a line by itself.
					 */
					if ( $this->tokens[ $item['end'] ]['line'] !== $this->tokens[ $end_of_this_item ]['line']
						&& \T_COMMA === $this->tokens[ $end_of_this_item ]['code']
						&& ( $this->tokens[ $end_of_this_item ]['column'] - 1 ) !== $expected_spaces
					) {
						$this->add_array_alignment_error(
							$end_of_this_item,
							'Comma after array item not aligned correctly; expected %s spaces, but found %s',
							'MultiLineArrayItemCommaNotAligned',
							$expected_spaces,
							( $this->tokens[ $end_of_this_item ]['column'] - 1 ),
							$expected_indent
						);
					}

					$this->phpcsFile->fixer->endChangeset();
				}
			}

			$end_of_previous_item = $end_of_this_item;
		}
	}
public function process_token( $stackPtr ) {
		if ( ! isset( $this->tab_width ) ) {
			$this->tab_width = PHPCSHelper::get_tab_width( $this->phpcsFile );
		}

		/*
		 * Determine the array opener & closer.
		 */
		$array_open_close = $this->find_array_open_close( $stackPtr );
		if ( false === $array_open_close ) {
			// Array open/close could not be determined.
			return;
		}

		$opener = $array_open_close['opener'];
		$closer = $array_open_close['closer'];

		if ( $this->tokens[ $opener ]['line'] === $this->tokens[ $closer ]['line'] ) {
			// Not interested in single line arrays.
			return;
		}

		/*
		 * Check the closing bracket is lined up with the start of the content on the line
		 * containing the array opener.
		 */
		$opener_line_spaces = $this->get_indentation_size( $opener );
		$closer_line_spaces = ( $this->tokens[ $closer ]['column'] - 1 );

		if ( $closer_line_spaces !== $opener_line_spaces ) {
			$error      = 'Array closer not aligned correctly; expected %s space(s) but found %s';
			$error_code = 'CloseBraceNotAligned';

			/*
			 * Report & fix the issue if the close brace is on its own line with
			 * nothing or only indentation whitespace before it.
			 */
			if ( 0 === $closer_line_spaces
				|| ( \T_WHITESPACE === $this->tokens[ ( $closer - 1 ) ]['code']
					&& 1 === $this->tokens[ ( $closer - 1 ) ]['column'] )
			) {
				$this->add_array_alignment_error(
					$closer,
					$error,
					$error_code,
					$opener_line_spaces,
					$closer_line_spaces,
					$this->get_indentation_string( $opener_line_spaces )
				);
			} else {
				/*
				 * Otherwise, only report the error, don't try and fix it (yet).
				 *
				 * It will get corrected in a future loop of the fixer once the closer
				 * has been moved to its own line by the `ArrayDeclarationSpacing` sniff.
				 */
				$this->phpcsFile->addError(
					$error,
					$closer,
					$error_code,
					array( $opener_line_spaces, $closer_line_spaces )
				);
			}

			unset( $error, $error_code );
		}

		/*
		 * Verify & correct the array item indentation.
		 */
		$array_items = $this->get_function_call_parameters( $stackPtr );
		if ( empty( $array_items ) ) {
			// Strange, no array items found.
			return;
		}

		$expected_spaces      = ( $opener_line_spaces + $this->tab_width );
		$expected_indent      = $this->get_indentation_string( $expected_spaces );
		$end_of_previous_item = $opener;

		foreach ( $array_items as $item ) {
			$end_of_this_item = ( $item['end'] + 1 );

			// Find the line on which the item starts.
			$first_content = $this->phpcsFile->findNext(
				array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE ),
				$item['start'],
				$end_of_this_item,
				true
			);

			// Deal with trailing comments.
			if ( false !== $first_content
				&& \T_COMMENT === $this->tokens[ $first_content ]['code']
				&& $this->tokens[ $first_content ]['line'] === $this->tokens[ $end_of_previous_item ]['line']
			) {
				$first_content = $this->phpcsFile->findNext(
					array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE, \T_COMMENT ),
					( $first_content + 1 ),
					$end_of_this_item,
					true
				);
			}

			if ( false === $first_content ) {
				$end_of_previous_item = $end_of_this_item;
				continue;
			}

			// Bow out from reporting and fixing mixed multi-line/single-line arrays.
			// That is handled by the ArrayDeclarationSpacingSniff.
			if ( $this->tokens[ $first_content ]['line'] === $this->tokens[ $end_of_previous_item ]['line']
				|| ( 1 !== $this->tokens[ $first_content ]['column']
					&& \T_WHITESPACE !== $this->tokens[ ( $first_content - 1 ) ]['code'] )
			) {
				return $closer;
			}

			$found_spaces = ( $this->tokens[ $first_content ]['column'] - 1 );

			if ( $found_spaces !== $expected_spaces ) {
				$this->add_array_alignment_error(
					$first_content,
					'Array item not aligned correctly; expected %s spaces but found %s',
					'ItemNotAligned',
					$expected_spaces,
					$found_spaces,
					$expected_indent
				);
			}

			// No need for further checking if this is a one-line array item.
			if ( $this->tokens[ $first_content ]['line'] === $this->tokens[ $item['end'] ]['line'] ) {
				$end_of_previous_item = $end_of_this_item;
				continue;
			}

			/*
			 * Multi-line array items.
			 *
			 * Verify & if needed, correct the indentation of subsequent lines.
			 * Subsequent lines may be indented more or less than the mimimum expected indent,
			 * but the "first line after" should be indented - at least - as much as the very first line
			 * of the array item.
			 * Indentation correction for subsequent lines will be based on that diff.
			 */

			// Find first token on second line of the array item.
			// If the second line is a heredoc/nowdoc, continue on until we find a line with a different token.
			// Same for the second line of a multi-line text string.
			for ( $ptr = ( $first_content + 1 ); $ptr <= $item['end']; $ptr++ ) {
				if ( $this->tokens[ $first_content ]['line'] !== $this->tokens[ $ptr ]['line']
					&& 1 === $this->tokens[ $ptr ]['column']
					&& false === $this->ignore_token( $ptr )
				) {
					break;
				}
			}

			$first_content_on_line2 = $this->phpcsFile->findNext(
				array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE ),
				$ptr,
				$end_of_this_item,
				true
			);

			if ( false === $first_content_on_line2 ) {
				/*
				 * Apparently there were only tokens in the ignore list on subsequent lines.
				 *
				 * In that case, the comma after the array item might be on a line by itself,
				 * so check its placement.
				 */
				if ( $this->tokens[ $item['end'] ]['line'] !== $this->tokens[ $end_of_this_item ]['line']
					&& \T_COMMA === $this->tokens[ $end_of_this_item ]['code']
					&& ( $this->tokens[ $end_of_this_item ]['column'] - 1 ) !== $expected_spaces
				) {
					$this->add_array_alignment_error(
						$end_of_this_item,
						'Comma after multi-line array item not aligned correctly; expected %s spaces, but found %s',
						'MultiLineArrayItemCommaNotAligned',
						$expected_spaces,
						( $this->tokens[ $end_of_this_item ]['column'] - 1 ),
						$expected_indent
					);
				}

				$end_of_previous_item = $end_of_this_item;
				continue;
			}

			$found_spaces_on_line2    = $this->get_indentation_size( $first_content_on_line2 );
			$expected_spaces_on_line2 = $expected_spaces;

			if ( $found_spaces < $found_spaces_on_line2 ) {
				$expected_spaces_on_line2 += ( $found_spaces_on_line2 - $found_spaces );
			}

			if ( $found_spaces_on_line2 !== $expected_spaces_on_line2 ) {

				$fix = $this->phpcsFile->addFixableError(
					'Multi-line array item not aligned correctly; expected %s spaces, but found %s',
					$first_content_on_line2,
					'MultiLineArrayItemNotAligned',
					array(
						$expected_spaces_on_line2,
						$found_spaces_on_line2,
					)
				);

				if ( true === $fix ) {
					$expected_indent_on_line2 = $this->get_indentation_string( $expected_spaces_on_line2 );

					$this->phpcsFile->fixer->beginChangeset();

					// Fix second line for the array item.
					if ( 1 === $this->tokens[ $first_content_on_line2 ]['column']
						&& \T_COMMENT === $this->tokens[ $first_content_on_line2 ]['code']
					) {
						$actual_comment = ltrim( $this->tokens[ $first_content_on_line2 ]['content'] );
						$replacement    = $expected_indent_on_line2 . $actual_comment;

						$this->phpcsFile->fixer->replaceToken( $first_content_on_line2, $replacement );

					} else {
						$this->fix_alignment_error( $first_content_on_line2, $expected_indent_on_line2 );
					}

					// Fix subsequent lines.
					for ( $i = ( $first_content_on_line2 + 1 ); $i <= $item['end']; $i++ ) {
						// We're only interested in the first token on each line.
						if ( 1 !== $this->tokens[ $i ]['column'] ) {
							if ( $this->tokens[ $i ]['line'] === $this->tokens[ $item['end'] ]['line'] ) {
								// We might as well quit if we're past the first token on the last line.
								break;
							}
							continue;
						}

						$first_content_on_line = $this->phpcsFile->findNext(
							array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE ),
							$i,
							$end_of_this_item,
							true
						);

						if ( false === $first_content_on_line ) {
							break;
						}

						// Ignore lines with heredoc and nowdoc tokens and subsequent lines in multi-line strings.
						if ( true === $this->ignore_token( $first_content_on_line ) ) {
							$i = $first_content_on_line;
							continue;
						}

						$found_spaces_on_line    = $this->get_indentation_size( $first_content_on_line );
						$expected_spaces_on_line = ( $expected_spaces_on_line2 + ( $found_spaces_on_line - $found_spaces_on_line2 ) );
						$expected_spaces_on_line = max( $expected_spaces_on_line, 0 ); // Can't be below 0.
						$expected_indent_on_line = $this->get_indentation_string( $expected_spaces_on_line );

						if ( $found_spaces_on_line !== $expected_spaces_on_line ) {
							if ( 1 === $this->tokens[ $first_content_on_line ]['column']
								&& \T_COMMENT === $this->tokens[ $first_content_on_line ]['code']
							) {
								$actual_comment = ltrim( $this->tokens[ $first_content_on_line ]['content'] );
								$replacement    = $expected_indent_on_line . $actual_comment;

								$this->phpcsFile->fixer->replaceToken( $first_content_on_line, $replacement );
							} else {
								$this->fix_alignment_error( $first_content_on_line, $expected_indent_on_line );
							}
						}

						// Move past any potential empty lines between the previous non-empty line and this one.
						// No need to do the fixes twice.
						$i = $first_content_on_line;
					}

					/*
					 * Check the placement of the comma after the array item as it might be on a line by itself.
					 */
					if ( $this->tokens[ $item['end'] ]['line'] !== $this->tokens[ $end_of_this_item ]['line']
						&& \T_COMMA === $this->tokens[ $end_of_this_item ]['code']
						&& ( $this->tokens[ $end_of_this_item ]['column'] - 1 ) !== $expected_spaces
					) {
						$this->add_array_alignment_error(
							$end_of_this_item,
							'Comma after array item not aligned correctly; expected %s spaces, but found %s',
							'MultiLineArrayItemCommaNotAligned',
							$expected_spaces,
							( $this->tokens[ $end_of_this_item ]['column'] - 1 ),
							$expected_indent
						);
					}

					$this->phpcsFile->fixer->endChangeset();
				}
			}

			$end_of_previous_item = $end_of_this_item;
		}
	}
Processes this test, when one of its tokens is encountered.

@param int $stackPtr The position of the current token in the stack.

@return void