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

fopen, php

Solution

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):

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`.

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

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

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.

Problem

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; ?> ```

Original source