diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d64a8bc7b..557b9df281 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,29 @@ This projects adheres to [Semantic Versioning](http://semver.org/) and [Keep a C `get_category_by_slug()`, `get_cat_ID()`, `count_user_posts()`, and `wp_old_slug_redirect()` to the list of restricted functions in the `WordPress.VIP.RestrictedFunctions` sniff. +## [0.7.0] - 2015-08-30 + +### Added +- Automatic error fixing to the `WordPress.Arrays.ArrayKeySpacingRestrictions` sniff. +- Functions and closures to the control structures checked by the `WordPress.WhiteSpace.ControlStructureSpacing` +sniff. +- Sniffing and fixing for extra spacing in the `WordPress.WhiteSpace.ControlStructureSpacing` +sniff. (Previously it only checked for insufficient spacing.) +- `.twig` files to the default ignored files. +- `esc_url_raw()` and `hash_equals()` to the list of sanitizing functions. +- `intval()` and `boolval()` to list of unslashing functions. +- `do_shortcode()` to the list of auto-escaped functions. + +### Removed +- `WordPress.Functions.FunctionDeclarationArgumentSpacing` in favor of the upstream +sniff `Squiz.Functions.FunctionDeclarationArgumentSpacing`. + +### Fixed +- Reference to incorrect issue in the inline docs of the `WordPress.VIP.SessionVariableUsage` +sniff. +- `WordPress.XSS.EscapeOutput` sniff incorrectly handling ternary conditions in +`echo` statements without parentheses in some cases. + ## [0.6.0] - 2015-06-30 ### Added diff --git a/WordPress-Core/ruleset.xml b/WordPress-Core/ruleset.xml index f228bd90f3..4a74646b84 100644 --- a/WordPress-Core/ruleset.xml +++ b/WordPress-Core/ruleset.xml @@ -72,13 +72,20 @@ 0 + + + + + + + + - diff --git a/WordPress/Sniff.php b/WordPress/Sniff.php index 4bc0042898..d6a9f0a448 100644 --- a/WordPress/Sniff.php +++ b/WordPress/Sniff.php @@ -117,6 +117,7 @@ abstract class WordPress_Sniff implements PHP_CodeSniffer_Sniff { 'comments_rss_link' => true, 'delete_get_calendar_cache' => true, 'disabled' => true, + 'do_shortcode' => true, 'do_shortcode_tag' => true, 'edit_bookmark_link' => true, 'edit_comment_link' => true, @@ -236,8 +237,10 @@ abstract class WordPress_Sniff implements PHP_CodeSniffer_Sniff { */ public static $sanitizingFunctions = array( 'absint' => true, + 'esc_url_raw' => true, 'filter_input' => true, 'filter_var' => true, + 'hash_equals' => true, 'in_array' => true, 'intval' => true, 'is_array' => true, @@ -280,6 +283,8 @@ abstract class WordPress_Sniff implements PHP_CodeSniffer_Sniff { */ public static $unslashingSanitizingFunctions = array( 'absint' => true, + 'boolval' => true, + 'intval' => true, 'is_array' => true, 'sanitize_key' => true, ); @@ -1036,6 +1041,40 @@ protected function is_comparison( $stackPtr ) { return false; } + + /** + * Check what type of 'use' statement a token is part of. + * + * The T_USE token has multiple different uses: + * + * 1. In a closure: function () use ( $var ) {} + * 2. In a class, to import a trait: use Trait_Name + * 3. In a namespace, to import a class: use Some\Class; + * + * This function will check the token and return 'closure', 'trait', or 'class', + * based on which of these uses the use is being used for. + * + * @param int $stackPtr The position of the token to check. + * + * @return string The type of use. + */ + protected function get_use_type( $stackPtr ) { + + // USE keywords inside closures. + $next = $this->phpcsFile->findNext( T_WHITESPACE, $stackPtr + 1, null, true ); + + if ( T_OPEN_PARENTHESIS === $this->tokens[ $next ]['code'] ) { + return 'closure'; + } + + // USE keywords for traits. + if ( $this->phpcsFile->hasCondition( $stackPtr, array( T_CLASS, T_TRAIT ) ) ) { + return 'trait'; + } + + // USE keywords for classes to import to a namespace. + return 'class'; + } } // EOF diff --git a/WordPress/Sniffs/Arrays/ArrayKeySpacingRestrictionsSniff.php b/WordPress/Sniffs/Arrays/ArrayKeySpacingRestrictionsSniff.php index 02d4dbcef7..0eb3fc97e9 100644 --- a/WordPress/Sniffs/Arrays/ArrayKeySpacingRestrictionsSniff.php +++ b/WordPress/Sniffs/Arrays/ArrayKeySpacingRestrictionsSniff.php @@ -55,11 +55,27 @@ public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr ) // It should have spaces only if it only has strings or numbers as the key if ( $need_spaces && ! ( $spaced1 && $spaced2 ) ) { $error = 'Array keys must be surrounded by spaces unless they contain a string or an integer.'; - $phpcsFile->addError( $error, $stackPtr, 'NoSpacesAroundArrayKeys' ); + $fix = $phpcsFile->addFixableError( $error, $stackPtr, 'NoSpacesAroundArrayKeys' ); + if ( $fix ) { + if ( ! $spaced1 ) { + $phpcsFile->fixer->addContentBefore( $stackPtr + 1, ' ' ); + } + if ( ! $spaced2 ) { + $phpcsFile->fixer->addContentBefore( $token['bracket_closer'], ' ' ); + } + } } elseif( ! $need_spaces && ( $spaced1 || $spaced2 ) ) { $error = 'Array keys must NOT be surrounded by spaces if they only contain a string or an integer.'; - $phpcsFile->addError( $error, $stackPtr, 'SpacesAroundArrayKeys' ); + $fix = $phpcsFile->addFixableError( $error, $stackPtr, 'SpacesAroundArrayKeys' ); + if ( $fix ) { + if ( $spaced1 ) { + $phpcsFile->fixer->replaceToken( $stackPtr + 1, '' ); + } + if ( $spaced2 ) { + $phpcsFile->fixer->replaceToken( $token['bracket_closer'] - 1, '' ); + } + } } }//end process() diff --git a/WordPress/Sniffs/Functions/FunctionDeclarationArgumentSpacingSniff.php b/WordPress/Sniffs/Functions/FunctionDeclarationArgumentSpacingSniff.php deleted file mode 100644 index 7efe868750..0000000000 --- a/WordPress/Sniffs/Functions/FunctionDeclarationArgumentSpacingSniff.php +++ /dev/null @@ -1,184 +0,0 @@ - - * @author Greg Sherwood - * @author Marc McIntyre - */ - -/** - * Enforces WordPress array format - * - * @category PHP - * @package PHP_CodeSniffer - * @author John Godley - * @author Greg Sherwood - * @author Marc McIntyre - */ -class WordPress_Sniffs_Functions_FunctionDeclarationArgumentSpacingSniff implements PHP_CodeSniffer_Sniff -{ - - - /** - * Returns an array of tokens this test wants to listen for. - * - * @return array - */ - public function register() - { - return array(T_FUNCTION); - - }//end register() - - - /** - * Processes this test, when one of its tokens is encountered. - * - * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the current token in the - * stack passed in $tokens. - * - * @return void - */ - public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - - $functionName = $phpcsFile->findNext(array(T_STRING), $stackPtr); - $openBracket = $tokens[$stackPtr]['parenthesis_opener']; - $closeBracket = $tokens[$stackPtr]['parenthesis_closer']; - - $multiLine = ($tokens[$openBracket]['line'] !== $tokens[$closeBracket]['line']); - - $nextParam = $openBracket; - $params = array(); - - while (($nextParam = $phpcsFile->findNext(T_VARIABLE, ($nextParam + 1), $closeBracket)) !== false) { - $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($nextParam + 1), ($closeBracket + 1), true); - if ($nextToken === false) { - break; - } - - $nextCode = $tokens[$nextToken]['code']; - - if ($nextCode === T_EQUAL) { - // Check parameter default spacing. - if (($nextToken - $nextParam) !== 2) { - $gap = strlen($tokens[($nextParam + 1)]['content']); - $arg = $tokens[$nextParam]['content']; - $error = "Expected 1 space between argument \"$arg\" and equals sign; ".($gap - 1)." found"; - $phpcsFile->addError($error, $nextToken, 'SpaceBeforeEquals'); - } - - if ($tokens[($nextToken + 1)]['code'] !== T_WHITESPACE) { - $gap = strlen($tokens[($nextToken + 1)]['content']); - $arg = $tokens[$nextParam]['content']; - $error = "Expected 1 space between default value and equals sign for argument \"$arg\";"; - $phpcsFile->addError($error, $nextToken, 'SpaceAfterEquals'); - } - } - - // Find and check the comma (if there is one). - $nextComma = $phpcsFile->findNext(T_COMMA, ($nextParam + 1), $closeBracket); - if ($nextComma !== false) { - // Comma found. - if ($tokens[($nextComma - 1)]['code'] === T_WHITESPACE) { - $space = strlen($tokens[($nextComma - 1)]['content']); - $arg = $tokens[$nextParam]['content']; - $error = "Expected 0 spaces between argument \"$arg\" and comma; $space found"; - $phpcsFile->addError($error, $nextToken, 'SpaceBeforeComma'); - } - } - - // Take references into account when expecting the - // location of whitespace. - if ($phpcsFile->isReference(($nextParam - 1)) === true) { - $whitespace = $tokens[($nextParam - 2)]; - } else { - $whitespace = $tokens[($nextParam - 1)]; - } - - if (empty($params) === false) { - // This is not the first argument in the function declaration. - $arg = $tokens[$nextParam]['content']; - - if ($whitespace['code'] === T_WHITESPACE) { - $gap = strlen($whitespace['content']); - - // Before we throw an error, make sure there is no type hint. - $comma = $phpcsFile->findPrevious(T_COMMA, ($nextParam - 1)); - $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($comma + 1), null, true); - if ($phpcsFile->isReference($nextToken) === true) { - $nextToken++; - } - - if ($nextToken !== $nextParam) { - // There was a type hint, so check the spacing between - // the hint and the variable as well. - $hint = $tokens[$nextToken]['content']; - - if ($gap !== 1) { - $error = "Expected 1 space between type hint and argument \"$arg\"; $gap found"; - $phpcsFile->addError($error, $nextToken, 'SpacingAfterHint'); - } - - if ($multiLine === false) { - if ($tokens[($comma + 1)]['code'] !== T_WHITESPACE) { - $error = "Expected 1 space between comma and type hint \"$hint\"; 0 found"; - $phpcsFile->addError($error, $nextToken, 'NoSpaceBeforeHint'); - } else { - $gap = strlen($tokens[($comma + 1)]['content']); - if ($gap !== 1) { - $error = "Expected 1 space between comma and type hint \"$hint\"; $gap found"; - $phpcsFile->addError($error, $nextToken, 'SpacingBeforeHint'); - } - } - } - } else if ($multiLine === false && $gap !== 1) { - $error = "Expected 1 space between comma and argument \"$arg\"; $gap found"; - $phpcsFile->addError($error, $nextToken, 'SpacingBeforeArg'); - }//end if - } else { - $error = "Expected 1 space between comma and argument \"$arg\"; 0 found"; - $phpcsFile->addError($error, $nextToken, 'NoSpaceBeforeArg'); - }//end if - } else { - // First argument in function declaration. - if ($whitespace['code'] === T_WHITESPACE) { - $gap = strlen($whitespace['content']); - $arg = $tokens[$nextParam]['content']; - - // Before we throw an error, make sure there is no type hint. - $bracket = $phpcsFile->findPrevious(T_OPEN_PARENTHESIS, ($nextParam - 1)); - $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($bracket + 1), null, true); - if ($phpcsFile->isReference($nextToken) === true) { - $nextToken++; - } - - if ($nextToken !== $nextParam) { - // There was a type hint, so check the spacing between - // the hint and the variable as well. - $hint = $tokens[$nextToken]['content']; - - if ($gap !== 1) { - $error = "Expected 1 space between type hint and argument \"$arg\"; $gap found"; - $phpcsFile->addError($error, $nextToken, 'SpacingAfterHint'); - } - } - }//end if - }//end if - - $params[] = $nextParam; - }//end while - - }//end process() - - -}//end class - -?> diff --git a/WordPress/Sniffs/VIP/SessionVariableUsageSniff.php b/WordPress/Sniffs/VIP/SessionVariableUsageSniff.php index aa6db76d18..8641b52a08 100644 --- a/WordPress/Sniffs/VIP/SessionVariableUsageSniff.php +++ b/WordPress/Sniffs/VIP/SessionVariableUsageSniff.php @@ -2,12 +2,13 @@ /** * WordPress_Sniffs_VIP_SessionVariableUsageSniff * - * Discourages the use of the session variable + * Discourages the use of the session variable. + * Creating a session writes a file to the server and is unreliable in a multi-server environment. * * @category PHP * @package PHP_CodeSniffer * @author Shady Sharaf - * @link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/issues/69 + * @link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/issues/75 */ class WordPress_Sniffs_VIP_SessionVariableUsageSniff extends Generic_Sniffs_PHP_ForbiddenFunctionsSniff { diff --git a/WordPress/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php b/WordPress/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php index 58f70d86ae..22605be5d5 100644 --- a/WordPress/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php +++ b/WordPress/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php @@ -22,8 +22,7 @@ * @author Greg Sherwood * @author Marc McIntyre */ -class WordPress_Sniffs_WhiteSpace_ControlStructureSpacingSniff implements PHP_CodeSniffer_Sniff -{ +class WordPress_Sniffs_WhiteSpace_ControlStructureSpacingSniff extends WordPress_Sniff { /** * A list of tokenizers this sniff supports. @@ -52,6 +51,16 @@ class WordPress_Sniffs_WhiteSpace_ControlStructureSpacingSniff implements PHP_Co */ public $space_before_colon = 'required'; + /** + * How many spaces should be between a T_CLOSURE and T_OPEN_PARENTHESIS. + * + * function[*]() {...} + * + * @since 0.7.0 + * + * @var int + */ + public $spaces_before_closure_open_paren = 1; /** * Returns an array of tokens this test wants to listen for. @@ -69,6 +78,9 @@ public function register() T_DO, T_ELSE, T_ELSEIF, + T_FUNCTION, + T_CLOSURE, + T_USE, ); }//end register() @@ -90,8 +102,11 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) $tokens = $phpcsFile->getTokens(); + $this->init( $phpcsFile ); + if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE && ! ( $tokens[$stackPtr]['code'] === T_ELSE && $tokens[($stackPtr + 1)]['code'] === T_COLON ) + && ! ( T_CLOSURE === $tokens[ $stackPtr ]['code'] && 0 === (int) $this->spaces_before_closure_open_paren ) ) { $error = 'Space after opening control structure is required'; if (isset($phpcsFile->fixer) === true) { @@ -106,12 +121,19 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) } } - if (isset($tokens[$stackPtr]['scope_closer']) === false) { - return; - } + if ( isset( $tokens[ $stackPtr ]['scope_closer'] ) === false ) { - $scopeOpener = $tokens[$stackPtr]['scope_opener']; - $scopeCloser = $tokens[$stackPtr]['scope_closer']; + if ( T_USE === $tokens[ $stackPtr ]['code'] && 'closure' === $this->get_use_type( $stackPtr ) ) { + $scopeOpener = $phpcsFile->findNext( T_OPEN_CURLY_BRACKET, $stackPtr + 1 ); + $scopeCloser = $tokens[ $scopeOpener ]['scope_closer']; + } else { + return; + } + + } else { + $scopeOpener = $tokens[ $stackPtr ]['scope_opener']; + $scopeCloser = $tokens[ $stackPtr ]['scope_closer']; + } // alternative syntax if ( $tokens[$scopeOpener]['code'] === T_COLON ) { @@ -156,21 +178,128 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) $parenthesisOpener = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true); - if (($stackPtr + 1) === $parenthesisOpener && $tokens[$parenthesisOpener]['code'] !== T_COLON) { - // Checking this: $value = my_function[*](...). - $error = 'No space before opening parenthesis is prohibited'; - if (isset($phpcsFile->fixer) === true) { - $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBeforeOpenParenthesis'); - if ($fix === true) { - $phpcsFile->fixer->beginChangeset(); - $phpcsFile->fixer->addContent($stackPtr, ' '); - $phpcsFile->fixer->endChangeset(); + // If this is a function declaration. + if ( $tokens[ $stackPtr ]['code'] === T_FUNCTION ) { + + if ( $tokens[ $parenthesisOpener ]['code'] === T_STRING ) { + + $function_name_ptr = $parenthesisOpener; + + } elseif ( $tokens[ $parenthesisOpener ]['code'] === T_BITWISE_AND ) { + + // This function returns by reference (function &function_name() {}). + $function_name_ptr = $parenthesisOpener = $phpcsFile->findNext( + PHP_CodeSniffer_Tokens::$emptyTokens, + $parenthesisOpener + 1, + null, + true + ); + } + + $parenthesisOpener = $phpcsFile->findNext( + PHP_CodeSniffer_Tokens::$emptyTokens, + $parenthesisOpener + 1, + null, + true + ); + + // Checking this: function my_function[*](...) {} + if ( $parenthesisOpener !== $function_name_ptr + 1 ) { + + $error = 'Space between function name and opening parenthesis is prohibited.'; + $fix = $phpcsFile->addFixableError( + $error, + $stackPtr, + 'SpaceBeforeFunctionOpenParenthesis', + $tokens[ $function_name_ptr + 1 ]['content'] + ); + + if ( true === $fix ) { + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->replaceToken( $function_name_ptr + 1, '' ); + $phpcsFile->fixer->endChangeset(); + } + } + + } elseif ( T_CLOSURE === $tokens[ $stackPtr ]['code'] ) { + + // Check if there is a use () statement. + if ( isset( $tokens[ $parenthesisOpener ]['parenthesis_closer'] ) ) { + + $usePtr = $phpcsFile->findNext( + PHP_CodeSniffer_Tokens::$emptyTokens, + $tokens[ $parenthesisOpener ]['parenthesis_closer'] + 1, + null, + true, + null, + true + ); + + // If it is, we set that as the "scope opener". + if ( T_USE === $tokens[ $usePtr ]['code'] ) { + $scopeOpener = $usePtr; + } + } + } + + if ( + T_COLON !== $tokens[ $parenthesisOpener ]['code'] + && T_FUNCTION !== $tokens[ $stackPtr ]['code'] + ) { + + if ( + T_CLOSURE === $tokens[ $stackPtr ]['code'] + && 0 === (int) $this->spaces_before_closure_open_paren + ) { + + if ( ($stackPtr + 1) !== $parenthesisOpener ) { + // Checking this: function[*](...) {}. + $error = 'Space before closure opening parenthesis is prohibited'; + $fix = $phpcsFile->addFixableError( $error, $stackPtr, 'SpaceBeforeClosureOpenParenthesis' ); + if ( $fix === true ) { + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->replaceToken( $stackPtr + 1, '' ); + $phpcsFile->fixer->endChangeset(); + } + } + + } elseif ( ($stackPtr + 1) === $parenthesisOpener ) { + + // Checking this: if[*](...) {}. + $error = 'No space before opening parenthesis is prohibited'; + if ( isset( $phpcsFile->fixer ) === true ) { + $fix = $phpcsFile->addFixableError( $error, $stackPtr, 'NoSpaceBeforeOpenParenthesis' ); + if ( $fix === true ) { + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->addContent( $stackPtr, ' ' ); + $phpcsFile->fixer->endChangeset(); + } + } else { + $phpcsFile->addError( $error, $stackPtr, 'NoSpaceBeforeOpenParenthesis' ); } - } else { - $phpcsFile->addError($error, $stackPtr, 'NoSpaceBeforeOpenParenthesis'); } } + if ( + T_WHITESPACE === $tokens[ $stackPtr + 1 ]['code'] + && ' ' !== $tokens[ $stackPtr + 1 ]['content'] + ) { + // Checking this: if [*](...) {}. + $error = 'Expected exactly one space before opening parenthesis; "%s" found.'; + $fix = $phpcsFile->addFixableError( + $error, + $stackPtr, + 'ExtraSpaceBeforeOpenParenthesis', + $tokens[ $stackPtr + 1 ]['content'] + ); + + if ( true === $fix ) { + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->replaceToken( $stackPtr + 1, ' ' ); + $phpcsFile->fixer->endChangeset(); + } + } + if ($tokens[($parenthesisOpener + 1)]['code'] !== T_WHITESPACE && $tokens[($parenthesisOpener + 1)]['code'] !== T_CLOSE_PARENTHESIS ) { @@ -188,22 +317,45 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) } } - if (isset($tokens[$parenthesisOpener]['parenthesis_closer']) === true) { + if ( isset($tokens[$parenthesisOpener]['parenthesis_closer']) === true ) { + $parenthesisCloser = $tokens[$parenthesisOpener]['parenthesis_closer']; - if ($tokens[($parenthesisCloser - 1)]['code'] !== T_WHITESPACE) { - $error = 'No space before closing parenthesis is prohibited'; - if (isset($phpcsFile->fixer) === true) { - $fix = $phpcsFile->addFixableError($error, $parenthesisCloser, 'NoSpaceBeforeCloseParenthesis'); - if ($fix === true) { - $phpcsFile->fixer->beginChangeset(); - $phpcsFile->fixer->addContentBefore($parenthesisCloser, ' '); - $phpcsFile->fixer->endChangeset(); - } - } else { - $phpcsFile->addError($error, $parenthesisCloser, 'NoSpaceBeforeCloseParenthesis'); - } - } + if ( $tokens[($parenthesisOpener + 1)]['code'] !== T_CLOSE_PARENTHESIS ) { + + if ($tokens[($parenthesisCloser - 1)]['code'] !== T_WHITESPACE) { + $error = 'No space before closing parenthesis is prohibited'; + if (isset($phpcsFile->fixer) === true) { + $fix = $phpcsFile->addFixableError($error, $parenthesisCloser, 'NoSpaceBeforeCloseParenthesis'); + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->addContentBefore($parenthesisCloser, ' '); + $phpcsFile->fixer->endChangeset(); + } + } else { + $phpcsFile->addError($error, $parenthesisCloser, 'NoSpaceBeforeCloseParenthesis'); + } + } + + if ( + $tokens[ $parenthesisCloser + 1 ]['code'] !== T_WHITESPACE + && $tokens[ $scopeOpener ]['code'] !== T_COLON + ) { + $error = 'Space between opening control structure and closing parenthesis is required'; + + if ( isset( $phpcsFile->fixer ) === true ) { + $fix = $phpcsFile->addFixableError( $error, $scopeOpener, 'NoSpaceAfterCloseParenthesis' ); + + if ( $fix === true ) { + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->addContentBefore( $scopeOpener, ' ' ); + $phpcsFile->fixer->endChangeset(); + } + } else { + $phpcsFile->addError( $error, $stackPtr, 'NoSpaceAfterCloseParenthesis' ); + } + } + } if (isset($tokens[$parenthesisOpener]['parenthesis_owner']) === true && $tokens[$parenthesisCloser]['line'] !== $tokens[$scopeOpener]['line'] @@ -218,7 +370,6 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) $phpcsFile->fixer->replaceToken($i, ''); } - // TODO: Should be a separate check for spacing between ")" and "{" only (when they are on same line). $phpcsFile->fixer->addContent($parenthesisCloser, ' '); $phpcsFile->fixer->endChangeset(); } @@ -226,7 +377,27 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) $phpcsFile->addError($error, $parenthesisOpener, 'OpenBraceNotSameLine'); }//end if return; - }//end if + + } elseif ( + T_WHITESPACE === $tokens[ $parenthesisCloser + 1 ]['code'] + && ' ' !== $tokens[ $parenthesisCloser + 1 ]['content'] + ) { + + // Checking this: if (...) [*]{} + $error = 'Expected exactly one space between closing parenthesis and opening control structure; "%s" found.'; + $fix = $phpcsFile->addFixableError( + $error, + $stackPtr, + 'ExtraSpaceAfterCloseParenthesis', + $tokens[ $parenthesisCloser + 1 ]['content'] + ); + + if ( true === $fix ) { + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->replaceToken( $parenthesisCloser + 1, ' ' ); + $phpcsFile->fixer->endChangeset(); + } + } }//end if if ($this->blank_line_check === true) { diff --git a/WordPress/Sniffs/XSS/EscapeOutputSniff.php b/WordPress/Sniffs/XSS/EscapeOutputSniff.php index 3096e85ff2..0becada3c7 100644 --- a/WordPress/Sniffs/XSS/EscapeOutputSniff.php +++ b/WordPress/Sniffs/XSS/EscapeOutputSniff.php @@ -172,16 +172,8 @@ public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr ) $ternary = $phpcsFile->findNext( T_INLINE_THEN, $stackPtr, $end_of_statement ); // If there is a ternary skip over the part before the ?. However, if - // there is a closing parenthesis ending the statement, we only do - // this when the opening parenthesis comes after the ternary. If the - // ternary is within the parentheses, it will be handled in the loop. - if ( - $ternary - && ( - T_CLOSE_PARENTHESIS !== $tokens[ $last_token ]['code'] - || $ternary < $tokens[ $last_token ]['parenthesis_opener'] - ) - ) { + // the ternary is within parentheses, it will be handled in the loop. + if ( $ternary && empty( $tokens[ $ternary ]['nested_parenthesis'] ) ) { $stackPtr = $ternary; } } diff --git a/WordPress/Tests/Arrays/ArrayKeySpacingRestrictionsUnitTest.inc.fixed b/WordPress/Tests/Arrays/ArrayKeySpacingRestrictionsUnitTest.inc.fixed new file mode 100644 index 0000000000..791e58c351 --- /dev/null +++ b/WordPress/Tests/Arrays/ArrayKeySpacingRestrictionsUnitTest.inc.fixed @@ -0,0 +1,31 @@ + - * @author Greg Sherwood - * @author Marc McIntyre - * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence - * @link http://pear.php.net/package/PHP_CodeSniffer - */ - -/** - * Unit test class for the FunctionDeclarationArgumentSpacing sniff. - * - * A sniff unit test checks a .inc file for expected violations of a single - * coding standard. Expected errors and warnings are stored in this class. - * - * @category PHP - * @package PHP_CodeSniffer - * @author Akeda Bagus - * @author Greg Sherwood - * @author Marc McIntyre - * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence - * @version Release: @package_version@ - * @link http://pear.php.net/package/PHP_CodeSniffer - */ -class WordPress_Tests_Functions_FunctionDeclarationArgumentSpacingUnitTest extends AbstractSniffUnitTest -{ - - - /** - * Returns the lines where errors should occur. - * - * The key of the array should represent the line number and the value - * should represent the number of errors that should occur on that line. - * - * @return array(int => int) - */ - public function getErrorList() - { - return array( - 3 => 2, - 5 => 1, - 9 => 1, - ); - - }//end getErrorList() - - - /** - * Returns the lines where warnings should occur. - * - * The key of the array should represent the line number and the value - * should represent the number of warnings that should occur on that line. - * - * @return array(int => int) - */ - public function getWarningList() - { - return array(); - - }//end getWarningList() - - -}//end class - -?> diff --git a/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.inc b/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.inc index 957a3f6767..821c40ae01 100644 --- a/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.inc +++ b/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.inc @@ -45,3 +45,57 @@ endif; if ( false ) : else : endif; + +function($arg){} // Bad +function ( $arg ) { + // OK +} + +function () { + // OK +} + +function something($arg){} // Bad +function foo( $arg ) { + // OK +} + +function no_params() { + // OK +} + +function another () {} // Bad, space before open parenthesis prohibited. +function and_another() {} // Bad, space before function name prohibited. +function +bar() {} // Bad +function baz() {} // Bad +function test() +{} // Bad + +function &return_by_ref() {} // OK + +// @codingStandardsChangeSetting WordPress.WhiteSpace.ControlStructureSpacing spaces_before_closure_open_paren 0 + +function() {} // OK +function( $arg ) {} // OK +function($arg){} // Bad +function () {} // Bad + +$closureWithArgsAndVars = function( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // OK +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // Bad + +// @codingStandardsChangeSetting WordPress.WhiteSpace.ControlStructureSpacing spaces_before_closure_open_paren 1 + +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // OK + +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use( $var1, $var2 ) {}; // Bad, no space before open parenthesis prohibited. +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // Bad, expected exactly one space before opening parenthesis. + +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ){}; // Bad, space between closing parenthesis and control structure required. +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // Bad, expected exactly one space between closing parenthesis and control structure. + +$closureWithArgsAndVars = function ( $arg1, $arg2 )use ( $var1, $var2 ) {}; // Bad, expected exactly one space between closing parenthesis and control structure. +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // Bad, expected exactly one space between closing parenthesis and control structure. + +// Namespaces. +use Foo\Admin; diff --git a/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.inc.fixed b/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.inc.fixed index 56168f5d31..22617542e2 100644 --- a/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.inc.fixed +++ b/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.inc.fixed @@ -14,7 +14,7 @@ if ( true ) { } // bad -if ( 'update' === $option_operation['operation'] ) { +if ( 'update' === $option_operation['operation'] ) { update_option( $option_operation['option_name'], $option_operation['old_value'] ); } @@ -25,7 +25,7 @@ if ( 'update' === $option_operation['operation'] ) { } // bad -if ( true ){} +if ( true ) {} if ( true ) { @@ -43,3 +43,55 @@ endif; if ( false ) : else : endif; + +function ( $arg ) {} // Bad +function ( $arg ) { + // OK +} + +function () { + // OK +} + +function something( $arg ) {} // Bad +function foo( $arg ) { + // OK +} + +function no_params() { + // OK +} + +function another() {} // Bad, space before open parenthesis prohibited. +function and_another() {} // Bad, space before function name prohibited. +function bar() {} // Bad +function baz() {} // Bad +function test() {} // Bad + +function &return_by_ref() {} // OK + +// @codingStandardsChangeSetting WordPress.WhiteSpace.ControlStructureSpacing spaces_before_closure_open_paren 0 + +function() {} // OK +function( $arg ) {} // OK +function( $arg ) {} // Bad +function() {} // Bad + +$closureWithArgsAndVars = function( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // OK +$closureWithArgsAndVars = function( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // Bad + +// @codingStandardsChangeSetting WordPress.WhiteSpace.ControlStructureSpacing spaces_before_closure_open_paren 1 + +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // OK + +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // Bad, no space before open parenthesis prohibited. +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // Bad, expected exactly one space before opening parenthesis. + +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // Bad, space between closing parenthesis and control structure required. +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // Bad, expected exactly one space between closing parenthesis and control structure. + +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // Bad, expected exactly one space between closing parenthesis and control structure. +$closureWithArgsAndVars = function ( $arg1, $arg2 ) use ( $var1, $var2 ) {}; // Bad, expected exactly one space between closing parenthesis and control structure. + +// Namespaces. +use Foo\Admin; diff --git a/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.php b/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.php index 672b688fed..7d732713cd 100644 --- a/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.php +++ b/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.php @@ -44,11 +44,27 @@ public function getErrorList() { $ret = array( 4 => 2, - 17 => 1, - 29 => 4, + 17 => 2, + 29 => 5, 37 => 1, 41 => 1, 42 => 1, + 49 => 5, + 58 => 3, + 67 => 1, + 68 => 1, + 69 => 1, + 71 => 1, + 72 => 1, + 81 => 3, + 82 => 1, + 85 => 1, + 91 => 2, + 92 => 1, + 94 => 1, + 95 => 1, + 97 => 1, + 98 => 1, ); // Uncomment when "$blank_line_check" parameter will be "true" by default. diff --git a/WordPress/Tests/XSS/EscapeOutputUnitTest.inc b/WordPress/Tests/XSS/EscapeOutputUnitTest.inc index 5b76af1b20..bcff315caa 100644 --- a/WordPress/Tests/XSS/EscapeOutputUnitTest.inc +++ b/WordPress/Tests/XSS/EscapeOutputUnitTest.inc @@ -157,3 +157,10 @@ echo sprintf( 'Howdy, %s', esc_html( $name ? $name : __( 'Partner' ) ) ); // OK _e( 'Something' ); // Bad esc_html_e( 'Something' ); // OK + +echo $something // Bad + . esc_attr( 'baz-' // Rest is OK + . $r + . ( $r === $active_round ? ' foo' : '' ) + . ( $r < $active_round ? ' bar' : '' ) + ) . 'something'; diff --git a/WordPress/Tests/XSS/EscapeOutputUnitTest.php b/WordPress/Tests/XSS/EscapeOutputUnitTest.php index 9124c7d141..abf7634354 100644 --- a/WordPress/Tests/XSS/EscapeOutputUnitTest.php +++ b/WordPress/Tests/XSS/EscapeOutputUnitTest.php @@ -75,6 +75,7 @@ public function getErrorList() 148 => 1, 151 => 2, 158 => 1, + 161 => 1, ); }//end getErrorList() diff --git a/project.ruleset.xml.example b/project.ruleset.xml.example index 8f1ef1f41c..894879b79d 100644 --- a/project.ruleset.xml.example +++ b/project.ruleset.xml.example @@ -8,6 +8,7 @@ /docroot/index.php /docroot/xmlrpc.php /docroot/wp-content/plugins/* + *.twig