即使資深 PHP 開發者,也可能忽略一些強大但未被充分利用的函數。本文精選 9 個提升開發效率、簡化代碼的實用特性。
不定義固定參數,直接在函數內獲取所有傳入參數。
<?php
function logMessage() {
$args = func_get_args(); // 所有參數陣列
$count = func_num_args(); // 參數個數
$first = func_get_arg(0); // 第 1 個參數 (0-based)
$msg = implode(' ', array_map(function($v) {
return is_scalar($v) ? $v : json_encode($v, JSON_UNESCAPED_UNICODE);
}, $args));
error_log("[$count args] $msg");
}
logMessage('User login', ['id' => 1001, 'ip' => '192.168.1.1']);
logMessage('Error', new Exception('DB connection failed'));
/*
[2 args] User login {"id":1001,"ip":"192.168.1.1"}
[2 args] Error {"message":"DB connection failed","code":0,"file":"...","line":...}
*/
?>
進階:PHP 5.6+ 可用 ... 運算子更優雅:function logMessage(...$args)。
<?php
// 所有 PHP 檔
$phpFiles = glob('*.php');
// 多副檔名 (需 GLOB_BRACE)
$codeFiles = glob('*.{php,js,css,html}', GLOB_BRACE);
// 遞歸掃描目錄 (PHP 5.5+ GLOB_BRACE + 生成器)
function rglob($pattern, $flags = 0) {
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
$files = array_merge($files, rglob($dir . '/' . basename($pattern), $flags));
}
return $files;
}
$allPhp = rglob('src/**/*.php'); // 需支援 ** (PHP 8.0+ 或自行實作)
?>
常用旗標:GLOB_NOSORT (不排序,較快)、GLOB_ONLYDIR、GLOB_BRACE、GLOB_ERR (讀取錯誤目錄停止)。
<?php
$defaults = ['host' => 'localhost', 'port' => 3306, 'charset' => 'utf8'];
$userCfg = ['port' => 3307, 'db' => 'myapp'];
$config = [...$defaults, ...$userCfg]; // 後者覆蓋前者
// ['host'=>'localhost', 'port'=>3307, 'charset'=>'utf8', 'db'=>'myapp']
// 函數調用解包
function connect($host, $port, $charset, $db) { /* ... */ }
connect(...$config); // 自動對應參數
?>
<?php
$sql = << '2024-01-01'
ORDER BY p.created_at DESC
LIMIT 20
SQL;
echo $sql; // 保留換行、縮排,變數可插值
// Nowdoc (單引號行為,不插值)
$template = <<<'TPL'
Hello {$name}, your code is {$code}.
TPL;
?>
<?php
$data = [10, 20, 10, 30, 20, 40];
$unique = array_values(array_unique($data)); // [10, 20, 30, 40] 鍵名重排 0..n
// 物件去重 (需自定義比較)
class User { public $id; public $name; }
$users = [new User(1,'A'), new User(2,'B'), new User(1,'A')];
$uniqueUsers = array_values(array_unique($users, SORT_REGULAR)); // PHP 7.2+ 支援物件比較
?>
<?php
function readLargeCsv(string $file): Generator {
$fh = new SplFileObject($file);
$fh->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY);
$fh->setCsvControl(','); // 分隔符
foreach ($fh as $row) {
yield $row; // 逐行產出,記憶體恆定
}
}
foreach (readLargeCsv('huge.csv') as $i => $row) {
if ($i === 0) continue; // 跳過標題
// 處理 $row
if ($i > 100000) break; // 測試只跑前 10 萬行
}
?>
<?php
// PHP 5.0+ 第三參數 recursive=true
mkdir('/var/www/app/storage/logs/2024/05', 0775, true); // 一次建立多層目錄
// 更安全:檢查是否存在、錯誤處理
$dir = '/var/www/app/storage/logs/2024/05';
if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
throw new RuntimeException("無法建立目錄: $dir");
}
?>
<?php
function getClientIp(): string {
$headers = [
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR'
];
foreach ($headers as $h) {
if (!empty($_SERVER[$h])) {
$ips = explode(',', $_SERVER[$h]);
foreach ($ips as $ip) {
$ip = trim($ip);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return $ip;
}
}
}
}
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
?>
<?php
// 註冊/修改密碼
$hash = password_hash($plainPassword, PASSWORD_DEFAULT); // 自動生成鹽、選擇最強演算法 (目前 bcrypt/argon2)
/* $hash 例:$2y$10$AbCdEfGhIjKlMnOpQrStUvWxYz1234567890abcd... */
// 登入驗證
if (password_verify($inputPassword, $hash)) {
// 成功
// 選擇性:檢查是否需重新雜湊 (演算法升級、cost 變更)
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
$newHash = password_hash($inputPassword, PASSWORD_DEFAULT);
// 更新資料庫
}
} else {
// 失敗
}
?>
嚴禁 使用 md5()、sha1()、crypt() 自行雜湊密碼。
| 特性/函數 | 最低版本 | 核心優勢 |
|---|---|---|
func_get_args() / ... | 4.0 / 5.6 | 不定參數、避免 func_get_args() 陣列複製 |
glob() | 4.3 | 模式匹配、支援多副檔名、遞歸掃描 |
陣列解包 ... | 7.4 | 合併、函數調用、可讀性極佳 |
| Heredoc/Nowdoc | 5.3 | 多行字串、SQL/模板無需轉義 |
array_unique() + array_values() | 4.0 / 4.0 | 去重並重排鍵名 |
Generator + SplFileObject | 5.5 / 5.1 | 大檔案恆定記憶體 |
mkdir(..., true) | 5.0 | 遞歸建立目錄 |
| 取得真實 IP | 通用 | 支援代理鏈、過濾私有/保留 IP |
password_hash() / password_verify() | 5.5 | 標準化、未來兼容、防時序攻擊 |