實戰整理:我常用的 PHP 功能語法與開發技巧 - 封面圖

寫 PHP 寫了這麼多年,老實說我不是那種每天背語法的人,而是哪個功能常在專案裡出現,我就一定會反覆確認並進行調整。

這篇文章是我自己在做系統、寫 API、串服務時,最常用、也最常查的 PHP 功能整理,算是幫自己整理。

字串處理

            
                // 字串擷取
                substr($string, $offset, $length);
                mb_substr($string, $offset, $length, 'UTF-8');     // 中文

                // 字串取代
                str_replace($search, $replace, $string);

                // 字串長度
                strlen($string);
                mb_strlen($string, 'UTF-8');       // 中文

                // 清除字串前後空白
                trim($string)

                // 是否包含某字串
                str_contains($string, '關鍵字');

                // 判斷開頭結尾
                str_starts_with($string, '關鍵字');
                str_ends_with($string, '關鍵字');

                // 大小寫轉換
                strtolower($string)              // 將字串轉換為小寫。
                strtoupper($string)              // 將字串轉換為大寫。
                ucfirst($string)                 // 將首字字母轉成大寫。

                // 字串分割與合併
                explode(',', $string);
                implode(',', $array);
            
        

JSON

            
                // 將陣列轉換成 JSON 字串
                // 資料有中文 JSON_UNESCAPED_UNICODE
                // 資料有網址 JSON_UNESCAPED_SLASHES
                // 字串排版 JSON_PRETTY_PRINT
                json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

                // 將字串轉回陣列
                $array = json_decode($json, true);

                // 判斷 JSON decode 是否成功
                if ($array === null && json_last_error() !== JSON_ERROR_NONE) {
                    // JSON 格式錯誤
                }
            
        

檔案處理

寫入檔案

                
                    // 寫入檔案
                    file_put_contents(
                        'data.json',
                        json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
                    );

                    // 寫入檔案 (追加模式)
                    // FILE_APPEND 追加模式
                    // LOCK_EX 避免多程序同時寫入導致資料亂掉
                    file_put_contents(
                        'app.log',
                        "[" . date('Y-m-d H:i:s') . "] something happened\n",
                        FILE_APPEND | LOCK_EX
                    );

                    // 寫入 CSV 檔
                    $fp = fopen('data.csv', 'w');
                    fwrite($fp, chr(0xEF) . chr(0xBB) . chr(0xBF));   // UTF-8 BOM
                    fputcsv($fp, ['id', 'name', 'score']);
                    fputcsv($fp, [1, 'Mark', 95]);
                    fputcsv($fp, [2, 'Alex', 88]);
                    fclose($fp);
                
            

讀取檔案

                
                    // 讀取檔案
                    $content = file_get_contents('data.json');

                    // 逐行讀取 (方法一)
                    $lines = file('data.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
                    foreach ($lines as $line) {
                        echo $line;
                    }

                    // 逐行讀取 (方法二、適合大型檔案)
                    $fp = fopen('data.txt', 'r');
                    while (($line = fgets($fp)) !== false) {
                        echo $line;
                    }
                    fclose($fp);

                    // 讀取 CSV (有欄位名稱的陣列)
                    $fp = fopen('data.csv', 'r');
                    $header = fgetcsv($fp);         // 第一筆為欄位名稱
                    $data = [];
                    while (($row = fgetcsv($fp)) !== false) {
                        $data[] = array_combine($header, $row);
                    }
                    fclose($fp);
                
            

時間戳記轉換

基本方法

根據 Wikipedia 上面的說明,Unix 時間戳記就是從 1970 年 1 月 1 日 0 時 0 分 0 秒起至現在的總秒數,因此當您要在時間戳記加上一段時間的話,只要將要加入的時間長度轉成秒相加即可,或用 strtotime 轉換也可以,strtotime 的用法很神奇,官方說明是寫將任何英文的時間描述解析成 Unix 時間戳記,詳細說明請參考 PHP 官網說明。 另外,請注意 PHP 取得的時間是伺服器的時間,所以請確認伺服器的時間及時區是否正確。

                
                    // 時間描述轉戳記
                    time();                                    // 當前時間的時間戳記
                    strtotime('now');                          // 當前時間的時間戳記
                    strtotime('2020-10-28 12:50:21');          // 特定時間的時間戳記
                    strtotime('2020-10-28 14:00:00 +1 day');   // 時間戳記相加

                    // 時間戳記轉時間
                    date('Y-m-d H:i:s', time());                             // 將現在時間戳記轉成特定格式
                    date('Y-m-d H:i:s', strtotime('2020-10-28 12:50:21'));   // 將特定時間戳記轉成特定格式
                
            

現代方法

                
                    // 建立時間
                    $dt = new DateTime('now', new DateTimeZone('Asia/Taipei'));
                    $dt = new DateTime('2025-01-10 08:30:00', new DateTimeZone('Asia/Taipei'));

                    // 指定時間字串格式
                    $dt = DateTime::createFromFormat(
                        'Y-m-d H:i:s',
                        '2025-01-10 08:30:00',
                        new DateTimeZone('Asia/Taipei')
                    );

                    // 根據格式輸出
                    echo $dt->format('Y/m/d H:i:s');

                    // 時間相加 / 相減
                    $dt = $dt->add(new DateInterval('P1D'));
                    $dt = $dt->sub(new DateInterval('P1D'));

                    // 時間相減
                    $dt1 = new DateTimeImmutable(
                        '2025-01-01 08:30:00',
                        new DateTimeZone('Asia/Taipei')
                    );
                    $dt2 = new DateTimeImmutable(
                        '2025-01-03 11:45:20',
                        new DateTimeZone('Asia/Taipei')
                    );
                    $interval = $dt2->diff($dt1);
                    $interval->y;       // 年
                    $interval->m;       // 月
                    $interval->d;       // 天(排除月、年)
                    $interval->h;       // 小時
                    $interval->i;       // 分鐘
                    $interval->s;       // 秒
                    $interval->days;    // 總天數差
                    echo $interval->format('%a 天 %h 小時 %i 分 %s 秒');  // 格式化輸出 (%a 是 $interval->days)

                    // 時間比較
                    if ($dt1 < $dt2) {
                    }
                
            

備註 1: DateInterval 字串範例:

  • P1Y2M3DT4H5M6S: 1 年 2 個月 3 天 4 小時 5 分 6 秒
  • P3DT6S: 3 天 6 秒 (只要有時間就一定要加 T)
  • P3DT5M6S: 3 天 5 分 6 秒 (只要有時間就一定要加 T)

備註 2: DateTime 及 DateTimeImmutable 差異

DateTime 是可變物件(mutable),對時間的任何調整都會直接改變原本的物件本身;DateTimeImmutable 是不可變物件(immutable),每次加減時間都會回傳一個新的時間物件,原本的時間永遠不會改變。 兩者雖然為不同的物件型別,但在 PHP 中它們都實作了 DateTimeInterface,因此可以直接使用 if 進行大小比較,且比較的是實際時間點而非物件本身。

cURL 呼叫 API

GET 範例

                
                    // GET 參數
                    $params = [
                        'foo1' => 'bar1',
                        'foo2' => 'bar2',
                        'foo3' => 'bar3',
                    ];
                    $queryString = http_build_query($params);

                    // cURL
                    $curl = curl_init();
                    curl_setopt($curl, CURLOPT_URL, URL . '?' . $queryString);
                    curl_setopt($curl, CURLOPT_TIMEOUT, 60);
                    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
                    curl_setopt($curl, CURLOPT_HTTPHEADER, [
                        'Authorization: Bearer ' . $token,                  // 設定 TOKEN
                        'Accept: application/json',
                    ]);
                    $response = curl_exec($curl);
                
            

POST 範例

                
                    $curl = curl_init();
                    curl_setopt($curl, CURLOPT_URL, URL);
                    curl_setopt($curl, CURLOPT_POST, true);
                    curl_setopt($curl, CURLOPT_POSTFIELDS, [
                        // 一般變數
                        'foo1' => 'bar1',
                        'foo2' => 'bar2',
                        'foo3' => 'bar3',

                        // 單檔上傳
                        'file' => new CURLFile($inputPath, mime_content_type($inputPath), basename($inputPath)),
                    ]);
                    curl_setopt($curl, CURLOPT_TIMEOUT, 60);
                    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
                    curl_setopt($curl, CURLOPT_HTTPHEADER, [
                        'Authorization: Bearer ' . $token,                  // 設定 TOKEN
                        'Accept: application/json',
                    ]);
                    $response = curl_exec($curl);
                
            
cURL 多檔上傳,請參考另一篇文章說明:

處理 Response

                
                    // 執行 cURL
                    $response = curl_exec($curl);

                    // cURL 執行錯誤
                    if ($response === false) {
                        $errno  = curl_errno($curl);
                        $errmsg = curl_error($curl);
                        throw new Exception('cURL Error ' . $errno . ': ' . $errmsg);
                    }

                    // 檢查 HTTP Code
                    $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
                    if ($httpCode < 200 || $httpCode >= 300) {
                        throw new Exception("HTTP {$httpCode}: {$response}");
                    }

                    // 如果回傳值為 JSON 字串,將 JSON Decode
                    $data = json_decode($response, true);
                    if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
                        throw new Exception('JSON decode error: ' . json_last_error_msg());
                    }

                    // 最後關閉
                    curl_close($curl);
                
            

取得 Header

取得 Request Header

                
                    $curl = curl_init();
                    curl_setopt($curl, CURLOPT_URL, URL);
                    curl_setopt($curl, CURLINFO_HEADER_OUT, true);    // 需設定 CURLINFO_HEADER_OUT
                    $response = curl_exec($ch);
                    $requestHeader = curl_getinfo($ch, CURLINFO_HEADER_OUT);
                
            

取得 Response Header

                
                    $curl = curl_init();
                    curl_setopt($curl, CURLOPT_URL, URL);
                    curl_setopt($curl, CURLOPT_HEADER, true);    // 需設定 CURLOPT_HEADER
                    $response = curl_exec($curl);

                    // 回傳內容如下:
                    HTTP/1.1 200 OK
                    Content-Type: application/json
                    Content-Length: 123
                    X-Request-ID: abc123

                    {"result": "ok"}

                    // 拆解 header 及 body
                    $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
                    $header = substr($response, 0, $headerSize);
                    $body = substr($response, $headerSize);
                
            

常用的 curl_setopt()

名稱 說明
CURLOPT_VERBOSE Debug 模式
CURLOPT_URL 目標網址
CURLOPT_CONNECTTIMEOUT 連線階段最多等多久
CURLOPT_TIMEOUT 整趟請求(連線 + 傳輸)最多等多久
CURLOPT_HEADER 回傳 response header
CURLINFO_HEADER_OUT 實際送出的 request header
CURLOPT_HTTPHEADER 設定 HTTP Header
不要設定 Content-Type,讓 cURL 自己決定。
CURLOPT_RETURNTRANSFER 設為 true 返回字串,而不是直接輸出
CURLOPT_POST POST 模式
CURLOPT_POSTFIELDS POST 欄位資料
CURLOPT_CUSTOMREQUEST 請求方式 (PUT、DELETE、PATCH、OPTIONS)
建議不要用 POST 及 GET,雖然可以使用
CURLOPT_SSL_VERIFYPEER 預設開啟,驗證憑證是不是可信 CA 簽發
CURLOPT_SSL_VERIFYHOST 預設開啟,驗證憑證是否屬於你請求的網域

下載檔案

            
                $url = 'https://example.com/image.jpg';
                $savePath = __DIR__ . '/download/image.jpg';

                // 確保目錄存在
                if (!is_dir(dirname($savePath))) {
                    mkdir(dirname($savePath), 0755, true);
                }

                // cURL下載
                $fp = fopen($savePath, 'w');
                $ch = curl_init($url);
                curl_setopt($ch, CURLOPT_FILE, $fp);
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
                curl_setopt($ch, CURLOPT_TIMEOUT, 60);
                curl_exec($ch);
                if (curl_errno($ch)) {
                    throw new Exception(curl_error($ch));
                }

                // 關閉
                curl_close($ch);
                fclose($fp);