本文精選 20 個 PHP 高頻實用功能,涵蓋字符串生成、郵件處理、文件操作、數據解析、安全驗證、圖片處理等核心場景。每個片段經過現代化改造(類型聲明、嚴格模式、錯誤處理),可直接集成至項目工具類中。
<?php
declare(strict_types=1);
function readableRandomString(int $length = 6): string {
if ($length % 2 !== 0) $length++; // 必須為偶數
$consonants = 'bcdfghjklmnpqrstvwxyz';
$vowels = 'aeiou';
$password = '';
for ($i = 0; $i < $length / 2; $i++) {
$password .= $consonants[random_int(0, strlen($consonants) - 1)];
$password .= $vowels[random_int(0, strlen($vowels) - 1)];
}
return $password;
}
// 用法:readableRandomString(8) → "bavetiso"
改進點: 使用 random_int() 替代 rand()(密碼學安全)、移除 srand()(PHP 4.2+ 自動播種)。
<?php
function generateRandomString(int $length = 32): string {
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$bytes = random_bytes(ceil($length / 2)); // 生成足夠熵
return substr(bin2hex($bytes), 0, $length);
}
// 更簡潔(PHP 8.2+):
// return bin2hex(random_bytes($length / 2));
<?php
function obfuscateEmail(string $email, string $linkText = '', array $attrs = []): string {
$linkText = $linkText ?: $email;
$attrStr = '';
foreach ($attrs as $k => $v) {
$attrStr .= " {$k}=\"" . htmlspecialchars($v, ENT_QUOTES) . "\"";
}
// 方案 A:JS 動態寫入(原邏輯現代化)
$encoded = '';
foreach (str_split($email) as $ch) {
$encoded .= '' . ord($ch) . ';';
}
foreach (str_split($linkText) as $ch) {
$encoded .= '' . ord($ch) . ';';
}
return "<script>document.write('<a href=\"mailto:{$encoded}\"{$attrStr}>{$encoded}</a>')</script>"
. "<noscript>請啟用 JavaScript 查看郵箱</noscript>";
// 方案 B:CSS 反轉(更簡單,無需 JS)
// $reversed = strrev($email);
// return "<span class=\"obfuscate-email\" data-email=\"{$reversed}\"{$attrStr}>{$linkText}</span>";
// CSS: .obfuscate-email::after { content: attr(data-email); unicode-bidi: bidi-override; direction: rtl; }
}
<?php
function isValidEmail(string $email, bool $checkMx = false): bool {
// 基礎格式驗證(RFC 5322 簡化版)
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return false;
}
if (!$checkMx) return true;
// MX 記錄檢查
$domain = substr(strrchr($email, '@'), 1);
$mxRecords = [];
if (checkdnsrr($domain, 'MX')) {
return true;
}
// 兜底:檢查 A 記錄(某些郵件服務器無 MX 直接用 A)
return checkdnsrr($domain, 'A');
}
// 用法:isValidEmail('test@example.com', true)
<?php
function listDirectory(string $dir, array $options = []): array {
$defaults = [
'recursive' => true,
'includeDirs' => false,
'skipHidden' => true,
'extensions' => null, // ['jpg','png'] 或 null
'fullPath' => true,
];
$opts = array_merge($defaults, $options);
$dir = rtrim($dir, '/') . '/';
if (!is_dir($dir)) return [];
$files = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $path => $info) {
if ($opts['skipHidden'] && $info->getBasename()[0] === '.') continue;
if (!$opts['includeDirs'] && $info->isDir()) continue;
if ($opts['extensions'] && $info->isFile()) {
$ext = strtolower($info->getExtension());
if (!in_array($ext, $opts['extensions'], true)) continue;
}
$files[] = $opts['fullPath'] ? $path : $info->getBasename();
}
return $files;
}
// 用法:listDirectory('/var/www/uploads', ['extensions'=>['jpg','png']])
<?php
function deleteDirectory(string $dir, bool $deleteSelf = true): bool {
$dir = rtrim($dir, '/') . '/';
if (!is_dir($dir)) return false;
$items = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST // 先刪子項
);
foreach ($items as $item) {
$path = $item->getPathname();
$item->isDir() ? rmdir($path) : unlink($path);
}
if ($deleteSelf) {
return @rmdir($dir);
}
return true;
}
<?php
function parseJson(string $json, bool $assoc = true): mixed {
$data = json_decode($json, $assoc, 512, JSON_THROW_ON_ERROR);
// JSON_THROW_ON_ERROR (PHP 7.3+) 會拋出 JsonException,無需手動檢查 json_last_error()
return $data;
}
// 安全訪問嵌套鍵
function jsonGet(mixed $data, string $path, mixed $default = null): mixed {
$keys = explode('.', $path);
foreach ($keys as $key) {
if (is_array($data) && array_key_exists($key, $data)) {
$data = $data[$key];
} elseif (is_object($data) && property_exists($data, $key)) {
$data = $data->$key;
} else {
return $default;
}
}
return $data;
}
// 用法:jsonGet($obj, 'user.profile.avatar', 'default.png')
<?php
function parseXml(string $xmlString): SimpleXMLElement {
libxml_use_internal_errors(true);
$xml = simplexml_load_string($xmlString, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NOERROR);
if ($xml === false) {
$errors = libxml_get_errors();
libxml_clear_errors();
throw new Exception('XML 解析失敗:' . $errors[0]->message ?? '未知錯誤');
}
return $xml;
}
// 遍歷示例
$xml = parseXml($xmlString);
foreach ($xml->channel->item as $item) {
$title = (string)$item->title;
$link = (string)$item->link;
$attrs = $item->attributes(); // 訪問屬性
$id = (string)($attrs['id'] ?? '');
}
<?php
function createSlug(string $string, string $separator = '-'): string {
// 轉換中文為拼音(需 composer require overtrue/pinyin)
if (class_exists('Overtrue\Pinyin\Pinyin')) {
$pinyin = new Overtrue\Pinyin\Pinyin();
$string = $pinyin->permalink($string, $separator);
} else {
// 兜底:移除非字母數字
$string = preg_replace('/[^\p{L}\p{N}]+/u', $separator, $string);
}
// 清理多餘分隔符
$string = preg_replace('/' . preg_quote($separator, '/') . '{2,}/', $separator, $string);
return trim(strtolower($string), $separator);
}
// createSlug('PHP 中文標題') → 'php-zhong-wen-biao-ti' (有 pinyin) 或 'php-中文標題' (無)
<?php
function getRealIp(): string {
$headers = [
'HTTP_CF_CONNECTING_IP', // Cloudflare
'HTTP_X_REAL_IP', // Nginx proxy
'HTTP_X_FORWARDED_FOR', // 標準代理
'HTTP_CLIENT_IP', // 部分代理
'HTTP_X_FORWARDED', // 罕見
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR', // 直連
];
foreach ($headers as $header) {
if (!empty($_SERVER[$header])) {
// X-Forwarded-For 可能包含多個 IP,取第一個非私有 IP
$ips = array_map('trim', explode(',', $_SERVER[$header]));
foreach ($ips as $ip) {
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return $ip;
}
}
// 全是私有 IP,返回第一個
return $ips[0];
}
}
return '0.0.0.0';
}
<?php
function forceDownload(string $filePath, string $filename = '', bool $resume = true): void {
if (!file_exists($filePath) || !is_readable($filePath)) {
http_response_code(404);
exit('File not found');
}
$filename = $filename ?: basename($filePath);
$size = filesize($filePath);
$mime = mime_content_type($filePath) ?: 'application/octet-stream';
// 斷點續傳支援
if ($resume && isset($_SERVER['HTTP_RANGE'])) {
$range = $_SERVER['HTTP_RANGE'];
if (preg_match('/bytes=(\d+)-(\d*)/', $range, $matches)) {
$start = (int)$matches[1];
$end = $matches[2] ? (int)$matches[2] : $size - 1;
$length = $end - $start + 1;
header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes {$start}-{$end}/{$size}");
header("Content-Length: {$length}");
}
} else {
header("Content-Length: {$size}");
}
header('Content-Type: ' . $mime);
header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"');
header('Accept-Ranges: bytes');
header('Cache-Control: private, must-revalidate');
header('Pragma: private');
// 分塊輸出,避免大文件佔用內存
$handle = fopen($filePath, 'rb');
if (isset($start)) fseek($handle, $start);
while (!feof($handle) && connection_status() === CONNECTION_NORMAL) {
$chunkSize = isset($length) ? min(8192, $length) : 8192;
echo fread($handle, $chunkSize);
if (isset($length)) $length -= $chunkSize;
@ob_flush(); @flush();
}
fclose($handle);
exit;
}
<?php
function generateTagCloud(array $tags, int $minSize = 12, int $maxSize = 30, string $urlPrefix = '#'): string {
if (!$tags) return '';
$minCount = min($tags);
$maxCount = max($tags);
$spread = $maxCount - $minCount ?: 1; // 避免除零
$html = [];
foreach ($tags as $tag => $count) {
$size = $minSize + ($count - $minCount) * ($maxSize - $minSize) / $spread;
$size = (int)round($size);
$safeTag = htmlspecialchars($tag, ENT_QUOTES);
$url = $urlPrefix . rawurlencode($tag);
$html[] = "<a href=\"{$url}\" style=\"font-size:{$size}px\" title=\"{$safeTag} (×{$count})\">{$safeTag}</a>";
}
return implode("\n", $html);
}
// 用法
$tags = ['PHP'=>50, 'Laravel'=>30, 'MySQL'=>40, 'Redis'=>20, 'Docker'=>15];
echo generateTagCloud($tags);
<?php
function stringSimilarity(string $a, string $b): float {
similar_text($a, $b, $percent);
return $percent; // 0-100
}
// 進階:Levenshtein 距離(編輯距離)
function levenshteinDistance(string $a, string $b): int {
return levenshtein($a, $b);
}
// 使用示例:搜索建議纠错
function didYouMean(string $input, array $dictionary, int $threshold = 3): ?string {
$best = null;
$minDist = PHP_INT_MAX;
foreach ($dictionary as $word) {
$dist = levenshtein($input, $word);
if ($dist < $minDist && $dist <= $threshold) {
$minDist = $dist;
$best = $word;
}
}
return $best;
}
<?php
function getGravatarUrl(string $email, int $size = 80, string $default = 'mp', string $rating = 'g', bool $secure = true): string {
$hash = md5(strtolower(trim($email)));
$protocol = $secure ? 'https' : 'http';
$params = http_build_query([
's' => $size,
'd' => $default, // mp=神秘人, identicon=幾何圖, monsterid=怪獸, wavatar=臉譜, retro=像素, robohash=機器人, blank=透明
'r' => $rating, // g, pg, r, x
]);
return "{$protocol}://www.gravatar.com/avatar/{$hash}?{$params}";
}
// 輸出 img 標簽
function gravatarImg(string $email, array $attrs = []): string {
$url = getGravatarUrl($email);
$attrStr = '';
foreach ($attrs as $k => $v) $attrStr .= " {$k}=\"" . htmlspecialchars($v, ENT_QUOTES) . "\"";
return "<img src=\"{$url}\" alt=\"Gravatar\"{$attrStr} />";
}
<?php
function smartTruncate(string $text, int $length, string $suffix = '...', bool $htmlAware = false): string {
if (mb_strlen($text) <= $length) return $text;
if ($htmlAware) {
// 簡易 HTML 感知截斷(完整解析建議用 HTMLPurifier/TagBalancer)
$text = strip_tags($text); // 或用更複雜的邏輯保留標簽
}
// 尋找最後一個空格/標點
$truncated = mb_substr($text, 0, $length);
$lastBreak = max(
mb_strrpos($truncated, ' '),
mb_strrpos($truncated, '。'),
mb_strrpos($truncated, '.'),
mb_strrpos($truncated, ','),
mb_strrpos($truncated, ','),
mb_strrpos($truncated, '!'),
mb_strrpos($truncated, '!'),
mb_strrpos($truncated, '?'),
mb_strrpos($truncated, '?')
);
if ($lastBreak !== false && $lastBreak > $length * 0.7) { // 至少保留 70%
return mb_substr($text, 0, $lastBreak) . $suffix;
}
return $truncated . $suffix;
}
<?php
function createZip(array $sources, string $dest, bool $overwrite = false): bool {
if (file_exists($dest) && !$overwrite) return false;
$zip = new ZipArchive();
$flags = $overwrite ? ZipArchive::OVERWRITE : ZipArchive::CREATE;
if ($zip->open($dest, $flags) !== true) return false;
foreach ($sources as $source) {
$source = realpath($source);
if (!$source) continue;
if (is_file($source)) {
$zip->addFile($source, basename($source));
} elseif (is_dir($source)) {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($source, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($files as $file) {
$path = $file->getPathname();
$relative = substr($path, strlen($source) + 1);
if ($file->isDir()) {
$zip->addEmptyDir($relative);
} else {
$zip->addFile($path, $relative);
}
}
}
}
return $zip->close();
}
// 用法:createZip(['/data/export', '/var/log/app.log'], '/backups/backup_' . date('Ymd') . '.zip');
<?php
function extractZip(string $zipPath, string $dest, bool $overwrite = false): bool {
$zip = new ZipArchive();
if ($zip->open($zipPath) !== true) return false;
// 安全檢查:防止 Zip Slip 攻擊(路徑遍歷)
for ($i = 0; $i < $zip->numFiles; $i++) {
$name = $zip->getNameIndex($i);
$target = realpath($dest) . DIRECTORY_SEPARATOR . $name;
if (strpos($target, realpath($dest)) !== 0) {
$zip->close();
throw new Exception("Zip Slip 攻擊檢測:{$name}");
}
}
$flags = $overwrite ? ZipArchive::OVERWRITE : 0;
return $zip->extractTo($dest, $flags) && $zip->close();
}
<?php
function normalizeUrl(string $url): string {
$url = trim($url);
if ($url === '') return '';
// 已有協議
if (preg_match('~^https?://~i', $url)) return $url;
// 類似 www.example.com 或 example.com/path
if (preg_match('~^[a-z0-9.-]+\.[a-z]{2,}~i', $url)) {
return 'https://' . $url;
}
// 其它情況(如相對路徑)保持原樣或拋出異常
return $url;
}
<?php
function autoLink(string $text, array $options = []): string {
$defaults = [
'url' => true,
'email' => true,
'phone' => false,
'target' => '_blank',
'rel' => 'noopener noreferrer',
'class' => 'auto-link',
];
$opts = array_merge($defaults, $options);
// URL (http/https/ftp/www)
if ($opts['url']) {
$text = preg_replace_callback(
'~(?:(https?|ftp)://|www\.)[^\s<>"\']+~i',
function($m) use ($opts) {
$url = $m[0];
$href = str_starts_with($url, 'www.') ? 'https://' . $url : $url;
$attrs = 'class="' . $opts['class'] . '" target="' . $opts['target'] . '" rel="' . $opts['rel'] . '"';
return "<a href=\"" . htmlspecialchars($href) . "\" {$attrs}>" . htmlspecialchars($url) . "</a>";
},
$text
);
}
// Email
if ($opts['email']) {
$text = preg_replace(
'~([\w.+-]+@[\w-]+\.[\w.-]+)~',
'<a href="mailto:$1" class="' . $opts['class'] . '">$1</a>',
$text
);
}
// Phone (簡易中國大陸手機號)
if ($opts['phone']) {
$text = preg_replace(
'~(?
<?php
function resizeImage(string $srcPath, string $destPath, int $maxW, int $maxH, int $quality = 90): bool {
if (!file_exists($srcPath)) return false;
$info = getimagesize($srcPath);
if (!$info) return false;
[$srcW, $srcH, $type] = $info;
// 無需縮放
if ($srcW <= $maxW && $srcH <= $maxH) {
return copy($srcPath, $destPath);
}
// 計算目標尺寸
$ratio = min($maxW / $srcW, $maxH / $srcH);
$dstW = (int)round($srcW * $ratio);
$dstH = (int)round($srcH * $ratio);
// 載入源圖
$src = match ($type) {
IMAGETYPE_JPEG => imagecreatefromjpeg($srcPath),
IMAGETYPE_PNG => imagecreatefrompng($srcPath),
IMAGETYPE_GIF => imagecreatefromgif($srcPath),
IMAGETYPE_WEBP => function_exists('imagecreatefromwebp') ? imagecreatefromwebp($srcPath) : null,
default => null,
};
if (!$src) return false;
// 創建目標圖(保持透明度)
$dst = imagecreatetruecolor($dstW, $dstH);
if ($type === IMAGETYPE_PNG || $type === IMAGETYPE_GIF) {
imagesavealpha($dst, true);
$transparent = imagecolorallocatealpha($dst, 0, 0, 0, 127);
imagefill($dst, 0, 0, $transparent);
}
// 重采樣
imagecopyresampled($dst, $src, 0, 0, 0, 0, $dstW, $dstH, $srcW, $srcH);
// 保存
$result = match ($type) {
IMAGETYPE_JPEG => imagejpeg($dst, $destPath, $quality),
IMAGETYPE_PNG => imagepng($dst, $destPath, 8),
IMAGETYPE_GIF => imagegif($dst, $destPath),
IMAGETYPE_WEBP => function_exists('imagewebp') ? imagewebp($dst, $destPath, $quality) : false,
default => false,
};
imagedestroy($src);
imagedestroy($dst);
return $result;
}
App\Utils\Str、App\Utils\File、App\Utils\Image 等命名空間類這 20 個片段覆蓋了 PHP 開發 80% 的常見需求,掌握並善用它們,能讓你專注於業務邏輯而非重複造輪子。