WordPress/Sniffs/Arrays/MultipleStatementAlignmentSniff.php
protected function process_multi_line_array( $stackPtr, $items, $opener, $closer ) {

		$this->maxColumn = (int) $this->maxColumn;
		$this->validate_align_multiline_items();

		/*
		 * Determine what the spacing before the arrow should be.
		 *
		 * Will unset any array items without double arrow and with new line whitespace
		 * if newlines are to be ignored, so the second foreach loop only has to deal
		 * with items which need attention.
		 *
		 * This sniff does not take incorrect indentation of array keys into account.
		 * That's for the `WordPress.Arrays.ArrayIndentation` sniff to fix.
		 * If that would affect the alignment, a second (or third) loop of the fixer
		 * will correct it (again) after the indentation has been fixed.
		 */
		$index_end_cols    = array(); // Keep track of the end column position of index keys.
		$double_arrow_cols = array(); // Keep track of arrow column position and count.
		$multi_line_count  = 0;
		$total_items       = \count( $items );

		foreach ( $items as $key => $item ) {
			if ( strpos( $item['raw'], '=>' ) === false ) {
				// Ignore items without assignment operators.
				unset( $items[ $key ] );
				continue;
			}

			// Find the position of the first double arrow.
			$double_arrow = $this->phpcsFile->findNext(
				\T_DOUBLE_ARROW,
				$item['start'],
				( $item['end'] + 1 )
			);

			if ( false === $double_arrow ) {
				// Shouldn't happen, just in case.
				unset( $items[ $key ] );
				continue;
			}

			// Make sure the arrow is for this item and not for a nested array value assignment.
			$has_array_opener = $this->phpcsFile->findNext(
				$this->register(),
				$item['start'],
				$double_arrow
			);

			if ( false !== $has_array_opener ) {
				// Double arrow is for a nested array.
				unset( $items[ $key ] );
				continue;
			}

			// Find the end of the array key.
			$last_index_token = $this->phpcsFile->findPrevious(
				\T_WHITESPACE,
				( $double_arrow - 1 ),
				$item['start'],
				true
			);

			if ( false === $last_index_token ) {
				// Shouldn't happen, but just in case.
				unset( $items[ $key ] );
				continue;
			}

			if ( true === $this->ignoreNewlines
				&& $this->tokens[ $last_index_token ]['line'] !== $this->tokens[ $double_arrow ]['line']
			) {
				// Ignore this item as it has a new line between the item key and the double arrow.
				unset( $items[ $key ] );
				continue;
			}

			$index_end_position                = ( $this->tokens[ $last_index_token ]['column'] + ( $this->tokens[ $last_index_token ]['length'] - 1 ) );
			$items[ $key ]['operatorPtr']      = $double_arrow;
			$items[ $key ]['last_index_token'] = $last_index_token;
			$items[ $key ]['last_index_col']   = $index_end_position;

			if ( $this->tokens[ $last_index_token ]['line'] === $this->tokens[ $item['end'] ]['line'] ) {
				$items[ $key ]['single_line'] = true;
			} else {
				$items[ $key ]['single_line'] = false;
				$multi_line_count++;
			}

			if ( ( $index_end_position + 2 ) <= $this->maxColumn ) {
				$index_end_cols[] = $index_end_position;
			}

			if ( ! isset( $double_arrow_cols[ $this->tokens[ $double_arrow ]['column'] ] ) ) {
				$double_arrow_cols[ $this->tokens[ $double_arrow ]['column'] ] = 1;
			} else {
				$double_arrow_cols[ $this->tokens[ $double_arrow ]['column'] ]++;
			}
		}
		unset( $key, $item, $double_arrow, $has_array_opener, $last_index_token );

		if ( empty( $items ) || empty( $index_end_cols ) ) {
			// No actionable array items found.
			return;
		}

		/*
		 * Determine whether the operators for multi-line items should be aligned.
		 */
		if ( 'always' === $this->alignMultilineItems ) {
			$alignMultilineItems = true;
		} elseif ( 'never' === $this->alignMultilineItems ) {
			$alignMultilineItems = false;
		} else {
			$percentage = (string) round( ( $multi_line_count / $total_items ) * 100, 0 );

			// Bit hacky, but this is the only comparison function in PHP which allows to
			// pass the comparison operator. And hey, it works ;-).
			$alignMultilineItems = version_compare( $percentage, $this->number, $this->operator );
		}

		/*
		 * If necessary, rebuild the $index_end_cols and $double_arrow_cols arrays
		 * excluding multi-line items.
		 */
		if ( false === $alignMultilineItems ) {
			$select_index_end_cols = array();
			$double_arrow_cols     = array();

			foreach ( $items as $item ) {
				if ( false === $item['single_line'] ) {
					continue;
				}

				if ( ( $item['last_index_col'] + 2 ) <= $this->maxColumn ) {
					$select_index_end_cols[] = $item['last_index_col'];
				}

				if ( ! isset( $double_arrow_cols[ $this->tokens[ $item['operatorPtr'] ]['column'] ] ) ) {
					$double_arrow_cols[ $this->tokens[ $item['operatorPtr'] ]['column'] ] = 1;
				} else {
					$double_arrow_cols[ $this->tokens[ $item['operatorPtr'] ]['column'] ]++;
				}
			}
		}

		/*
		 * Determine the expected position of the double arrows.
		 */
		if ( ! empty( $select_index_end_cols ) ) {
			$max_index_width = max( $select_index_end_cols );
		} else {
			$max_index_width = max( $index_end_cols );
		}

		$expected_col = ( $max_index_width + 2 );

		if ( false === $this->exact && ! empty( $double_arrow_cols ) ) {
			/*
			 * If the alignment does not have to be exact, see if a majority
			 * group of the arrows is already at an acceptable position.
			 */
			arsort( $double_arrow_cols, \SORT_NUMERIC );
			reset( $double_arrow_cols );
			$count = current( $double_arrow_cols );

			if ( $count > 1 || ( 1 === $count && \count( $items ) === 1 ) ) {
				// Allow for several groups of arrows having the same $count.
				$filtered_double_arrow_cols = array_keys( $double_arrow_cols, $count, true );

				foreach ( $filtered_double_arrow_cols as $col ) {
					if ( $col > $expected_col && $col <= $this->maxColumn ) {
						$expected_col = $col;
						break;
					}
				}
			}
		}
		unset( $max_index_width, $count, $filtered_double_arrow_cols, $col );

		/*
		 * Verify and correct the spacing around the double arrows.
		 */
		foreach ( $items as $item ) {
			if ( $this->tokens[ $item['operatorPtr'] ]['column'] === $expected_col
				&& $this->tokens[ $item['operatorPtr'] ]['line'] === $this->tokens[ $item['last_index_token'] ]['line']
			) {
				// Already correctly aligned.
				continue;
			}

			if ( \T_WHITESPACE !== $this->tokens[ ( $item['operatorPtr'] - 1 ) ]['code'] ) {
				$before = 0;
			} else {
				if ( $this->tokens[ $item['last_index_token'] ]['line'] !== $this->tokens[ $item['operatorPtr'] ]['line'] ) {
					$before = 'newline';
				} else {
					$before = $this->tokens[ ( $item['operatorPtr'] - 1 ) ]['length'];
				}
			}

			/*
			 * Deal with index sizes larger than maxColumn and with multi-line
			 * array items which should not be aligned.
			 */
			if ( ( $item['last_index_col'] + 2 ) > $this->maxColumn
				|| ( false === $alignMultilineItems && false === $item['single_line'] )
			) {

				if ( ( $item['last_index_col'] + 2 ) === $this->tokens[ $item['operatorPtr'] ]['column']
					&& $this->tokens[ $item['operatorPtr'] ]['line'] === $this->tokens[ $item['last_index_token'] ]['line']
				) {
					// MaxColumn/Multi-line item exception, already correctly aligned.
					continue;
				}

				$prefix = 'LongIndex';
				if ( false === $alignMultilineItems && false === $item['single_line'] ) {
					$prefix = 'MultilineItem';
				}

				$error_code = $prefix . 'SpaceBeforeDoubleArrow';
				if ( 0 === $before ) {
					$error_code = $prefix . 'NoSpaceBeforeDoubleArrow';
				}

				$fix = $this->phpcsFile->addFixableWarning(
					'Expected 1 space between "%s" and double arrow; %s found.',
					$item['operatorPtr'],
					$error_code,
					array(
						$this->tokens[ $item['last_index_token'] ]['content'],
						$before,
					)
				);

				if ( true === $fix ) {
					$this->phpcsFile->fixer->beginChangeset();

					// Remove whitespace tokens between the end of the index and the arrow, if any.
					for ( $i = ( $item['last_index_token'] + 1 ); $i < $item['operatorPtr']; $i++ ) {
						$this->phpcsFile->fixer->replaceToken( $i, '' );
					}

					// Add the correct whitespace.
					$this->phpcsFile->fixer->addContent( $item['last_index_token'], ' ' );

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

			/*
			 * Deal with the space before double arrows in all other cases.
			 */
			$expected_whitespace = $expected_col - ( $this->tokens[ $item['last_index_token'] ]['column'] + $this->tokens[ $item['last_index_token'] ]['length'] );

			$fix = $this->phpcsFile->addFixableWarning(
				'Array double arrow not aligned correctly; expected %s space(s) between "%s" and double arrow, but found %s.',
				$item['operatorPtr'],
				'DoubleArrowNotAligned',
				array(
					$expected_whitespace,
					$this->tokens[ $item['last_index_token'] ]['content'],
					$before,
				)
			);

			if ( true === $fix ) {
				if ( 0 === $before || 'newline' === $before ) {
					$this->phpcsFile->fixer->beginChangeset();

					// Remove whitespace tokens between the end of the index and the arrow, if any.
					for ( $i = ( $item['last_index_token'] + 1 ); $i < $item['operatorPtr']; $i++ ) {
						$this->phpcsFile->fixer->replaceToken( $i, '' );
					}

					// Add the correct whitespace.
					$this->phpcsFile->fixer->addContent(
						$item['last_index_token'],
						str_repeat( ' ', $expected_whitespace )
					);

					$this->phpcsFile->fixer->endChangeset();
				} elseif ( $expected_whitespace > $before ) {
					// Add to the existing whitespace to prevent replacing tabs with spaces.
					// That's the concern of another sniff.
					$this->phpcsFile->fixer->addContent(
						( $item['operatorPtr'] - 1 ),
						str_repeat( ' ', ( $expected_whitespace - $before ) )
					);
				} else {
					// Too much whitespace found.
					$this->phpcsFile->fixer->replaceToken(
						( $item['operatorPtr'] - 1 ),
						str_repeat( ' ', $expected_whitespace )
					);
				}
			}
		}
	}
protected function process_multi_line_array( $stackPtr, $items, $opener, $closer ) {

		$this->maxColumn = (int) $this->maxColumn;
		$this->validate_align_multiline_items();

		/*
		 * Determine what the spacing before the arrow should be.
		 *
		 * Will unset any array items without double arrow and with new line whitespace
		 * if newlines are to be ignored, so the second foreach loop only has to deal
		 * with items which need attention.
		 *
		 * This sniff does not take incorrect indentation of array keys into account.
		 * That's for the `WordPress.Arrays.ArrayIndentation` sniff to fix.
		 * If that would affect the alignment, a second (or third) loop of the fixer
		 * will correct it (again) after the indentation has been fixed.
		 */
		$index_end_cols    = array(); // Keep track of the end column position of index keys.
		$double_arrow_cols = array(); // Keep track of arrow column position and count.
		$multi_line_count  = 0;
		$total_items       = \count( $items );

		foreach ( $items as $key => $item ) {
			if ( strpos( $item['raw'], '=>' ) === false ) {
				// Ignore items without assignment operators.
				unset( $items[ $key ] );
				continue;
			}

			// Find the position of the first double arrow.
			$double_arrow = $this->phpcsFile->findNext(
				\T_DOUBLE_ARROW,
				$item['start'],
				( $item['end'] + 1 )
			);

			if ( false === $double_arrow ) {
				// Shouldn't happen, just in case.
				unset( $items[ $key ] );
				continue;
			}

			// Make sure the arrow is for this item and not for a nested array value assignment.
			$has_array_opener = $this->phpcsFile->findNext(
				$this->register(),
				$item['start'],
				$double_arrow
			);

			if ( false !== $has_array_opener ) {
				// Double arrow is for a nested array.
				unset( $items[ $key ] );
				continue;
			}

			// Find the end of the array key.
			$last_index_token = $this->phpcsFile->findPrevious(
				\T_WHITESPACE,
				( $double_arrow - 1 ),
				$item['start'],
				true
			);

			if ( false === $last_index_token ) {
				// Shouldn't happen, but just in case.
				unset( $items[ $key ] );
				continue;
			}

			if ( true === $this->ignoreNewlines
				&& $this->tokens[ $last_index_token ]['line'] !== $this->tokens[ $double_arrow ]['line']
			) {
				// Ignore this item as it has a new line between the item key and the double arrow.
				unset( $items[ $key ] );
				continue;
			}

			$index_end_position                = ( $this->tokens[ $last_index_token ]['column'] + ( $this->tokens[ $last_index_token ]['length'] - 1 ) );
			$items[ $key ]['operatorPtr']      = $double_arrow;
			$items[ $key ]['last_index_token'] = $last_index_token;
			$items[ $key ]['last_index_col']   = $index_end_position;

			if ( $this->tokens[ $last_index_token ]['line'] === $this->tokens[ $item['end'] ]['line'] ) {
				$items[ $key ]['single_line'] = true;
			} else {
				$items[ $key ]['single_line'] = false;
				$multi_line_count++;
			}

			if ( ( $index_end_position + 2 ) <= $this->maxColumn ) {
				$index_end_cols[] = $index_end_position;
			}

			if ( ! isset( $double_arrow_cols[ $this->tokens[ $double_arrow ]['column'] ] ) ) {
				$double_arrow_cols[ $this->tokens[ $double_arrow ]['column'] ] = 1;
			} else {
				$double_arrow_cols[ $this->tokens[ $double_arrow ]['column'] ]++;
			}
		}
		unset( $key, $item, $double_arrow, $has_array_opener, $last_index_token );

		if ( empty( $items ) || empty( $index_end_cols ) ) {
			// No actionable array items found.
			return;
		}

		/*
		 * Determine whether the operators for multi-line items should be aligned.
		 */
		if ( 'always' === $this->alignMultilineItems ) {
			$alignMultilineItems = true;
		} elseif ( 'never' === $this->alignMultilineItems ) {
			$alignMultilineItems = false;
		} else {
			$percentage = (string) round( ( $multi_line_count / $total_items ) * 100, 0 );

			// Bit hacky, but this is the only comparison function in PHP which allows to
			// pass the comparison operator. And hey, it works ;-).
			$alignMultilineItems = version_compare( $percentage, $this->number, $this->operator );
		}

		/*
		 * If necessary, rebuild the $index_end_cols and $double_arrow_cols arrays
		 * excluding multi-line items.
		 */
		if ( false === $alignMultilineItems ) {
			$select_index_end_cols = array();
			$double_arrow_cols     = array();

			foreach ( $items as $item ) {
				if ( false === $item['single_line'] ) {
					continue;
				}

				if ( ( $item['last_index_col'] + 2 ) <= $this->maxColumn ) {
					$select_index_end_cols[] = $item['last_index_col'];
				}

				if ( ! isset( $double_arrow_cols[ $this->tokens[ $item['operatorPtr'] ]['column'] ] ) ) {
					$double_arrow_cols[ $this->tokens[ $item['operatorPtr'] ]['column'] ] = 1;
				} else {
					$double_arrow_cols[ $this->tokens[ $item['operatorPtr'] ]['column'] ]++;
				}
			}
		}

		/*
		 * Determine the expected position of the double arrows.
		 */
		if ( ! empty( $select_index_end_cols ) ) {
			$max_index_width = max( $select_index_end_cols );
		} else {
			$max_index_width = max( $index_end_cols );
		}

		$expected_col = ( $max_index_width + 2 );

		if ( false === $this->exact && ! empty( $double_arrow_cols ) ) {
			/*
			 * If the alignment does not have to be exact, see if a majority
			 * group of the arrows is already at an acceptable position.
			 */
			arsort( $double_arrow_cols, \SORT_NUMERIC );
			reset( $double_arrow_cols );
			$count = current( $double_arrow_cols );

			if ( $count > 1 || ( 1 === $count && \count( $items ) === 1 ) ) {
				// Allow for several groups of arrows having the same $count.
				$filtered_double_arrow_cols = array_keys( $double_arrow_cols, $count, true );

				foreach ( $filtered_double_arrow_cols as $col ) {
					if ( $col > $expected_col && $col <= $this->maxColumn ) {
						$expected_col = $col;
						break;
					}
				}
			}
		}
		unset( $max_index_width, $count, $filtered_double_arrow_cols, $col );

		/*
		 * Verify and correct the spacing around the double arrows.
		 */
		foreach ( $items as $item ) {
			if ( $this->tokens[ $item['operatorPtr'] ]['column'] === $expected_col
				&& $this->tokens[ $item['operatorPtr'] ]['line'] === $this->tokens[ $item['last_index_token'] ]['line']
			) {
				// Already correctly aligned.
				continue;
			}

			if ( \T_WHITESPACE !== $this->tokens[ ( $item['operatorPtr'] - 1 ) ]['code'] ) {
				$before = 0;
			} else {
				if ( $this->tokens[ $item['last_index_token'] ]['line'] !== $this->tokens[ $item['operatorPtr'] ]['line'] ) {
					$before = 'newline';
				} else {
					$before = $this->tokens[ ( $item['operatorPtr'] - 1 ) ]['length'];
				}
			}

			/*
			 * Deal with index sizes larger than maxColumn and with multi-line
			 * array items which should not be aligned.
			 */
			if ( ( $item['last_index_col'] + 2 ) > $this->maxColumn
				|| ( false === $alignMultilineItems && false === $item['single_line'] )
			) {

				if ( ( $item['last_index_col'] + 2 ) === $this->tokens[ $item['operatorPtr'] ]['column']
					&& $this->tokens[ $item['operatorPtr'] ]['line'] === $this->tokens[ $item['last_index_token'] ]['line']
				) {
					// MaxColumn/Multi-line item exception, already correctly aligned.
					continue;
				}

				$prefix = 'LongIndex';
				if ( false === $alignMultilineItems && false === $item['single_line'] ) {
					$prefix = 'MultilineItem';
				}

				$error_code = $prefix . 'SpaceBeforeDoubleArrow';
				if ( 0 === $before ) {
					$error_code = $prefix . 'NoSpaceBeforeDoubleArrow';
				}

				$fix = $this->phpcsFile->addFixableWarning(
					'Expected 1 space between "%s" and double arrow; %s found.',
					$item['operatorPtr'],
					$error_code,
					array(
						$this->tokens[ $item['last_index_token'] ]['content'],
						$before,
					)
				);

				if ( true === $fix ) {
					$this->phpcsFile->fixer->beginChangeset();

					// Remove whitespace tokens between the end of the index and the arrow, if any.
					for ( $i = ( $item['last_index_token'] + 1 ); $i < $item['operatorPtr']; $i++ ) {
						$this->phpcsFile->fixer->replaceToken( $i, '' );
					}

					// Add the correct whitespace.
					$this->phpcsFile->fixer->addContent( $item['last_index_token'], ' ' );

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

			/*
			 * Deal with the space before double arrows in all other cases.
			 */
			$expected_whitespace = $expected_col - ( $this->tokens[ $item['last_index_token'] ]['column'] + $this->tokens[ $item['last_index_token'] ]['length'] );

			$fix = $this->phpcsFile->addFixableWarning(
				'Array double arrow not aligned correctly; expected %s space(s) between "%s" and double arrow, but found %s.',
				$item['operatorPtr'],
				'DoubleArrowNotAligned',
				array(
					$expected_whitespace,
					$this->tokens[ $item['last_index_token'] ]['content'],
					$before,
				)
			);

			if ( true === $fix ) {
				if ( 0 === $before || 'newline' === $before ) {
					$this->phpcsFile->fixer->beginChangeset();

					// Remove whitespace tokens between the end of the index and the arrow, if any.
					for ( $i = ( $item['last_index_token'] + 1 ); $i < $item['operatorPtr']; $i++ ) {
						$this->phpcsFile->fixer->replaceToken( $i, '' );
					}

					// Add the correct whitespace.
					$this->phpcsFile->fixer->addContent(
						$item['last_index_token'],
						str_repeat( ' ', $expected_whitespace )
					);

					$this->phpcsFile->fixer->endChangeset();
				} elseif ( $expected_whitespace > $before ) {
					// Add to the existing whitespace to prevent replacing tabs with spaces.
					// That's the concern of another sniff.
					$this->phpcsFile->fixer->addContent(
						( $item['operatorPtr'] - 1 ),
						str_repeat( ' ', ( $expected_whitespace - $before ) )
					);
				} else {
					// Too much whitespace found.
					$this->phpcsFile->fixer->replaceToken(
						( $item['operatorPtr'] - 1 ),
						str_repeat( ' ', $expected_whitespace )
					);
				}
			}
		}
	}
Process a multi-line array.

@since 0.14.0

@param int   $stackPtr The position of the current token in the stack.
@param array $items    Info array containing information on each array item.
@param int   $opener   The position of the array opener.
@param int   $closer   The position of the array closer.

@return void