首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 代碼靜態分析:基於 token_get_all 移除空白與註釋的實現原理

     編輯:PHP 相關知識

PHP 內置 php_strip_whitespace() 僅支持文件路徑,無法直接處理字符串變量中的代碼。ThinkPHP 框架內置的 strip_whitespace() 利用 token_get_all() 詞法分析器,實現了對任意 PHP 代碼字符串的空白字符壓縮與註釋剝離。本文深度解析其核心邏輯,並提供可直接使用的增強版函數。

核心思路:詞法單元流處理

token_get_all($code) 將 PHP 代碼解析為「詞法單元」數組:

  • 單字符標記(如 ;、{、$)以字符串形式出現。
  • 複雜結構(關鍵字、標識符、字符串、註釋、空白)以 [TOKEN_TYPE, 'token_content', line_number] 數組形式出現。

遍歷單元流,根據類型決定保留、轉換或丟棄:

  • 保留:普通字符、關鍵字、標識符、字符串字面量等語義內容。
  • 壓縮:連續空白(T_WHITESPACE)壓縮為單個空格。
  • 丟棄:註釋(T_COMMENT、T_DOC_COMMENT)。
  • 特殊處理:Heredoc/Nowdoc 結構(T_START_HEREDOC/T_END_HEREDOC)需保持原樣。

增強版實現(支持 PHP 8+、更健壯)

<?php
/**
 * 移除 PHP 代碼字符串中的空白字符與註釋
 * @param string $source PHP 代碼字符串
 * @return string 處理後的代碼
 */
function strip_whitespace(string $source): string {
    $tokens = token_get_all($source);
    $output = '';
    $lastWasWhitespace = false;

    foreach ($tokens as $token) {
        if (is_string($token)) {
            // 單字符標記:直接輸出
            $output .= $token;
            $lastWasWhitespace = false;
            continue;
        }

        [$id, $text] = $token;

        match ($id) {
            T_COMMENT, T_DOC_COMMENT => {
                // 完全丟棄註釋
                $lastWasWhitespace = false;
            },
            T_WHITESPACE => {
                // 連續空白壓縮為單個空格
                if (!$lastWasWhitespace) {
                    $output .= ' ';
                    $lastWasWhitespace = true;
                }
            },
            T_START_HEREDOC => {
                // Heredoc 開始:輸出 <<< 並進入特殊模式
                $output .= '<<<';
                $lastWasWhitespace = false;
            },
            T_END_HEREDOC => {
                // Heredoc 結束:輸出結束標記,並跳過後續的分號或關閉標記
                $output .= $text; // 實際為結束標識符
                $lastWasWhitespace = false;
                // 可選:跳過緊隨其後的分號或 ?>,避免重複輸出
            },
            default => {
                // 其他所有 token:關鍵字、變量、字符串、數字等,原樣輸出內容
                $output .= $text;
                $lastWasWhitespace = false;
            }
        }
    }

    return trim($output);
}

測試用例

$code = <<'PHP'
<?php
/**
 * 這是文檔註釋
 */
function foo($bar) { // 行內註釋
    $baz = "hello world"; /* 塊註釋 */
    return $baz . '!';
}
// 末尾註釋
PHP;

echo strip_whitespace($code);
// 輸出:
// 

原版 ThinkPHP 代碼細節補充

原版中對 T_END_HEREDOC 的處理較為特殊:

case T_END_HEREDOC:
    $stripStr .= "<<<\n"; // 這裡似乎有筆誤,實際應輸出結束標識符
    for ($k = $i + 1; $k < $j; $k++) {
        if (is_string($tokens[$k]) && $tokens[$k] === ';') {
            $i = $k; // 跳過分號
            break;
        } elseif ($tokens[$k][0] === T_CLOSE_TAG) {
            break;
        }
    }
    break;

建議使用上述 match 表達式版本,邏輯更清晰、兼容 PHP 8.0+,並正確處理 Heredoc/Nowdoc 內部不應被壓縮的空白。

應用場景與局限

場景適用性說明
代碼壓縮/混淆預處理✅ 高減小體積,配合變量重命名等進一步混淆
模板引擎編譯輸出優化✅ 高編譯後的 PHP 模板去除註釋空白,提升執行速度
代碼審計/靜態分析預處理✅ 中統一代碼風格,便於後續 AST 分析
保留語義的代碼格式化❌ 否本函數會破壞縮進、換行,不可用於美化代碼
處理非 PHP 代碼(JS/CSS/HTML)❌ 否token_get_all 僅識別 PHP 語法

總結

基於 token_get_all() 的詞法分析是 PHP 元編程的基石。通過精確識別 T_WHITESPACE、T_COMMENT 等單元類型,我們能在不破壞代碼語義的前提下實現「去註釋、壓縮空白」。掌握此技術,可構建代碼壓縮器、模板編譯優化器、靜態分析預處理器等進階工具。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved