良好的代碼規範能降低認知負荷、減少 Bug、提升團隊協作效率、便於重構與維護。本文梳理 11 項 PHP 開發核心規範,結合現代工具鏈 (PHP_CodeSniffer, PHP-CS-Fixer, Psalm, PHPStan, Rector) 落地執行,助你寫出專業級代碼。
// 開發環境:顯示所有錯誤,嚴格模式
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php/error.log');
// 生產環境:不顯示,只記錄,關閉嚴格標準提示
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
現代最佳實踐: 使用 php -d error_reporting=E_ALL 啟動內建伺服器;框架 (Laravel/Symfony) 已內建優異的異常處理與日誌系統,遵循框架配置即可。
WET (Write Everything Twice) 是技術債的溫床。
// ❌ WET:硬編碼散落各處
$pdo = new PDO('mysql:host=localhost;dbname=shop', 'root', 'secret');
$pdo2 = new PDO('mysql:host=localhost;dbname=shop', 'root', 'secret');
// ✅ DRY:配置集中、依賴注入、單例/容器管理
// config/database.php
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
'host' => env('DB_HOST', '127.0.0.1'),
'database' => env('DB_DATABASE', 'shop'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
],
],
];
// 使用 Laravel 服務容器自動解析
$users = DB::table('users')->get();
declare(strict_types=1); 頂行。else/elseif/catch/finally 與大括號同行。true/false/null 小寫;類型聲明、返回類型緊貼冒號 function foo(): int。工具強制統一:
# 安裝 composer require --dev squizlabs/php_codesniffer friendsofphp/php-cs-fixer # 檢查 (PSR-12) ./vendor/bin/phpcs --standard=PSR12 src/ # 自動修復 ./vendor/bin/php-cs-fixer fix src/ --rules=@PSR12
| 元素 | 風格 | 範例 | 備註 |
|---|---|---|---|
| 類/介面/Trait/Enum | StudlyCaps (大駝峰) | UserService, PaymentInterface | 名詞或名詞短語 |
| 方法/函數/變數/屬性 | camelCase (小駝峰) | getUserById(), $orderTotal | 動詞開頭;布林建議 is/has/can/should 前綴 |
| 常數 | SCREAMING_SNAKE_CASE | MAX_RETRY_COUNT, DEFAULT_LOCALE | 類常數用 const,全域常數極少用 define |
| 私有/受保護屬性 | camelCase + 下劃線前綴 | private $connection (PSR-12 不強制前綴,團隊自行約定) | PSR-12 無強制,但許多團隊採用 _ 區分可見性 |
| 資料庫表/欄位 | snake_case | user_profiles, created_at | 避免保留字,複數表名 |
守衛子句 早期返回 是消除深層嵌套的核心技巧:
// ❌ 箭頭代碼:難讀、難測、難維護
function processOrder(Order $order): bool {
if ($order->isValid()) {
if ($order->hasStock()) {
if ($payment->charge($order->total)) {
if ($inventory->reserve($order->items)) {
$order->confirm();
return true;
}
}
}
}
return false;
}
// ✅ 守衛子句:線性閱讀、單一職責
function processOrder(Order $order): bool {
if (!$order->isValid()) return false;
if (!$order->hasStock()) return false;
if (!$payment->charge($order->total)) return false;
if (!$inventory->reserve($order->items)) return false;
$order->confirm();
return true;
}
/**
* 根據用戶 ID 獲取訂單列表
*
* @param int $userId 用戶 ID
* @param int $limit 最大筆數
* @param int $offset 偏移量
* @return Order[] 訂單集合
* @throws RepositoryException 查詢失敗
*/
public function getOrdersByUserId(int $userId, int $limit = 20, int $offset = 0): array
{
// PHP 8.0+ 可直接用返回類型 array (需 phpstan/phpdoc 擴展)
return $this->orderRepository->findByUser($userId, $limit, $offset);
}
工具: phpstan 靜態分析檢查 PHPDoc 與代碼一致性;rector 自動補全/升級類型聲明。
.env 只存在於本地/部署伺服器,.env.example 提交作為模板。phpinfo()、var_dump($GLOBALS) 等調試代碼嚴禁 進入生產代碼庫。// 1. 輸入驗證 (表單請求 / DTO)
class StoreUserRequest extends FormRequest {
public function rules(): array {
return [
'email' => 'required|email|unique:users,email',
'age' => 'required|integer|between:18,120',
'bio' => 'nullable|string|max:500',
];
}
}
// 2. 業務層再次防禦 (值物件/實體建構子驗證)
class Email {
public function __construct(private string $value) {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email: $value");
}
}
}
// 3. 輸出語境編碼 (Blade/Twig 自動逃逸、API 輸出 JSON)
{{ $user->bio }} {{-- Blade 自動 htmlentities --}}
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
| 層級 | 技術 | 場景 | TTL 建議 |
|---|---|---|---|
| HTTP 快取 | ETag, Last-Modified, Cache-Control | 靜態資源、公開 API | 長 (天/月) |
| 應用層快取 | Redis (Predis/phpredis), Memcached | 熱點數據、計算結果、Session | 分鐘~小時 |
| Opcode 快取 | OPcache (PHP 內建) | 所有生產環境必開 | 永久 (重啟/部署清除) |
| 查詢快取 | MySQL Query Cache (已棄用) / ProxySQL | 極少用,依賴應用層 | - |
關鍵: 快取鍵設計包含版本/租戶/語言;寫入時失效 (Cache Aside 模式);監控 hit_rate 低於 80% 需優化。
// ❌ 無意義複製
$desc = strip_tags($_POST['description']);
echo $desc;
// ✅ 直接使用、解構賦值 (PHP 7.1+)
echo strip_tags($_POST['description'] ?? '');
// ✅ 批量解構
['email' => $email, 'name' => $name] = $validatedData;
// 或物件屬性提升 (PHP 8.0+)
public function __construct(
private string $email,
private string $name,
) {}
架構原則: 領域驅動設計 (DDD) 分層 —— Domain (核心業務) → Application (用例/命令/查詢) → Infrastructure (資料庫/外部 API/框架) → Interface (HTTP/CLI/Queue)。依賴反轉 讓核心業務不依賴框架。
{
"scripts": {
"cs-check": "phpcs --standard=PSR12 src/",
"cs-fix": "php-cs-fixer fix src/ --rules=@PSR12",
"stan": "phpstan analyse src/ --level=max",
"psalm": "psalm --show-info=false",
"test": "phpunit",
"ci": [
"@cs-check",
"@stan",
"@psalm",
"@test"
]
},
"require-dev": {
"squizlabs/php_codesniffer": "^3.7",
"friendsofphp/php-cs-fixer": "^3.15",
"phpstan/phpstan": "^1.10",
"vimeo/psalm": "^5.15",
"phpunit/phpunit": "^10.0"
}
}
執行 composer ci 即可在 CI/CD 流水線中強制代碼品質門檻。
規範不是目的,可維護性、可擴展性、團隊協作效率 才是。從今天開始:
PHP_CodeSniffer + PHP-CS-Fixer 統一風格。declare(strict_types=1); + PHPStan Level 5+ 靜態分析。好代碼像好文章,讓人一眼看懂意圖,十年後依然優雅。