Подтвердить что ты не робот

В чем разница между режимами fopen "r +" и "rw +" в PHP?

Многие ответы здесь в Qaru используют 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+" - нет.

Здесь script для тестирования (это доказывает, что 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; 
?>
4b9b3361

Ответ 1

Я думаю, вы довольно многое поняли. Хотя, я думаю, вам нужен авторитетный ответ.

Верно, что в документации не упоминается "rw+". Тем не менее, есть что-то лучше: исходный код PHP!


Отверстие кролика

Мне было трудно ориентироваться в исходном коде до тех пор, пока я не нашел статью с исходным кодом PHP для разработчиков PHP (часть 1, часть 2, часть 3, Часть 4). Я знаю, что ссылки перепрыгивают между двумя сайтами, эта серия такова. Кстати, я не нашел объявленную часть 5.

Примечание. Эти статьи старые, они говорят о PHP 5.4.

Посмотрим, что на самом деле делает fopen... давайте попадаем в кроличью нору...

Сначала мы рассмотрим определение функции (которое я нашел по рекомендации связанной части 2 выше). Я заметил, что он использует php_stream_open_wrapper_ex, который я найден в другом месте, он просто использует _php_stream_open_wrapper_ex, который мы находим в stream.c.

Что делает _php_stream_open_wrapper_ex с mode? Он передает его stream_opener.

Поиск определения stream_opener привел меня к типу php_stream_wrapper_ops в php_streams.h.

Поиск использования типа php_stream_wrapper_ops привел меня к plain_wrapper.c.

На самом деле существует много инициализаций php_stream_wrapper_ops, которые позволяют открывать разные вещи. Мы смотрим на php_fopen_wrapper.c, поскольку он имеет инициализацию php_stream_wrapper_ops, где stream_opener есть php_plain_files_stream_opener.

Мы добираемся туда...

php_plain_files_stream_opener далее в том же файле. Он делегирует php_stream_fopen_rel.

php_streams.h определяет php_stream_fopen_rel с помощью _php_stream_fopen. Что называется назад на plain_wrapper.c.

Наконец, _php_stream_fopen вызывает php_stream_parse_fopen_modes. Который возьмет строку и выведет некоторые флаги, Yay!


Wonderland

Давайте посмотрим на 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;
}

Для абстрактного, это то, что он делает (игнорируя детали):

  • Он принимает первый символ mode и проверяет, есть ли он r, w, a, x, c. Если он распознает любой из них, он устанавливает соответствующий флаг. В противном случае мы имеем FAILURE.

  • Он ищет + где-то в строке и устанавливает соответствующие флаги.

  • Он ищет e, n и t (в зависимости от директив препроцессора) где-то в строке и устанавливает соответствующие флаги.

  • Возврат SUCCESS.


Возвращение в реальный мир

Вы спросили:

В чем разница между режимами fopen "r +" и "rw +" в PHP?

Ничего. PHP заботится только о том, что строка начинается с "r" и имеет "+". "w" игнорируется.

Заключительная записка. Хотя заманчиво играть с ней и писать такие вещи, как "read+", будьте осторожны с этим, потому что эта буква может когда-нибудь иметь какое-то значение. Он не был бы совместимым с переходом. Фактически, в некотором контексте "e" уже имеет смысл. Вместо этого я предлагаю, придерживаясь документации.


Спасибо за оправдание, чтобы посмотреть исходный код PHP.