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

PHP 系統執行與檔案操作函數速查手冊 (第九輯):exec、shell、檔案鎖、目錄掃描與串流處理

     編輯:PHP 綜合知識

前言

PHP 與作業系統互動、檔案系統操作是後端開發核心能力。本文系統整理 `exec` 族函數、檔案鎖、目錄掃描、串流處理、臨時檔案等高頻函數,解析參數差異、安全風險與最佳實踐。

一、系統指令執行函數族

對照表:三大執行函數核心差異

函數回傳值輸出處理適用場景阻塞性
exec()最後一行輸出 (string)可選陣列參數接收所有行需捕獲結果碼、簡單指令阻塞
system()最後一行輸出直接輸出到標準輸出即時顯示輸出、安裝腳本阻塞
passthru()無 (void)原始二進位直接輸出圖片/PDF/二進位串流阻塞
shell_exec() / `反引號`完整輸出 (string)捕獲所有輸出需完整輸出字串處理阻塞
proc_open()資源控制代碼雙向管道、非阻塞可控複雜互動、長時間程序可非阻塞

參數詳解

// exec(string $command, array &$output = null, int &$return_var = null): string
$output = [];
$returnVar = 0;
$lastLine = exec("ls -la /tmp", $output, $returnVar);
// $output = ["total 12", "drwxrwxrwt 2 root root 4096 Jan 15 10:00 .", ...]
// $returnVar = 0 (成功)

// system(string $command, int &$return_var = null): string
$lastLine = system("ls -la /tmp", $returnVar); // 直接輸出到瀏覽器/終端

// passthru(string $command, int &$return_var = null): void
header("Content-Type: image/jpeg");
passthru("/usr/bin/convert /path/to/input.png jpeg:-"); // 圖片轉檔直接輸出

安全關鍵:escapeshellcmd / escapeshellarg

$userInput = $_GET['filename'] ?? '';
// 錯誤:exec("ls $userInput"); // 注入風險:"; rm -rf /"

// 正確:參數級跳脫
$safeArg = escapeshellarg($userInput); // 單引號包裹並跳脫內部單引號
exec("ls {$safeArg}");

// 或指令級跳脫 (較不推薦,僅跳脫特殊字元)
$safeCmd = escapeshellcmd("ls {$userInput}");
exec($safeCmd);

二、檔案鎖定:flock

bool flock(resource $stream, int $operation, int &$wouldblock = null): bool

操作常數 (鎖定模式)

常數數值說明
LOCK_SH1共享鎖 (讀鎖),多程序可同時持有
LOCK_EX2獨佔鎖 (寫鎖),同一時間僅一程序
LOCK_UN3釋放鎖
LOCK_NB4非阻塞模式 (搭配 LOCK_SH\|LOCK_EX 使用)

經典寫入模式 (獨佔鎖 + 非阻塞重試)

function safeWrite(string $file, string $data): bool {
    $fp = fopen($file, 'c+'); // 開啟讀寫,不截斷
    if (!$fp) return false;
    
    $retry = 0;
    while (!flock($fp, LOCK_EX | LOCK_NB)) {
        if (++$retry > 10) { fclose($fp); return false; }
        usleep(10000); // 10ms 重試
    }
    
    ftruncate($fp, 0); // 截斷
    fwrite($fp, $data);
    fflush($fp);
    flock($fp, LOCK_UN);
    fclose($fp);
    return true;
}

三、目錄掃描與檔案資訊

opendir / readdir / closedir (底層迭代器)

$dir = opendir("/var/www/html");
if ($dir) {
    while (($file = readdir($dir)) !== false) {
        if ($file !== '.' && $file !== '..') {
            echo $file . "\n";
        }
    }
    closedir($dir);
}

// 現代替代:DirectoryIterator (SPL, 物件導向)
foreach (new DirectoryIterator("/var/www/html") as $fileInfo) {
    if (!$fileInfo->isDot()) echo $fileInfo->getFilename() . "\n";
}

chdir / getcwd - 工作目錄切換

echo getcwd();           // /current/path
chdir("/tmp");           // 切換
echo getcwd();           // /tmp

磁碟空間與檔案屬性

函數功能範例回傳
disk_free_space($dir)目錄可用空間 (bytes)10737418240 (10GB)
disk_total_space($dir)總空間53687091200 (50GB)
filesize($file)檔案大小1024
fileperms($file)權限 (八進位)0100644 (需用 substr(sprintf('%o', $p), -4))
fileowner / filegroup擁有者 UID/GID1000 / 1000
filetype($file)類型"file", "dir", "link", "fifo", "block", "char", "unknown"
is_readable / is_writable / is_executable / is_file / is_dir / is_link布林檢查true/false

檔案時間戳

fileatime($file)  // 最後存取時間
filemtime($file)  // 最後修改時間 (最常用)
filectime($file)  // 最後狀態變更時間 (inode 變更)

touch($file)      // 更新 atime/mtime 為現在 (或指定時間)
touch($file, time() - 86400); // 設為 1 天前

四、檔案串流操作 (資源控制代碼)

fopen 模式速查

模式讀寫建立截斷指標位置備註
r✓開頭檔案必須存在
r+✓✓開頭檔案必須存在
w✓✓✓開頭覆蓋現有
w+✓✓✓✓開頭覆蓋現有
a✓✓結尾追加模式
a+✓✓✓結尾追加可讀
c✓✓開頭不截斷 (PHP 5.2+)
c+✓✓✓開頭不截斷,推薦寫入用
x✓✓開頭獨佔建立,存在則失敗

核心讀寫函數

$fp = fopen("data.txt", "c+");

// 讀取
fgets($fp, 1024)      // 讀一行 (含換行),長度限制
fgetss($fp, 1024)     // 讀一行並去除 HTML/PHP 標籤
fgetc($fp)            // 讀單一字元
fread($fp, 8192)      // 讀指定位元組 (二進位安全)
stream_get_contents($fp) // 讀取剩餘所有 (PHP 5+)

// 寫入
fwrite($fp, $data)    // 寫入字串 (回傳寫入位元組數)
fputs($fp, $data)     // fwrite 別名
fprintf($fp, "%s\n", $var) // 格式化寫入

// 指標控制
fseek($fp, 0, SEEK_SET)   // 移至開頭
fseek($fp, -10, SEEK_END) // 移至倒數 10 位元組
ftell($fp)                // 取得目前位置
rewind($fp)               // 等同 fseek($fp, 0)

// 狀態檢查
feof($fp)                 // 是否到達 EOF
fflush($fp)               // 強制刷新緩衝區到磁碟

// 關閉
fclose($fp);

高層便利函數

file_get_contents("file.txt")      // 一次讀全檔 (字串)
file("file.txt")                   // 讀全檔為陣列 (每行一元素)
readfile("file.txt")               // 直接輸出到標準輸出 (回傳位元組數)
file_put_contents("file.txt", $data, FILE_APPEND | LOCK_EX) // 原子寫入 (PHP 5+)

tempnam("/tmp", "prefix")          // 建立唯一暫存檔名
tmpfile()                          // 建立匿名暫存檔 (關閉/腳本結束自動刪除)

五、實戰:安全下載大檔案 (串流輸出 + 斷點續傳)

function streamDownload(string $filePath, string $fileName = '') {
    if (!is_file($filePath)) { http_response_code(404); return; }
    
    $fileName = $fileName ?: basename($filePath);
    $size = filesize($filePath);
    $fp = fopen($filePath, 'rb');
    
    // HTTP 標頭
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . rawurlencode($fileName) . '"');
    header('Content-Length: ' . $size);
    header('Accept-Ranges: bytes');
    header('Cache-Control: no-cache, must-revalidate');
    
    // 斷點續傳支援
    if (isset($_SERVER['HTTP_RANGE'])) {
        list($unit, $range) = explode('=', $_SERVER['HTTP_RANGE']);
        if ($unit === 'bytes') {
            list($start, $end) = explode('-', $range);
            $start = (int)$start;
            $end = $end ? (int)$end : $size - 1;
            if ($start < $size && $end < $size) {
                header('HTTP/1.1 206 Partial Content');
                header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
                header('Content-Length: ' . ($end - $start + 1));
                fseek($fp, $start);
            }
        }
    }
    
    // 串流輸出 (避免記憶體爆炸)
    while (!feof($fp) && connection_status() === CONNECTION_NORMAL) {
        echo fread($fp, 8192);
        flush();
    }
    fclose($fp);
}

總結

系統執行首選 `proc_open` 掌控雙向管道,簡單場景用 `exec` 捕獲輸出,**務必 `escapeshellarg` 防注入**。檔案操作核心是 **`fopen` 模式選擇 + `flock` 並發控制**,大檔案必用串流 `fread`/`fwrite` 避免記憶體耗盡。現代專案目錄掃描推薦 SPL `DirectoryIterator`/`RecursiveDirectoryIterator`,檔案讀寫優先 `file_get_contents`/`file_put_contents` (支援 `LOCK_EX` 原子寫入)。理解這些底層機制,才能寫出高效、安全、可靠的系統級 PHP 程式。

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