前言
字串處理是 PHP 最高頻操作。本文精選核心函數:編碼轉換、查找定位、截取擷取、替換修改、正則表達式、序列化儲存,附參數詳解、效能比較與實戰陷阱。
一、編碼與轉義
二進位/十六進位互轉
bin2hex($str) // 二進位字串 → 十六進位字串
hex2bin($hex) // 十六進位 → 二進位 (PHP 5.4+)
// 範例
$binary = "Hello";
$hex = bin2hex($binary); // "48656c6c6f"
$back = hex2bin($hex); // "Hello"
// 實戰:安全儲存二進位資料 (如圖片、加密輸出) 到資料庫文字欄位
字串修剪
| 函數 | 功能 | 範例 |
| trim($str, $charlist) | 兩端去除空白/指定字元 | trim(" abc ") → "abc" |
| ltrim($str, $charlist) | 左端去除 | ltrim("/path/", "/") → "path/" |
| rtrim($str, $charlist) | 右端去除 | rtrim("path/", "/") → "path" |
| chop($str, $charlist) | rtrim 別名 | |
大小寫轉換 (單位元組)
strtolower($str) // 全小寫
strtoupper($str) // 全大寫
ucfirst($str) // 首字大寫
ucwords($str) // 每單字首字大寫
// 注意:中文等多位元組字元請用 mb_strtolower 等 (需 mbstring)
字元轉換:strtr
// 單字元對應轉換 (效能最好)
$trans = ['ä'=>'ae', 'ö'=>'oe', 'ü'=>'ue', 'ß'=>'ss'];
echo strtr("Grüße", $trans); // "Gruesse"
// 兩字串對應 (長度需相等)
echo strtr("abc", "abc", "123"); // "123"
// 陣列鍵值對替換 (長鍵優先,避免重複替換)
$replace = ['apple'=>'fruit', 'app'=>'application'];
echo strtr("I like app and apple", $replace);
// "I like application and fruit" (長鍵優先)
二、查找與定位
| 函數 | 功能 | 回傳 | 大小寫 | 多位元組 |
| strpos($haystack, $needle, $offset) | 首次出現位置 | int/false | 敏感 | 否 |
| stripos($haystack, $needle, $offset) | 首次出現 (不區分大小寫) | int/false | 不敏感 | 否 |
| strrpos($haystack, $needle, $offset) | 最後出現位置 | int/false | 敏感 | 否 |
| strripos($haystack, $needle, $offset) | 最後出現 (不區分大小寫) | int/false | 不敏感 | 否 |
| mb_strpos / mb_stripos / mb_strrpos / mb_strripos | 多位元組版本 | int/false | 對應 | 是 |
關鍵陷阱:嚴格比較
$pos = strpos("abc", "a"); // 0
if ($pos) { ... } // 錯誤!0 視為 false
if ($pos !== false) { ... } // 正確
if ($pos === false) { ... } // 未找到
三、截取與擷取
substr - 核心截取
substr(string $string, int $offset, int|null $length = null): string
$str = "abcdefgh";
substr($str, 2); // "cdefgh" (從索引 2 到結尾)
substr($str, 2, 3); // "cde" (長度 3)
substr($str, -3); // "fgh" (倒數 3 字元)
substr($str, -5, 2); // "de" (倒數 5 起長度 2)
substr($str, 0, -2); // "abcdef" (排除最後 2 字元)
從找到位置擷取
| 函數 | 功能 | 範例 |
| strstr($haystack, $needle, $before_needle) | 從首次出現處到結尾 (或之前) | strstr("user@host", "@") → "@host" |
| stristr() | strstr 不區分大小寫版 | |
| strchr() | strstr 別名 | |
| strrchr($haystack, $needle) | 從最後一次出現處到結尾 | strrchr("/path/to/file", "/") → "/file" |
分割與合併
explode(string $separator, string $string, int $limit = PHP_INT_MAX): array
implode(string $glue, array $pieces): string
// explode 範例
$str = "a,b,c,d";
explode(",", $str); // ['a','b','c','d']
explode(",", $str, 2); // ['a', 'b,c,d'] (限制分割數)
explode(",", $str, -1); // ['a','b','c'] (PHP 5.1+,排除最後)
// implode 範例
implode("|", ['a','b','c']); // "a|b|c"
implode("", ['a','b','c']); // "abc"
四、替換與正則
簡單替換:str_replace / str_ireplace
str_replace($search, $replace, $subject, &$count = null): string|array
// 字串替換
str_replace("old", "new", "old old old"); // "new new new"
// 陣列批次替換 (順序重要:長字串優先)
$search = ['apple', 'app'];
$replace = ['fruit', 'application'];
str_replace($search, $replace, "I have an app and an apple");
// "I have an application and an fruit" (app 先被替換,導致 apple 變 applicationle)
// 修正:長字串優先
$search = ['apple', 'app'];
$replace = ['fruit', 'application'];
// 或用 strtr 陣列模式 (自動長鍵優先)
// 計算替換次數
$count = 0;
str_replace("a", "b", "aaaa", $count); // $count = 4
正則替換:preg_replace
preg_replace($pattern, $replacement, $subject, $limit = -1, &$count = null): string|array
// 移除多餘空白
preg_replace('/\s+/', ' ', "a b\tc\nd"); // "a b c d"
// 回調函數替換 (PHP 7.4+ 箭頭函數)
preg_replace_callback('/(\d+)/', fn($m) => $m[1]*2, "a1b2c3"); // "a2b4c6"
// 具名捕獲群組替換 (PHP 7+)
$pattern = '/(?\d{4})-(?\d{2})-(?\d{2})/';
$replacement = '$/$/$';
preg_replace($pattern, $replacement, '2024-01-15'); // "01/15/2024"
// 分割:preg_split
preg_split('/\s*,\s*/', 'a, b , c'); // ['a','b','c'] (處理逗號前後空白)
舊式正則 (已移除 PHP 7+):ereg 族群
**ereg, eregi, ereg_replace, eregi_replace, split, spliti, sql_regcase 已於 PHP 7.0 移除**,請全面遷移至 PCRE (preg_*) 函數。
五、特殊字元處理
| 函數 | 功能 | 範例 |
| quotemeta($str) | 正則特殊字元加反斜線 | quotemeta("a.b*c") → "a\.b\*c" |
| addslashes($str) | 單雙引號、反斜線、NULL 加反斜線 | addslashes("O'Reilly") → "O\'Reilly" |
| stripslashes($str) | 移除反斜線 | stripslashes("O\'Reilly") → "O'Reilly" |
| addcslashes($str, $charlist) | 指定字元加反斜線 | addcslashes("abc", "a") → "\abc" |
| stripcslashes($str) | 移除 addcslashes 加的反斜線 | |
六、序列化儲存
serialize(mixed $value): string
unserialize(string $data, array $options = []): mixed
// 範例
$data = ['user' => 'john', 'roles' => ['admin', 'editor'], 'active' => true];
$serialized = serialize($data);
// a:3:{s:4:"user";s:4:"john";s:5:"roles";a:2:{i:0;s:5:"admin";i:1;s:6:"editor";}s:6:"active";b:1;}
$restored = unserialize($serialized);
// ['user'=>'john', 'roles'=>['admin','editor'], 'active'=>true]
// 安全選項 (PHP 7+)
unserialize($data, ['allowed_classes' => false]); // 禁止物件反序列化 (防 RCE)
unserialize($data, ['allowed_classes' => ['MyClass']]); // 僅允許特定類別
序列化 vs JSON
| 特性 | serialize | json_encode |
| 資料類型支援 | 所有 PHP 型別 (含物件、資源*) | 純量、陣列、物件 (公開屬性) |
| 可讀性 | 低 | 高 |
| 跨語言 | 否 | 是 (標準) |
| 效能 | 較快 | 略慢 |
| 安全性 | 反序列化有 RCE 風險 | 安全 |
七、實戰:安全的序列化儲存類別
class SafeSerializer {
public static function pack(mixed $data, int $ttl = 0): string {
$payload = [
'data' => $data,
'time' => time(),
'ttl' => $ttl,
'hash' => hash_hmac('sha256', serialize($data), self::getKey())
];
return base64_encode(serialize($payload));
}
public static function unpack(string $packed): mixed {
$payload = @unserialize(base64_decode($packed), ['allowed_classes' => false]);
if (!$payload || !isset($payload['data'], $payload['hash'], $payload['time'], $payload['ttl'])) {
throw new Exception('Invalid payload');
}
// 驗證 HMAC
if (!hash_equals($payload['hash'], hash_hmac('sha256', serialize($payload['data']), self::getKey()))) {
throw new Exception('Tampered payload');
}
// 檢查 TTL
if ($payload['ttl'] > 0 && time() - $payload['time'] > $payload['ttl']) {
throw new Exception('Expired payload');
}
return $payload['data'];
}
private static function getKey(): string {
return hash_hmac('sha256', 'serializer_salt', ini_get('session.secret') ?: 'fallback_secret');
}
}
// 使用
$token = SafeSerializer::pack(['user_id' => 123, 'role' => 'admin'], 3600); // 1小時有效
$data = SafeSerializer::unpack($token);
總結
字串處理核心在於:**查找用 `strpos`/`mb_strpos` (嚴格比較)、截取用 `substr`/`mb_substr`、簡單替換用 `str_replace` (注意順序) 或 `strtr` (長鍵優先)、複雜模式用 `preg_replace`、序列化優先 JSON、PHP 專用才用 `serialize` (必加 HMAC 驗證)**。多位元組環境**全系列改用 `mb_*` 前綴函數**,並設定 `mb_internal_encoding('UTF-8')`。避開已移除的 `ereg` 族群,擁抱 PCRE 正則標準。