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 链接
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 {
/*
* 我们希望访问时间比当前访问时间晚 1 周
*/
$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
15 年前
这是一个基于
许多用户提交的代码片段的增强版 '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;
}

?>

|=---------[ 示例输出 ]

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

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

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

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

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

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

[time] => Array
(
[mtime] => 1227685253
[atime] => 1227685138
[ctime] => 1227685253
[accessed] => 2008 Nov Tue 23:38:58
[modified] => 2008 Nov Tue 23:40:53
[created] => 2008 Nov Tue 23:40:53
)
)
webmaster at askapache dot com
10 年前
在 GNU/Linux 系统上,你可以通过统计 '/proc' 目录下的硬链接来获取当前正在运行的进程数,如下所示:

$ stat -c '%h' /proc
118

你也可以在 php 中通过对 /proc 执行 stat 操作,并获取返回数组中的 [3] 'nlink'(链接数)来实现相同的功能。

以下是我正在使用的函数,它在被多次调用时会执行 clearstatcache()。

<?php

/**
* 返回正在运行的进程数量
*
* @link https://php.net/clearstatcache
* @link https://php.net/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
18 年前
Windows 上的文件日期和夏令时存在一个重要(但鲜为人知)的问题。这会影响 stat() 返回的 'atime' 和 'mtime' 元素,还会影响其他与文件系统相关的函数,例如 fileatime() 和 filemtime()。

在冬季(没有夏令时的情况下),Windows 会为给定文件报告一个特定的时间戳。但是,当夏天来临,夏令时开始时,Windows 会报告一个不同的时间戳!即使文件本身没有被修改,Windows 也会在夏令时期间将它读取的每个时间戳向前移动一个小时。

这一切都源于微软决定使用一种过时的跟踪文件日期的方法,以确保在 10 月份夏令时结束时的“重复小时”期间没有时间歧义,并保持与旧的 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 年前
针对第一行是
Re note posted by "admin at smitelli dot com"

我认为你把转换反了。如果你系统处于夏令时,而文件没有,你应该将 filemtime 加一个小时。相反,如果文件时间处于夏令时,而当前操作系统时间没有,你应该减去一个小时。

以下是一个简化、修正后的版本
<?php
function getmodtime($file) { // 返回文件修改的时间。
$mtime = filemtime($file);
// date('I') 在夏令时开启时返回 1,关闭时返回 0。
$diff = date('I')-date('I', $mtime);
// diff = 0,如果文件时间和操作系统时间都处于相同的夏令时设置
// diff = 1,如果操作系统处于夏令时,而文件没有
// diff = -1,如果文件处于夏令时,而操作系统没有
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 设置为冬季(非夏令时)
touch($file1, $time1); // 将 file1 设置为夏季(夏令时)

$ftime0 = filemtime($file0);
$ftime1 = filemtime($file1);
echo
"\nUncorrected: \n";
echo
'File 0: ' . ($ftime0-$time0) ."\n";
echo
'File 1: ' . ($ftime1-$time1) ."\n";
// 如果你的系统根据夏令时进行调整,则上述值中应该有一个是 3600 或 -3600,具体取决于时间。

$ftime0 = getmodtime($file0); // 使用 filemtime 修正
$ftime1 = getmodtime($file1); // 使用 filemtime 修正
echo "\nCorrected: \n";
echo
'File 0: ' . ($ftime0-$time0) ."\n";
echo
'File 1: ' . ($ftime1-$time1) ."\n";
// 两个修正后的值输出应该都是 0。
?>

输出
------------------------------
(在夏季运行时)
------------------------------
Date0 (ST): Tuesday, 01-Jan-08 10:00:00 EST
Date1 (DT): Friday, 01-Aug-08 10:00:00 EDT

Uncorrected
File 0: -3600
File 1: 0

Corrected
File 0: 0
File 1: 0
------------------------------
(在冬季运行时 -- 日期省略)
------------------------------
Uncorrected
File 0: 0
File 1: 3600

Corrected
File 0: 0
File 1: 0

针对 Re note posted by "admin at smitelli dot com",你下面的版本在替换到我的测试中时,会产生以下输出
------------------------------
(在夏季运行时 -- 日期省略)
------------------------------
Uncorrected
File 0: -3600
File 1: 0

Corrected
File 0: -7200
File 1: 0
------------------------------
你可以看到,操作与预期相反。
com dot gmail at algofoogle
19 年前
Re note posted by "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 /* 套接字 */
#define S_IFWHT 0160000 /* 白出 */
#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 年前
以下是 UNIX 手册页关于 stat 对文件更改和文件修改之间差异的说明

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
9 年前
如果在 32 位系统中未指定 noserverino 选项,则 stat() 可能无法在挂载的 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
19 年前
关于如何判断文件是否为文件夹,还有方便的 "is_dir" 函数。
Hellhound
16 年前
要忽略索引号或名称细节... 请使用

list($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks)
= lstat($directory_element);
Anonymous
18 年前
Re note posted by "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
19 年前
如果 2GB 的限制让您感到困扰,您可以使用这种完整的技巧。使用它来代替 filesize()

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