61

Many answers here in Stack Overflow use fopen($file, "rw+"), but the manual doesn't list the "rw+" mode, there's only the "r+" mode (or "w+" mode).

So I was wondering, what does the "rw+" mode do? What's the difference between fopen($file, "rw+" or "r+"? I'm asking this because there is no documentation on the "rw+" mode.

One approach is to consider that the modes are additive, but I couldn't find any mention of combining modes in the fopen manual page (besides, what's the sense of combining "r" with "w+", if "w+" already makes it readable?). But most importantly, w+ mode truncates the file, while rw+ does not truncate it (therefore, they cannot be additive). Probably there is no rw+ mode, despite its use by Stack Overflow users. Maybe it works because the parser ignores the "w" letter, as the rw+ mode appears to be === r+?

To clarify my question: what is the "rw+" mode, that is, how does it differ from other modes? I only received two answers in the comments: either that I should check the documentation (I already checked and rechecked) and a wrong answer, which said it is equal to "w+" (it isn't). "w+" truncates the file, while "rw+" doesn't.

Here's a script for testing (it proves that w+ truncates the file, but rw+ doesn't):

<?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; 
?>
Veve
  • 6,182
  • 5
  • 37
  • 53
flen
  • 1,441
  • 1
  • 14
  • 39
  • 2
    The manual _does_ list those. It's `r` and `w+`. It's simply a combination. – Magnus Eriksson May 05 '17 at 05:38
  • 1
    @MagnusEriksson Thank you! But what does it mean? What's the difference? I mean, if "w+" is used, then it is already readable. What's the sense in putting "r" too? – flen May 05 '17 at 05:49
  • Yes, in this case, you should be able to omit the `r`-flag. You can see the difference between `r+` and `w+` in the manual. `w+` does more. – Magnus Eriksson May 05 '17 at 06:09
  • @MagnusEriksson OK, so there's no difference, it should only be `"w+"`? And how did you know about the possibility of combination of modes? I re-read the whole fopen part, couln't find it – flen May 05 '17 at 06:14
  • 1
    @MagnusEriksson By the way, you're answer is wrong, it is not equal to `"w+"`. It seems to be equal to `"r+"`. I just changed my code and the results went wrong with `"w+"` instead of `"rw+"` – flen May 05 '17 at 06:31
  • It looks like the `+` means "read", so if you add `r` to the mode, I would guess you're saying "open a file for writing, reading and reading". You're effectively asking for permission to read twice, which would be redundant. – halfer May 16 '17 at 14:02
  • 1
    I've cast a reopen vote, as I agree with the request to reopen (it's perhaps borderline, but I do think it has a specific answer). However please do not use posts to complain about voting decisions, especially if your tone is angry or sarcastic - that does not belong here. There is a Meta site where you are welcome to appeal voting decisions if you wish (we encourage it where a poster is not convinced by the close reason). – halfer May 16 '17 at 14:05
  • 1
    Thank you for your reply @halfer ! Unfortunally, `rw+` is not the same as `w+` plus the `r` mode. From my tests, it seems to be the same as the `r+` mode, and it has nothing to do with the `w` mode. I'm sorry if my question is still unclear, could you please let me know what makes it a borderline case? For me it was clear, since I want to know what the undocumented `rw+` mode does. I need to finish a job for this weekend, but I'll edit my question soon and post it in the Meta site. – flen May 17 '17 at 19:54
  • I regarded it as borderline on the basis that the answer I thought we had was not very useful (i.e. that you were requesting a permission twice). However since you believe you have found differences in behaviour then the question is much more worthy of being opened (and it is reopened now in any case). I think there's too many **Edit** addenda so I will try to iron those out, but maybe you could edit the question to show your tests? That would make for a much clearer question. – halfer May 18 '17 at 08:11
  • 2
    Thank you for the edits @halfer ! I'm still busy on something I need to deliver as soon as possible, but I'll make the edits this week. The main test was quite simple: my code breaks if I substitute `rw+` for `w+`, but works exactly as it's supposed to if I substitute it with `r+`. The reason is simple: `w+` truncates the file, but `rw+` doesn't, it behaves as far as I could tell identically to `r+` (that does not truncate the file). I ran some other tests on this but pretty basic stuff, always got `rw+` === `r+` – flen May 22 '17 at 00:32
  • 5
    This question is being discussed [in this meta question](https://meta.stackoverflow.com/q/350512/472495). – halfer Jun 11 '17 at 10:40

1 Answers1

71

I think you have pretty much figured this out. Although I guess, you want authoritative answer.

It is true that the documentation does not mention "rw+". Yet, there is something better: PHP source code!


The Rabbit Hole

I had a hard time trying to navigate the source code until I found the article series PHP's Source Code For PHP Developers (Part 1, Part 2, Part 3, Part 4). I know the links jump between two sites, that series is hip like that. By the way, I didn't find the announced Part 5.

Note: Those articles are old, they are talking about PHP 5.4.

Let us see what fopen actually does... let us fall into the rabbit hole...

First we look at the 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.

What does _php_stream_open_wrapper_ex do with mode? It passes it to stream_opener.

Looking for the definition of stream_opener took me to the type php_stream_wrapper_ops in php_streams.h.

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

There are actually a lot of initializations of php_stream_wrapper_ops that allow to open different things. We are looking at php_fopen_wrapper.c because it has an initialization of php_stream_wrapper_ops where stream_opener is php_plain_files_stream_opener.

We are getting there...

php_plain_files_stream_opener is further 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.

Finally, _php_stream_fopen calls php_stream_parse_fopen_modes. Which will take the string and output some flags, Yay!


Wonderland

Let us have a look at 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;
}

For abstract, this is what it does (ignoring the details):

  1. It takes the first character of mode and checks if it is r, w, a, x, c. If it recognizes any of those, it sets the appropriate flag. Otherwise we have a FAILURE.

  2. It looks for + somewhere in the string and sets the appropriate flags.

  3. It looks for e, n and t (depending on Preprocessor Directives) somewhere in the string and sets the appropriate flags.

  4. Return SUCCESS.


Back to the real world

You asked:

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

Nothing. PHP only cares that the string starts with "r" and has a "+". The "w" is ignored.

Final note: While it is tempting to play with it and write stuff like "read+", be careful with that, because those letter could someday have some meaning. It would not be forward compatible. In fact, in some context, "e" already has a meaning. Instead, I suggest, sticking to the documentation.


Thanks for the excuse to give a look at the PHP source code.

Theraot
  • 18,248
  • 4
  • 45
  • 72
  • 11
    So technically you could pass `"read+"`, or `"rrrrrrrrrrrrrrrrrrrrrrrrrr+"` – Tas Jun 11 '17 at 12:07
  • 5
    Only words that do not contain `e`, `n` or `t` are equivalent to plain `r`. So `"rrrrrrrrrrrrr"` yes, but `"read"` or any of @Theraot's or @Nisse's suggestions, no. Unless you do actually want the extra flags. – alexis Jun 11 '17 at 20:16
  • 6
    I know I'm supposed to avoid "thanks" comments, but this answer is awesome, thank you very much for it! It also sheds light on other languages (e.g., C, where I think the parser also ignores the "w' https://adelekeadelaja.blogspot.com.br/2015/06/c-programming-file-rw-mode-not-working.html ), I think people naturally go for "rw" because it's more intuitive to think of "read-write", but they forget about the plus (I think universally "w+" implies "read"). But I digress, thanks a lot again! – flen Jun 11 '17 at 23:34