|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2012-01-06 14:29 UTC] james dot turner dot phpninja at gmail dot com
Description:
------------
fread does not throw or generate any error when attempting to read from a write only file stream.
Test script:
---------------
<?php
$tmp = tempnam(sys_get_temp_dir(), 'test_');
$stream = fopen($tmp, 'w');
$data = "";
while(!feof($stream)){
if(false === ($data .= fread($stream, 8192))){
break;
};
}
?>
Expected result:
----------------
Either feof to return false indicating end of file
Or fread erroring or returning false as a result of attempting to read a write-only stream.
Actual result:
--------------
An infinite loop will occur.
feof will never end (doesn't reach the end of the file because it's in write mode)
fread will never error despite attempting to read from a write only stream.
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Tue Oct 28 15:00:02 2025 UTC |
CONFIRMED for both 5.3.8 and 5.3.7 on Arch64, and for 5.3.4 on an unknown Linux. Note however, that the test script provided by OP is wrong. It should be: ------------ BEGIN CODE ------------ $tmp = tempnam(sys_get_temp_dir(), 'test_'); $stream = fopen($tmp, 'w'); $data = ""; while(!feof($stream)){ if(false === ($data = fread($stream, 8192))){ break; // ^---- no dot here }; } ------------- END CODE ------------- OP's code will fail regardless of the bug, because .= always produces a string.This is not a bug. fread only returns false if given invalid arguments. The bug is that you try to read from a stream that's write-only. This C program has analogous behavior: #include<stdio.h> int main(void) { FILE *f = fopen("/tmp/foobaz", "w"); printf("feof: %d\n", feof(f)); printf("read: %zd\n", fread((char[100]) {}, 1, 100, f)); printf("feof: %d\n", feof(f)); return 0; } gcc --std=c99 h.c && ./a.out feof: 0 read: 0 feof: 0Let me change your code just a bit: ------------------------------------------- #include<stdio.h> int main(void) { FILE *f = fopen("/tmp/foobaz", "w"); printf("feof: %d\n", feof(f)); printf("ferror: %d\n", ferror(f)); // <- HERE printf("read: %zd\n", fread((char[100]) {}, 1, 100, f)); printf("feof: %d\n", feof(f)); printf("ferror: %d\n", ferror(f)); // <- AND HERE return 0; } gcc --std=c99 h.c && ./a.out feof: 0 ferror: 0 read: 0 feof: 0 ferror: 1 ------------------------------------------- In PHP there is no `ferror`. `feof` does work of both stdio's `feof` and `ferror`, as described in the documentation [1]: "Returns TRUE if the file pointer is at EOF or an error occurs". Therefore, if we use analogy to stdio, `feof` should return `TRUE` in this case. [1] http://pl.php.net/manual/en/function.feof.phpYou are right: no one should try to read from write-only stream. Also no one should try to divide by 0 and no one should pass something other than stream to `fread`. But people do it all the time and that's why errors, warnings, exceptions and status codes exist. Using an invalid type of a stream is an error too and should be reported. If returning `FALSE` from `fread` breaks something, than I agree: `fread` bahaviour should not be changed. However a warning does not break anything, so it can be emitted. Since `php_stream` stores `fopen` flags in `mode`, I believe this can be done easily: --------------- BEGIN DIFF --------------- diff php5.3-201201041830/ext/standard/file.c php5.3-201201041830-modified/ext/standard/file.c 1893a1894,1898 > > if (strpbrk(stream->mode, "+r") == NULL) { /* r or any + mode is fine */ > php_error_docref(NULL TSRMLS_CC, E_WARNING, > "Reading from a write-only stream"); > } ---------------- END DIFF ---------------- After this modification some of ext/standard/tests/file/007* tests will fail, but this is expected. I agree that this case is a feature request.