【问题标题】:Best way to automatically remove comments from PHP code从 PHP 代码中自动删除注释的最佳方法
【发布时间】:2010-10-04 23:15:50
【问题描述】:

从 PHP 文件中删除 cmets 的最佳方法是什么?

我想做一些类似于 strip-whitespace() 的事情 - 但它也不应该删除换行符。

EG:

我想要这个:

<?PHP
// something
if ($whatsit) {
    do_something(); # we do something here
    echo '<html>Some embedded HTML</html>';
}
/* another long 
comment
*/
some_more_code();
?>

变成:

<?PHP
if ($whatsit) {
    do_something();
    echo '<html>Some embedded HTML</html>';
}
some_more_code();
?>

(尽管如果在删除 cmets 的地方仍然存在空行,那就不行了)。

这可能是不可能的,因为需要保留嵌入的 html - 这就是在 google 上出现的问题。

【问题讨论】:

  • 查看混淆器。虽然你必须找到一个可配置的——只剥离 cmets。
  • 有人肯定会问为什么:代码需要到客户端服务器进行部署,所以我们要确保没有不应该的地方。
  • 您是在讨论 cmets 中的不当内容吗?或者这只是为了大小 - 较小的 PHP 脚本几乎没有性能差异,除非在高使用率或不寻常的情况下(Zend 通常是比剥离它们更好的答案)。
  • cmets 中有一些我们不想冒险被阅读的东西。他们不应该在那里 - 但现在为时已晚。
  • 除非您进行混淆,否则我不愿意删除 cmets。您可能会发现有时需要客户端服务器上的这些 cmets。另外,您是否向他们明确表示该代码是与 cmets 一起提供的?他们可能不喜欢引入不同顾问时的惊喜……

标签: php comments strip


【解决方案1】:

我会使用tokenizer。这是我的解决方案。它应该适用于 PHP 4 和 5:

$fileStr = file_get_contents('path/to/file');
$newStr  = '';

$commentTokens = array(T_COMMENT);
    
if (defined('T_DOC_COMMENT')) {
    $commentTokens[] = T_DOC_COMMENT; // PHP 5
}

if (defined('T_ML_COMMENT')) {
    $commentTokens[] = T_ML_COMMENT;  // PHP 4
}

$tokens = token_get_all($fileStr);

foreach ($tokens as $token) {    
    if (is_array($token)) {
        if (in_array($token[0], $commentTokens)) {
            continue;
        }
        
        $token = $token[1];
    }

    $newStr .= $token;
}

echo $newStr;

【讨论】:

  • 您应该从foreach 块中取出$commentTokens 初始化,否则+1 并感谢:)
  • @Raveren,你说得对。我不知道当时我在想什么将那段代码放入循环中。感谢您指出。
  • @lonut 谢谢!非常有用:-)
  • @IonuțG.Stan 我一直在尝试实现这一点,但它破坏了很多代码。这是一个示例:``` ### 版本 ### const MARKDOWNLIB_VERSION = "1.6.0"; ### 简单函数接口 ### public static function defaultTransform($text) { ``` 变成 ``` ### 版本 # const MARKDOWNLIB_VERSION = "1.6.0"; ### 简单函数接口 # public static function defaultTransform($text) { ``` 不确定这是否会在这里格式化...
  • @AndrewChristensen 我无法复制它。你用的是什么 PHP 版本?
【解决方案2】:

根据接受的答案,我还需要保留文件的行号,所以这里是接受答案的变体:

    /**
     * Removes the php comments from the given valid php string, and returns the result.
     *
     * Note: a valid php string must start with <?php.
     *
     * If the preserveWhiteSpace option is true, it will replace the comments with some whitespaces, so that
     * the line numbers are preserved.
     *
     *
     * @param string $str
     * @param bool $preserveWhiteSpace
     * @return string
     */
    function removePhpComments(string $str, bool $preserveWhiteSpace = true): string
    {
        $commentTokens = [
            \T_COMMENT,
            \T_DOC_COMMENT,
        ];
        $tokens = token_get_all($str);


        if (true === $preserveWhiteSpace) {
            $lines = explode(PHP_EOL, $str);
        }


        $s = '';
        foreach ($tokens as $token) {
            if (is_array($token)) {
                if (in_array($token[0], $commentTokens)) {
                    if (true === $preserveWhiteSpace) {
                        $comment = $token[1];
                        $lineNb = $token[2];
                        $firstLine = $lines[$lineNb - 1];
                        $p = explode(PHP_EOL, $comment);
                        $nbLineComments = count($p);
                        if ($nbLineComments < 1) {
                            $nbLineComments = 1;
                        }
                        $firstCommentLine = array_shift($p);

                        $isStandAlone = (trim($firstLine) === trim($firstCommentLine));

                        if (false === $isStandAlone) {
                            if (2 === $nbLineComments) {
                                $s .= PHP_EOL;
                            }

                            continue; // just remove inline comments
                        }

                        // stand alone case
                        $s .= str_repeat(PHP_EOL, $nbLineComments - 1);
                    }
                    continue;
                }
                $token = $token[1];
            }

            $s .= $token;
        }
        return $s;
    }

注意:这是针对 php 7+ 的(我不关心与旧 php 版本的向后兼容性)。

【讨论】:

    【解决方案3】:

    php -wphp_strip_whitespace($filename);

    documentation

    【讨论】:

    • 这很有用,但 OP 专门要求提供不删除换行符的解决方案。
    【解决方案4】:

    2019 年可能会这样

    <?php
    /*   hi there !!!
    here are the comments */
    //another try
    
    echo removecomments('index.php');
    
    /*   hi there !!!
    here are the comments */
    //another try
    function removecomments($f){
        $w=Array(';','{','}');
        $ts = token_get_all(php_strip_whitespace($f));
        $s='';
        foreach($ts as $t){
            if(is_array($t)){
                $s .=$t[1];
            }else{
                $s .=$t;
                if( in_array($t,$w) ) $s.=chr(13).chr(10);
            }
        }
    
        return $s;
    }
    
    ?>
    

    如果你想查看结果,让我们先在 xampp 中运行它,然后你会得到一个空白页面,但是如果你右键单击并单击查看源代码,你会得到你的 php 脚本.. 它正在加载自己,它正在删除所有 cmets 以及标签。 我也更喜欢这个解决方案,因为我用它来加速我的框架一个文件引擎“m.php”,在 php_strip_whitespace 之后,我观察到没有这个脚本的所有源代码是最慢的:我做了 10 个基准测试,然后我计算了数学平均值(我认为 php 7 正在解析时恢复丢失的cr_lf,或者当这些丢失时需要一段时间)

    【讨论】:

      【解决方案5】:

      在命令提示符下运行命令php --strip file.php(即cmd.exe),然后浏览到http://www.writephponline.com/phpbeautifier

      这里,file.php是你自己的文件。

      【讨论】:

      • 不会--strip(或-w)也去掉空格吗?
      【解决方案6】:

      Bash 解决方案:如果您想从当前目录开始的所有 PHP 文件中递归删除 cmets,您可以在终端中编写此单行代码。 (它使用temp1 文件来存储PHP 内容以供处理) 请注意,这将使用 cmets 去除所有空格。

       find . -type f -name '*.php' | while read VAR; do php -wq $VAR > temp1  ;  cat temp1 > $VAR; done
      

      那么你应该删除temp1之后的文件。

      如果安装了PHP_BEAUTIFER那么你可以在没有 cmets 的情况下获得格式良好的代码

       find . -type f -name '*.php' | while read VAR; do php -wq $VAR > temp1; php_beautifier temp1 > temp2;  cat temp2 > $VAR; done;
      

      然后删除两个文件(temp1temp2

      【讨论】:

        【解决方案7】:

        更强大的版本:删除文件夹中的所有 cmets

        <?php
        $di = new RecursiveDirectoryIterator(__DIR__,RecursiveDirectoryIterator::SKIP_DOTS);
        $it = new RecursiveIteratorIterator($di);
        $fileArr = [];
        foreach($it as $file){
            if(pathinfo($file,PATHINFO_EXTENSION) == "php"){
                ob_start();
                echo $file;
                $file = ob_get_clean();
                $fileArr[] = $file;
            }
        }
        $arr = [T_COMMENT,T_DOC_COMMENT];
        $count = count($fileArr);
        for($i=1;$i < $count;$i++){
            $fileStr = file_get_contents($fileArr[$i]);
            foreach(token_get_all($fileStr) as $token){
                if(in_array($token[0],$arr)){
                    $fileStr = str_replace($token[1],'',$fileStr);
                }            
            }
            file_put_contents($fileArr[$i],$fileStr);
        }
        

        【讨论】:

        • 我喜欢它!我会试试的。
        【解决方案8】:

        如果您已经使用 UltraEdit 之类的编辑器,您可以打开一个或多个 PHP 文件,然后使用 简单的 Find&Replace (CTRL+R) 和以下命令Perl 正则表达式

        (?s)/\*.*\*/
        

        请注意,上面的正则表达式也会删除 sring 中的 cmets,即在 echo "hello/*babe*/"; 中,/*babe*/ 也会被删除。因此,如果您要删除 cmets 的文件很少,这可能是一个解决方案,为了绝对确保它不会错误地替换不是注释的内容,您必须运行 Find&Replace 命令并在每次替换时批准。

        【讨论】:

          【解决方案9】:

          对于 ajax/json 响应,我使用以下 PHP 代码从 HTML/JavaScript 代码中删除 cmets,因此它会更小(我的代码大约增加 15%)。

          // Replace doubled spaces with single ones (ignored in HTML any way)
          $html = preg_replace('@(\s){2,}@', '\1', $html);
          // Remove single and multiline comments, tabs and newline chars
          $html = preg_replace(
              '@(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|((?<!:)//.*)|[\t\r\n]@i',
              '',
              $html
          );
          

          简短而有效,但如果您的代码具有 $itty 语法,可能会产生意想不到的结果。

          【讨论】:

          • 这个正则表达式不会删除字符串中的 cmets 吗? IE。在echo "hello /*baby*/ boy"; 中,您的正则表达式不会错误地删除刺痛中的/*baby*/ 吗?
          • @MarcoDemaio 会的。为避免这种情况,您将需要解析器,而不是一些简单的正则表达式,因为您需要遵循引用状态并知道注释所在的位置以及不需要它们的位置。 JSON 不适用于复杂的数据结构,您应该避免出现数据内部可能存在单行或多行 cmets 的情况。
          【解决方案10】:
          /*
          * T_ML_COMMENT does not exist in PHP 5.
          * The following three lines define it in order to
          * preserve backwards compatibility.
          *
          * The next two lines define the PHP 5 only T_DOC_COMMENT,
          * which we will mask as T_ML_COMMENT for PHP 4.
          */
          
          if (! defined('T_ML_COMMENT')) {
              define('T_ML_COMMENT', T_COMMENT);
          } else {
              define('T_DOC_COMMENT', T_ML_COMMENT);
          }
          
          /*
           * Remove all comment in $file
           */
          
          function remove_comment($file) {
              $comment_token = array(T_COMMENT, T_ML_COMMENT, T_DOC_COMMENT);
          
              $input = file_get_contents($file);
              $tokens = token_get_all($input);
              $output = '';
          
              foreach ($tokens as $token) {
                  if (is_string($token)) {
                      $output .= $token;
                  } else {
                      list($id, $text) = $token;
          
                      if (in_array($id, $comment_token)) {
                          $output .= $text;
                      }
                  }
              }
          
              file_put_contents($file, $output);
          }
          
          /*
           * Glob recursive
           * @return ['dir/filename', ...]
           */
          
          function glob_recursive($pattern, $flags = 0) {
              $file_list = glob($pattern, $flags);
          
              $sub_dir = glob(dirname($pattern) . '/*', GLOB_ONLYDIR);
              // If sub directory exist
              if (count($sub_dir) > 0) {
                  $file_list = array_merge(
                      glob_recursive(dirname($pattern) . '/*/' . basename($pattern), $flags),
                      $file_list
                  );
              }
          
              return $file_list;
          }
          
          // Remove all comment of '*.php', include sub directory
          foreach (glob_recursive('*.php') as $file) {
              remove_comment($file);
          }
          

          【讨论】:

            【解决方案11】:

            这是上面发布的函数,修改为递归地从目录及其所有子目录中的所有 php 文件中删除所有 cmets:

            function rmcomments($id) {
                if (file_exists($id)) {
                    if (is_dir($id)) {
                        $handle = opendir($id);
                        while($file = readdir($handle)) {
                            if (($file != ".") && ($file != "..")) {
                                rmcomments($id."/".$file); }}
                        closedir($handle); }
                    else if ((is_file($id)) && (end(explode('.', $id)) == "php")) {
                        if (!is_writable($id)) { chmod($id,0777); }
                        if (is_writable($id)) {
                            $fileStr = file_get_contents($id);
                            $newStr  = '';
                            $commentTokens = array(T_COMMENT);
                            if (defined('T_DOC_COMMENT')) { $commentTokens[] = T_DOC_COMMENT; }
                            if (defined('T_ML_COMMENT')) { $commentTokens[] = T_ML_COMMENT; }
                            $tokens = token_get_all($fileStr);
                            foreach ($tokens as $token) {    
                                if (is_array($token)) {
                                    if (in_array($token[0], $commentTokens)) { continue; }
                                    $token = $token[1]; }
                                $newStr .= $token; }
                            if (!file_put_contents($id,$newStr)) {
                                $open = fopen($id,"w");
                                fwrite($open,$newStr);
                                fclose($open); }}}}}
            
            rmcomments("path/to/directory");
            

            【讨论】:

              【解决方案12】:
              $fileStr = file_get_contents('file.php');
              foreach (token_get_all($fileStr) as $token ) {
                  if ($token[0] != T_COMMENT) {
                      continue;
                  }
                  $fileStr = str_replace($token[1], '', $fileStr);
              }
              
              echo $fileStr;
              

              编辑 我意识到 Ionut G. Stan 已经提出了这个建议,但我将在此处留下示例

              【讨论】:

              • 我认为上面的 sn-p 应该可以正常工作。它实际上比我想象的要简单。
              【解决方案13】:

              如何使用 php -w 生成一个去除 cmets 和空格的文件,然后使用像 PHP_Beautifier 这样的美化器来重新格式化以提高可读性?

              【讨论】:

              • 感谢您的建议 - 另一种方式使用起来更快,因为所有位都已在服务器上。
              • 是的,我喜欢分词器的答案,更简单!
              • 有时,最简单的答案是最好的:)
              • 在 *nix 机器上,这可以归结为在控制台上运行:$ php -qw your_code.php | php_beautifier 2&gt;/dev/null。请注意屏幕上可能仍会出现错误 & Co. - 为避免这种情况,只需在 (CLI) php.ini 文件中将 display_errors 设置为 Off
              • 什么是 php -w 以及如何在 Windows 10 上执行此操作?
              【解决方案14】:

              问题在于,一个不太健壮的匹配算法(例如简单的正则表达式)会在明显不应该的时候开始剥离:

              if (preg_match('#^/*' . $this->index . '#', $this->permalink_structure)) {  
              

              它可能不会影响您的代码,但最终有人会被您的脚本所吸引。因此,您将不得不使用一个比您预期的更了解该语言的实用程序。

              -亚当

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-09-26
                • 2011-05-23
                • 2015-01-09
                • 1970-01-01
                • 2010-09-07
                • 2014-05-15
                • 2015-11-01
                • 1970-01-01
                相关资源
                最近更新 更多