diff --git a/tasks/code_sniffer/MailPoet/Sniffs/ControlStructures/FunctionDeclarationSniff.php b/tasks/code_sniffer/MailPoet/Sniffs/ControlStructures/FunctionDeclarationSniff.php index 5f29235292..a648e72649 100644 --- a/tasks/code_sniffer/MailPoet/Sniffs/ControlStructures/FunctionDeclarationSniff.php +++ b/tasks/code_sniffer/MailPoet/Sniffs/ControlStructures/FunctionDeclarationSniff.php @@ -12,342 +12,342 @@ use PHP_CodeSniffer\Sniffs\Sniff; use PHP_CodeSniffer\Util\Tokens; class FunctionDeclarationSniff implements Sniff { + /** + * A list of tokenizers this sniff supports. + * + * @var array + */ + public $supportedTokenizers = [ + 'PHP', + 'JS', + ]; + /** + * The number of spaces code should be indented. + * + * @var int + */ + public $indent = 2; - /** - * A list of tokenizers this sniff supports. - * - * @var array - */ - public $supportedTokenizers = [ - 'PHP', - 'JS', + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + return [ + T_FUNCTION, + T_CLOSURE, ]; - /** - * The number of spaces code should be indented. - * - * @var int - */ - public $indent = 2; + }//end register() - /** - * Returns an array of tokens this test wants to listen for. - * - * @return array - */ - public function register() { - return [ - T_FUNCTION, - T_CLOSURE, - ]; + /** + * Processes this test, when one of its tokens is encountered. + * + * @param \PHP_CodeSniffer\Files\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(File $phpcsFile, $stackPtr) { + $tokens = $phpcsFile->getTokens(); - }//end register() + if (isset($tokens[$stackPtr]['parenthesis_opener']) === false + || isset($tokens[$stackPtr]['parenthesis_closer']) === false + || $tokens[$stackPtr]['parenthesis_opener'] === null + || $tokens[$stackPtr]['parenthesis_closer'] === null + ) { + return; + } - /** - * Processes this test, when one of its tokens is encountered. - * - * @param \PHP_CodeSniffer\Files\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(File $phpcsFile, $stackPtr) { - $tokens = $phpcsFile->getTokens(); + $openBracket = $tokens[$stackPtr]['parenthesis_opener']; - if (isset($tokens[$stackPtr]['parenthesis_opener']) === false - || isset($tokens[$stackPtr]['parenthesis_closer']) === false - || $tokens[$stackPtr]['parenthesis_opener'] === null - || $tokens[$stackPtr]['parenthesis_closer'] === null + + if ($this->isMultiLineDeclaration($phpcsFile, $stackPtr, $openBracket, $tokens) === true) { + $this->processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens); + } else { + $this->processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens); + } + + }//end process() + + /** + * Determine if this is a multi-line function declaration. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $openBracket The position of the opening bracket + * in the stack passed in $tokens. + * @param array $tokens The stack of tokens that make up + * the file. + * + * @return bool + */ + public function isMultiLineDeclaration($phpcsFile, $stackPtr, $openBracket, $tokens) { + $closeBracket = $tokens[$openBracket]['parenthesis_closer']; + if ($tokens[$openBracket]['line'] !== $tokens[$closeBracket]['line']) { + return true; + } + + return false; + + }//end isMultiLineDeclaration() + + /** + * Processes single-line declarations. + * + * Just uses the Generic BSD-Allman brace sniff. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param array $tokens The stack of tokens that make up + * the file. + * + * @return void + */ + public function processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens) { + + + }//end processSingleLineDeclaration() + + /** + * Processes multi-line declarations. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param array $tokens The stack of tokens that make up + * the file. + * + * @return void + */ + public function processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens) { + $this->processArgumentList($phpcsFile, $stackPtr, $this->indent); + + $closeBracket = $tokens[$stackPtr]['parenthesis_closer']; + if ($tokens[$stackPtr]['code'] === T_CLOSURE) { + $use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']); + if ($use !== false) { + $open = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1)); + $closeBracket = $tokens[$open]['parenthesis_closer']; + } + } + + if (isset($tokens[$stackPtr]['scope_opener']) === false) { + return; + } + + // The opening brace needs to be one space away from the closing parenthesis. + $opener = $tokens[$stackPtr]['scope_opener']; + if ($tokens[$opener]['line'] !== $tokens[$closeBracket]['line']) { + $error = 'The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line'; + $fix = $phpcsFile->addFixableError($error, $opener, 'NewlineBeforeOpenBrace'); + if ($fix === true) { + $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), $closeBracket, true); + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->addContent($prev, ' {'); + + // If the opener is on a line by itself, removing it will create + // an empty line, so just remove the entire line instead. + $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($opener - 1), $closeBracket, true); + $next = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), null, true); + + if ($tokens[$prev]['line'] < $tokens[$opener]['line'] + && $tokens[$next]['line'] > $tokens[$opener]['line'] ) { - return; - } - - $openBracket = $tokens[$stackPtr]['parenthesis_opener']; - - - if ($this->isMultiLineDeclaration($phpcsFile, $stackPtr, $openBracket, $tokens) === true) { - $this->processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens); - } else { - $this->processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens); - } - - }//end process() - - /** - * Determine if this is a multi-line function declaration. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the current token - * in the stack passed in $tokens. - * @param int $openBracket The position of the opening bracket - * in the stack passed in $tokens. - * @param array $tokens The stack of tokens that make up - * the file. - * - * @return bool - */ - public function isMultiLineDeclaration($phpcsFile, $stackPtr, $openBracket, $tokens) { - $closeBracket = $tokens[$openBracket]['parenthesis_closer']; - if ($tokens[$openBracket]['line'] !== $tokens[$closeBracket]['line']) { - return true; - } - - return false; - - }//end isMultiLineDeclaration() - - /** - * Processes single-line declarations. - * - * Just uses the Generic BSD-Allman brace sniff. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the current token - * in the stack passed in $tokens. - * @param array $tokens The stack of tokens that make up - * the file. - * - * @return void - */ - public function processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens) { - - - }//end processSingleLineDeclaration() - - /** - * Processes multi-line declarations. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the current token - * in the stack passed in $tokens. - * @param array $tokens The stack of tokens that make up - * the file. - * - * @return void - */ - public function processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens) { - $this->processArgumentList($phpcsFile, $stackPtr, $this->indent); - - $closeBracket = $tokens[$stackPtr]['parenthesis_closer']; - if ($tokens[$stackPtr]['code'] === T_CLOSURE) { - $use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']); - if ($use !== false) { - $open = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1)); - $closeBracket = $tokens[$open]['parenthesis_closer']; - } - } - - if (isset($tokens[$stackPtr]['scope_opener']) === false) { - return; - } - - // The opening brace needs to be one space away from the closing parenthesis. - $opener = $tokens[$stackPtr]['scope_opener']; - if ($tokens[$opener]['line'] !== $tokens[$closeBracket]['line']) { - $error = 'The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line'; - $fix = $phpcsFile->addFixableError($error, $opener, 'NewlineBeforeOpenBrace'); - if ($fix === true) { - $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), $closeBracket, true); - $phpcsFile->fixer->beginChangeset(); - $phpcsFile->fixer->addContent($prev, ' {'); - - // If the opener is on a line by itself, removing it will create - // an empty line, so just remove the entire line instead. - $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($opener - 1), $closeBracket, true); - $next = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), null, true); - - if ($tokens[$prev]['line'] < $tokens[$opener]['line'] - && $tokens[$next]['line'] > $tokens[$opener]['line'] - ) { - // Clear the whole line. - for ($i = ($prev + 1); $i < $next; $i++) { - if ($tokens[$i]['line'] === $tokens[$opener]['line']) { - $phpcsFile->fixer->replaceToken($i, ''); - } - } - } else { - // Just remove the opener. - $phpcsFile->fixer->replaceToken($opener, ''); - if ($tokens[$next]['line'] === $tokens[$opener]['line']) { - $phpcsFile->fixer->replaceToken(($opener + 1), ''); + // Clear the whole line. + for ($i = ($prev + 1); $i < $next; $i++) { + if ($tokens[$i]['line'] === $tokens[$opener]['line']) { + $phpcsFile->fixer->replaceToken($i, ''); } } - - $phpcsFile->fixer->endChangeset(); - }//end if - } else { - $prev = $tokens[($opener - 1)]; - if ($prev['code'] !== T_WHITESPACE) { - $length = 0; } else { - $length = strlen($prev['content']); - } - - if ($length !== 1) { - $error = 'There must be a single space between the closing parenthesis and the opening brace of a multi-line function declaration; found %s spaces'; - $fix = $phpcsFile->addFixableError($error, ($opener - 1), 'SpaceBeforeOpenBrace', [$length]); - if ($fix === true) { - if ($length === 0) { - $phpcsFile->fixer->addContentBefore($opener, ' '); - } else { - $phpcsFile->fixer->replaceToken(($opener - 1), ' '); - } + // Just remove the opener. + $phpcsFile->fixer->replaceToken($opener, ''); + if ($tokens[$next]['line'] === $tokens[$opener]['line']) { + $phpcsFile->fixer->replaceToken(($opener + 1), ''); } - - return; - }//end if - }//end if - - }//end processMultiLineDeclaration() - - /** - * Processes multi-line argument list declarations. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the current token - * in the stack passed in $tokens. - * @param int $indent The number of spaces code should be indented. - * @param string $type The type of the token the brackets - * belong to. - * - * @return void - */ - public function processArgumentList($phpcsFile, $stackPtr, $indent, $type='function') { - $tokens = $phpcsFile->getTokens(); - - // We need to work out how far indented the function - // declaration itself is, so we can work out how far to - // indent parameters. - $functionIndent = 0; - for ($i = ($stackPtr - 1); $i >= 0; $i--) { - if ($tokens[$i]['line'] !== $tokens[$stackPtr]['line']) { - break; } + + $phpcsFile->fixer->endChangeset(); + }//end if + } else { + $prev = $tokens[($opener - 1)]; + if ($prev['code'] !== T_WHITESPACE) { + $length = 0; + } else { + $length = strlen($prev['content']); } - // Move $i back to the line the function is or to 0. - $i++; + if ($length !== 1) { + $error = 'There must be a single space between the closing parenthesis and the opening brace of a multi-line function declaration; found %s spaces'; + $fix = $phpcsFile->addFixableError($error, ($opener - 1), 'SpaceBeforeOpenBrace', [$length]); + if ($fix === true) { + if ($length === 0) { + $phpcsFile->fixer->addContentBefore($opener, ' '); + } else { + $phpcsFile->fixer->replaceToken(($opener - 1), ' '); + } + } - if ($tokens[$i]['code'] === T_WHITESPACE) { - $functionIndent = $tokens[$i]['length']; + return; + }//end if + }//end if + + }//end processMultiLineDeclaration() + + /** + * Processes multi-line argument list declarations. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $indent The number of spaces code should be indented. + * @param string $type The type of the token the brackets + * belong to. + * + * @return void + */ + public function processArgumentList($phpcsFile, $stackPtr, $indent, $type = 'function') { + $tokens = $phpcsFile->getTokens(); + + // We need to work out how far indented the function + // declaration itself is, so we can work out how far to + // indent parameters. + $functionIndent = 0; + for ($i = ($stackPtr - 1); $i >= 0; $i--) { + if ($tokens[$i]['line'] !== $tokens[$stackPtr]['line']) { + break; } + } - // The closing parenthesis must be on a new line, even - // when checking abstract function definitions. - $closeBracket = $tokens[$stackPtr]['parenthesis_closer']; - $prev = $phpcsFile->findPrevious( - T_WHITESPACE, - ($closeBracket - 1), - null, - true + // Move $i back to the line the function is or to 0. + $i++; + + if ($tokens[$i]['code'] === T_WHITESPACE) { + $functionIndent = $tokens[$i]['length']; + } + + // The closing parenthesis must be on a new line, even + // when checking abstract function definitions. + $closeBracket = $tokens[$stackPtr]['parenthesis_closer']; + $prev = $phpcsFile->findPrevious( + T_WHITESPACE, + ($closeBracket - 1), + null, + true + ); + + if ($tokens[$closeBracket]['line'] !== $tokens[$tokens[$closeBracket]['parenthesis_opener']]['line'] + && $tokens[$prev]['line'] === $tokens[$closeBracket]['line'] + ) { + $error = 'The closing parenthesis of a multi-line ' . $type . ' declaration must be on a new line'; + $fix = $phpcsFile->addFixableError($error, $closeBracket, 'CloseBracketLine'); + if ($fix === true) { + $phpcsFile->fixer->addNewlineBefore($closeBracket); + } + } + + // If this is a closure and is using a USE statement, the closing + // parenthesis we need to look at from now on is the closing parenthesis + // of the USE statement. + if ($tokens[$stackPtr]['code'] === T_CLOSURE) { + $use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']); + if ($use !== false) { + $open = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1)); + $closeBracket = $tokens[$open]['parenthesis_closer']; + + $prev = $phpcsFile->findPrevious( + T_WHITESPACE, + ($closeBracket - 1), + null, + true ); - if ($tokens[$closeBracket]['line'] !== $tokens[$tokens[$closeBracket]['parenthesis_opener']]['line'] - && $tokens[$prev]['line'] === $tokens[$closeBracket]['line'] + if ($tokens[$closeBracket]['line'] !== $tokens[$tokens[$closeBracket]['parenthesis_opener']]['line'] + && $tokens[$prev]['line'] === $tokens[$closeBracket]['line'] ) { - $error = 'The closing parenthesis of a multi-line ' . $type . ' declaration must be on a new line'; - $fix = $phpcsFile->addFixableError($error, $closeBracket, 'CloseBracketLine'); - if ($fix === true) { + $error = 'The closing parenthesis of a multi-line use declaration must be on a new line'; + $fix = $phpcsFile->addFixableError($error, $closeBracket, 'UseCloseBracketLine'); + if ($fix === true) { $phpcsFile->fixer->addNewlineBefore($closeBracket); + } } - } + }//end if + }//end if - // If this is a closure and is using a USE statement, the closing - // parenthesis we need to look at from now on is the closing parenthesis - // of the USE statement. - if ($tokens[$stackPtr]['code'] === T_CLOSURE) { - $use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']); - if ($use !== false) { - $open = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1)); - $closeBracket = $tokens[$open]['parenthesis_closer']; + // Each line between the parenthesis should be indented 4 spaces. + $openBracket = $tokens[$stackPtr]['parenthesis_opener']; + $lastLine = $tokens[$openBracket]['line']; + for ($i = ($openBracket + 1); $i < $closeBracket; $i++) { + if ($tokens[$i]['line'] !== $lastLine) { + if ($i === $tokens[$stackPtr]['parenthesis_closer'] + || ($tokens[$i]['code'] === T_WHITESPACE + && (($i + 1) === $closeBracket + || ($i + 1) === $tokens[$stackPtr]['parenthesis_closer'])) + ) { + // Closing braces need to be indented to the same level + // as the function. + $expectedIndent = $functionIndent; + } else { + $expectedIndent = ($functionIndent + $indent); + } - $prev = $phpcsFile->findPrevious( - T_WHITESPACE, - ($closeBracket - 1), - null, - true - ); + // We changed lines, so this should be a whitespace indent token. + if ($tokens[$i]['code'] !== T_WHITESPACE) { + $foundIndent = 0; + } else { + if ($tokens[$i]['line'] !== $tokens[($i + 1)]['line']) { + // This is an empty line, so don't check the indent. + $foundIndent = $expectedIndent; - if ($tokens[$closeBracket]['line'] !== $tokens[$tokens[$closeBracket]['parenthesis_opener']]['line'] - && $tokens[$prev]['line'] === $tokens[$closeBracket]['line'] - ) { - $error = 'The closing parenthesis of a multi-line use declaration must be on a new line'; - $fix = $phpcsFile->addFixableError($error, $closeBracket, 'UseCloseBracketLine'); + $error = 'Blank lines are not allowed in a multi-line ' . $type . ' declaration'; + $fix = $phpcsFile->addFixableError($error, $i, 'EmptyLine'); if ($fix === true) { - $phpcsFile->fixer->addNewlineBefore($closeBracket); + $phpcsFile->fixer->replaceToken($i, ''); + } + } else { + $foundIndent = $tokens[$i]['length']; + } + } + + if ($expectedIndent !== $foundIndent) { + $error = 'Multi-line ' . $type . ' declaration not indented correctly; expected %s spaces but found %s'; + $data = [ + $expectedIndent, + $foundIndent, + ]; + + $fix = $phpcsFile->addFixableError($error, $i, 'Indent', $data); + if ($fix === true) { + $spaces = str_repeat(' ', $expectedIndent); + if ($foundIndent === 0) { + $phpcsFile->fixer->addContentBefore($i, $spaces); + } else { + $phpcsFile->fixer->replaceToken($i, $spaces); } } - }//end if + } + + $lastLine = $tokens[$i]['line']; }//end if - // Each line between the parenthesis should be indented 4 spaces. - $openBracket = $tokens[$stackPtr]['parenthesis_opener']; - $lastLine = $tokens[$openBracket]['line']; - for ($i = ($openBracket + 1); $i < $closeBracket; $i++) { - if ($tokens[$i]['line'] !== $lastLine) { - if ($i === $tokens[$stackPtr]['parenthesis_closer'] - || ($tokens[$i]['code'] === T_WHITESPACE - && (($i + 1) === $closeBracket - || ($i + 1) === $tokens[$stackPtr]['parenthesis_closer'])) - ) { - // Closing braces need to be indented to the same level - // as the function. - $expectedIndent = $functionIndent; - } else { - $expectedIndent = ($functionIndent + $indent); - } - - // We changed lines, so this should be a whitespace indent token. - if ($tokens[$i]['code'] !== T_WHITESPACE) { - $foundIndent = 0; - } else if ($tokens[$i]['line'] !== $tokens[($i + 1)]['line']) { - // This is an empty line, so don't check the indent. - $foundIndent = $expectedIndent; - - $error = 'Blank lines are not allowed in a multi-line ' . $type . ' declaration'; - $fix = $phpcsFile->addFixableError($error, $i, 'EmptyLine'); - if ($fix === true) { - $phpcsFile->fixer->replaceToken($i, ''); - } - } else { - $foundIndent = $tokens[$i]['length']; - } - - if ($expectedIndent !== $foundIndent) { - $error = 'Multi-line ' . $type . ' declaration not indented correctly; expected %s spaces but found %s'; - $data = [ - $expectedIndent, - $foundIndent, - ]; - - $fix = $phpcsFile->addFixableError($error, $i, 'Indent', $data); - if ($fix === true) { - $spaces = str_repeat(' ', $expectedIndent); - if ($foundIndent === 0) { - $phpcsFile->fixer->addContentBefore($i, $spaces); - } else { - $phpcsFile->fixer->replaceToken($i, $spaces); - } - } - } - - $lastLine = $tokens[$i]['line']; - }//end if - - if ($tokens[$i]['code'] === T_ARRAY || $tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) { - // Skip arrays as they have their own indentation rules. - if ($tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) { - $i = $tokens[$i]['bracket_closer']; - } else { - $i = $tokens[$i]['parenthesis_closer']; - } - - $lastLine = $tokens[$i]['line']; - continue; + if ($tokens[$i]['code'] === T_ARRAY || $tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) { + // Skip arrays as they have their own indentation rules. + if ($tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) { + $i = $tokens[$i]['bracket_closer']; + } else { + $i = $tokens[$i]['parenthesis_closer']; } - }//end for - }//end processArgumentList() + $lastLine = $tokens[$i]['line']; + continue; + } + }//end for + + }//end processArgumentList() }//end class diff --git a/tests/integration/Newsletter/ShortcodesTest.php b/tests/integration/Newsletter/ShortcodesTest.php index fa23e05bec..d1182919b2 100644 --- a/tests/integration/Newsletter/ShortcodesTest.php +++ b/tests/integration/Newsletter/ShortcodesTest.php @@ -105,14 +105,15 @@ class ShortcodesTest extends \MailPoetTest { $shortcode = ['[some:shortcode arg1="val1" arg2="val2"]']; // WP style arguments $result = $shortcodesObject->process($shortcode); expect($result[0])->false(); - add_filter('mailpoet_newsletter_shortcode', function( - $shortcode, $newsletter, $subscriber, $queue, $content, $arguments -) { - expect($arguments)->count(2); - expect($arguments['arg1'])->equals('val1'); - expect($arguments['arg2'])->equals('val2'); - if (strpos($shortcode, '[some:shortcode') === 0) return 'success'; - }, 10, 6); + add_filter( + 'mailpoet_newsletter_shortcode', + function($shortcode, $newsletter, $subscriber, $queue, $content, $arguments) { + expect($arguments)->count(2); + expect($arguments['arg1'])->equals('val1'); + expect($arguments['arg2'])->equals('val2'); + if (strpos($shortcode, '[some:shortcode') === 0) return 'success'; + }, 10, 6 + ); $result = $shortcodesObject->process($shortcode); expect($result[0])->equals('success'); }