PHP 效能優化涵蓋語法層面、函式選擇、記憶體管理、快取策略等多個維度。本文整理 40 條實戰驗證的高效能寫法,附上原理說明與效能比較。
<?php
// ✅ 單引號:不解析變數,較快
echo 'Hello ' . $name;
// ❌ 雙引號:需解析變數
echo "Hello $name";
// ✅ echo 多參數(無連接開銷)
echo 'Hello ', $name, '!';
<?php
class Math {
// ✅ 靜態方法:無需實例化,呼叫快 ~4 倍
public static function add(int $a, int $b): int {
return $a + $b;
}
// ❌ 實例方法:需建立物件
public function addInstance(int $a, int $b): int {
return $a + $b;
}
}
Math::add(1, 2); // 快
// (new Math())->addInstance(1, 2); // 慢
<?php
// ✅ $row['id'] 比 $row[id] 快 7 倍
// $row[id] 會先嘗試尋找常數 id,再當作字串
$id = $row['id'];
<?php
// ✅ 快
echo $str1, $str2, $str3;
// ❌ 慢:字串連接建立暫時變數
echo $str1 . $str2 . $str3;
// ❌ print 回傳值,有微小開銷
print $str;
<?php
// ✅ foreach 專為陣列優化
foreach ($arr as $item) { ... }
// ✅ for 循環預計算 count
$count = count($arr);
for ($i = 0; $i < $count; $i++) { ... }
// ❌ 每次迴圈都呼叫 count()
for ($i = 0; $i < count($arr); $i++) { ... }
<?php
$largeData = fetchHugeDataset();
// 處理完後
unset($largeData);
// 或設為 null
$largeData = null;
// 觸發 GC(PHP 7+ 通常自動處理)
gc_collect_cycles();
<?php
// ⚠️ 魔術方法有額外開銷,非必要勿用
class Heavy {
public function __get($name) { ... } // 讀取不存在屬性
public function __set($name, $value) { ... } // 寫入不存在屬性
public function __call($name, $args) { ... } // 呼叫不存在方法
public function __autoload() { ... } // 已棄用
}
// ✅ 明確定義屬性與方法,避免觸發魔術方法
<?php
// ❌ 每次都檢查已載入清單
require_once 'file.php';
// ✅ Composer PSR-4 自動載入,僅載入需要的類別
require_once __DIR__ . '/vendor/autoload.php';
<?php
// ✅ 避免搜尋 include_path
include __DIR__ . '/config.php';
include '/var/www/shared/lib.php';
// ❌ 相對路徑需搜尋 include_path
include 'config.php';
<?php
// ✅ 精確、無函式呼叫開銷
$startTime = $_SERVER['REQUEST_TIME'];
// 或高精度
$startTimeFloat = $_SERVER['REQUEST_TIME_FLOAT'];
// ❌ time() 有函式呼叫開銷
$startTime = time();
<?php
// ✅ 最快:strtr 單字元對應
$str = strtr($str, ['&' => '&', '<' => '<', '>' => '>']);
// ✅ 次快:str_replace 陣列批次替換
$str = str_replace(['&', '<', '>'], ['&', '<', '>'], $str);
// ❌ 最慢:正則表達式
$str = preg_replace('/[&<>]/', fn($m) => ['&'=>'&','<'=>'<','>'=>'>'][$m[0]], $str);
<?php
// 當替換對象較少時,多次單一替換可能比陣列替換快
$str = str_replace('a', 'b', $str);
$str = str_replace('c', 'd', $str);
// vs
$str = str_replace(['a','c'], ['b','d'], $str);
<?php
// ✅ switch 使用跳躍表,O(1) 查找
switch ($action) {
case 'create': ... break;
case 'read': ... break;
case 'update': ... break;
case 'delete': ... break;
}
// ❌ 多重 if-else 線性查找 O(n)
if ($action === 'create') ...
elseif ($action === 'read') ...
<?php
// ❌ 極慢:@ 會修改錯誤報告層級、產生例外處理開銷
$file = @file_get_contents($path);
// ✅ 正確錯誤處理
if (file_exists($path)) {
$file = file_get_contents($path);
} else {
// 處理不存在
}
// ✅ 或使用 try-catch (PHP 8+ 許多函式拋出例外)
try {
$file = file_get_contents($path);
} catch (Throwable $e) {
// 處理
}
# .htaccess 或 httpd.conf
AddOutputFilterByType DEFLATE text/html text/css application/json
AddOutputFilterByType DEFLATE application/javascript text/xml application/xml
<?php
// ✅ 短連線:請求結束自動關閉
$pdo = new PDO($dsn, $user, $pass);
// 執行查詢...
// 腳本結束自動斷開
// ⚠️ 長連線需小心管理
$pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_PERSISTENT => true]);
<?php
// 避免在迴圈中產生錯誤
for ($i = 0; $i < 10000; $i++) {
// ❌ 觸發 Notice 會大幅降速
echo $undefinedVar;
// ✅ 正確檢查
echo $definedVar ?? '';
}
<?php
// 基準:局部變數遞增
$local = 0;
$local++; // 最快 (基準)
// 全域變數:慢 2 倍
$GLOBALS['g'] = 0;
$GLOBALS['g']++;
// 物件屬性:慢 3 倍
$obj->prop = 0;
$obj->prop++;
// 未定義局部變數:慢 9-10 倍
$undefined++; // 先初始化為 0 再遞增
// 定義但未使用的局部變數:同遞增局部變數開銷
function test() {
$unused = 1; // 定義未用
$used = 1;
$used++;
}
<?php
$i = 0;
++$i; // ✅ 3 opcodes,直接遞增
$i++; // ❌ 4 opcodes,建立暫存變數
<?php
// ✅ 靜態內容用 .html 或快取輸出
// ❌ 簡單頁面也用 PHP 處理
; php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.revalidate_freq=60 ; 生產環境 60s 或 0 (需重啟)
opcache.validate_timestamps=1
opcache.save_comments=1
<?php
// Redis 範例
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$cacheKey = 'user:123:profile';
$data = $redis->get($cacheKey);
if ($data === false) {
$data = fetchUserProfileFromDb(123);
$redis->setex($cacheKey, 3600, json_encode($data));
}
$user = json_decode($data, true);
<?php
$str = 'hello';
// ❌ strlen() 函式呼叫開銷
if (strlen($str) < 5) { ... }
// ✅ isset() 語言結構,無函式呼叫
// 檢查第 5 個字元 (索引 4) 是否存在
if (!isset($str[4])) { ... } // 長度 < 5
<?php
// ❌ 每次迴圈呼叫 count()
for ($i = 0; $i < count($arr); $i++) { ... }
// ✅ 預先計算
$count = count($arr);
for ($i = 0; $i < $count; $i++) { ... }
測試證實:類別定義 10 或 100 個方法,呼叫特定方法速度無差異。
繼承鏈越短、方法越接近呼叫處,解析越快。
減少不必要的函式/方法封裝層級。
重申第 1 點,確保不需變數插值時用單引號。
<?php
// ❌ 過度封裝簡單邏輯
class UserHelper {
public static function getName($user) { return $user['name']; }
}
// ✅ 簡單情況直接存取
$name = $user['name'];
<?php
// ✅ 純資料傳輸物件 (DTO) 用陣列或 readonly class (PHP 8.1+)
readonly class UserDto {
public function __construct(
public int $id,
public string $name,
public string $email
) {}
}
// 大量資料陣列記憶體效率優於物件陣列
YAGNI 原則:只在真正需要重用時才抽出方法。
過早優化是萬惡之源。先寫正確、可讀、可測試的程式碼。
<?php
// ✅ 內建函式通常用 C 實作,極快
$max = max($arr);
$sum = array_sum($arr);
$filtered = array_filter($arr, fn($x) => $x > 0);
$unique = array_unique($arr);
// ❌ 手寫迴圈重複造輪子
; php.ini
opcache.jit_buffer_size=100M
opcache.jit=tracing
; 適用於 CPU 密集型計算(數學、圖像處理)
; I/O 密集型 (Web 請求) 效果有限
| 優先級 | 項目 | 影響 | 難度 |
|---|---|---|---|
| 🔴 關鍵 | OPcache 啟用 | ⚡⚡⚡⚡⚡ | 低 |
| 🔴 關鍵 | 資料庫索引/查詢優化 | ⚡⚡⚡⚡⚡ | 中 |
| 🔴 關鍵 | 快取策略 (Redis/資料庫查詢快取) | ⚡⚡⚡⚡ | 中 |
| 🟠 高 | Composer 自動載入優化 (--classmap-authoritative) | ⚡⚡⚡ | 低 |
| 🟠 高 | 靜態資源 CDN + 壓縮 | ⚡⚡⚡ | 低 |
| 🟡 中 | 語法微優化 (單引號、foreach、isset 技巧) | ⚡⚡ | 低 |
| 🟢 低 | 遞增/遞減運算子選擇 | ⚡ | 低 |