php 寫的 api 除錯

直接在命令列執行 PHP 檔案時,系統會把它當成一般的腳本路徑,所以沒辦法直接解析網址後面的參數字串。當你輸入問號時,終端機通常會誤認那是檔案名稱的一部分,或者把 & 符號當成執行多個指令。

使用內建開發伺服器測試

如果你只是想測試 API 的執行結果,最簡單的方法是啟動 PHP 內建的伺服器。在專案目錄下輸入:

php -S localhost:8000

接著開啟瀏覽器或用 curl 存取 http://localhost:8000/api.php?action=decompose&text=%E7%97%9B 就能看到結果。這最貼近真實網頁環境。


在命令列模擬 GET 參數

如果一定要在純文字介面執行,可以使用 PHP 的 CGI 模式(如果環境有安裝的話),或是手動在執行前定義變數。你可以利用 php-cgi 指令並帶入參數字串:

php-cgi -f api.php action=decompose text=%E7%97%9B

如果只有一般的 php 指令,你必須在 PHP 程式碼中稍微修改,讓它能接收命令列參數。或者用一種比較取巧的方法,在執行時透過環境變數傳入:

QUERY_STRING="action=decompose&text=%E7%97%9B" php api.php

不過這需要你的 api.php 是透過讀取 $_SERVER[‘QUERY_STRING’] 來處理資料,而不是直接讀取 $_GET。


使用 CURL 模擬請求

這是在伺服器端測試 API 最常用的方式。不需要改動任何程式碼,只要你的網頁伺服器(如 Apache 或 Nginx)正在運作,直接輸入:

curl "http://127.0.0.1/api.php?action=decompose&text=%E7%97%9B"

記得網址要加上引號,避免 & 符號被終端機誤判。這樣就能直接在螢幕上看到 API 回傳的內容,也最符合你原本 log 紀錄的行為。


直接顯示在網頁上

最快的方法就是用 var_dump 或 print_r。這會把變數的內容直接吐到瀏覽器畫面。var_dump 會顯示變數的類型和長度,資訊最完整。print_r 則適合用來看陣列,版面比較乾淨。

PHP

$data = ['status' => 'ok', 'code' => 200];

// 顯示詳細資訊
var_dump($data);

// 顯示好讀的列表
print_r($data);

這招很直覺。但在測試 API 時要注意。因為這些除錯文字會直接插在原本的 JSON 回傳內容前面。可能會導致前端解析錯誤。

印在伺服器終端機

如果你不想破壞網頁原本的回傳格式。建議把除錯訊息寫到伺服器的黑底白字視窗裡。也就是你執行 php -S 的那個地方。使用 error_log 函式就可以做到。

PHP

// 印出純文字
error_log("這裡有問題");

// 印出陣列或是物件,要先轉成字串
// print_r 的第二個參數設為 true 會回傳字串而不是直接印出
error_log(print_r($data, true));

這樣網頁運作一切正常。你轉頭看執行指令的視窗就能看到數值。這對 API 開發最友善。

印出後直接暫停

有時候程式碼很長。你只想確認某個地方的變數,不想看後面的結果。可以在印出後加上 die 或 exit。

PHP

var_dump($test_variable);
die(); // 程式執行到這裡就會直接結束

需要我幫你寫一個簡單的 Log 輔助函式,讓你在開發時不用每次都打 print_r 嗎?


SQL預處理參數寫法有誤

這段程式碼最大的問題在 SQL 語句。你在字串引號裡面直接寫 :part。資料庫會把它當成純文字 %:part%。它不會把這當成要替換的變數。bindValue 綁定會失效。搜尋結果永遠是空的。

正確做法是把百分比符號放在變數綁定時處理。或者在 SQL 裡用字串連接。

修正方式參考

把 SQL 改乾淨一點。只留參數佔位符。

// SQL 裡面不要寫 %,也不要引號包住變數
$stmt = $db->prepare("SELECT char FROM component WHERE paired1 LIKE :part");

// 在綁定時加上前後的 %
$stmt->bindValue(':part', '%' . $keyword . '%', SQLITE3_TEXT);

這樣 SQLite 才能正確吃到帶有通配符的查詢字串。

只抓了一筆資料

另一個邏輯盲點是 fetchArray 只跑了一次。如果資料庫裡符合條件的字有好幾個。這段程式碼只會抓第一筆。其他的都會被漏掉。通常搜尋功能會需要用 while 迴圈把所有結果都跑一遍。

// 改用迴圈抓取所有符合的結果
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
    if (!empty($row['char'])) {
        $matched_chars[] = $row['char'];
    }
}

關鍵字命名風險

char 是 SQL 的保留字。用來當欄位名稱很危險。雖然 SQLite 有時候比較寬容。但最好養成習慣用引號包起來。寫成 “char”。或者換個欄位名稱比較保險。避免語法解析錯誤。


程式碼功能解說

這段程式碼的主要目的是從資料庫查詢結果中,把所有找到的字元一個一個抓出來,並存進一個陣列裡。它使用了 while 迴圈,這代表只要資料庫裡還有資料,它就會不斷執行大括號內的動作。

逐行細節拆解

第一行 fetchArray(SQLITE3_ASSOC) 是關鍵。這是在告訴程式以關聯陣列的形式讀取每一筆資料。簡單來說,就是讓你可以直接用欄位名稱來存取資料。每次迴圈執行時,變數 row 就會代表目前讀到的那一列數據。

接下來的 if (!empty($row[‘char’])) 是一道檢查手續。程式會確認讀到的 char 欄位裡面到底有沒有東西。如果這個欄位是空的,或者根本沒內容,它就不會執行後面的動作。這樣可以確保最後拿到的清單是乾淨且有效的。

最後的 $matched_chars[] = $row[‘char’] 則是把資料塞進陣列的操作。中括號代表在 matched_chars 這個陣列的最後面新增一個位置,把剛才確認過有內容的字元放進去。等迴圈全部跑完,這個陣列就會收集好所有符合條件的字元。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *