PHP + MySQL 是經典組合,但許多開發者僅停留在基礎 CRUD。本文提煉 8 個提升代碼質量、性能、安全性、可維護性的核心技巧,助你從「能跑」邁向「專業」。
數據庫結果集天然適合關聯數組,foreach 比 for 更優雅:
$users = [
['id'=>1, 'name'=>'Alice', 'role'=>'admin'],
['id'=>2, 'name'=>'Bob', 'role'=>'user'],
];
foreach ($users as $user) {
echo "{$user['name']} ({$user['role']})
";
}
// 帶鍵名遍歷
$user = ['id'=>1, 'name'=>'Alice', 'role'=>'admin'];
foreach ($user as $key => $val) {
echo "$key: $val
";
}
PHP 提供 50+ 數組函數 (array_map, array_filter, array_column, array_key_exists 等),善用它們替代手寫循環。
echo "Hello $name";echo "User: {$user['name']}, ID: {$user['id']}";echo "Name: {$obj->name}"; (方法需先賦值給變量)。$arr = ['one'=>1, 'two'=>2];
// 錯誤:echo "Value: $arr[one]"; // Notice: Undefined constant one
// 正確:
echo "Value: {$arr['one']}"; // 推薦
echo "Value: {$arr[\"one\"]}"; // 雙引號內雙引號需轉義
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'u', 'p');
$stmt = $pdo->query('SELECT cust_id, surname, firstname FROM customer');
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo "ID: {$row['cust_id']}
";
echo "Surname: {$row['surname']}
";
echo "Firstname: {$row['firstname']}
";
}
多表連查別名:
SELECT w.`name` AS wname, r.`name` AS rname
FROM winery w JOIN region r ON w.region_id = r.region_id;
// 存取:$row['wname'], $row['rname']
聚合函數別名:
SELECT COUNT(*) AS total FROM customer;
// 存取:$row['total'] (而非 $row['COUNT(*)'])
for ($counter=0; $counter<10; $Counter++) { // $Counter != $counter
myFunction();
}
瀏覽器容錯機制不同,缺少 </table>、</div> 可能導致渲染崩潰。解決: 查看頁面源碼、使用 W3C 驗證器、模板引擎自動閉合。
error_reporting(E_ALL);
ini_set('display_errors', '1'); // 開發環境
// 生產環境:display_errors=0, log_errors=1, error_log=/var/log/php_errors.log
用戶點擊鏈接執行動作 (刪除、狀態變更) 後停留在原頁:
<?php // action.php
// 執行數據庫操作
$pdo->exec("DELETE FROM items WHERE id={$_POST['id']}");
// 重定向回來源頁
header("Location: " . $_SERVER['HTTP_REFERER']);
exit; // 關鍵:阻止後續代碼輸出干擾 Header
?>
兩大陷阱:
exit:後續輸出導致 headers already sent 警告。<?php 開頭無前綴空白,關閉標籤 ?> 可省略。問題: 表單提交後刷新頁面,瀏覽器重發 POST,導致重複入庫。
模式:Post/Redirect/Get (PRG)
<?php // addcust.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo->prepare('INSERT INTO customer (surname, firstname) VALUES (?,?)')
->execute([$_POST['surname'], $_POST['firstname']]);
header('Location: cust_receipt.php');
exit;
}
?>
<form method="post">...</form>
// cust_receipt.php 只顯示成功信息,刷新無副作用
緊急報表需獨佔表讀寫,使用表鎖:
$pdo->exec('LOCK TABLES reports WRITE');
// 執行複雜報表查詢
$rows = $pdo->query('SELECT ... FROM reports ...')->fetchAll();
$pdo->exec('UNLOCK TABLES');
注意: 鎖表阻塞所有寫入、部分讀取,僅用於極短時間、低併發、維護窗口。高併發場景改用行鎖 (InnoDB SELECT ... FOR UPDATE)、優化索引、讀寫分離、緩存。
傳統 mysql_query() (已棄用) / mysqli_query() / PDO::query() 默認緩衝整個結果集到內存,大數據集導致 OOM。
非緩衝查詢 (mysqli) :
$mysqli = new mysqli('localhost', 'u', 'p', 'db');
$mysqli->query('SET SESSION max_execution_time=300'); // 長查詢
$result = $mysqli->query('SELECT * FROM huge_table', MYSQLI_USE_RESULT); // 非緩衝
while ($row = $result->fetch_assoc()) {
// 流式處理,內存常數級
process($row);
}
$result->free();
PDO 非緩衝:
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$stmt = $pdo->query('SELECT * FROM huge_table');
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
process($row);
}
限制: 非緩衝查詢期間不能執行其他查詢;無法使用 num_rows (結果集大小未知);需快速讀取完畢釋放連接。
這 8 個技巧貫穿 數據結構選擇、字符串處理、查詢模式、錯誤預防、Web 交互模式、併發控制、大數據處理 全鏈路。內化為肌肉記憶,配合現代工具 (PDO、Prepared Statements、PHPStan、PHPUnit、Composer),即能編寫出安全、高效、可維護的 PHP+MySQL 應用。記住:最佳實踐不是束縛,而是踩過坑後留下的捷徑。