[php] stat() 返回负的文件大小?整数溢出初探

in 前后端开发 with 0 comment

今天我发现一个神奇的问题,有一个文件获取不到文件大小

奇怪?

但是解压和右键完全没有问题,不是磁盘问题

属性

于是我手动stat了一下,好家伙,居然是负的

/mnt $ php -r "print_r(stat('18no.zip'));"
Array
(
    [dev] => 2048
    [ino] => 228
    [mode] => 33206
    [nlink] => 1
    [uid] => 0
    [gid] => 0
    [rdev] => 0
    [size] => -2008364745
    [atime] => 1716639595
    [mtime] => 1716104887
    [ctime] => 1716639573
    [blksize] => 4096
    [blocks] => 12854640
)

负的?我忽然想到了PHP官网的说明

整型数 int 的字长和平台有关,尽管通常最大值是大约二十亿(32 位有符号)。64 位平台下的最大值通常是大约 9E18。 PHP 不支持无符号的 int。int 值的字长可以用常量 PHP_INT_SIZE来表示, 最大值可以用常量 PHP_INT_MAX 来表示, 最小值可以用常量 PHP_INT_MIN 表示。

明白了,超过PHP_INT_MAX被解释成负数了,那么怎么办呢?
有一个变通的办法,使用blocks(分配的 512 字节块数)代替size
可惜Windows端不能用(64位的)

[dev] => 82248709
[ino] => 8796093022436
[mode] => 33206
[nlink] => 1
[uid] => 0
[gid] => 0
[rdev] => 0
[size] => 6581569847
[atime] => 1716639595
[mtime] => 1716104887
[ctime] => 1716104887
[blksize] => -1
[blocks] => -1

解决问题可以使用单位制,这样就无关CPU的

if($stat['size'] > 0){
    foreach (['B','KB','MB','GB','TB','PB'] as $i => $unit)
        if($stat['size'] / 1024 ** $i < 800){
            $size = round($stat['size'] / 1024 ** $i,1) . $unit;
            break;
        }
}elseif($stat['blocks'] > 0){
    foreach (['B','KB','MB','GB','TB','PB'] as $i => $unit)
        if($stat['blocks'] / 1024 ** $i < 2){
            $size = round(($stat['size'] / 1024 ** $i) * 512,1) . $unit;
            break;
        }
}else{
    $size = '-';
}

成功!!!

/mnt $ php test.php
6.1GB
Responses