summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.STREAMS44
-rw-r--r--main/fopen_wrappers.h13
-rwxr-xr-xmain/php_streams.h25
-rwxr-xr-xmain/streams.c56
4 files changed, 120 insertions, 18 deletions
diff --git a/README.STREAMS b/README.STREAMS
index 897d40db4e..6100c8b272 100644
--- a/README.STREAMS
+++ b/README.STREAMS
@@ -51,7 +51,16 @@ Where:
IGNORE_URL - do not use plugin wrappers
REPORT_ERRORS - show errors in a standard format if something
goes wrong.
- opened_path is used to return the path of the actual file opened.
+ STREAM_MUST_SEEK - If you really need to be able to seek the stream
+ and don't need to be able to write to the original
+ file/URL, use this option to arrange for the stream
+ to be copied (if needed) into a stream that can
+ be seek()ed.
+
+ opened_path is used to return the path of the actual file opened,
+ but if you used STREAM_MUST_SEEK, may not be valid. You are
+ responsible for efree()ing opened_path. opened_path may be (and usually
+ is) NULL.
If you need to open a specific stream, or convert standard resources into
streams there are a range of functions to do this defined in php_streams.h.
@@ -109,6 +118,39 @@ The buffer is allocated using pemalloc(); you need to call pefree() to
release the memory when you are done.
As with copy_to_stream, this function will try use mmap where it can.
+If you have an existing stream and need to be able to seek() it, you
+can use this function to copy the contents into a new stream that can
+be seek()ed:
+
+PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream);
+
+It returns one of the following values:
+#define PHP_STREAM_UNCHANGED 0 /* orig stream was seekable anyway */
+#define PHP_STREAM_RELEASED 1 /* newstream should be used; origstream is no longer valid */
+#define PHP_STREAM_FAILED 2 /* an error occurred while attempting conversion */
+#define PHP_STREAM_CRITICAL 3 /* an error occurred; origstream is in an unknown state; you should close origstream */
+
+make_seekable will always set newstream to be the stream that is valid
+if the function succeeds.
+When you have finished, remember to close the stream.
+
+NOTE: If you only need to seek forwards, there is no need to call this
+function, as the php_stream_seek can emulate forward seeking when the
+whence parameter is SEEK_CUR.
+
+NOTE: Writing to the stream may not affect the original source, so it
+only makes sense to use this for read-only use.
+
+NOTE: If the origstream is network based, this function will block
+until the whole contents have been downloaded.
+
+NOTE: Never call this function with an origstream that is referenced
+as a resource! It will close the origstream on success, and this
+can lead to a crash when the resource is later used/released.
+
+NOTE: If you are opening a stream and need it to be seekable, use the
+STREAM_MUST_SEEK option to php_stream_open_wrapper();
+
Casting Streams
===============
What if your extension needs to access the FILE* of a user level file pointer?
diff --git a/main/fopen_wrappers.h b/main/fopen_wrappers.h
index f64efd40c8..1a9ca6cc3a 100644
--- a/main/fopen_wrappers.h
+++ b/main/fopen_wrappers.h
@@ -22,19 +22,6 @@
#include "php_globals.h"
-#define IGNORE_PATH 0
-#define USE_PATH 1
-#define IGNORE_URL 2
-/* There's no USE_URL. */
-#define ENFORCE_SAFE_MODE 4
-#define REPORT_ERRORS 8
-
-#ifdef PHP_WIN32
-# define IGNORE_URL_WIN IGNORE_URL
-#else
-# define IGNORE_URL_WIN 0
-#endif
-
PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC);
PHPAPI char *expand_filepath(const char *filepath, char *real_path TSRMLS_DC);
diff --git a/main/php_streams.h b/main/php_streams.h
index 65a83c8036..402156c335 100755
--- a/main/php_streams.h
+++ b/main/php_streams.h
@@ -146,13 +146,36 @@ PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_
#define php_stream_is_persistent(stream) (stream)->is_persistent
/* Wrappers support */
+
+#define IGNORE_PATH 0
+#define USE_PATH 1
+#define IGNORE_URL 2
+/* There's no USE_URL. */
+#define ENFORCE_SAFE_MODE 4
+#define REPORT_ERRORS 8
+/* If you don't need to write to the stream, but really need to
+ * be able to seek, use this flag in your options. */
+#define STREAM_MUST_SEEK 16
+
+#ifdef PHP_WIN32
+# define IGNORE_URL_WIN IGNORE_URL
+#else
+# define IGNORE_URL_WIN 0
+#endif
+
int php_init_stream_wrappers(TSRMLS_D);
int php_shutdown_stream_wrappers(TSRMLS_D);
PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC);
PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC);
-
PHPAPI php_stream *php_stream_open_wrapper(char *path, char *mode, int options, char **opened_path TSRMLS_DC);
+#define PHP_STREAM_UNCHANGED 0 /* orig stream was seekable anyway */
+#define PHP_STREAM_RELEASED 1 /* newstream should be used; origstream is no longer valid */
+#define PHP_STREAM_FAILED 2 /* an error occurred while attempting conversion */
+#define PHP_STREAM_CRITICAL 3 /* an error occurred; origstream is in an unknown state; you should close origstream */
+/* DO NOT call this on streams that are referenced by resources! */
+PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream);
+
#endif
/*
diff --git a/main/streams.c b/main/streams.c
index e84f6804b3..b67bc837fe 100755
--- a/main/streams.c
+++ b/main/streams.c
@@ -166,7 +166,7 @@ PHPAPI char *php_stream_gets(php_stream *stream, char *buf, size_t maxlen)
return stream->ops->gets(stream, buf, maxlen);
} else {
/* unbuffered fgets - poor performance ! */
- size_t n = 0;
+ size_t n = 1;
char *c = buf;
/* TODO: look at error returns? */
@@ -944,7 +944,7 @@ PHPAPI php_stream *php_stream_open_wrapper(char *path, char *mode, int options,
goto out;
}
- if ((options & USE_PATH) && PG(include_path) != NULL) {
+ if ((options & USE_PATH) && PG(include_path) != NULL) {
stream = php_stream_fopen_with_path(path, mode, PG(include_path), opened_path TSRMLS_CC);
goto out;
}
@@ -954,7 +954,29 @@ PHPAPI php_stream *php_stream_open_wrapper(char *path, char *mode, int options,
stream = php_stream_fopen(path, mode, opened_path TSRMLS_CC);
out:
- if (stream == NULL && (options & REPORT_ERRORS)) {
+ if (stream != NULL && (options & STREAM_MUST_SEEK)) {
+ php_stream *newstream;
+
+ switch(php_stream_make_seekable(stream, &newstream)) {
+ case PHP_STREAM_UNCHANGED:
+ return stream;
+ case PHP_STREAM_RELEASED:
+ return newstream;
+ default:
+ php_stream_close(stream);
+ stream = NULL;
+ if (options & REPORT_ERRORS) {
+ char *tmp = estrdup(path);
+ php_strip_url_passwd(tmp);
+ zend_error(E_WARNING, "%s(\"%s\") - could not make seekable - %s",
+ get_active_function_name(TSRMLS_C), tmp, strerror(errno));
+ efree(tmp);
+
+ options ^= REPORT_ERRORS;
+ }
+ }
+ }
+ if (stream == NULL && (options & REPORT_ERRORS)) {
char *tmp = estrdup(path);
php_strip_url_passwd(tmp);
zend_error(E_WARNING, "%s(\"%s\") - %s", get_active_function_name(TSRMLS_C), tmp, strerror(errno));
@@ -963,6 +985,34 @@ out:
return stream;
}
+PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream)
+{
+ assert(newstream != NULL);
+
+ *newstream = NULL;
+
+ if (origstream->ops->seek != NULL) {
+ *newstream = origstream;
+ return PHP_STREAM_UNCHANGED;
+ }
+
+ /* Use a tmpfile and copy the old streams contents into it */
+
+ *newstream = php_stream_fopen_tmpfile();
+
+ if (*newstream == NULL)
+ return PHP_STREAM_FAILED;
+
+ if (php_stream_copy_to_stream(origstream, *newstream, PHP_STREAM_COPY_ALL) == 0) {
+ php_stream_close(*newstream);
+ *newstream = NULL;
+ return PHP_STREAM_CRITICAL;
+ }
+
+ php_stream_close(origstream);
+
+ return PHP_STREAM_RELEASED;
+}
/*
* Local variables: