【问题标题】:Are there PHP linter rules to prevent obvious comments?是否有 PHP linter 规则来防止明显的注释?
【发布时间】:2020-03-09 01:19:58
【问题描述】:

我在一个多语言软件代码库(python、JS、java、PHP、C)上工作,我以前的同事在其中评论了所有内容。 但是,绝大多数 cmets 完全没有用。 例如:

/**
 * Get warning file info
 */
function getWarningFileInfos() {
   ...
}

/**
 * Compute the speed
 */
function computeSpeed() {
    ...
}

我想设置 linter 规则以确保不会再次写入此类 cmets。 你知道有这种功能的 linter,或者可以很容易地添加这个功能吗? (最好是与非英语语言 cmets 兼容的 linter)

【问题讨论】:

    标签: php comments linter


    【解决方案1】:

    这是一个需要向您的同事传授 cmets 的用途以及应该编写什么样的 cmets 的问题。

    如果你只是自动阻止与方法同名的 cmets,你最终会出现细微的变化:

    /**
     * Get warning file info
     */
    function getWarningFileInfos() {
       ...
    }
    

    变成:

    /**
     * Get the warning file info
     */
    function getWarningFileInfos() {
       ...
    }
    

    ... linter 规则将接受它。这不是一个真正可以通过 linting 规则解决的问题。

    如果您有能力要求您的同事提供正确的 cmets,请他们正确地重写 cmets 是一个很好的练习,可以教他们应该编写什么 cmets。

    没有任何 linter 可以将无用的评论变成有用的评论。

    如果你只想删除所有糟糕的 cmets,你可以使用正则表达式:

    /\*.{1,50}\*/ 将查找所有短于 50 个字符的 cmets(编辑器必须支持正则表达式设置“.matches newline”)。

    替换为空并手动检查文件以检查您没有删除任何有价值的内容。假设大多数这些愚蠢的 cmets 都很短。 50 个字符是任意的,将其更改为最适合您的字符。

    【讨论】:

      【解决方案2】:

      GitHub上没有这样的linter,所以你必须自己写一个。

      PHP_CodeSniffer 是 PHP 世界中最著名和最强大的 linter,所以这里是基于 PHP_CodeSniffer 3.5.2 的快速而肮脏的实现,它使用 similar_text 函数来比较函数名和注释字符串,如果百分比超过 60%,它会将此功能标记为错误,并显示消息“包含异常评论”。

      文件src/Standards/StackOverflow/Sniffs/Commenting/UnusualCommentSniff.php

      <?php
      
      namespace PHP_CodeSniffer\Standards\StackOverflow\Sniffs\Commenting;
      
      use PHP_CodeSniffer\Files\File;
      use PHP_CodeSniffer\Sniffs\Sniff;
      use PHP_CodeSniffer\Util\Tokens;
      
      class UnusualCommentSniff implements Sniff
      {
          public function register()
          {
              return [T_FUNCTION];
          }
      
          public function process(File $phpcsFile, $stackPtr)
          {
              $tokens = $phpcsFile->getTokens();
              $find   = Tokens::$methodPrefixes;
              $find[] = T_WHITESPACE;
      
              $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
              if ($tokens[$commentEnd]['code'] != T_DOC_COMMENT_CLOSE_TAG) {
                  return;
              }
              $commentStart = $tokens[$commentEnd]['comment_opener'];
      
              $shortDesc = $this->findShortDescriptionInComment($phpcsFile, $commentStart, $commentEnd);
              $funcName = $phpcsFile->getDeclarationName($stackPtr);
              similar_text($funcName, $shortDesc, $percent);
              if ($percent > 60) {
                  $error = 'Contains unusual comment';
                  $fix = $phpcsFile->addFixableError($error, $stackPtr, 'code');
                  if ($fix) {
                      $fixer = $phpcsFile->fixer;
                      for ($i = $commentStart; $i < $commentEnd + 1; $i++) {
                          $fixer->replaceToken($i, '');
                      }
                  }
              }
          }
      
          protected function findShortDescriptionInComment($phpcsFile, $commentStart, $commentEnd)
          {
              $tokens = $phpcsFile->getTokens();
      
              $empty = [
                  T_DOC_COMMENT_WHITESPACE,
                  T_DOC_COMMENT_STAR,
              ];
      
              $short = $phpcsFile->findNext($empty, $commentStart + 1, $commentEnd, true);
              if ($short === false) {
                  return;
              }
              $shortContent = null;
              if ($tokens[$short]['code'] === T_DOC_COMMENT_STRING) {
                  $shortContent = $tokens[$short]['content'];
                  $shortEnd     = $short;
                  for ($i = ($short + 1); $i < $commentEnd; $i++) {
                      if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
                          if ($tokens[$i]['line'] === ($tokens[$shortEnd]['line'] + 1)) {
                              $shortContent .= $tokens[$i]['content'];
                              $shortEnd      = $i;
                          } else {
                              break;
                          }
                      }
                  }
              }
      
              return $shortContent;
          }
      }
      

      POC

      像这样构造文件和目录

      $ pwd
      /home/gasolwu/Code/PHP_CodeSniffer/src/Standards
      $ git describe
      3.5.2-89-g80ebd4a1a
      $ tree -C StackOverflow/
      StackOverflow/
      ├── ruleset.xml
      └── Sniffs
          └── Commenting
              └── UnusualCommentSniff.php
      
      2 directories, 2 files
      $ cat StackOverflow/ruleset.xml
      <?xml version="1.0"?>
      <ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Zend" xsi:noNamespaceSchemaLocation="../../../phpcs.xsd">
      </ruleset>
      

      为测试准备输入文件

      $ cat input.php
      <?php
      
      class Foo
      {
      
          /**
           * Get warning file info
           */
          private function getWarningFileInfos() {
          }
      
          /**
           * Compute the speed
           */
          public function computeSpeed() {
          }
      
          /**
           * This comment contains some useful information
           * So that should not be deleted
           */
          public function shoot() {
          }
      }
      

      使用命令phpcs运行

      $ bin/phpcs --standard=Stackoverflow ./input.php
      
      FILE: /data1/home/admin/gasolwu/Code/PHP_CodeSniffer/input.php
      ----------------------------------------------------------------------
      FOUND 2 ERRORS AFFECTING 2 LINES
      ----------------------------------------------------------------------
        9 | ERROR | [x] Contains unusual comment
       15 | ERROR | [x] Contains unusual comment
      ----------------------------------------------------------------------
      PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY
      ----------------------------------------------------------------------
      
      Time: 44ms; Memory: 3.75MB
      

      通过命令phpcbf自动修复此类错误

      $ bin/phpcbf --standard=Stackoverflow ./input.php
      
      PHPCBF RESULT SUMMARY
      -----------------------------------------------------------------------------
      FILE                                                         FIXED  REMAINING
      -----------------------------------------------------------------------------
      /data1/home/admin/gasolwu/Code/PHP_CodeSniffer/input.php     2      0
      -----------------------------------------------------------------------------
      A TOTAL OF 2 ERRORS WERE FIXED IN 1 FILE
      -----------------------------------------------------------------------------
      
      Time: 52ms; Memory: 3.75MB
      
      $ cat input.php
      <?php
      
      class Foo
      {
      
      
          private function getWarningFileInfos() {
          }
      
      
          public function computeSpeed() {
          }
      
          /**
           * This comment contains some useful information
           * So that should not be deleted
           */
          public function shoot() {
          }
      }
      

      【讨论】:

      • +1 因为它很好,但除非你训练开发人员编写正确的 cmets,否则你会得到像“计算速度”而不是“计算速度”这样的 cmets,并且 linter 会接受那些只是美好的。它并没有解决根本问题,即他的同事不知道在评论中写什么。此外,修复包括删除 cmets,它不会生成正确的 cmets,因此最终结果是“无评论”而不是“无用 cmets”。
      • @Sylverdrag 你说得对,这种方法根本不能解决问题。我认为最好的方法是由两种方法组成,首先,编写一些编码指南并教给你的同事。其次,在 CI 管道上自动检查。
      • 为您的团队编写一些规则,无论是 linter 还是文档。
      • 非常感谢!我会尽快测试它。当然,第一步是与我的同事交谈,我们计划下周召开一次关于编码标准的会议
      【解决方案3】:

      如果你真的需要它,你可以使用这样疯狂的东西:

      $c = '/**
       * Get warning file info
       */
      function getWarningFileInfos() {
         ...
      }
      
      /**
       * Compute the speed
       */
      function computeSpeed() {
          ...
      }';
      
      preg_match_all('~\*/[ \t]*\r?\n(?:[ \t]|\w)*function[ \t]+(\S+)[ \t]*\(~isu', $c, $matchesAll);
      foreach ($matchesAll[1] as $n) {
          $words = preg_split('~(?=\p{Lu})~', $n);
          $regex = '~/\*(?:\s|\*)*' . implode('', array_map(function($w) {
              return '(?:(?:a|the)\s+)?' . preg_quote(preg_replace('~e?s$~is', '', $w), '~') . '(?:e?s)?' . '\s+';
          }, $words)) . '(?:\s|\*)*\*+/[ \t]*(?=\r?\n(?:[ \t]|\w)*function[ \t]+' . preg_quote($n, '~') . '[ \t]*\()~isu';
          var_dump($regex);
          $c = preg_replace($regex, '', $c);
      }
      
      var_dump($c);
      

      示例输出:

      string(231) "~/\*(?:\s|\*)*(?:(?:a|the)\s+)?get(?:e?s)?\s+(?:(?:a|the)\s+)?Warning(?:e?s)?\s+(?:(?:a|the)\s+)?File(?:e?s)?\s+(?:(?:a|the)\s+)?Info(?:e?s)?\s+(?:\s|\*)*\*+/[ \t]*(?=\r?\n(?:[ \t]|\w)*function[ \t]+getWarningFileInfos[ \t]*\()~isu"
      string(162) "~/\*(?:\s|\*)*(?:(?:a|the)\s+)?compute(?:e?s)?\s+(?:(?:a|the)\s+)?Speed(?:e?s)?\s+(?:\s|\*)*\*+/[ \t]*(?=\r?\n(?:[ \t]|\w)*function[ \t]+computeSpeed[ \t]*\()~isu"
      string(88) "
      function getWarningFileInfos() {
         ...
      }
      
      
      function computeSpeed() {
          ...
      }"
      

      除了教育你的程序员之外可能没有任何工具,上面的代码对你有帮助吗?

      【讨论】:

        猜你喜欢
        • 2020-06-05
        • 1970-01-01
        • 2016-05-15
        • 2019-06-16
        • 1970-01-01
        • 2018-01-11
        • 1970-01-01
        • 2021-12-21
        • 1970-01-01
        相关资源
        最近更新 更多