199 lines
5.7 KiB
PHP
199 lines
5.7 KiB
PHP
<?php
|
|
/**
|
|
* WordPress Coding Standard.
|
|
*
|
|
* @package WPCS\WordPressCodingStandards
|
|
* @link https://github.com/WordPress/WordPress-Coding-Standards
|
|
* @license https://opensource.org/licenses/MIT MIT
|
|
*/
|
|
|
|
namespace WordPressCS\WordPress\Sniffs\WhiteSpace;
|
|
|
|
use WordPressCS\WordPress\Sniff;
|
|
use WordPressCS\WordPress\PHPCSHelper;
|
|
use PHP_CodeSniffer\Util\Tokens;
|
|
|
|
/**
|
|
* Warn on line indentation ending with spaces for precision alignment.
|
|
*
|
|
* WP demands tabs for indentation. In rare cases, spaces for precision alignment can be
|
|
* intentional and acceptable, but more often than not, this is a typo.
|
|
*
|
|
* The `Generic.WhiteSpace.DisallowSpaceIndent` sniff already checks for space indentation
|
|
* and auto-fixes to tabs.
|
|
*
|
|
* This sniff only checks for precision alignments which can not be corrected by the
|
|
* `Generic.WhiteSpace.DisallowSpaceIndent` sniff.
|
|
*
|
|
* As this may be intentional, this sniff explicitly does *NOT* contain a fixer.
|
|
*
|
|
* @link https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/#indentation
|
|
*
|
|
* @package WPCS\WordPressCodingStandards
|
|
*
|
|
* @since 0.14.0
|
|
*/
|
|
class PrecisionAlignmentSniff extends Sniff {
|
|
|
|
/**
|
|
* A list of tokenizers this sniff supports.
|
|
*
|
|
* @var array
|
|
*/
|
|
public $supportedTokenizers = array(
|
|
'PHP',
|
|
'JS',
|
|
'CSS',
|
|
);
|
|
|
|
/**
|
|
* Allow for providing a list of tokens for which (preceding) precision alignment should be ignored.
|
|
*
|
|
* <rule ref="WordPress.WhiteSpace.PrecisionAlignment">
|
|
* <properties>
|
|
* <property name="ignoreAlignmentTokens" type="array">
|
|
* <element value="T_COMMENT"/>
|
|
* <element value="T_INLINE_HTML"/>
|
|
* </property>
|
|
* </properties>
|
|
* </rule>
|
|
*
|
|
* @var array
|
|
*/
|
|
public $ignoreAlignmentTokens = array();
|
|
|
|
/**
|
|
* The --tab-width CLI value that is being used.
|
|
*
|
|
* @var int
|
|
*/
|
|
private $tab_width;
|
|
|
|
/**
|
|
* Returns an array of tokens this test wants to listen for.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function register() {
|
|
return array(
|
|
\T_OPEN_TAG,
|
|
\T_OPEN_TAG_WITH_ECHO,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Processes this test, when one of its tokens is encountered.
|
|
*
|
|
* @param int $stackPtr The position of the current token in the stack.
|
|
*
|
|
* @return int Integer stack pointer to skip the rest of the file.
|
|
*/
|
|
public function process_token( $stackPtr ) {
|
|
if ( ! isset( $this->tab_width ) ) {
|
|
$this->tab_width = PHPCSHelper::get_tab_width( $this->phpcsFile );
|
|
}
|
|
|
|
// Handle any custom ignore tokens received from a ruleset.
|
|
$ignoreAlignmentTokens = $this->merge_custom_array( $this->ignoreAlignmentTokens );
|
|
|
|
$check_tokens = array(
|
|
\T_WHITESPACE => true,
|
|
\T_INLINE_HTML => true,
|
|
\T_DOC_COMMENT_WHITESPACE => true,
|
|
\T_COMMENT => true,
|
|
);
|
|
$check_tokens += Tokens::$phpcsCommentTokens;
|
|
|
|
for ( $i = 0; $i < $this->phpcsFile->numTokens; $i++ ) {
|
|
|
|
if ( 1 !== $this->tokens[ $i ]['column'] ) {
|
|
continue;
|
|
} elseif ( isset( $check_tokens[ $this->tokens[ $i ]['code'] ] ) === false
|
|
|| ( isset( $this->tokens[ ( $i + 1 ) ] )
|
|
&& \T_WHITESPACE === $this->tokens[ ( $i + 1 ) ]['code'] )
|
|
|| $this->tokens[ $i ]['content'] === $this->phpcsFile->eolChar
|
|
|| isset( $ignoreAlignmentTokens[ $this->tokens[ $i ]['type'] ] )
|
|
|| ( isset( $this->tokens[ ( $i + 1 ) ] )
|
|
&& isset( $ignoreAlignmentTokens[ $this->tokens[ ( $i + 1 ) ]['type'] ] ) )
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
$spaces = 0;
|
|
switch ( $this->tokens[ $i ]['type'] ) {
|
|
case 'T_WHITESPACE':
|
|
$spaces = ( $this->tokens[ $i ]['length'] % $this->tab_width );
|
|
break;
|
|
|
|
case 'T_DOC_COMMENT_WHITESPACE':
|
|
$length = $this->tokens[ $i ]['length'];
|
|
$spaces = ( $length % $this->tab_width );
|
|
|
|
if ( isset( $this->tokens[ ( $i + 1 ) ] )
|
|
&& ( \T_DOC_COMMENT_STAR === $this->tokens[ ( $i + 1 ) ]['code']
|
|
|| \T_DOC_COMMENT_CLOSE_TAG === $this->tokens[ ( $i + 1 ) ]['code'] )
|
|
&& 0 !== $spaces
|
|
) {
|
|
// One alignment space expected before the *.
|
|
--$spaces;
|
|
}
|
|
break;
|
|
|
|
case 'T_COMMENT':
|
|
case 'T_PHPCS_ENABLE':
|
|
case 'T_PHPCS_DISABLE':
|
|
case 'T_PHPCS_SET':
|
|
case 'T_PHPCS_IGNORE':
|
|
case 'T_PHPCS_IGNORE_FILE':
|
|
/*
|
|
* Indentation whitespace for subsequent lines of multi-line comments
|
|
* are tokenized as part of the comment.
|
|
*/
|
|
$comment = ltrim( $this->tokens[ $i ]['content'] );
|
|
$whitespace = str_replace( $comment, '', $this->tokens[ $i ]['content'] );
|
|
$length = \strlen( $whitespace );
|
|
$spaces = ( $length % $this->tab_width );
|
|
|
|
if ( isset( $comment[0] ) && '*' === $comment[0] && 0 !== $spaces ) {
|
|
--$spaces;
|
|
}
|
|
break;
|
|
|
|
case 'T_INLINE_HTML':
|
|
if ( $this->tokens[ $i ]['content'] === $this->phpcsFile->eolChar ) {
|
|
$spaces = 0;
|
|
} else {
|
|
/*
|
|
* Indentation whitespace for inline HTML is part of the T_INLINE_HTML token.
|
|
*/
|
|
$content = ltrim( $this->tokens[ $i ]['content'] );
|
|
$whitespace = str_replace( $content, '', $this->tokens[ $i ]['content'] );
|
|
$spaces = ( \strlen( $whitespace ) % $this->tab_width );
|
|
}
|
|
|
|
/*
|
|
* Prevent triggering on multi-line /*-style inline javascript comments.
|
|
* This may cause false negatives as there is no check for being in a
|
|
* <script> tag, but that will be rare.
|
|
*/
|
|
if ( isset( $content[0] ) && '*' === $content[0] && 0 !== $spaces ) {
|
|
--$spaces;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ( $spaces > 0 && ! $this->has_whitelist_comment( 'precision alignment', $i ) ) {
|
|
$this->phpcsFile->addWarning(
|
|
'Found precision alignment of %s spaces.',
|
|
$i,
|
|
'Found',
|
|
array( $spaces )
|
|
);
|
|
}
|
|
}
|
|
|
|
// Ignore the rest of the file.
|
|
return ( $this->phpcsFile->numTokens + 1 );
|
|
}
|
|
|
|
}
|