PHP 開發中有許多「看似相同、實則大不同」的概念,搞混它們會導致邏輯錯誤、性能損耗、安全漏洞。本文系統梳理 9 對高頻易混淆點,給出語法規則、底層差異、最佳實踐與避坑案例,助你寫出更精準、健壯的代碼。
| 特性 | echo | |
|---|---|---|
| 類型 | 語言結構 | 語言結構 (表現為函數) |
| 返回值 | 無 (void) | 總是 1 (int) |
| 參數 | 多參數 echo $a, $b (無逗號則串聯) | 單參數 print $a |
| 表達式使用 | 不可 $x = echo 1 ❌ | 可 $x = print "hi"; // $x=1 ✅ |
| 性能 | 略快 (無返回值開銷) | 略慢 |
最佳實踐: 純輸出用 echo (或 = $var ?> 模板標籤);需在表達式中輸出並用返回值時用 print。
| 特性 | include | require |
|---|---|---|
| 載入時機 | 運行期 (執行到該行) | 編譯期 (腳本啟動前) |
| 失敗後果 | E_WARNING,腳本繼續 | E_COMPILE_ERROR,腳本終止 |
| 條件載入 | 支援 if ($cond) include 'a.php'; | 不支援 (編譯期已載入),條件判斷無效 |
| _once 變體 | 防重複載入,維護已載入表 | 同理 |
避坑: require 在循環/條件中**不會**動態載入不同文件,需用 include。現代框架均用 Composer 自動加載,**極少直接用 include/require**。
<?php
$vars = [
'empty_str' => '',
'null' => null,
'zero_int' => 0,
'zero_str' => '0',
'false' => false,
'empty_arr' => [],
];
foreach ($vars as $name => $val) {
echo "$name: ";
echo "empty=" . (empty($val) ? 'T' : 'F');
echo " isset=" . (isset($val) ? 'T' : 'F');
echo " bool=" . ((bool)$val ? 'T' : 'F');
echo " ==null=" . ($val == null ? 'T' : 'F');
echo " ===null=" . ($val === null ? 'T' : 'F');
echo "\n";
}
/* 輸出關鍵差異:
empty_str : empty=T isset=T bool=F ==null=T ===null=F
null : empty=T isset=F bool=F ==null=T ===null=T
zero_int : empty=T isset=T bool=F ==null=T ===null=F
zero_str : empty=T isset=T bool=F ==null=T ===null=F ⚠️ '0' 視為空!
false : empty=T isset=T bool=F ==null=T ===null=F
empty_arr : empty=T isset=T bool=F ==null=T ===null=F
*/
鐵律:
empty():""、0、"0"、null、false、[] 全為 true。**業務主鍵 ID 為 0 時慎用**。isset():僅檢查變量**存在且不為 null**。isset($arr['key']) 判斷鍵存在。首選判斷變量/數組鍵是否設置。=== null:精確判斷 null。(bool)$val:邏輯判斷轉換。<?php
var_dump('' == null); // true ⚠️
var_dump('' === null); // false ✅
var_dump(0 == 'abc'); // true ⚠️ 'abc' 轉數字為 0
var_dump(0 === 'abc'); // false ✅
var_dump(false == '0'); // true ⚠️
var_dump(false === '0'); // false ✅
規則: == 觸發類型轉換 (轉數字或布爾),=== 同時比較類型與值。**嚴格模式下 (declare(strict_types=1)) 僅影響函數參數/返回類型,不影響 ==/=== 行為**。
最佳實踐: **默認使用 ===**,除非明確需要類型轉換語義 (如 $_GET['id'] == 0 兼容字符串 "0")。
<?php
class Demo {
const CONST_VAL = 'C';
public static $staticProp = 'S';
public $instanceProp = 'I';
public static function staticMethod() {
echo self::CONST_VAL; // ✅ 訪問常量
echo self::$staticProp; // ✅ 訪問靜態屬性
// echo $this->instanceProp; // ❌ 靜態方法無 $this
}
public function instanceMethod() {
echo self::CONST_VAL; // ✅
echo self::$staticProp; // ✅
echo $this->instanceProp; // ✅ 訪問實例屬性
// echo $this::CONST_VAL; // ❌ 語法錯誤
}
}
規則:
self:: (或 ClassName::) 訪問:**常量、靜態屬性、靜態方法**。$this-> 訪問:**實例屬性、實例方法**。static:: 解析為調用類,self:: 解析為定義類。static::method() 實現多態靜態調用。| 函數 | 返回值 | 大小寫 | 性能 | 用途 |
|---|---|---|---|---|
strstr | 針頭位置到末尾的字符串 | 敏感 | 較慢 (分配新字符串) | 需要子串內容 |
stristr | 同上 | 不敏感 | 較慢 | 同前 |
strpos | 整數位置 (0-based),未找到 false | 敏感 | 最快 (僅返回偏移) | 僅判斷是否包含 |
stripos | 同上 | 不敏感 | 快 | 同前 |
避坑: if (strpos($hay, $needle)) **錯誤**!位置 0 會被當作 false。必須 if (strpos($hay, $needle) !== false) 或 if (str_contains($hay, $needle)) (PHP 8+)。
| 場景 | HTTP_HOST | SERVER_NAME |
|---|---|---|
| 來源 | 請求 Header Host (客戶端可控) | Web 服務器配置 ServerName (服務端可控) |
| 含端口 | 是 (非標準端口) | 否 |
| 反向代理 | 可能為內網 IP/端口 | 通常為公網域名 |
| 可信度 | 低 (可偽造) | 高 |
| 推薦用法 | 生成給用戶看的鏈接、CORS Origin | 內部邏輯判斷、安全驗證、生成絕對 URL |
最佳實踐: 關鍵業務 (支付回調域名驗證、生成分享鏈接) 用 SERVER_NAME 或配置常量 APP_DOMAIN;HTTP_HOST 僅用於顯示/非安全場景。端口映射/內網穿透環境優先 HTTP_X_FORWARDED_HOST。
<?php
$a = ['a' => 1, 'b' => 2];
$b = ['b' => 3, 'c' => 4];
var_dump(array_merge($a, $b)); // ['a'=>1, 'b'=>3, 'c'=>4] 數字鍵重新索引
var_dump($a + $b); // ['a'=>1, 'b'=>2, 'c'=>4] 字符串鍵後者不覆蓋,數字鍵同理
規則: array_merge:字符串鍵後覆蓋前,數字鍵**重新索引從 0 開始**。+:鍵衝突時**保留第一個**,不重新索引。
| 特性 | define() | const |
|---|---|---|
| 作用域 | 全局 (命名空間下) | 類常量/命名空間常量 (編譯期) |
| 值類型 | 任意表達式 (函數調用、變量) | 編譯期常量表達式 (標量、數組 PHP 7+、常量) |
| 大小寫敏感 | 可選 define('FOO', 1, true) 不敏感 | 嚴格敏感 |
| 性能 | 運行期解析 | 編譯期解析,更快 |
最佳實踐: 類常量用 const;全局配置常量用 const (命名空間下);動態值 (如版本號從文件讀取) 用 define()。
| 場景 | 推薦選擇 | 核心理由 |
|---|---|---|
| 輸出變量 | echo / = ?> | 無返回值開銷、多參數支援 |
| 載入文件 | Composer Autoload | 標準化、性能、避免 include/require 坑 |
| 判斷變量存在 | isset() | 不視 0/false/'' 為未設置 |
| 判斷空值 | === null / !== null | 精確、無類型轉換 |
| 比較值 | === | 類型安全、可預測 |
| 訪問類常量/靜態 | self:: / static:: (LSB) | 語法規定 |
| 判斷包含子串 | strpos !== false / str_contains (PHP 8+) | 性能最好、語義清晰 |
| 獲取可信域名 | SERVER_NAME / 常量 APP_DOMAIN | 不可偽造 |
| 合併配置數組 | array_merge (字符串鍵覆蓋) 或 + (保留首個) | 按語義選擇 |
這 9 對易混淆點覆蓋了 PHP 開發 80% 的「隱性 Bug 來源」。建議將速查卡打印貼在工位,Code Review 時重點檢查。隨著 PHP 8.1/8.2/8.3 新特性 (str_contains, array_is_list, never 返回類型, readonly 屬性) 推出,部分舊坑被語言層面修復,**保持版本升級、啟用嚴格模式、使用靜態分析 (PHPStan/Psalm)** 是從根本上規避此類問題的長效機制。