diff --git a/ext/standard/file.c b/ext/standard/file.c index 4c31ee0eae661..5c0bf178bbbfd 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -566,6 +566,11 @@ PHP_FUNCTION(file_get_contents) RETURN_FALSE; } + /* disabling the read buffer allows doing the whole transfer + in just one read() system call */ + if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) + php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); + if (offset != 0 && php_stream_seek(stream, offset, ((offset > 0) ? SEEK_SET : SEEK_END)) < 0) { php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset); php_stream_close(stream); diff --git a/ext/standard/tests/streams/bug54946.phpt b/ext/standard/tests/streams/bug54946.phpt index 3be2a1367bc15..2e56b8abc5c80 100644 --- a/ext/standard/tests/streams/bug54946.phpt +++ b/ext/standard/tests/streams/bug54946.phpt @@ -31,11 +31,6 @@ fclose($stream); unlink($filename); ?> --EXPECTF-- -Notice: stream_get_contents(): Read of 8192 bytes failed with errno=9 Bad file descriptor in %s on line %d string(0) "" - -Notice: stream_get_contents(): Read of 8192 bytes failed with errno=9 Bad file descriptor in %s on line %d string(0) "" - -Notice: stream_get_contents(): Read of 8192 bytes failed with errno=9 Bad file descriptor in %s on line %d string(0) "" diff --git a/main/streams/streams.c b/main/streams/streams.c index 8a7e328007215..f2c599b3f8c2e 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -706,7 +706,11 @@ PHPAPI ssize_t _php_stream_read(php_stream *stream, char *buf, size_t size) break; } - if (!stream->readfilters.head && ((stream->flags & PHP_STREAM_FLAG_NO_BUFFER) || stream->chunk_size == 1)) { + /* if the read buffer is currently empty, don't buffer + * large reads (larger than chunk_size), because + * buffering would only hurt performance in such an + * edge case */ + if (!stream->readfilters.head && ((stream->flags & PHP_STREAM_FLAG_NO_BUFFER) || stream->chunk_size == 1 || (stream->writepos == stream->readpos && size >= stream->chunk_size))) { toread = stream->ops->read(stream, buf, size); if (toread < 0) { /* Report an error if the read failed and we did not read any data @@ -1470,6 +1474,26 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, int return ZSTR_EMPTY_ALLOC(); } + if (php_stream_is(src, PHP_STREAM_IS_STDIO) && + src->readfilters.head == NULL && + php_stream_stat(src, &ssbuf) == 0 && +#ifdef S_ISREG + S_ISREG(ssbuf.sb.st_mode) && +#endif + ssbuf.sb.st_size >= 0) { + /* optimized code path for unfiltered regular files: + * if we know the exact size, we can allocate the + * right buffer size and issue only one read() system + * call */ + + if (ssbuf.sb.st_size <= src->position) + return ZSTR_EMPTY_ALLOC(); + + const size_t remaining = ssbuf.sb.st_size - src->position; + if (remaining < maxlen) + maxlen = remaining; + } + if (maxlen == PHP_STREAM_COPY_ALL) { maxlen = 0; } @@ -1517,7 +1541,7 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, int ptr = ZSTR_VAL(result); // TODO: Propagate error? - while ((ret = php_stream_read(src, ptr, max_len - len)) > 0){ + while (!php_stream_eof(src) && (ret = php_stream_read(src, ptr, max_len - len)) > 0){ len += ret; if (len + min_room >= max_len) { result = zend_string_extend(result, max_len + step, persistent);