首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 開發必備 20 個實用功能代碼片段:從隨機字符串到圖片處理

     編輯:PHP 相關知識

前言

本文精選 20 個 PHP 高頻實用功能,涵蓋字符串生成、郵件處理、文件操作、數據解析、安全驗證、圖片處理等核心場景。每個片段經過現代化改造(類型聲明、嚴格模式、錯誤處理),可直接集成至項目工具類中。


1. 可讀性隨機字符串(密碼/驗證碼)

<?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+ 自動播種)。


2. 通用隨機字符串(API Key/Token)

<?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));

3. 郵箱地址混淆防采集

<?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; }
}

4. 嚴格郵箱驗證(含 MX 記錄檢查)

<?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)

5. 遞歸列出目錄內容(含過濾)

<?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']])

6. 遞歸刪除目錄(安全版)

<?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;
}

7. JSON 數據解析與異常處理

<?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')

8. XML 解析(SimpleXML + 命名空間支援)

<?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'] ?? '');
}

9. URL 友好 Slug 生成(支援中文)

<?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-中文標題' (無)

10. 獲取客戶端真實 IP(支援代理/CDN)

<?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';
}

11. 強制文件下載(支援大文件斷點續傳)

<?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;
}

12. 標簽雲生成(字體大小加權)

<?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);

13. 字符串相似度比較

<?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;
}

14. Gravatar 頭像獲取(支援多種參數)

<?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} />";
}

15. 智能截斷字符串(保持單詞完整)

<?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;
}

16. ZIP 壓縮(目錄/文件列表)

<?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');

17. 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();
}

18. URL 規範化(補全協議)

<?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;
}

19. 文本自動鏈接化(URL/Email/電話)

<?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(
            '~(?

20. 圖片等比縮放(GD 版,含輸出緩存控制)

<?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;
}

總結與工程化建議

  1. 統一入口: 將以上函數整理為 App\Utils\Str、App\Utils\File、App\Utils\Image 等命名空間類
  2. 依賴注入: 避免全局函數,通過容器注入工具服務
  3. 單元測試: 為每個工具函數編寫 PHPUnit 測試,覆蓋邊界條件
  4. 靜態分析: 運行 PHPStan/Psalm 修復類型錯誤
  5. 性能基準: 對高頻函數(如自動鏈接、圖片處理)進行 Benchmark,必要時用 C 擴展或異步隊列

這 20 個片段覆蓋了 PHP 開發 80% 的常見需求,掌握並善用它們,能讓你專注於業務邏輯而非重複造輪子。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved