PHP 中的 fopen 模式 "r+" 和 "rw+" 有什么区别?

What is the difference between fopen modes "r+" and "rw+" in PHP?

Stack Overflow 中的许多答案都使用 fopen($file, "rw+"),但是 manual 没有列出 "rw+" 模式,只有 "r+" 模式(或 "w+" 模式).

所以我想知道,"rw+" 模式有什么作用? fopen($file, "rw+""r+" 之间有什么区别? 我问这个是因为没有关于 "rw+" 模式的文档。

一种方法是考虑模式是加法,但我在fopen手册页中找不到任何关于组合模式的提及(此外,什么是将 "r""w+" 结合起来的意义,如果 "w+" 已经使其可读?)。但最重要的是,w+ 模式会截断文件,而 rw+ 不会截断文件 (因此,它们不能相加)。可能没有 rw+ 模式,尽管它被 Stack Overflow 用户使用。也许它有效是因为解析器忽略了 "w" 字母,因为 rw+ 模式似乎是 === r+?

澄清我的问题:什么是"rw+"模式,即它与其他模式有何不同?我在评论中只收到了两个答案:要么我应该检查文档(我已经检查并重新检查)和一个错误的答案,它说它等于 "w+"(它不是)。 "w+" 截断文件,而 "rw+" 不截断文件。

这是一个测试脚本(它证明 w+ 会截断文件,但 rw+ 不会):

<?php 
$file = "somefile"; 
$fileOpened = fopen($file, "w"); 
fwrite($fileOpened, "0000000000000000000"); 
fclose($fileOpened);  

$fileOpened = fopen($file, "rw+"); 
fwrite($fileOpened, "data"); 
fclose($fileOpened); 
$fileOpened = fopen($file, "r"); 
$output = fgets($fileOpened); 
echo "First, with 'rw+' mode:<br>".$output; 

$fileOpened = fopen($file, "w+"); 
fwrite($fileOpened, "data"); 
fclose($fileOpened); 
$fileOpened = fopen($file, "r"); 
$output = fgets($fileOpened); 
echo "<br><br><br>Now with only 'w+' mode:<br>".$output; 
?>

我想你已经很清楚了。虽然我猜,你想要权威的答案。

文档确实没有提到"rw+"。然而,还有更好的东西:PHP source code!


兔子洞

在找到 PHP 系列文章 PHP 开发人员的源代码 (Part 1, Part 2, Part 3, Part 4) 之前,我很难尝试浏览源代码。 我知道两个站点之间的链接跳转,那个系列很时髦。对了,我没找到公布的Part 5

:那些文章都是老的,说的是PHP5.4.

让我们看看 fopen 到底做了什么...让我们掉进兔子洞...

首先我们看function definition (which I found following the advice of the linked part 2 above). I notice it uses php_stream_open_wrapper_ex, which I found elsewhere, it just uses _php_stream_open_wrapper_ex which we find in stream.c.

_php_stream_open_wrapper_exmode 有什么关系? It passes it to stream_opener.

寻找 stream_opener 的定义让我找到了类型 php_stream_wrapper_ops in php_streams.h

Searching for uses of the type php_stream_wrapper_ops leaded me to plain_wrapper.c.

php_stream_wrapper_ops实际上有很多初始化允许打开不同的东西。我们正在查看 php_fopen_wrapper.c,因为它具有 php_stream_wrapper_ops 的初始化,其中 stream_openerphp_plain_files_stream_opener

我们快到了...

php_plain_files_stream_openerfurther down in the same file. It delegates to php_stream_fopen_rel.

php_streams.h defines php_stream_fopen_rel by using _php_stream_fopen. Which is back on plain_wrapper.c.

最后,_php_stream_fopen calls php_stream_parse_fopen_modes。这将获取字符串并输出一些标志,耶!


仙境

让我们看看php_stream_parse_fopen_modes:

PHPAPI int php_stream_parse_fopen_modes(const char *mode, int *open_flags)
{
    int flags;

    switch (mode[0]) {
        case 'r':
            flags = 0;
            break;
        case 'w':
            flags = O_TRUNC|O_CREAT;
            break;
        case 'a':
            flags = O_CREAT|O_APPEND;
            break;
        case 'x':
            flags = O_CREAT|O_EXCL;
            break;
        case 'c':
            flags = O_CREAT;
            break;
        default:
            /* unknown mode */
            return FAILURE;
    }

    if (strchr(mode, '+')) {
        flags |= O_RDWR;
    } else if (flags) {
        flags |= O_WRONLY;
    } else {
        flags |= O_RDONLY;
    }

#if defined(O_CLOEXEC)
    if (strchr(mode, 'e')) {
        flags |= O_CLOEXEC;
    }
#endif

#if defined(O_NONBLOCK)
    if (strchr(mode, 'n')) {
        flags |= O_NONBLOCK;
    }
#endif

#if defined(_O_TEXT) && defined(O_BINARY)
    if (strchr(mode, 't')) {
        flags |= _O_TEXT;
    } else {
        flags |= O_BINARY;
    }
#endif

    *open_flags = flags;
    return SUCCESS;
}

对于摘要,这是它的作用(忽略细节):

  1. 它取mode的第一个字符并检查它是否是rwaxc。如果它识别出其中任何一个,它就会设置适当的标志。否则我们有 FAILURE.

  2. 它在字符串中的某处查找 + 并设置适当的标志。

  3. 它在字符串的某处查找 ent(取决于预处理器指令)并设置适当的标志。

  4. ReturnSUCCESS.


回到现实世界

您问的是:

What is the difference between fopen modes “r+” and “rw+” in PHP?

没有。 PHP只关心字符串以"r"开头,有一个"+""w" 被忽略。

最后的说明:虽然很想玩弄它并写一些像 "read+" 这样的东西,但要小心,因为这些字母有一天可能会有一些意义。它不会向前兼容。事实上,在某些上下文中,"e" 已经具有了意义。相反,我建议坚持文档。


感谢您提供查看 PHP 源代码的借口。