實戰整理:我常用的 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);