PHP Conference Japan 2024

fopen

(PHP 4、PHP 5、PHP 7、PHP 8)

fopen開啟檔案或 URL

描述

fopen(
    字串 $filename,
    字串 $mode,
    布林 $use_include_path = false,
    ?資源 $context = null
):資源|false

fopen()filename 指定的具名資源繫結到一個串流。

參數

filename

如果 filename 的格式為「scheme://...」,則會假定其為 URL,而 PHP 將會為該 scheme 搜尋通訊協定處理常式(也稱為封裝器)。如果沒有註冊該通訊協定的封裝器,PHP 將會發出通知,以協助您追蹤指令碼中的潛在問題,然後繼續如同 filename 指定一般檔案一樣。

如果 PHP 已決定 filename 指定本機檔案,則會嘗試在該檔案上開啟串流。該檔案必須可讓 PHP 存取,因此您需要確保檔案存取權限允許此存取。如果您已啟用 open_basedir,則可能適用進一步的限制。

如果 PHP 已決定 filename 指定已註冊的通訊協定,而且該通訊協定註冊為網路 URL,PHP 將會檢查以確保 allow_url_fopen 已啟用。如果關閉,PHP 將會發出警告,而且 fopen() 呼叫將會失敗。

注意:

支援的通訊協定清單可以在支援的通訊協定和封裝器中找到。某些通訊協定(也稱為 封裝器)支援 context 和/或 php.ini 選項。請參閱所使用通訊協定的特定頁面,以取得可設定選項的清單。(例如 php.iniuser_agenthttp 封裝器使用)。

在 Windows 平台上,請小心逸出檔案路徑中使用的任何反斜線,或使用正斜線。

<?php
$handle
= fopen("c:\\\\folder\\\\resource.txt", "r");
?>

mode

mode 參數指定您需要的串流存取類型。它可能是下列其中一個

使用 modefopen() 可能的模式清單
mode 描述
'r' 僅開啟用於讀取;將檔案指標放在檔案的開頭。
'r+' 開啟用於讀取和寫入;將檔案指標放在檔案的開頭。
'w' 僅開啟用於寫入;將檔案指標放在檔案的開頭,並將檔案截斷為零長度。如果檔案不存在,請嘗試建立它。
'w+' 開啟用於讀取和寫入;否則它具有與 'w' 相同的行為。
'a' 僅開啟用於寫入;將檔案指標放在檔案的末尾。如果檔案不存在,請嘗試建立它。在此模式下,fseek() 無效,寫入始終會附加。
'a+' 開啟用於讀取和寫入;將檔案指標放在檔案的末尾。如果檔案不存在,請嘗試建立它。在此模式下,fseek() 只會影響讀取位置,寫入始終會附加。
'x' 建立並僅開啟用於寫入;將檔案指標放在檔案的開頭。如果檔案已存在,則 fopen() 呼叫將會失敗,並傳回 false,且會產生 E_WARNING 層級的錯誤。如果檔案不存在,請嘗試建立它。這相當於為基礎 open(2) 系統呼叫指定 O_EXCL|O_CREAT 旗標。
'x+' 建立並開啟用於讀取和寫入;否則它具有與 'x' 相同的行為。
'c' 僅開啟檔案用於寫入。如果檔案不存在,則會建立該檔案。如果檔案存在,則既不會被截斷(與 'w' 相反),也不會讓對此函式的呼叫失敗(與 'x' 的情況相同)。檔案指標會放在檔案的開頭。如果想要在嘗試修改檔案之前取得諮詢鎖定(請參閱 flock()),這可能很有用,因為使用 'w' 可能會在取得鎖定之前截斷檔案(如果需要截斷,可以在要求鎖定後使用 ftruncate())。
'c+' 開啟檔案用於讀取和寫入;否則它具有與 'c' 相同的行為。
'e' 在開啟的檔案描述子設定 close-on-exec 旗標。僅適用於在符合 POSIX.1-2008 的系統上編譯的 PHP。

注意:

不同的作業系統系列具有不同的換行慣例。當您寫入文字檔案並想要插入換行符號時,您需要為您的作業系統使用正確的換行符號。以 Unix 為基礎的系統使用 \n 作為換行符號,以 Windows 為基礎的系統使用 \r\n 作為換行符號,而以 Macintosh 為基礎的系統(Mac OS Classic)使用 \r 作為換行符號。

如果您在寫入檔案時使用錯誤的換行符號,您可能會發現開啟這些檔案的其他應用程式看起來會「怪怪的」。

Windows 提供文字模式轉換旗標 ('t'),這會在處理檔案時透明地將 \n 轉換為 \r\n。相反地,您也可以使用 'b' 強制使用二進位模式,這不會轉換您的資料。若要使用這些旗標,請指定 'b''t' 作為 mode 參數的最後一個字元。

預設的轉譯模式為 'b'。如果您處理的是純文字檔案,且在腳本中使用 \n 來分隔行尾,同時希望檔案能以舊版記事本等應用程式讀取,則可使用 't' 模式。在所有其他情況下,您都應使用 'b'

如果在處理二進位檔案時指定 't' 旗標,您的資料可能會發生奇怪的問題,包括影像檔案損壞以及 \r\n 字元出現奇怪的問題。

注意:

為求可攜性,強烈建議您重寫使用或依賴 't' 模式的程式碼,使其改為使用正確的行尾符號和 'b' 模式。

注意 mode 參數會被 php://outputphp://inputphp://stdinphp://stdoutphp://stderrphp://fd 串流封裝器忽略。

use_include_path

如果也想在 include_path 中搜尋檔案,可將可選的第三個參數 use_include_path 設為 true

context

內容串流 資源

傳回值

成功時傳回檔案指標資源,失敗時傳回 false

錯誤/例外

失敗時,會發出一個 E_WARNING

更新日誌

版本 描述
7.0.16, 7.1.2 新增了 'e' 選項。

範例

範例 #1 fopen() 範例

<?php
$handle
= fopen("/home/rasmus/file.txt", "r");
$handle = fopen("/home/rasmus/file.gif", "wb");
$handle = fopen("http://www.example.com/", "r");
$handle = fopen("ftp://user:password@example.com/somefile.txt", "w");
?>

注意事項

警告

當使用 SSL 時,Microsoft IIS 會違反協定,在未傳送 close_notify 指示符的情況下關閉連線。當您到達資料末尾時,PHP 會將此回報為「SSL: Fatal Protocol Error」。為了避免此問題,應將 error_reporting 的值降低到不包含警告的層級。當您使用 https:// 封裝器開啟串流時,PHP 可以偵測到有錯誤的 IIS 伺服器軟體,並會抑制警告。當使用 fsockopen() 建立 ssl:// socket 時,開發人員有責任偵測並抑制此警告。

注意:

如果您在使用 PHP 的伺服器模組版本時,在讀寫檔案時遇到問題,請記得確保您使用的檔案和目錄可供伺服器程序存取。

注意:

filename 為目錄時,此函式也可能成功。如果您不確定 filename 是檔案還是目錄,您可能需要在呼叫 fopen() 之前使用 is_dir() 函式。

參見

新增註解

使用者貢獻註解 21 則註解

146
chapman at worldtakeoverindustries dot com
12 年前
注意 - 在 'w' 模式下使用 fopen 不會像您預期的那樣更新檔案的修改時間 (filemtime)。您可能需要在寫入並關閉檔案後發出 touch(),以更新其修改時間。如果您打算保住頭髮,這在快取情況下可能變得至關重要。
43
匿名
4 年前
/***** 溫馨提醒 *****/
非常重要。除非您想刪除檔案中的所有內容,否則請勿使用 "w" 旗標。
10
php-manual at merlindynamics dot com
4 年前
有一個未記錄的模式可以使 fopen 變成非封鎖模式 (在 Windows 上無法運作)。在模式參數中新增 'n',fopen 將不會封鎖,但是如果管道不存在,則會引發錯誤。

$fp = fopen("/tmp/debug", "a"); //如果管道不存在,則封鎖

$fp = fopen("/tmp/debug", "an"); //如果管道不存在,則引發錯誤
25
php at delhelsa dot com
16 年前
在 Apache 2.2.4 上使用 php 5.2.5,如果需要絕對路徑,則使用 fopen() 或 readfile() 存取 FTP 伺服器上的檔案需要額外的正斜線。

也就是說,如果名為 bullbes.txt 的檔案儲存在 FTP 伺服器 example.com 上的 /var/school/ 下,而且您嘗試以使用者 blossom 和密碼 buttercup 來存取它,則 URL 會是

ftp://blossom:buttercup@example.com//var/school/bubbles.txt

請注意兩個正斜線。第二個正斜線似乎是必要的,這樣伺服器才不會將路徑解讀為相對於 blossom 在 townsville 的主目錄。
12
petepostma-deletethis at gmail dot com
7 年前
要透過文字描述來了解 fopen 模式的預期結果需要一段時間。這個 CSV 表格可以幫助您更快地了解,以找到您正在尋找的模式

模式,建立,讀取,寫入,指標開始位置,截斷檔案,注意事項,目的
r,,y,,開頭,,如果檔案不存在則失敗,基本讀取現有檔案
r+,,y,y,開頭,,如果檔案不存在則失敗,基本讀取/寫入現有檔案
w,y,,y,開頭+結尾,y,"建立、清除、寫入檔案"
w+,y,y,y,開頭+結尾,y,"建立、清除、寫入檔案 (含讀取選項)"
a,y,,y,結尾,,,"從檔案結尾寫入,如果需要則建立"
a+,y,y,y,結尾,,,"從檔案結尾寫入,如果需要則建立,並具有讀取選項"
x,y,,y,開頭,,如果檔案存在則失敗,"類似 w,但會防止覆寫現有檔案"
x+,y,y,y,開頭,,如果檔案存在則失敗,"類似 w+,但會防止覆寫現有檔案"
c,y,,y,開頭,,,開啟/建立檔案以進行寫入,而不會刪除目前的內容
c+,y,y,y,開頭,,,"開啟/建立可讀取的檔案,然後寫回"
12
ideacode
19 年前
請注意,是否可以開啟目錄取決於作業系統。以下幾行程式碼

<?php
// Windows ($fh === false)
$fh = fopen('c:\\Temp', 'r');

// UNIX (is_resource($fh) === true)
$fh = fopen('/tmp', 'r');
?>

示範在 Windows (2000,可能也包括 XP) 上,您無法開啟目錄 (錯誤為「拒絕存取」),無論該目錄的安全權限為何。

在 UNIX 上,您可以順利讀取原生檔案系統的目錄格式。
6
splogamurugan at gmail dot com
13 年前
在開啟包含多位元組資料 (例如:données multi-octets) 的檔案時,遇到一些編碼問題。得知它使用 windows-1250。使用 iconv 將其轉換為 UTF-8,就解決了問題。

<?php
function utf8_fopen_read($fileName) {
$fc = iconv('windows-1250', 'utf-8', file_get_contents($fileName));
$handle=fopen("php://memory", "rw");
fwrite($handle, $fc);
fseek($handle, 0);
return
$handle;
}
?>

範例用法

<?php
$fh
= utf8_fopen_read("./tpKpiBundle.csv");
while ((
$data = fgetcsv($fh, 1000, ",")) !== false) {
foreach (
$data as $value) {
echo
$value . "<br />\n";
}
}
?>

希望對您有幫助。
4
durwood at speakeasy dot NOSPAM dot net
19 年前
當我將伺服器移至新的 Fedora 4 安裝時,我怎樣也無法讓某個 PHP 腳本正常運作。問題是,當嘗試透過 Apache 將檔案作為 URL 存取時,fopen() 會失敗 -- 即使從 shell 執行時運作正常,而且即使從任何瀏覽器都可輕鬆讀取檔案。在嘗試將責任歸咎於 Apache、RedHat,甚至是我家的貓和狗之後,我終於在 Redhat 的網站上找到了這份錯誤報告

https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=164700

基本上問題出在 SELinux (我對它一無所知) -- 你必須執行以下指令,才能讓 SELinux 允許 php 開啟網頁檔案

/usr/sbin/setsebool httpd_can_network_connect=1

若要永久變更,請使用 -P 選項執行

/usr/sbin/setsebool -P httpd_can_network_connect=1

希望這對其他人有幫助 -- 我確實花了很長時間才追蹤到問題。
3
php at richardneill dot org
13 年前
如果要開啟的檔案是 fifo,`fopen()` 會被阻擋。無論是以 "r" 或 "w" 模式開啟都是如此。(請參考 man 7 fifo:這是正確的預設行為;雖然 Linux 支援對 fifo 進行非阻擋的 `fopen()`,但 PHP 不支援)。
其結果是,你無法得知最初的 fifo 讀/寫是否會被阻擋,因為要做到這點你需要 `stream_select()`,而 `stream_select()` 又需要 `fopen()` 已經發生!
2
etters dot ayoub at gmail dot com
6 年前
此函數在建立資料夾之前,會檢查遞迴權限和遞迴存在的父資料夾。以避免產生錯誤/警告。

/**
* 此函數在建立資料夾之前,會檢查遞迴權限和遞迴存在的父資料夾,
* 以避免產生錯誤/警告。
*
* @return bool
* true 表示資料夾已建立或存在且可寫入。
* False 表示資料夾不存在且無法建立。
*/
function createWritableFolder($folder)
{
if (file_exists($folder)) {
// 資料夾存在。
return is_writable($folder);
}
// 資料夾不存在,檢查父資料夾。
$folderParent = dirname($folder);
if($folderParent != '.' && $folderParent != '/' ) {
if(!createWritableFolder(dirname($folder))) {
// 建立父資料夾失敗。
return false;
}
// 父資料夾已建立。
}

if ( is_writable($folderParent) ) {
// 父資料夾可寫入。
if ( mkdir($folder, 0777, true) ) {
// 資料夾已建立。
return true;
}
// 建立資料夾失敗。
}
// 父資料夾不可寫入。
return false;
}

/**
* 此函數在建立資料夾之前,會檢查遞迴權限和遞迴存在的父資料夾,
* 在建立檔案/資料夾之前。以避免產生錯誤/警告。
*
* @return bool
* true 表示已建立或檔案存在且可寫入。
* False 表示檔案不存在且無法建立。
*/
function createWritableFile($file)
{
// 檢查設定檔是否存在。
if (file_exists($file)) {
// 檢查設定檔是否可寫入。
return is_writable($file);
}

// 檢查設定檔資料夾是否存在,並嘗試建立設定檔。
if(createWritableFolder(dirname($file)) && ($handle = fopen($file, 'a'))) {
fclose($handle);
return true; // 設定檔已建立。
}
// 無法存取的設定檔。
return false;
}
4
dan at cleandns dot com
21 年前
<?php
#即將更新上次使用者計數器腳本,因為
#因為檔案鎖定而中止寫入是不正確的。

$counter_file = '/tmp/counter.txt';
clearstatcache();
ignore_user_abort(true); ## 防止重新整理中止檔案操作並損壞檔案
if (file_exists($counter_file)) {
$fh = fopen($counter_file, 'r+');
while(
1) {
if (
flock($fh, LOCK_EX)) {
#$buffer = chop(fgets($fh, 2));
$buffer = chop(fread($fh, filesize($counter_file)));
$buffer++;
rewind($fh);
fwrite($fh, $buffer);
fflush($fh);
ftruncate($fh, ftell($fh));
flock($fh, LOCK_UN);
break;
}
}
}
else {
$fh = fopen($counter_file, 'w+');
fwrite($fh, "1");
$buffer="1";
}
fclose($fh);

print
"Count is $buffer";

?>
4
info at b1g dot de
19 年前
簡單的類別,用於取得 HTTP URL。支援 "Location:"-重新導向。適用於 `allow_url_fopen=false` 的伺服器。可與 SSL 安全的主機搭配使用。

<?php
# 使用方式:
$r = new HTTPRequest('http://www.example.com');
echo
$r->DownloadToString();

class
HTTPRequest
{
var
$_fp; // HTTP socket
var $_url; // 完整 URL
var $_host; // HTTP 主機
var $_protocol; // 協定 (HTTP/HTTPS)
var $_uri; // 請求 URI
var $_port; // 連接埠

// 掃描 URL
function _scan_url()
{
$req = $this->_url;

$pos = strpos($req, '://');
$this->_protocol = strtolower(substr($req, 0, $pos));

$req = substr($req, $pos+3);
$pos = strpos($req, '/');
if(
$pos === false)
$pos = strlen($req);
$host = substr($req, 0, $pos);

if(
strpos($host, ':') !== false)
{
list(
$this->_host, $this->_port) = explode(':', $host);
}
else
{
$this->_host = $host;
$this->_port = ($this->_protocol == 'https') ? 443 : 80;
}

$this->_uri = substr($req, $pos);
if(
$this->_uri == '')
$this->_uri = '/';
}

// 建構函式
function HTTPRequest($url)
{
$this->_url = $url;
$this->_scan_url();
}

// 下載 URL 成字串
function DownloadToString()
{
$crlf = "\r\n";

// 產生請求
$req = 'GET ' . $this->_uri . ' HTTP/1.0' . $crlf
. 'Host: ' . $this->_host . $crlf
. $crlf;

// 擷取
$this->_fp = fsockopen(($this->_protocol == 'https' ? 'ssl://' : '') . $this->_host, $this->_port);
fwrite($this->_fp, $req);
while(
is_resource($this->_fp) && $this->_fp && !feof($this->_fp))
$response .= fread($this->_fp, 1024);
fclose($this->_fp);

// 分割標頭和主體
$pos = strpos($response, $crlf . $crlf);
if(
$pos === false)
return(
$response);
$header = substr($response, 0, $pos);
$body = substr($response, $pos + 2 * strlen($crlf));

// 解析標頭
$headers = array();
$lines = explode($crlf, $header);
foreach(
$lines as $line)
if((
$pos = strpos($line, ':')) !== false)
$headers[strtolower(trim(substr($line, 0, $pos)))] = trim(substr($line, $pos+1));

// 重新導向?
if(isset($headers['location']))
{
$http = new HTTPRequest($headers['location']);
return(
$http->DownloadToString($http));
}
else
{
return(
$body);
}
}
}
?>
1
apathetic012 at gmail dot com
12 年前
當使用 fopen() 時,會有一個 $http_response_header 變數可用,其中包含回應標頭的陣列。
1
ken dot gregg at rwre dot com
21 年前
如果提供的路徑沒有檔案名稱,PHP 會開啟目錄。我剛遇到這個問題。我沒有檢查串接字串中的檔案名稱部分。

例如

<?php
$fd
= fopen('/home/mydir/' . $somefile, 'r');
?>

如果 $somefile = '',將會開啟目錄

如果您嘗試使用檔案控制代碼讀取,將會取得二進位目錄內容。我嘗試了附加模式,它會產生錯誤,因此似乎沒有危險性。

這是使用 FreeBSD 4.5 和 PHP 4.3.1 的情況。在 4.1.1 和 PHP 4.1.2 上行為相同。我沒有測試其他版本/作業系統組合。
-1
kasper at webmasteren dot eu
12 年前
「請勿將下列保留的裝置名稱用作檔案名稱
CON、PRN、AUX、NUL、COM1、COM2、COM3、COM4、COM5、COM6、COM7、COM8、COM9、LPT1、
LPT2、LPT3、LPT4、LPT5、LPT6、LPT7、LPT8 和 LPT9。也請避免這些名稱
後面緊接著副檔名;例如,不建議使用 NUL.txt。
如需更多資訊,請參閱命名空間」
這是 Windows 的限制。
請參閱
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
-1
wvss at gmail dot com
2 年前
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件</title>
</head>
<body>
<?php
// generiereHostliste.php

function generiereHostliste($file) {

// 從 Rechnerliste.csv 讀取
$fp = fopen($file, "r");
while(
$row = fgetcsv($fp, 0, ";")) {
$liste[]=[$row[0].";10.16.".$row[1].".".$row[2]];

}
fclose($fp);

// 寫入 Hostliste.csv
$fp = fopen("Hostliste.csv", "w");
foreach(
$liste as $row) {
echo
"<pre>";
print_r($row);
echo
"</pre>";
fputcsv($fp, $row, ";");
}
fclose($fp);
}
// 測試
$file = "Rechnerliste.csv";
generiereHostliste($file);

?>
</body>
</html>
-2
flobee
18 年前
下載:我需要一個函式來模擬 "wget url" 的功能,而且不要將資料緩衝到記憶體中,以避免處理大型檔案時出現問題。
<?php
function download($file_source, $file_target) {
$rh = fopen($file_source, 'rb');
$wh = fopen($file_target, 'wb');
if (
$rh===false || $wh===false) {
// 讀取或開啟檔案錯誤
return true;
}
while (!
feof($rh)) {
if (
fwrite($wh, fread($rh, 1024)) === FALSE) {
// '下載錯誤:無法寫入檔案 ('.$file_target.')';
return true;
}
}
fclose($rh);
fclose($wh);
// 沒有錯誤
return false;
}
?>
-1
keithm at aoeex dot NOSPAM dot com
23 年前
我正在開發一個 win32 的主控台腳本,並且注意到一些事情。在 win32 上,似乎您無法重新開啟輸入串流進行讀取,而是必須開啟一次,然後從那裡讀取。另外,我不知道這是不是一個錯誤,但似乎 fgets() 會讀取到換行符號為止。返回的字元數量是正確的,但它不會停止讀取並返回到腳本。我目前不知道有什麼解決方法,但我會繼續研究。

這是一些程式碼,用於解決 stdin 的關閉和重新開啟問題。

<?php
function read($length='255'){
if (!isset(
$GLOBALS['StdinPointer'])){
$GLOBALS['StdinPointer']=fopen("php://stdin","r");
}
$line=fgets($GLOBALS['StdinPointer'],$length);
return
trim($line);
}
echo
"輸入你的名字: ";
$name=read();
echo
"輸入你的年齡: ";
$age=read();
echo
"嗨 $name,你 $age 歲真是太棒了!";
@
fclose($StdinPointer);
?>
-4
ceo at l-i-e dot com
18 年前
如果您需要在 URL 上使用 fopen() 時設定逾時,您可以這樣做:
<?php
$timeout
= 3;
$old = ini_set('default_socket_timeout', $timeout);
$file = fopen('http://example.com', 'r');
ini_set('default_socket_timeout', $old);
stream_set_timeout($file, $timeout);
stream_set_blocking($file, 0);
// 其餘為標準程式碼
?>
-1
bohwaz
10 個月前
請注意,您無法寫入 HTTP 資源,例如執行 PUT 請求。

您會收到這個錯誤訊息:'Failed to open stream: HTTP wrapper does not support writeable connections'

要執行 PUT,您只能填入 HTTP 上下文的 'content' 鍵,或是改用 Curl。
-1
Derrick
1 年前
以 "r+" 模式開啟檔案,然後在讀取檔案之前嘗試使用 ftruncate 設定檔案指標位置,會導致檔案資料遺失,如同您以 "w" 模式開啟檔案一樣。

例如

$File = fopen($FilePath,"r+"); // 以讀寫模式開啟檔案

ftruncate($File, 0); // 設定指標位置(將會清除資料)

while(! feof($File)) { // 繼續直到到達檔案末尾

$Line = fgets($File); // 從檔案中取得一行到字串
$Line = trim($Line); // 修剪字串中的換行符號
}

ftruncate($File,0); // (不會清除資料)

fclose($File);
To Top