PHP 解釋執行特性決定了程式碼層面的優化空間巨大。本文整理 15 個實戰驗證的優化技巧,涵蓋變數處理、迴圈判斷、資料庫查詢、輸出緩衝、檔案操作等核心場景。
return 優於 global<?php
// ✅ 推薦:return 回傳,區域變數,無副作用
function parseUser($str) {
return explode('|', $str);
}
$user = parseUser($raw);
// ❌ 不推薦:global 污染全域命名空間,難除錯
function parseUserGlobal($str) {
global $detail;
$detail = explode('|', $str);
}
parseUserGlobal($raw);
$user = $detail;
?>
原因: global 破壞封裝,增加耦合,JIT 編譯器難以優化。
REQUEST_URI 優於 HTTP_REFERER<?php
// ✅ 穩定、快速、不依賴客戶端 Header
$parts = explode('/', $_SERVER['REQUEST_URI']);
array_pop($parts);
$baseUrl = 'https://' . $_SERVER['HTTP_HOST'] . implode('/', $parts);
// ❌ HTTP_REFERER 可被偽造、瀏覽器可能不送、HTTPS→HTTP 會丟失
$refParts = explode('/', $_SERVER['HTTP_REFERER']);
array_pop($refParts);
$baseUrl = implode('/', $refParts);
?>
=== 優於 ==<?php
$a = 2; $b = '2';
// ❌ 型別轉換開銷
if ($a == $b) { ... }
// ✅ 直接比較型別與值,效能略高且邏輯更嚴謹
if ($a === $b) { ... } // false
if ((int)$a === (int)$b) { ... } // true,明確轉型
?>
註:現代 PHP (7.4+) 差異微小,但 === 避免隱性轉型 Bug,建議作為預設習慣。
WHERE IN 替代深分頁 LIMIT offset, n<?php
// ❌ 深分頁效能指數級下降
// SELECT * FROM logs ORDER BY id DESC LIMIT 1000000, 20;
// ✅ 使用 ID 範圍查詢(需連續或近似連續 ID)
$lastId = 1000000; // 上一頁最大 ID
$stmt = $pdo->prepare("SELECT * FROM logs WHERE id < ? ORDER BY id DESC LIMIT 20");
$stmt->execute([$lastId]);
// ✅ 或使用 WHERE IN 查詢已知 ID 列表(如從 Redis 取得熱門 ID)
$ids = [1001, 1005, 1010, 1020];
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("SELECT * FROM logs WHERE id IN ($placeholders)");
$stmt->execute($ids);
?>
原理: LIMIT offset, n 需掃描前 offset 行;WHERE id < last_id 可直接利用索引定位。
PHP 進程模型、檔案系統、記憶體管理在 Linux 下更穩定高效。生產環境**強制使用 Linux**。
ob_start() 加速輸出,ob_gzhandler 更強<?php
// ✅ 開啟輸出緩衝,減少網路封包碎片
ob_start();
// ✅ 進階:啟用 GZIP 壓縮傳輸(需 zlib 擴展)
ob_start('ob_gzhandler');
echo "大量 HTML 內容...";
// 腳本結束自動 flush
?>
效果: 減少 TCP 封包數量,降低頻寬,提升首字節時間 (TTFB)。
if ($var) 與 if (empty($var)) 最快<?php
// ✅ 直接判斷真假值,內部優化最好
if ($online) { ... }
if (empty($data)) { ... }
// ❌ 顯式比較較慢
if ($online == true) { ... }
if ($data == '') { ... }
if ($data === '') { ... }
?>
!= 與 <> 效能相當選擇團隊風格統一即可,推薦 != 可讀性較佳。
'...' 與雙引號 "..." 現代 PHP 差異極小舊版本單引號不解析變數略快,PHP 7+ 優化後差異可忽略。**統一風格**較重要(推薦單引號,雙引號僅用於需插值處)。
SELECT, FROM, WHERESELECT *,明確列出欄位EXPLAIN 分析執行計畫setcookie() 即時生效技巧<?php
// ✅ 先賦值給變數,再 setcookie,下一行即可讀取 $_COOKIE
if ($online) {
$online1 = $online;
setcookie('online1', $online, $expire, $path, $domain, $secure);
// 當前請求即可用 $_COOKIE['online1']
}
// ❌ 直接 setcookie,需下一次請求才能讀取
if ($online) {
setcookie('online1', $online, $expire, $path, $domain, $secure);
}
?>
原理: PHP 會在 setcookie 後更新內部 $_COOKIE 超全域陣列(僅限賦值後的變數名)。
fopen + flock + fread 優於 file()<?php
// ✅ 穩定、可控、支援鎖定、大檔案不佔記憶體
function readFileSafe($filename) {
$handle = @fopen($filename, 'rb');
if (!$handle) return false;
@flock($handle, LOCK_SH);
$data = @fread($handle, filesize($filename));
@fclose($handle);
return $data;
}
// ❌ file() 將整檔讀入陣列,大檔案記憶體爆炸,無鎖定機制
$lines = file($filename);
?>
<?php
function substr_utf8($str, $len, $suffix = '...') {
if (mb_strlen($str, 'UTF-8') <= $len) return $str;
return mb_substr($str, 0, $len, 'UTF-8') . $suffix;
}
// 使用範例
$title = substr_utf8($article['title'], 30);
?>
原版代碼用 ord() 判斷中文雙位元組,僅適用 GBK/Big5,**UTF-8 環境請改用 mb_substr**。
ctype_upper() / 正則優於手動 ASCII 迴圈<?php
// ✅ 內建函式,C 層實作,極快
if (preg_match('/[A-Z]/', $username)) { /* 含大寫 */ }
// ✅ 或 ctype 函式(僅 ASCII)
if (ctype_upper($username)) { /* 全大寫 */ }
// ❌ 手動迴圈檢查 ASCII 碼,慢且易錯
for ($i = 65; $i <= 90; $i++) { ... }
?>
<?php
function readLines($filename) {
$handle = fopen($filename, 'r');
while (($line = fgets($handle)) !== false) {
yield rtrim($line);
}
fclose($handle);
}
// 使用:逐行處理,記憶體恆定
foreach (readLines('huge.log') as $line) {
process($line);
}
?>
**黃金法則:先測量(Profiling),再優化。** 使用 Xdebug、Blackfire、Tideways 找出真正瓶頸,針對性修正,避免過早優化。