PHP Conference Japan 2024

stat

(PHP 4, PHP 5, PHP 7, PHP 8)

stat取得檔案資訊

說明

stat(string $filename): array|false

收集由 filename 所指定檔案的統計資訊。如果 filename 是一個符號連結,統計資訊將來自該檔案本身,而不是符號連結。在 PHP 7.4.0 之前的版本中,在 Windows NTS 建置版本中,sizeatimemtimectime 統計資訊來自符號連結。

lstat()stat() 相同,只是它會基於符號連結的狀態。

參數

filename

檔案路徑。

回傳值

stat()fstat() 結果格式
數值型 關聯型 說明
0 dev 裝置編號 ***
1 ino inode 編號 ****
2 mode inode 保護模式 *****
3 nlink 連結數量
4 uid 擁有者使用者 ID *
5 gid 擁有者群組 ID *
6 rdev 裝置型別,如果 inode 是裝置
7 size 以位元組為單位的檔案大小
8 atime 上次存取時間 (Unix 時間戳記)
9 mtime 上次修改時間 (Unix 時間戳記)
10 ctime 上次 inode 變更時間 (Unix 時間戳記)
11 blksize 檔案系統 IO 的區塊大小 **
12 blocks 已配置的 512 位元組區塊數量 **

* 在 Windows 上,這將永遠是 0

** 僅在支援 st_blksize 型別的系統上有效 - 其他系統(例如 Windows)會回傳 -1

*** 在 Windows 上,從 PHP 7.4.0 開始,這是包含該檔案的磁碟區序號,它是一個 64 位元的 *無號* 整數,因此可能會溢位。先前,對於 stat(),它是磁碟機代號的數值表示法(例如,C:2),對於 lstat(),則是 0

**** 在 Windows 上,從 PHP 7.4.0 開始,這是與該檔案相關聯的識別碼,它是一個 64 位元的 *無號* 整數,因此可能會溢位。先前,它總是 0

***** 在 Windows 上,可寫入權限位元會根據唯讀檔案屬性設定,並且所有使用者、群組和擁有者都報告相同的值。與 is_writable() 不同,不會將 ACL 納入考量。

mode 的值包含數個函式讀取的資訊。當以八進制表示時,從右邊開始,前三個數字由 chmod() 回傳。下一個數字會被 PHP 忽略。接下來的兩個數字表示檔案型別

mode 檔案型別
八進制 mode 含義
0140000 socket
0120000 link
0100000 一般檔案
0060000 區塊裝置
0040000 目錄
0020000 字元裝置
0010000 fifo
因此,例如,一個一般檔案可以是 0100644,而一個目錄可以是 0040755

如果發生錯誤,stat() 會回傳 false

注意 因為 PHP 的整數型別是有號數,而且許多平台使用 32 位元的整數,所以對於大於 2GB 的檔案,某些檔案系統函式可能會回傳非預期的結果。

錯誤/例外

發生錯誤時,會發出 E_WARNING

變更記錄

版本 說明
7.4.0 在 Windows 上,裝置編號現在是包含該檔案的磁碟區序號,而 inode 編號是與該檔案相關聯的識別碼。
7.4.0 符號連結的 sizeatimemtimectime 統計資訊永遠是目標的資訊。先前在 Windows 的 NTS 建置版本中並非如此。

範例

範例 1 stat() 範例

<?php
/* 取得檔案統計資訊 */
$stat = stat('C:\php\php.exe');

/*
* 印出檔案存取時間,這與呼叫 fileatime() 相同
*/
echo '存取時間:' . $stat['atime'];

/*
* 印出檔案修改時間,這與呼叫 filemtime() 相同
*/
echo '修改時間:' . $stat['mtime'];

/* 印出裝置編號 */
echo '裝置編號:' . $stat['dev'];
?>

範例 2 將 stat() 資訊與 touch() 一起使用

<?php
/* 取得檔案統計資訊 */
$stat = stat('C:\php\php.exe');

/* 我們是否未能取得統計資訊? */
if (!$stat) {
echo
'stat() 呼叫失敗...';
} else {
/*
* 我們希望存取時間是在目前存取時間之後一週
*/
$atime = $stat['atime'] + 604800;

/* 修改檔案 */
if (!touch('some_file.txt', time(), $atime)) {
echo
'修改檔案失敗...';
} else {
echo
'touch() 回傳成功...';
}
}
?>

注意事項

注意:

請注意,時間解析度可能因檔案系統而異。

注意此函式的結果會被快取。有關更多詳細資訊,請參閱 clearstatcache()

提示

從 PHP 5.0.0 開始,此函式也可以與某些 URL 封裝器一起使用。請參閱 支援的協定與封裝器,以判斷哪些封裝器支援 stat() 系列的功能。

參見

新增筆記

使用者提供的筆記 18 個筆記

webmaster at askapache dot com
16 年前
這是一個基於以下內容的加強版 'stat' 函數:
許多使用者提交的程式碼片段和
@ http://www.askapache.com/security/chmod-stat.html

給它一個檔名,它會回傳一個類似 stat 的陣列。

<?php

function alt_stat($file) {

clearstatcache();
$ss=@stat($file);
if(!
$ss) return false; //無法取得檔案狀態

$ts=array(
0140000=>'ssocket',
0120000=>'llink',
0100000=>'-file',
0060000=>'bblock',
0040000=>'ddir',
0020000=>'cchar',
0010000=>'pfifo'
);

$p=$ss['mode'];
$t=decoct($ss['mode'] & 0170000); // 檔案編碼位元

$str =(array_key_exists(octdec($t),$ts))?$ts[octdec($t)]{0}:'u';
$str.=(($p&0x0100)?'r':'-').(($p&0x0080)?'w':'-');
$str.=(($p&0x0040)?(($p&0x0800)?'s':'x'):(($p&0x0800)?'S':'-'));
$str.=(($p&0x0020)?'r':'-').(($p&0x0010)?'w':'-');
$str.=(($p&0x0008)?(($p&0x0400)?'s':'x'):(($p&0x0400)?'S':'-'));
$str.=(($p&0x0004)?'r':'-').(($p&0x0002)?'w':'-');
$str.=(($p&0x0001)?(($p&0x0200)?'t':'x'):(($p&0x0200)?'T':'-'));

$s=array(
'perms'=>array(
'umask'=>sprintf("%04o",@umask()),
'human'=>$str,
'octal1'=>sprintf("%o", ($ss['mode'] & 000777)),
'octal2'=>sprintf("0%o", 0777 & $p),
'decimal'=>sprintf("%04o", $p),
'fileperms'=>@fileperms($file),
'mode1'=>$p,
'mode2'=>$ss['mode']),

'owner'=>array(
'fileowner'=>$ss['uid'],
'filegroup'=>$ss['gid'],
'owner'=>
(
function_exists('posix_getpwuid'))?
@
posix_getpwuid($ss['uid']):'',
'group'=>
(
function_exists('posix_getgrgid'))?
@
posix_getgrgid($ss['gid']):''
),

'file'=>array(
'filename'=>$file,
'realpath'=>(@realpath($file) != $file) ? @realpath($file) : '',
'dirname'=>@dirname($file),
'basename'=>@basename($file)
),

'filetype'=>array(
'type'=>substr($ts[octdec($t)],1),
'type_octal'=>sprintf("%07o", octdec($t)),
'is_file'=>@is_file($file),
'is_dir'=>@is_dir($file),
'is_link'=>@is_link($file),
'is_readable'=> @is_readable($file),
'is_writable'=> @is_writable($file)
),

'device'=>array(
'device'=>$ss['dev'], //裝置
'device_number'=>$ss['rdev'], //裝置號碼,如果是裝置的話
'inode'=>$ss['ino'], //檔案序號
'link_count'=>$ss['nlink'], //連結計數
'link_to'=>($s['type']=='link') ? @readlink($file) : ''
),

'size'=>array(
'size'=>$ss['size'], //檔案大小,以位元組為單位。
'blocks'=>$ss['blocks'], //已配置的 512 位元組區塊數
'block_size'=> $ss['blksize'] //I/O 的最佳區塊大小。
),

'time'=>array(
'mtime'=>$ss['mtime'], //上次修改時間
'atime'=>$ss['atime'], //上次存取時間。
'ctime'=>$ss['ctime'], //上次狀態變更時間
'accessed'=>@date('Y M D H:i:s',$ss['atime']),
'modified'=>@date('Y M D H:i:s',$ss['mtime']),
'created'=>@date('Y M D H:i:s',$ss['ctime'])
),
);

clearstatcache();
return
$s;
}

?>

|=---------[ 範例輸出 ]

陣列(
[perms] => 陣列
(
[umask] => 0022
[human] => -rw-r--r--
[octal1] => 644
[octal2] => 0644
[decimal] => 100644
[fileperms] => 33188
[mode1] => 33188
[mode2] => 33188
)

[filetype] => 陣列
(
[type] => file
[type_octal] => 0100000
[is_file] => 1
[is_dir] =>
[is_link] =>
[is_readable] => 1
[is_writable] => 1
)

[owner] => 陣列
(
[fileowner] => 035483
[filegroup] => 23472
[owner_name] => askapache
[group_name] => grp22558
)

[file] => 陣列
(
[filename] => /home/askapache/askapache-stat/htdocs/ok/g.php
[realpath] =>
[dirname] => /home/askapache/askapache-stat/htdocs/ok
[basename] => g.php
)

[device] => 陣列
(
[device] => 25
[device_number] => 0
[inode] => 92455020
[link_count] => 1
[link_to] =>
)

[size] => 陣列
(
[size] => 2652
[blocks] => 8
[block_size] => 8192
)

[time] => 陣列
(
[mtime] => 1227685253
[atime] => 1227685138
[ctime] => 1227685253
[accessed] => 2008 年 11 月 週二 23:38:58
[modified] => 2008 年 11 月 週二 23:40:53
[created] => 2008 年 11 月 週二 23:40:53
)
)
webmaster at askapache dot com
10 年前
在 GNU/Linux 上,您可以對 '/proc' 目錄執行硬連結的 stat,藉此取得機器上目前正在執行的處理程序數量,如下所示:

$ stat -c '%h' /proc
118

您可以在 PHP 中對 /proc 執行 stat,並抓取傳回陣列中的 [3] 'nlink' - 連結數量,來達到相同的效果。

這是我正在使用的函式,當被呼叫多次時,它會執行 clearstatcache()。

<?php

/**
* 傳回正在執行的處理程序數量
*
* @link https://php.dev.org.tw/clearstatcache
* @link https://php.dev.org.tw/stat stat 語法的說明。
* @author http://www.askapache.com/php/get-number-running-proccesses.html
* @return int
*/
function get_process_count() {
static
$ver, $runs = 0;

// 檢查 php 版本是否支援 clearstatcache 參數,但只檢查一次
if ( is_null( $ver ) )
$ver = version_compare( PHP_VERSION, '5.3.0', '>=' );

// 只有在函式被呼叫多次時才呼叫 clearstatcache() */
if ( $runs++ > 0 ) { // 檢查 $runs > 0,然後將 $runs 遞增一。

// 如果 php 版本 >= 5.3.0
if ( $ver ) {
clearstatcache( true, '/proc' );
} else {
// 如果 php 版本 < 5.3.0
clearstatcache();
}
}

$stat = stat( '/proc' );

// 如果 stat 成功且 nlink 值存在,則傳回該值,否則傳回 0
return ( ( false !== $stat && isset( $stat[3] ) ) ? $stat[3] : 0 );
}

?>

範例 1 get_process_count() 範例

<?php
$num_procs
= get_process_count();
var_dump( $num_procs );
?>

以上範例會輸出

int(118)

這是當時正在執行的處理程序數量。
admin at smitelli dot com
19 年前
Windows 上的檔案日期存在一個重要(但鮮為人知)的問題,那就是日光節約時間。這會影響 stat() 傳回的 'atime' 和 'mtime' 元素,也會影響其他與檔案系統相關的函式,例如 fileatime() 和 filemtime()。

在冬季月份(日光節約時間未生效時),Windows 會為給定的檔案報告某個時間戳記。然而,當夏季來臨且日光節約時間開始時,Windows 會報告不同的時間戳記!即使檔案根本沒有被修改過,Windows 也會在日光節約時間期間,將它讀取到的每個時間戳記向前移動整整一小時。

這一切都源於微軟決定使用一種老套的方法來追蹤檔案日期,以確保在 10 月份 DST 結束時的「重複小時」期間不會出現模糊的時間,並保持與較舊的 FAT 分割區的相容性等等。關於這一切的詳細說明可以在以下網址找到:http://www.codeproject.com/datetime/dstbugs.asp

這值得注意的是,因為 *nix 平台沒有這個問題。如果您嘗試在平台之間移動追蹤檔案時間戳記的腳本,可能會引入一些難以追蹤的錯誤。

我花費了相當多的時間來偵錯我自己的其中一個受到此問題影響的腳本。我將檔案修改時間儲存在 MySQL 表格中,然後使用該資訊來查看自上次執行腳本以來哪些檔案被修改過。每次日光節約時間變更後,腳本看到的每個檔案都被視為自上次執行以來「已變更」,因為所有的時間戳記都差了 +/- 3600 秒。

以下的一行程式碼可能是能想到的最不正確的修復方式之一,但它在生產環境中完美地運作...假設 $file_date 是您剛從檔案讀取的 Unix 時間戳記

<?php
if (date('I') == 1) $file_date -= 3600;
?>

這將確保您正在處理的時間戳記始終一致地報告,無論機器是否處於日光節約時間。
mail4rico at gmail dot com
16 年前
針對第一行為下列內容的註解:
回覆 "admin at smitelli dot com" 發表的註解

我認為您的轉換方式是反向的。如果系統處於 DST 且檔案不是,您應該將一個小時加到 filemtime。相反地,如果檔案時間是 DST 且目前的 OS 時間不是,您應該減去一個小時。

以下是一個簡化的更正版本
<?php
function getmodtime($file) { //傳回檔案被修改的時間。
$mtime = filemtime($file);
//date('I') 如果 DST 開啟則傳回 1,如果關閉則傳回 0。
$diff = date('I')-date('I', $mtime);
//diff = 0 如果檔案時間和作業系統時間都在相同的 DST 設定中
//diff = 1 如果作業系統是 DST 且檔案不是
//diff = -1 如果檔案是 DST 且作業系統不是
return $mtime + $diff*3600;
}
?>
以下是一個測試
<?php
//建立兩個虛擬檔案:
$file0 = 'file1.txt';
$file1 = 'file2.txt';
file_put_contents($file0, '');
file_put_contents($file1, '');

$time0=strtotime('Jan 1 2008 10:00'); echo 'Date0 (ST): ' . date(DATE_COOKIE, $time0)."\n";
$time1=strtotime('Aug 1 2008 10:00'); echo 'Date1 (DT): ' . date(DATE_COOKIE, $time1)."\n";
touch($file0, $time0); //將 file0 設定為冬季 (非 DST)
touch($file1, $time1); //將 file1 設定為夏季 (DST)

$ftime0 = filemtime($file0);
$ftime1 = filemtime($file1);
echo
"\n未更正: \n";
echo
'檔案 0: ' . ($ftime0-$time0) ."\n";
echo
'檔案 1: ' . ($ftime1-$time1) ."\n";
//如果您的系統會針對 DST 進行調整,則上面的 _其中一個_ 應該是 3600 或 -3600,具體取決於一年中的時間

$ftime0 = getmodtime($file0); //使用 filemtime 更正
$ftime1 = getmodtime($file1); //使用 filemtime 更正
echo "\n已更正: \n";
echo
'檔案 0: ' . ($ftime0-$time0) ."\n";
echo
'檔案 1: ' . ($ftime1-$time1) ."\n";
//兩個更正後的值輸出都應該是 0。
?>

輸出
------------------------------
(在夏季執行時)
------------------------------
Date0 (ST): 星期二, 01-Jan-08 10:00:00 EST
Date1 (DT): 星期五, 01-Aug-08 10:00:00 EDT

未更正
檔案 0: -3600
檔案 1: 0

已更正
檔案 0: 0
檔案 1: 0
------------------------------
(在冬季執行時 -- 日期省略)
------------------------------
未更正
檔案 0: 0
檔案 1: 3600

已更正
檔案 0: 0
檔案 1: 0

針對回覆 "admin at smitelli dot com" 發表的註解,以下版本在代入我的測試時會產生以下輸出
------------------------------
(在夏季執行時 -- 日期省略)
------------------------------
未更正
檔案 0: -3600
檔案 1: 0

已更正
檔案 0: -7200
檔案 1: 0
------------------------------
您可以看到該操作與它應該執行的操作相反。
com dot gmail at algofoogle
19 年前
回覆 "salisbm at hotmail dot com" 發表的註解

S_IFDIR 並非單一位元旗標。它是一個依賴 "S_IFMT" 位元遮罩的常數。如同 stat.h 所指示,在與任何其他的 "S_IF..." 常數比較之前,應該將此位元遮罩應用於 "mode" 參數。

#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)

也就是說,以下的方法是不正確的

<?php
define
('S_IFDIR',040000);
if (
$mode & S_IFDIR)
{
/*
不正確!
格式可能是 S_IFDIR,但也可能是
S_IFBLK、S_IFSOCK 或 S_IFWHT。
*/
}
?>

...正確的方式應該是

<?php
define
('S_IFMT',0170000);
define('S_IFDIR',040000);
if (
S_IFDIR == ($mode & S_IFMT)) { /* ... */ }
?>

然而,正如 "svend at svendtofte dot com" 所指出的,還有 "is_dir" 函數可用於此目的,以及 "is_file" 和 "is_link" 來涵蓋最常見的格式類型...
salisbm at hotmail dot com
21 年前
我很好奇如何判斷一個檔案是否為目錄... 因此我在 http://www.hmug.org/man/2/stat.html 上找到了關於模式位元的以下資訊
#define S_IFMT 0170000 /* 檔案類型 */
#define S_IFIFO 0010000 /* 具名管道 (fifo) */
#define S_IFCHR 0020000 /* 字元特殊檔 */
#define S_IFDIR 0040000 /* 目錄 */
#define S_IFBLK 0060000 /* 區塊特殊檔 */
#define S_IFREG 0100000 /* 一般檔案 */
#define S_IFLNK 0120000 /* 符號連結 */
#define S_IFSOCK 0140000 /* socket */
#define S_IFWHT 0160000 /* whiteout */
#define S_ISUID 0004000 /* 執行時設定使用者 ID */
#define S_ISGID 0002000 /* 執行時設定群組 ID */
#define S_ISVTX 0001000 /* 即使在使用後也保留交換文字 */
#define S_IRUSR 0000400 /* 讀取權限,擁有者 */
#define S_IWUSR 0000200 /* 寫入權限,擁有者 */
#define S_IXUSR 0000100 /* 執行/搜尋權限,擁有者 */

請注意,這些數字是八進位格式。然後,要檢查檔案是否為目錄,在呼叫 fstat 之後,我會執行

if ($fstats[mode] & 040000)
... 這一定是個目錄
ian at eiloart dot com
25 年前
這是關於 stat 的 UNIX 手冊頁對於檔案變更和檔案修改之間的差異的說明

st_mtime 上次修改資料的時間。由以下函數更改:creat()、mknod()、pipe()、utime() 和 write(2)。

st_ctime 上次變更檔案狀態的時間。由以下函數更改:chmod()、chown()、creat()、link(2)、mknod()、pipe()、unlink(2)、utime() 和 write()。

因此,修改是資料的變更,而如果修改檔案權限等等,也會發生變更。
JulieC
17 年前
由 "marting.dc AT gmail.com" 提供的 dir_size 函數效果很好,只是 $mas 變數未初始化。在 while() 迴圈之前新增

$mas = 0;

marting.dc AT gmail.com
18 年前
如果您想知道目錄大小,此函數將對您有所幫助

<?php
function dir_size($dir)
{
$handle = opendir($dir);

while (
$file = readdir($handle)) {
if (
$file != '..' && $file != '.' && !is_dir($dir.'/'.$file)) {
$mas += filesize($dir.'/'.$file);
} else if (
is_dir($dir.'/'.$file) && $file != '..' && $file != '.') {
$mas += dir_size($dir.'/'.$file);
}
}
return
$mas;
}
echo
dir_size('DIRECTORIO').' Bytes';
?>
mao at nospam dot com
19 年前
如果您的遠端伺服器停用了 ftp (以及相關的 sftp) 通訊協定,可能會很難弄清楚如何「stat」遠端檔案。以下方法對我有效

<?php

$conn
= ssh2_connect($host, 22);
ssh2_auth_password($conn, $user, $password);
$stream = ssh2_exec($conn, "stat $fileName > $remotedest");
ssh2_scp_recv($conn, $remotedest, $localdest);
$farray = file($localdest);
print_r($farray);
?>
digitalaudiorock at hotmail dot com
15 年前
關於在 32 位元系統上對大於 2GB 的檔案執行 stat() 會失敗的問題,請注意,在 Linux 和 Windows 之間的行為似乎有所不同。在 Windows 下,無法知道它是否失敗。

根據我的經驗,在 Linux 下,對超出整數大小的檔案執行 stat() 會產生警告並返回 false。但是,在 Windows 下,它會靜默地截斷大小的高位元,導致不正確的數字。您唯一知道它失敗的方式是,如果截斷剛好讓符號位元開啟,從而產生負數大小。也就是說,沒有可靠的方法知道它是否失敗。

filesize() 也適用於此。

Tom
carlos [at] encore-lab [dot] com
10 年前
如果您在掛載時未指定 noserverino 選項,則 stat() 可能無法在 32 位元系統上的掛載 CIFS 上運作。例如

mount -t cifs -o user="user",password="password",noserverino //example.local/share /mnt/mount-point

其他基於 stat() 資料的函數,例如檔案時間函數和 is_dir(),也會受到相同的影響。

發生這種情況是因為如果您未指定 noserverino 選項,則遠端 inode 可能基於 64 位元,因此本機系統無法處理它。
svend at svendtofte dot com
20 年前
關於如何判斷檔案是否為資料夾的說明,還有一個方便的 "is_dir" 函數。
Hellhound
16 年前
要忽略索引號碼或名稱的細節,請使用

list($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks)
= lstat($directory_element);
Anonymous
18 年前
回覆 "admin at smitelli dot com" 發表的註解

我不確定這如何全年運作,因為您必須根據實際檔案本身,以及系統的目前 DST 設定,修改內部的和外部的 DST。

例如,使用 filemtime,stat 也是如此。

<?php

$mtime
= filemtime($file);

if (
date('I') == 1) {
// Win DST 已啟用,將標準時間
// 檔案調整回「實際」檔案 UTC。
if (date('I', $mtime) == 0) {
$mtime -= 3600;
}
} else {
// Win DST 已停用,將日光時間
// 檔案向前調整為「實際」檔案 UTC。
if (date('I', $mtime) == 1) {
$mtime += 3600;
}
}

echo
gmdate('Y-m-d H:i:s', $mtime);

?>

這只是另一個「不」在伺服器機房中使用 Windows 的原因範例。
antonixyz at gmx dot net
16 年前
<?php
$stat
= stat($filepath);
$mode = $stat[2];
?>
與以下程式碼相同
<?php $mode = fileperms($filepath); ?>

至少在我的 Linux 機器上是如此。
Anonymous
20 年前
如果 2GB 的限制讓您感到困擾,您可以使用這個完整的 hack。取代 filesize() 使用

function file_size($file) {
$size = filesize($file);
if ( $size == 0)
$size = exec("ls -l $file | awk '{print $5}'");
return $size;
}
To Top