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

PHP 創建文件與目錄的最佳實踐代碼示例

     編輯:PHP 相關知識

在 PHP 中安全、可靠地創建文件需要處理目錄遞歸創建、權限設置、寫入失敗回滾等細節。本文提供一個生產級的封裝函數。

核心代碼

<?php
/**
 * 安全創建文件並寫入內容
 *
 * @param string $filePath  完整文件路徑
 * @param string $content   寫入內容
 * @param int    $dirPerm   目錄權限(默認 0755)
 * @param int    $filePerm  文件權限(默認 0644)
 * @return bool             成功返回 true,失敗拋出異常
 */
function createFileSafe(string $filePath, string $content, int $dirPerm = 0755, int $filePerm = 0644): bool {
    // 1. 規範化路徑
    $filePath = realpath(dirname($filePath)) . DIRECTORY_SEPARATOR . basename($filePath);
    $dir = dirname($filePath);

    // 2. 遞歸創建目錄
    if (!is_dir($dir)) {
        if (!mkdir($dir, $dirPerm, true) && !is_dir($dir)) {
            throw new RuntimeException("無法創建目錄: $dir");
        }
    }

    // 3. 以原子方式寫入(臨時文件 + rename)
    $tmpName = $filePath . '.tmp.' . bin2hex(random_bytes(8));
    $handle = @fopen($tmpName, 'wb');
    if (!$handle) {
        throw new RuntimeException("無法創建臨時文件: $tmpName");
    }

    try {
        // 獨佔鎖防止併發寫入
        if (!flock($handle, LOCK_EX)) {
            throw new RuntimeException("無法獲取文件鎖: $tmpName");
        }

        $bytesWritten = fwrite($handle, $content);
        if ($bytesWritten === false || $bytesWritten !== strlen($content)) {
            throw new RuntimeException("文件寫入失敗或不完整: $filePath");
        }

        // 刷新緩衝區
        fflush($handle);
        flock($handle, LOCK_UN);
        fclose($handle);

        // 設置文件權限
        if (!chmod($tmpName, $filePerm)) {
            @unlink($tmpName);
            throw new RuntimeException("無法設置文件權限: $tmpName");
        }

        // 原子重命名(同文件系統下保證原子性)
        if (!rename($tmpName, $filePath)) {
            @unlink($tmpName);
            throw new RuntimeException("文件重命名失敗: $tmpName -> $filePath");
        }

        return true;
    } catch (Exception $e) {
        // 確保清理臨時文件
        if (is_resource($handle)) {
            flock($handle, LOCK_UN);
            fclose($handle);
        }
        @unlink($tmpName);
        throw $e;
    }
}

// ========== 使用示例 ==========
try {
    $dateDir = date('Ymd');
    $file = "/var/www/data/$dateDir/log_" . date('His') . ".txt";
    $content = "這是測試內容\n時間: " . date('Y-m-d H:i:s') . "\n";

    if (createFileSafe($file, $content)) {
        echo "文件創建成功: $file";
    }
} catch (Exception $e) {
    error_log("創建文件失敗: " . $e->getMessage());
    echo "操作失敗: " . $e->getMessage();
}
?>

關鍵點解析

  1. 目錄遞歸創建:`mkdir($dir, $perm, true)` 遞歸創建父目錄,`true` 參數至關重要。
  2. 原子寫入:寫入臨時文件 → `rename()` 替換目標文件,避免讀取到半寫數據。
  3. 文件鎖:`flock(LOCK_EX)` 防止多進程併發寫入衝突。
  4. 權限控制:目錄 `0755`,文件 `0644`,遵循最小權限原則。
  5. 異常處理:統一拋出 `RuntimeException`,上層統一捕獲記錄日誌。
  6. 路徑安全:使用 `realpath(dirname())` 防止目錄遍歷攻擊。

常見場景擴展

追加寫入(日誌)

function appendFileSafe(string $filePath, string $content): bool {
    $dir = dirname($filePath);
    if (!is_dir($dir) && !mkdir($dir, 0755, true)) return false;
    return (bool)file_put_contents($filePath, $content, FILE_APPEND | LOCK_EX);
}

大文件分塊寫入

function writeLargeFile(string $filePath, Generator $chunks): bool {
    $tmp = $filePath . '.part';
    $fp = fopen($tmp, 'wb');
    foreach ($chunks as $chunk) {
        fwrite($fp, $chunk);
    }
    fclose($fp);
    return rename($tmp, $filePath);
}

掌握這些模式,能有效避免文件操作中的數據損壞、權限問題、併發衝突等線上故障。

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