From: Wez Furlong Date: Tue, 20 Aug 2002 20:47:47 +0000 (+0000) Subject: Implement filter API for streams. X-Git-Tag: RELEASE_0_91~368 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9d348ea80059301a099b8f3dce25429c0760666d;p=php Implement filter API for streams. Filters can be stacked onto a stream; more details will follow in docs and on php-dev. Implement "string.rot13" filter Allows the following script: $fp = fopen("file.txt", "r"); stream_filter_prepend($fp, "string.rot13"); // File contents will be subject to a rot13 transformation before // being output. fpassthru($fp); fclose($fp); --- diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index e9abf4e749..c25f874a9e 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -607,6 +607,8 @@ function_entry basic_functions[] = { PHP_FE(stream_context_set_params, NULL) PHP_FE(stream_context_set_option, NULL) PHP_FE(stream_context_get_options, NULL) + PHP_FE(stream_filter_prepend, NULL) + PHP_FE(stream_filter_append, NULL) PHP_FE(fgetcsv, NULL) PHP_FE(flock, NULL) PHP_FE(get_meta_tags, NULL) @@ -980,6 +982,7 @@ PHP_MINIT_FUNCTION(basic) PHP_MINIT(file) (INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(pack) (INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(browscap) (INIT_FUNC_ARGS_PASSTHRU); + PHP_MINIT(string_filters) (INIT_FUNC_ARGS_PASSTHRU); #if defined(HAVE_LOCALECONV) && defined(ZTS) PHP_MINIT(localeconv) (INIT_FUNC_ARGS_PASSTHRU); @@ -1043,6 +1046,7 @@ PHP_MSHUTDOWN_FUNCTION(basic) PHP_MSHUTDOWN(assert) (SHUTDOWN_FUNC_ARGS_PASSTHRU); PHP_MSHUTDOWN(url_scanner_ex) (SHUTDOWN_FUNC_ARGS_PASSTHRU); PHP_MSHUTDOWN(file) (SHUTDOWN_FUNC_ARGS_PASSTHRU); + PHP_MSHUTDOWN(string_filters) (SHUTDOWN_FUNC_ARGS_PASSTHRU); #if defined(HAVE_LOCALECONV) && defined(ZTS) PHP_MSHUTDOWN(localeconv) (SHUTDOWN_FUNC_ARGS_PASSTHRU); #endif diff --git a/ext/standard/file.c b/ext/standard/file.c index 0404fa0a1b..bd0202f018 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -724,7 +724,7 @@ PHP_FUNCTION(stream_context_set_option) zval *options = NULL, *zcontext = NULL, *zvalue = NULL; php_stream_context *context; char *wrappername, *optionname; - long wrapperlen, optionlen; + int wrapperlen, optionlen; if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "rssz", &zcontext, &wrappername, &wrapperlen, @@ -791,6 +791,49 @@ PHP_FUNCTION(stream_context_create) } /* }}} */ +static void apply_filter_to_stream(int append, INTERNAL_FUNCTION_PARAMETERS) +{ + zval *zstream; + php_stream *stream; + char *filtername, *filterparams = NULL; + int filternamelen, filterparamslen = 0; + php_stream_filter *filter; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|s", &zstream, + &filtername, &filternamelen, &filterparams, &filterparamslen) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(stream, php_stream*, &zstream, -1, "stream", le_stream); + + filter = php_stream_filter_create(filtername, filterparams, filterparamslen, php_stream_is_persistent(stream) TSRMLS_CC); + if (filter == NULL) + RETURN_FALSE; + + if (append) + php_stream_filter_append(stream, filter); + else + php_stream_filter_prepend(stream, filter); + + RETURN_TRUE; +} + +/* {{{ proto bool stream_filter_prepend(resource stream, string filtername[, string filterparams]) + Prepend a filter to a stream */ +PHP_FUNCTION(stream_filter_prepend) +{ + apply_filter_to_stream(0, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto bool stream_filter_append(resource stream, string filtername[, string filterparams]) + Append a filter to a stream */ +PHP_FUNCTION(stream_filter_append) +{ + apply_filter_to_stream(1, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + /* {{{ proto resource fopen(string filename, string mode [, bool use_include_path [, resource context]]) Open a file or a URL and return a file pointer */ PHP_NAMED_FUNCTION(php_if_fopen) diff --git a/ext/standard/file.h b/ext/standard/file.h index 0ea36c7907..c2af165679 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -77,6 +77,8 @@ PHP_FUNCTION(stream_context_create); PHP_FUNCTION(stream_context_set_params); PHP_FUNCTION(stream_context_set_option); PHP_FUNCTION(stream_context_get_options); +PHP_FUNCTION(stream_filter_prepend); +PHP_FUNCTION(stream_filter_append); PHP_MINIT_FUNCTION(user_streams); PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC); diff --git a/ext/standard/php_string.h b/ext/standard/php_string.h index d63f91b254..6fbc57bd42 100644 --- a/ext/standard/php_string.h +++ b/ext/standard/php_string.h @@ -89,6 +89,9 @@ PHP_FUNCTION(strcoll); PHP_FUNCTION(money_format); #endif +PHP_MINIT_FUNCTION(string_filters); +PHP_MSHUTDOWN_FUNCTION(string_filters); + #if defined(HAVE_LOCALECONV) && defined(ZTS) PHP_MINIT_FUNCTION(localeconv); PHP_MSHUTDOWN_FUNCTION(localeconv); diff --git a/ext/standard/string.c b/ext/standard/string.c index 0940eab539..3b62ea00c4 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -3883,15 +3883,15 @@ PHP_FUNCTION(sscanf) } /* }}} */ +static char rot13_from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static char rot13_to[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"; + /* {{{ proto string str_rot13(string str) Perform the rot13 transform on a string */ PHP_FUNCTION(str_rot13) { zval **arg; - static char xfrom[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - static char xto[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"; - if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg)) { WRONG_PARAM_COUNT; } @@ -3899,7 +3899,7 @@ PHP_FUNCTION(str_rot13) *return_value = **arg; zval_copy_ctor(return_value); - php_strtr(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), xfrom, xto, 52); + php_strtr(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), rot13_from, rot13_to, 52); } /* }}} */ @@ -3926,6 +3926,82 @@ PHP_FUNCTION(money_format) { /* }}} */ #endif +/* {{{ rot13 stream filter implementation */ +static size_t strfilter_rot13_write(php_stream *stream, php_stream_filter *thisfilter, + const char *buf, size_t count TSRMLS_DC) +{ + char rotbuf[1024]; + size_t chunk; + size_t wrote = 0; + + while (count > 0) { + chunk = count; + if (chunk > sizeof(rotbuf)) + chunk = sizeof(rotbuf); + + PHP_STRLCPY(rotbuf, buf, sizeof(rotbuf), chunk); + buf += chunk; + count -= chunk; + + php_strtr(rotbuf, chunk, rot13_from, rot13_to, 52); + wrote += php_stream_filter_write_next(stream, thisfilter, rotbuf, chunk); + } + + return wrote; +} + +static size_t strfilter_rot13_read(php_stream *stream, php_stream_filter *thisfilter, + char *buf, size_t count TSRMLS_DC) +{ + size_t read; + + read = php_stream_filter_read_next(stream, thisfilter, buf, count); + php_strtr(buf, read, rot13_from, rot13_to, 52); + + return read; +} + +static int strfilter_rot13_flush(php_stream *stream, php_stream_filter *thisfilter TSRMLS_DC) +{ + return php_stream_filter_flush_next(stream, thisfilter); +} + +static int strfilter_rot13_eof(php_stream *stream, php_stream_filter *thisfilter TSRMLS_DC) +{ + return php_stream_filter_eof_next(stream, thisfilter); +} + + +static php_stream_filter_ops strfilter_rot13_ops = { + strfilter_rot13_write, + strfilter_rot13_read, + strfilter_rot13_flush, + strfilter_rot13_eof, + NULL, + "string.rot13" +}; + +static php_stream_filter *strfilter_rot13_create(const char *filtername, const char *filterparams, + int filterparamslen, int persistent TSRMLS_DC) +{ + return php_stream_filter_alloc(&strfilter_rot13_ops, NULL, persistent); +} + +static php_stream_filter_factory strfilter_rot13_factory = { + strfilter_rot13_create +}; + +PHP_MINIT_FUNCTION(string_filters) +{ + return php_stream_filter_register_factory("string.rot13", &strfilter_rot13_factory); +} + +PHP_MSHUTDOWN_FUNCTION(string_filters) +{ + return php_stream_filter_unregister_factory("string.rot13"); +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/main/php_streams.h b/main/php_streams.h index e226d93195..867048f4a8 100755 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -91,6 +91,7 @@ typedef struct _php_stream php_stream; typedef struct _php_stream_wrapper php_stream_wrapper; typedef struct _php_stream_context php_stream_context; +typedef struct _php_stream_filter php_stream_filter; /* callback for status notifications */ typedef void (*php_stream_notification_func)(php_stream_context *context, @@ -176,13 +177,51 @@ struct _php_stream_wrapper { char **err_stack; }; -/* pushes an error message onto the stack for a wrapper instance */ -PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...); +typedef struct _php_stream_filter_ops { + size_t (*write)(php_stream *stream, php_stream_filter *thisfilter, + const char *buf, size_t count TSRMLS_DC); + size_t (*read)(php_stream *stream, php_stream_filter *thisfilter, + char *buf, size_t count TSRMLS_DC); + int (*flush)(php_stream *stream, php_stream_filter *thisfilter TSRMLS_DC); + int (*eof)(php_stream *stream, php_stream_filter *thisfilter TSRMLS_DC); + void (*dtor)(php_stream_filter *thisfilter TSRMLS_DC); + const char *label; +} php_stream_filter_ops; + +struct _php_stream_filter { + php_stream_filter_ops *fops; + void *abstract; /* for use by filter implementation */ + php_stream_filter *next; + php_stream_filter *prev; + int is_persistent; + php_stream *stream; +}; + +#define php_stream_filter_write_next(stream, thisfilter, buf, size) \ + (thisfilter)->next ? (thisfilter)->next->fops->write((stream), (thisfilter)->next, (buf), (size) TSRMLS_CC) \ + : (stream)->ops->write((stream), (buf), (size) TSRMLS_CC) + +#define php_stream_filter_read_next(stream, thisfilter, buf, size) \ + (thisfilter)->next ? (thisfilter)->next->fops->read((stream), (thisfilter)->next, (buf), (size) TSRMLS_CC) \ + : (stream)->ops->read((stream), (buf), (size) TSRMLS_CC) +#define php_stream_filter_flush_next(stream, thisfilter) \ + (thisfilter)->next ? (thisfilter)->next->fops->flush((stream), (thisfilter) TSRMLS_CC) \ + : (stream)->ops->flush((stream) TSRMLS_CC) + +#define php_stream_filter_eof_next(stream, thisfilter) \ + (thisfilter)->next ? (thisfilter)->next->fops->eof((stream), (thisfilter) TSRMLS_CC) \ + : (stream)->ops->read((stream), NULL, 0 TSRMLS_CC) == EOF ? 1 : 0 + + + struct _php_stream { php_stream_ops *ops; void *abstract; /* convenience pointer for abstraction */ + php_stream_filter *filterhead; + php_stream_filter *filtertail; + php_stream_wrapper *wrapper; /* which wrapper was used to open the stream */ void *wrapperthis; /* convenience pointer for a instance of a wrapper */ zval *wrapperdata; /* fgetwrapperdata retrieves this */ @@ -198,6 +237,7 @@ struct _php_stream { FILE *stdiocast; /* cache this, otherwise we might leak! */ #if ZEND_DEBUG int __exposed; /* non-zero if exposed as a zval somewhere */ + char *__orig_path; /* it really helps when debugging "unclosed" streams */ #endif php_stream_context *context; @@ -213,8 +253,28 @@ PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, int persistent, const char *mode STREAMS_DC TSRMLS_DC); #define php_stream_alloc(ops, thisptr, persistent, mode) _php_stream_alloc((ops), (thisptr), (persistent), (mode) STREAMS_CC TSRMLS_CC) -#define php_stream_get_resource_id(stream) (stream)->rsrc_id +/* stack filter onto a stream */ +PHPAPI void php_stream_filter_prepend(php_stream *stream, php_stream_filter *filter); +PHPAPI void php_stream_filter_append(php_stream *stream, php_stream_filter *filter); +PHPAPI php_stream_filter *php_stream_filter_remove(php_stream *stream, php_stream_filter *filter, int call_dtor TSRMLS_DC); +PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC); +PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC); +#define php_stream_filter_alloc(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_CC TSRMLS_CC) +#define php_stream_filter_alloc_rel(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_REL_CC TSRMLS_CC) +#define php_stream_filter_remove_head(stream, call_dtor) php_stream_filter_remove((stream), (stream)->filterhead, (call_dtor) TSRMLS_CC) +#define php_stream_filter_remove_tail(stream, call_dtor) php_stream_filter_remove((stream), (stream)->filtertail, (call_dtor) TSRMLS_CC) + +typedef struct _php_stream_filter_factory { + php_stream_filter *(*create_filter)(const char *filtername, const char *filterparams, int filterparamslen, int persistent TSRMLS_DC); +} php_stream_filter_factory; + +PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC); +PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC); +PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, const char *filterparams, int filterparamslen, int persistent TSRMLS_DC); + + +#define php_stream_get_resource_id(stream) (stream)->rsrc_id #if ZEND_DEBUG /* use this to tell the stream that it is OK if we don't explicitly close it */ # define php_stream_auto_cleanup(stream) { (stream)->__exposed++; } @@ -403,13 +463,17 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char #define php_stream_open_wrapper(path, mode, options, opened) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), NULL STREAMS_CC TSRMLS_CC) #define php_stream_open_wrapper_ex(path, mode, options, opened, context) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), (context) STREAMS_CC TSRMLS_CC) +/* pushes an error message onto the stack for a wrapper instance */ +PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...); + + #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! */ #define PHP_STREAM_NO_PREFERENCE 0 #define PHP_STREAM_PREFER_STDIO 1 +/* DO NOT call this on streams that are referenced by resources! */ PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC TSRMLS_DC); #define php_stream_make_seekable(origstream, newstream, flags) _php_stream_make_seekable((origstream), (newstream), (flags) STREAMS_CC TSRMLS_CC) diff --git a/main/streams.c b/main/streams.c index 50b2099d63..0cfe5d38b3 100755 --- a/main/streams.c +++ b/main/streams.c @@ -111,6 +111,8 @@ fprintf(stderr, "stream_free: %s:%p in_free=%d opts=%08x\n", stream->ops->label, stream->in_free++; + php_stream_flush(stream); + if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0) { /* Remove entry from the resource list */ zend_list_delete(stream->rsrc_id); @@ -139,6 +141,10 @@ fprintf(stderr, "stream_free: %s:%p in_free=%d opts=%08x\n", stream->ops->label, } if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) { + + while (stream->filterhead) { + php_stream_filter_remove_head(stream, 1); + } if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) { stream->wrapper->wops->stream_closer(stream->wrapper, stream TSRMLS_CC); @@ -156,7 +162,7 @@ fprintf(stderr, "stream_free: %s:%p in_free=%d opts=%08x\n", stream->ops->label, * as leaked; it will log a warning, but lets help it out and display what kind * of stream it was. */ char leakbuf[512]; - snprintf(leakbuf, sizeof(leakbuf), __FILE__ "(%d) : Stream of type '%s' 0x%08X was not closed\n", __LINE__, stream->ops->label, (unsigned int)stream); + snprintf(leakbuf, sizeof(leakbuf), __FILE__ "(%d) : Stream of type '%s' 0x%08X (path:%s) was not closed\n", __LINE__, stream->ops->label, (unsigned int)stream, stream->__orig_path); # if defined(PHP_WIN32) OutputDebugString(leakbuf); # else @@ -172,10 +178,133 @@ fprintf(stderr, "stream_free: %s:%p in_free=%d opts=%08x\n", stream->ops->label, } /* }}} */ +static HashTable stream_filters_hash; + +PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC) +{ + return zend_hash_add(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern), factory, sizeof(*factory), NULL); +} + +PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC) +{ + return zend_hash_del(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern)); +} + +/* We allow very simple pattern matching for filter factories: + * if "charset.utf-8/sjis" is requested, we search first for an exact + * match. If that fails, we try "charset.*". + * This means that we don't need to clog up the hashtable with a zillion + * charsets (for example) but still be able to provide them all as filters */ +PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, const char *filterparams, int filterparamslen, int persistent TSRMLS_DC) +{ + php_stream_filter_factory *factory; + php_stream_filter *filter = NULL; + int n; + char *period; + + n = strlen(filtername); + + if (SUCCESS == zend_hash_find(&stream_filters_hash, (char*)filtername, n, (void**)&factory)) { + filter = factory->create_filter(filtername, filterparams, filterparamslen, persistent TSRMLS_CC); + } else if ((period = strchr(filtername, '.'))) { + /* try a wildcard */ + char wildname[128]; + + PHP_STRLCPY(wildname, filtername, sizeof(wildname) - 1, period-filtername + 1); + strcat(wildname, "*"); + + if (SUCCESS == zend_hash_find(&stream_filters_hash, wildname, strlen(wildname), (void**)&factory)) { + filter = factory->create_filter(filtername, filterparams, filterparamslen, persistent TSRMLS_CC); + } + } + + if (filter == NULL) { + /* TODO: these need correct docrefs */ + if (factory == NULL) + php_error_docref(NULL, E_WARNING TSRMLS_CC, "unable to locate filter \"%s\"", filtername); + else + php_error_docref(NULL, E_WARNING TSRMLS_CC, "unable to create or locate filter \"%s\"", filtername); + } + + return filter; +} + +PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC) +{ + php_stream_filter *filter; + + filter = (php_stream_filter*) pemalloc_rel_orig(sizeof(php_stream_filter), persistent); + memset(filter, 0, sizeof(php_stream_filter)); + + filter->fops = fops; + filter->abstract = abstract; + filter->is_persistent = persistent; + + return filter; +} + +PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC) +{ + if (filter->fops->dtor) + filter->fops->dtor(filter TSRMLS_CC); + pefree(filter, filter->is_persistent); +} + +PHPAPI void php_stream_filter_prepend(php_stream *stream, php_stream_filter *filter) +{ + filter->next = stream->filterhead; + filter->prev = NULL; + + if (stream->filterhead) { + stream->filterhead->prev = filter; + } else { + stream->filtertail = filter; + } + stream->filterhead = filter; + filter->stream = stream; +} + +PHPAPI void php_stream_filter_append(php_stream *stream, php_stream_filter *filter) +{ + filter->prev = stream->filtertail; + filter->next = NULL; + if (stream->filtertail) { + stream->filtertail->next = filter; + } else { + stream->filterhead = filter; + } + stream->filtertail = filter; + filter->stream = stream; +} + +PHPAPI php_stream_filter *php_stream_filter_remove(php_stream *stream, php_stream_filter *filter, int call_dtor TSRMLS_DC) +{ + assert(stream == filter->stream); + + if (filter->prev) { + filter->prev->next = filter->next; + } else { + stream->filterhead = filter->next; + } + if (filter->next) { + filter->next->prev = filter->prev; + } else { + stream->filtertail = filter->prev; + } + if (call_dtor) { + php_stream_filter_free(filter TSRMLS_CC); + return NULL; + } + return filter; +} + /* {{{ generic stream operations */ PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC) { - return stream->ops->read == NULL ? 0 : stream->ops->read(stream, buf, size TSRMLS_CC); + if (stream->filterhead) + return stream->filterhead->fops->read(stream, stream->filterhead, buf, size TSRMLS_CC); + + return stream->ops->read(stream, buf, size TSRMLS_CC); } PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC) @@ -183,7 +312,11 @@ PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC) /* we define our stream reading function so that it must return EOF when an EOF condition occurs, when working in unbuffered mode and called with these args */ - return stream->ops->read == NULL ? -1 : stream->ops->read(stream, NULL, 0 TSRMLS_CC) == EOF ? 1 : 0; + + if (stream->filterhead) + return stream->filterhead->fops->eof(stream, stream->filterhead TSRMLS_CC); + + return stream->ops->read(stream, NULL, 0 TSRMLS_CC) == EOF ? 1 : 0; } PHPAPI int _php_stream_putc(php_stream *stream, int c TSRMLS_DC) @@ -240,30 +373,33 @@ PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_D PHPAPI char *_php_stream_gets(php_stream *stream, char *buf, size_t maxlen TSRMLS_DC) { - - if (maxlen == 0) { - buf[0] = 0; - return buf; - } - - if (stream->ops->gets) { - return stream->ops->gets(stream, buf, maxlen TSRMLS_CC); - } else if (stream->ops->read == NULL) { + if (maxlen == 0) return NULL; - } else { - /* unbuffered fgets - poor performance ! */ + + if (stream->filterhead || stream->ops->gets == NULL) { + /* unbuffered fgets - performance not so good! */ char *c = buf; /* TODO: look at error returns? */ - while (--maxlen > 0 && stream->ops->read(stream, buf, 1 TSRMLS_CC) == 1 && *buf++ != '\n'); + while (--maxlen > 0 && php_stream_read(stream, buf, 1 TSRMLS_CC) == 1 && *buf++ != '\n') + ; *buf = '\0'; + return c == buf && maxlen > 0 ? NULL : c; + + } else if (stream->ops->gets) { + return stream->ops->gets(stream, buf, maxlen TSRMLS_CC); } + /* should not happen */ + return NULL; } PHPAPI int _php_stream_flush(php_stream *stream TSRMLS_DC) { + if (stream->filterhead) + stream->filterhead->fops->flush(stream, stream->filterhead TSRMLS_CC); + if (stream->ops->flush) { return stream->ops->flush(stream TSRMLS_CC); } @@ -275,7 +411,12 @@ PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t coun assert(stream); if (buf == NULL || count == 0 || stream->ops->write == NULL) return 0; - return stream->ops->write(stream, buf, count TSRMLS_CC); + + if (stream->filterhead) { + return stream->filterhead->fops->write(stream, stream->filterhead, buf, count TSRMLS_CC); + } else { + return stream->ops->write(stream, buf, count TSRMLS_CC); + } } PHPAPI off_t _php_stream_tell(php_stream *stream TSRMLS_DC) @@ -291,6 +432,10 @@ PHPAPI off_t _php_stream_tell(php_stream *stream TSRMLS_DC) PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC) { if (stream->ops->seek) { + + if (stream->filterhead) + stream->filterhead->fops->flush(stream, stream->filterhead TSRMLS_CC); + return stream->ops->seek(stream, offset, whence TSRMLS_CC); } @@ -331,6 +476,7 @@ PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC) #ifdef HAVE_MMAP if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET) + && stream->filterhead == NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 0)) { struct stat sbuf; @@ -394,6 +540,7 @@ PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen * buffering layer. * */ if ( php_stream_is(src, PHP_STREAM_IS_STDIO) && + src->filterhead == NULL && php_stream_tell(src) == 0 && SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0)) { @@ -475,6 +622,7 @@ PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size * buffering layer. * */ if ( php_stream_is(src, PHP_STREAM_IS_STDIO) && + src->filterhead == NULL && php_stream_tell(src) == 0 && SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0)) { @@ -1014,6 +1162,8 @@ PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show int flags = castas & PHP_STREAM_CAST_MASK; castas &= ~PHP_STREAM_CAST_MASK; + /* filtered streams can only be cast as stdio, and only when fopencookie is present */ + if (castas == PHP_STREAM_AS_STDIO) { if (stream->stdiocast) { if (ret) { @@ -1026,6 +1176,7 @@ PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show * first, to avoid doubling up the layers of stdio with an fopencookie */ if (php_stream_is(stream, PHP_STREAM_IS_STDIO) && stream->ops->cast && + stream->filterhead == NULL && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) { goto exit_success; @@ -1062,6 +1213,12 @@ PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show #endif } + + if (stream->filterhead) { + php_error_docref(NULL, E_WARNING TSRMLS_CC, "cannot cast a filtered stream on this system"); + return FAILURE; + } + if (stream->ops->cast && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) goto exit_success; @@ -1105,12 +1262,13 @@ exit_success: /* {{{ wrapper init and registration */ int php_init_stream_wrappers(TSRMLS_D) { - return zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1); + return zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1) == SUCCESS && zend_hash_init(&stream_filters_hash, 0, NULL, NULL, 1) == SUCCESS ? SUCCESS : FAILURE; } int php_shutdown_stream_wrappers(TSRMLS_D) { zend_hash_destroy(&url_stream_wrappers_hash); + zend_hash_destroy(&stream_filters_hash); return SUCCESS; } @@ -1381,6 +1539,10 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio php_stream *stream = NULL; php_stream_wrapper *wrapper = NULL; char *path_to_open; +#if ZEND_DEBUG + char *copy_of_path = NULL; +#endif + if (opened_path) *opened_path = NULL; @@ -1405,6 +1567,13 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio stream->wrapper = wrapper; } +#if ZEND_DEBUG + if (stream) { + copy_of_path = estrdup(path); + stream->__orig_path = copy_of_path; + } +#endif + if (stream != NULL && (options & STREAM_MUST_SEEK)) { php_stream *newstream; @@ -1412,6 +1581,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio case PHP_STREAM_UNCHANGED: return stream; case PHP_STREAM_RELEASED: +#if ZEND_DEBUG + newstream->__orig_path = copy_of_path; +#endif return newstream; default: php_stream_close(stream); @@ -1484,6 +1656,10 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio efree(wrapper->err_stack); wrapper->err_stack = NULL; } +#if ZEND_DEBUG + if (stream == NULL && copy_of_path != NULL) + efree(copy_of_path); +#endif return stream; } /* }}} */ @@ -1498,7 +1674,7 @@ PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int optio if (stream == NULL) return NULL; - + if (php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_TRY_HARD|PHP_STREAM_CAST_RELEASE, (void**)&fp, REPORT_ERRORS) == FAILURE) {