Loading coder_sniffer/Drupal/Sniffs/Commenting/DocCommentAlignmentSniff.php +3 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,9 @@ class DocCommentAlignmentSniff implements Sniff } } else if ($tokens[($i + 2)]['code'] === T_DOC_COMMENT_TAG && $tokens[($i + 1)]['content'] !== ' ' // Special @code/@endcode tags can have more than 1 space. && $tokens[($i + 2)]['content'] !== '@code' && $tokens[($i + 2)]['content'] !== '@endcode' ) { $error = 'Expected 1 space after asterisk; %s found'; $data = [strlen($tokens[($i + 1)]['content'])]; Loading coder_sniffer/Drupal/Sniffs/Commenting/DocCommentSniff.php +15 −3 Original line number Diff line number Diff line Loading @@ -369,6 +369,10 @@ class DocCommentSniff implements Sniff $currentTag = null; $previousTag = null; $isNewGroup = null; $ignoreTags = [ '@code', '@endcode', ]; foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) { if ($pos > 0) { $prev = $phpcsFile->findPrevious( Loading Loading @@ -408,6 +412,9 @@ class DocCommentSniff implements Sniff && (in_array($currentTag, ['@param', '@return', '@throws']) === true || in_array($previousTag, ['@param', '@return', '@throws']) === true) && $previousTag !== $currentTag // Ignore code blocks in comments, they can be anywhere. && in_array($previousTag, $ignoreTags) === false && in_array($currentTag, $ignoreTags) === false ) { $error = 'Separate the %s and %s sections by a blank line.'; $fix = $phpcsFile->addFixableError($error, $tag, 'TagGroupSpacing', [$previousTag, $currentTag]); Loading Loading @@ -489,19 +496,24 @@ class DocCommentSniff implements Sniff } $foundTags = []; $lastPos = 0; foreach ($tokens[$stackPtr]['comment_tags'] as $pos => $tag) { $tagName = $tokens[$tag]['content']; // Skip code tags, they can be anywhere. if (in_array($tagName, $ignoreTags) === true) { continue; } if (isset($foundTags[$tagName]) === true) { $lastTag = $tokens[$stackPtr]['comment_tags'][($pos - 1)]; $lastTag = $tokens[$stackPtr]['comment_tags'][$lastPos]; if ($tokens[$lastTag]['content'] !== $tagName) { $error = 'Tags must be grouped together in a doc comment'; $phpcsFile->addError($error, $tag, 'TagsNotGrouped'); } continue; } $foundTags[$tagName] = true; $lastPos = $pos; } }//end process() Loading coder_sniffer/Drupal/Sniffs/Commenting/FunctionCommentSniff.php +73 −9 Original line number Diff line number Diff line Loading @@ -205,12 +205,23 @@ class FunctionCommentSniff implements Sniff $return = $tag; // Any strings until the next tag belong to this comment. if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) { $end = $tokens[$commentStart]['comment_tags'][($pos + 1)]; $skipTags = [ '@code', '@endcode', ]; $skipPos = ($pos + 1); while (isset($tokens[$commentStart]['comment_tags'][$skipPos]) === true && in_array($tokens[$commentStart]['comment_tags'][$skipPos], $skipTags) === true ) { $skipPos++; } $end = $tokens[$commentStart]['comment_tags'][$skipPos]; } else { $end = $tokens[$commentStart]['comment_closer']; } } } }//end if }//end foreach $type = null; if ($isSpecialMethod === false && $methodName !== $className) { Loading Loading @@ -531,10 +542,26 @@ class FunctionCommentSniff implements Sniff // Any strings until the next tag belong to this comment. if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) { $end = $tokens[$commentStart]['comment_tags'][($pos + 1)]; // Ignore code tags and include them within this comment. $skipTags = [ '@code', '@endcode', ]; $skipPos = $pos; while (isset($tokens[$commentStart]['comment_tags'][($skipPos + 1)]) === true && in_array($tokens[$tokens[$commentStart]['comment_tags'][($skipPos + 1)]]['content'], $skipTags) === true ) { $skipPos++; } if ($skipPos === $pos) { $skipPos++; } $end = $tokens[$commentStart]['comment_tags'][$skipPos]; } else { $end = $tokens[$commentStart]['comment_closer']; } }//end if for ($i = ($tag + 3); $i < $end; $i++) { if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) { Loading Loading @@ -847,12 +874,16 @@ class FunctionCommentSniff implements Sniff $commentToken = $lastLine['token']; } // Don't show an error if the end of the comment is in a code // example. if ($this->isInCodeExample($phpcsFile, $commentToken, $param['tag']) === false) { $fix = $phpcsFile->addFixableError($error, $commentToken, 'ParamCommentFullStop'); if ($fix === true) { // Add a full stop as the last character of the comment. $phpcsFile->fixer->addContent($commentToken, '.'); } } } }//end foreach // Missing parameters only apply to methods and not function because on Loading Loading @@ -1012,4 +1043,37 @@ class FunctionCommentSniff implements Sniff }//end isAliasedType() /** * Determines if a comment line is part of an @code/@endcode example. * * @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 $commentStart The position of the start of the comment * in the stack passed in $tokens. * * @return boolean Returns true if the comment line is within a @code block, * false otherwise. */ protected function isInCodeExample(File $phpcsFile, $stackPtr, $commentStart) { $tokens = $phpcsFile->getTokens(); if (strpos($tokens[$stackPtr]['content'], '@code') !== false) { return true; } $prevTag = $phpcsFile->findPrevious([T_DOC_COMMENT_TAG], ($stackPtr - 1), $commentStart); if ($prevTag === false) { return false; } if ($tokens[$prevTag]['content'] === '@code') { return true; } return false; }//end isInCodeExample() }//end class coder_sniffer/Drupal/Test/good/good.php +111 −0 Original line number Diff line number Diff line Loading @@ -1652,3 +1652,114 @@ class Test4 { } } /** * Foo. */ interface Test5Interface { /** * Orders the result set by a given field. * * If called multiple times, the query will order by each specified field in * the order this method is called. * * If the query uses DISTINCT or GROUP BY conditions, fields or expressions * that are used for the order must be selected to be compatible with some * databases like PostgreSQL. The PostgreSQL driver can handle simple cases * automatically but it is suggested to explicitly specify them. Additionally, * when ordering on an alias, the alias must be added before orderBy() is * called. * * @param string $field * The field on which to order. The field is escaped for security so only * valid field and alias names are possible. To order by an expression, add * the expression with addExpression() first and then use the alias to order * on. * * Example: * @code * $query->addExpression('SUBSTRING(thread, 1, (LENGTH(thread) - 1))', 'order_field'); * $query->orderBy('order_field', 'ASC'); * @endcode * @param string $direction * The direction to sort. Legal values are "ASC" and "DESC". Any other value * will be converted to "ASC". * * @return \Drupal\Core\Database\Query\SelectInterface * The called object. */ public function orderBy($field, $direction = 'ASC'); /** * Example with multiple code blocks in param docs. * * @param string $param1 * Just some Example param. * @param ... * Any additional arguments are passed on to the functions called by * drupal_form_submit(), including the unique form constructor function. * For example, the node_edit form requires that a node object be passed * in here when it is called. Arguments that need to be passed by reference * should not be included here, but rather placed directly in the $form * build info array so that the reference can be preserved. For example, a * form builder function with the following signature: * @code * function mymodule_form($form, &$form_state, &$object) { * } * @endcode * would be called via drupal_form_submit() as follows: * @code * $form_state['values'] = $my_form_values; * $form_state['build_info']['args'] = array(&$object); * drupal_form_submit('mymodule_form', $form_state); * @endcode */ public function test1($param1); /** * This is an example of a doc block that is good. * * We want to show some example code: * @code * if ($something) { * $x = $y; * } * @endcode * Some more example code: * @code * if ($something) { * $x = $y; * } * @endcode * And one more piece of example code: * @code * if ($something) { * $x = $y; * } * @endcode * Followed by some summary text. */ public function test2(); /** * This is good. * * @return string * Here is a comment, let's explain the return value with an example: * @code * if ($something) { * $x = $y; * } * @endcode * And then the comment goes on here. You want more code? Here you go: * @code * if ($something) { * $x = $y; * } * @endcode * And this is the end. */ public function test3(); } Loading
coder_sniffer/Drupal/Sniffs/Commenting/DocCommentAlignmentSniff.php +3 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,9 @@ class DocCommentAlignmentSniff implements Sniff } } else if ($tokens[($i + 2)]['code'] === T_DOC_COMMENT_TAG && $tokens[($i + 1)]['content'] !== ' ' // Special @code/@endcode tags can have more than 1 space. && $tokens[($i + 2)]['content'] !== '@code' && $tokens[($i + 2)]['content'] !== '@endcode' ) { $error = 'Expected 1 space after asterisk; %s found'; $data = [strlen($tokens[($i + 1)]['content'])]; Loading
coder_sniffer/Drupal/Sniffs/Commenting/DocCommentSniff.php +15 −3 Original line number Diff line number Diff line Loading @@ -369,6 +369,10 @@ class DocCommentSniff implements Sniff $currentTag = null; $previousTag = null; $isNewGroup = null; $ignoreTags = [ '@code', '@endcode', ]; foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) { if ($pos > 0) { $prev = $phpcsFile->findPrevious( Loading Loading @@ -408,6 +412,9 @@ class DocCommentSniff implements Sniff && (in_array($currentTag, ['@param', '@return', '@throws']) === true || in_array($previousTag, ['@param', '@return', '@throws']) === true) && $previousTag !== $currentTag // Ignore code blocks in comments, they can be anywhere. && in_array($previousTag, $ignoreTags) === false && in_array($currentTag, $ignoreTags) === false ) { $error = 'Separate the %s and %s sections by a blank line.'; $fix = $phpcsFile->addFixableError($error, $tag, 'TagGroupSpacing', [$previousTag, $currentTag]); Loading Loading @@ -489,19 +496,24 @@ class DocCommentSniff implements Sniff } $foundTags = []; $lastPos = 0; foreach ($tokens[$stackPtr]['comment_tags'] as $pos => $tag) { $tagName = $tokens[$tag]['content']; // Skip code tags, they can be anywhere. if (in_array($tagName, $ignoreTags) === true) { continue; } if (isset($foundTags[$tagName]) === true) { $lastTag = $tokens[$stackPtr]['comment_tags'][($pos - 1)]; $lastTag = $tokens[$stackPtr]['comment_tags'][$lastPos]; if ($tokens[$lastTag]['content'] !== $tagName) { $error = 'Tags must be grouped together in a doc comment'; $phpcsFile->addError($error, $tag, 'TagsNotGrouped'); } continue; } $foundTags[$tagName] = true; $lastPos = $pos; } }//end process() Loading
coder_sniffer/Drupal/Sniffs/Commenting/FunctionCommentSniff.php +73 −9 Original line number Diff line number Diff line Loading @@ -205,12 +205,23 @@ class FunctionCommentSniff implements Sniff $return = $tag; // Any strings until the next tag belong to this comment. if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) { $end = $tokens[$commentStart]['comment_tags'][($pos + 1)]; $skipTags = [ '@code', '@endcode', ]; $skipPos = ($pos + 1); while (isset($tokens[$commentStart]['comment_tags'][$skipPos]) === true && in_array($tokens[$commentStart]['comment_tags'][$skipPos], $skipTags) === true ) { $skipPos++; } $end = $tokens[$commentStart]['comment_tags'][$skipPos]; } else { $end = $tokens[$commentStart]['comment_closer']; } } } }//end if }//end foreach $type = null; if ($isSpecialMethod === false && $methodName !== $className) { Loading Loading @@ -531,10 +542,26 @@ class FunctionCommentSniff implements Sniff // Any strings until the next tag belong to this comment. if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) { $end = $tokens[$commentStart]['comment_tags'][($pos + 1)]; // Ignore code tags and include them within this comment. $skipTags = [ '@code', '@endcode', ]; $skipPos = $pos; while (isset($tokens[$commentStart]['comment_tags'][($skipPos + 1)]) === true && in_array($tokens[$tokens[$commentStart]['comment_tags'][($skipPos + 1)]]['content'], $skipTags) === true ) { $skipPos++; } if ($skipPos === $pos) { $skipPos++; } $end = $tokens[$commentStart]['comment_tags'][$skipPos]; } else { $end = $tokens[$commentStart]['comment_closer']; } }//end if for ($i = ($tag + 3); $i < $end; $i++) { if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) { Loading Loading @@ -847,12 +874,16 @@ class FunctionCommentSniff implements Sniff $commentToken = $lastLine['token']; } // Don't show an error if the end of the comment is in a code // example. if ($this->isInCodeExample($phpcsFile, $commentToken, $param['tag']) === false) { $fix = $phpcsFile->addFixableError($error, $commentToken, 'ParamCommentFullStop'); if ($fix === true) { // Add a full stop as the last character of the comment. $phpcsFile->fixer->addContent($commentToken, '.'); } } } }//end foreach // Missing parameters only apply to methods and not function because on Loading Loading @@ -1012,4 +1043,37 @@ class FunctionCommentSniff implements Sniff }//end isAliasedType() /** * Determines if a comment line is part of an @code/@endcode example. * * @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 $commentStart The position of the start of the comment * in the stack passed in $tokens. * * @return boolean Returns true if the comment line is within a @code block, * false otherwise. */ protected function isInCodeExample(File $phpcsFile, $stackPtr, $commentStart) { $tokens = $phpcsFile->getTokens(); if (strpos($tokens[$stackPtr]['content'], '@code') !== false) { return true; } $prevTag = $phpcsFile->findPrevious([T_DOC_COMMENT_TAG], ($stackPtr - 1), $commentStart); if ($prevTag === false) { return false; } if ($tokens[$prevTag]['content'] === '@code') { return true; } return false; }//end isInCodeExample() }//end class
coder_sniffer/Drupal/Test/good/good.php +111 −0 Original line number Diff line number Diff line Loading @@ -1652,3 +1652,114 @@ class Test4 { } } /** * Foo. */ interface Test5Interface { /** * Orders the result set by a given field. * * If called multiple times, the query will order by each specified field in * the order this method is called. * * If the query uses DISTINCT or GROUP BY conditions, fields or expressions * that are used for the order must be selected to be compatible with some * databases like PostgreSQL. The PostgreSQL driver can handle simple cases * automatically but it is suggested to explicitly specify them. Additionally, * when ordering on an alias, the alias must be added before orderBy() is * called. * * @param string $field * The field on which to order. The field is escaped for security so only * valid field and alias names are possible. To order by an expression, add * the expression with addExpression() first and then use the alias to order * on. * * Example: * @code * $query->addExpression('SUBSTRING(thread, 1, (LENGTH(thread) - 1))', 'order_field'); * $query->orderBy('order_field', 'ASC'); * @endcode * @param string $direction * The direction to sort. Legal values are "ASC" and "DESC". Any other value * will be converted to "ASC". * * @return \Drupal\Core\Database\Query\SelectInterface * The called object. */ public function orderBy($field, $direction = 'ASC'); /** * Example with multiple code blocks in param docs. * * @param string $param1 * Just some Example param. * @param ... * Any additional arguments are passed on to the functions called by * drupal_form_submit(), including the unique form constructor function. * For example, the node_edit form requires that a node object be passed * in here when it is called. Arguments that need to be passed by reference * should not be included here, but rather placed directly in the $form * build info array so that the reference can be preserved. For example, a * form builder function with the following signature: * @code * function mymodule_form($form, &$form_state, &$object) { * } * @endcode * would be called via drupal_form_submit() as follows: * @code * $form_state['values'] = $my_form_values; * $form_state['build_info']['args'] = array(&$object); * drupal_form_submit('mymodule_form', $form_state); * @endcode */ public function test1($param1); /** * This is an example of a doc block that is good. * * We want to show some example code: * @code * if ($something) { * $x = $y; * } * @endcode * Some more example code: * @code * if ($something) { * $x = $y; * } * @endcode * And one more piece of example code: * @code * if ($something) { * $x = $y; * } * @endcode * Followed by some summary text. */ public function test2(); /** * This is good. * * @return string * Here is a comment, let's explain the return value with an example: * @code * if ($something) { * $x = $y; * } * @endcode * And then the comment goes on here. You want more code? Here you go: * @code * if ($something) { * $x = $y; * } * @endcode * And this is the end. */ public function test3(); }