diff --git a/ext/standard/tests/file/userwrapper_001.phpt b/ext/standard/tests/file/userwrapper_001.phpt new file mode 100644 index 0000000000000..5546ab1c0de11 --- /dev/null +++ b/ext/standard/tests/file/userwrapper_001.phpt @@ -0,0 +1,83 @@ +--TEST-- +Userstream with STREAM_IS_URI flag unlink, rename, mkdir, rmdir, and url_stat. +--FILE-- +1, 'ino'=>2, 'mode'=>0644, 'nlink'=>3, + 'uid'=>100, 'gid'=>1000, 'rdev'=>-1, 'size'=>31337, + 'atime'=>1234567890, 'mtime'=>1231231231, 'ctime'=>1234564564, + 'blksize'=>-1, 'blocks'=>-1); + } +} + +stream_wrapper_register('test', 'test', STREAM_IS_URI); + +unlink('test:example.com/path/to/file'); +rename('test:example.com/path/to/from', 'test:example.com/path/to/to'); +/* We *want* this to fail and thus not output the watch statement */ +@rename('test:example.com/path/to/from', 'http://example.com/path/to/to'); +mkdir('test:example.com/path/to/directory', 0755); +rmdir('test:example.com/path/to/directory'); +print_r(stat('test:example.com/path/to/file')); +echo "Filesize = " . filesize('test:example.com/path/to/file') . "\n"; +echo "filemtime = " . filemtime('test:example.com/path/to/file') . "\n"; +?> +--EXPECT-- +Unlinking file: test:example.com/path/to/file +Renaming test:example.com/path/to/from to test:example.com/path/to/to +Making directory: test:example.com/path/to/directory as 755 +Removing directory: test:example.com/path/to/directory +Stating file: test:example.com/path/to/file +Array +( + [0] => 1 + [1] => 2 + [2] => 420 + [3] => 3 + [4] => 100 + [5] => 1000 + [6] => -1 + [7] => 31337 + [8] => 1234567890 + [9] => 1231231231 + [10] => 1234564564 + [11] => -1 + [12] => -1 + [dev] => 1 + [ino] => 2 + [mode] => 420 + [nlink] => 3 + [uid] => 100 + [gid] => 1000 + [rdev] => -1 + [size] => 31337 + [atime] => 1234567890 + [mtime] => 1231231231 + [ctime] => 1234564564 + [blksize] => -1 + [blocks] => -1 +) +Filesize = 31337 +filemtime = 1231231231 + diff --git a/main/php_streams.h b/main/php_streams.h index d10d087fb4956..a3a04a9bac908 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -164,6 +164,7 @@ struct _php_stream_wrapper { php_stream_wrapper_ops *wops; /* operations the wrapper can perform */ void *abstract; /* context for the wrapper */ int is_url; /* so that PG(allow_url_fopen) can be respected */ + int is_uri; /* so that scheme complies with RFC 2396 and opaque_part (without leading "//"" after ":") can be respected */ }; #define PHP_STREAM_FLAG_NO_SEEK 0x1 @@ -599,6 +600,7 @@ END_EXTERN_C() /* Definitions for user streams */ #define PHP_STREAM_IS_URL 1 +#define PHP_STREAM_IS_URI 2 /* Stream metadata definitions */ /* Create if referred resource does not exist */ diff --git a/main/streams/memory.c b/main/streams/memory.c index 41c09faca881e..3da0ad80cb55a 100644 --- a/main/streams/memory.c +++ b/main/streams/memory.c @@ -770,6 +770,7 @@ PHPAPI php_stream_wrapper php_stream_rfc2397_wrapper = { &php_stream_rfc2397_wops, NULL, 1, /* is_url */ + 1, /* is_uri */ }; /* diff --git a/main/streams/streams.c b/main/streams/streams.c index ed4b257848fe7..055958de57ca4 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1753,7 +1753,7 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash); php_stream_wrapper *wrapper = NULL; const char *p, *protocol = NULL; - size_t n = 0; + size_t n = 0, slashes = 0; if (path_for_open) { *path_for_open = (char*)path; @@ -1767,8 +1767,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const n++; } - if ((*p == ':') && (n > 1) && (!strncmp("//", p+1, 2) || (n == 4 && !memcmp("data:", path, 5)))) { + if ((*p == ':') && (n > 1)) { protocol = path; + slashes = !strncmp("//", p+1, 2); } if (protocol) { @@ -1781,16 +1782,24 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const if (n >= sizeof(wrapper_name)) { n = sizeof(wrapper_name) - 1; } - PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n); - - php_error_docref(NULL, E_WARNING, "Unable to find the wrapper \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name); + /* Raising for :// so : can be handled with php_plain_files_wrapper if no wrapper registered */ + if (slashes == 1) { + PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n); + php_error_docref(NULL, E_WARNING, "Unable to find the wrapper \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name); + } wrapper = NULL; protocol = NULL; } } efree(tmp); } + + if (wrapper && wrapper->is_uri == 0 && slashes == 0) { + wrapper = NULL; + protocol = NULL; + } + /* TODO: curl based streams probably support file:// properly */ if (!protocol || !strncasecmp(protocol, "file", n)) { /* fall back on regular file access */ diff --git a/main/streams/userspace.c b/main/streams/userspace.c index da39dcaf7c2bc..8c4d2d92e222e 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -96,6 +96,7 @@ PHP_MINIT_FUNCTION(user_streams) REGISTER_LONG_CONSTANT("STREAM_MKDIR_RECURSIVE", PHP_STREAM_MKDIR_RECURSIVE, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_IS_URL", PHP_STREAM_IS_URL, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_IS_URI", PHP_STREAM_IS_URI, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_OPTION_BLOCKING", PHP_STREAM_OPTION_BLOCKING, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_OPTION_READ_TIMEOUT", PHP_STREAM_OPTION_READ_TIMEOUT, CONST_CS|CONST_PERSISTENT); @@ -505,6 +506,7 @@ PHP_FUNCTION(stream_wrapper_register) uwrap->wrapper.wops = &user_stream_wops; uwrap->wrapper.abstract = uwrap; uwrap->wrapper.is_url = ((flags & PHP_STREAM_IS_URL) != 0); + uwrap->wrapper.is_uri = ((flags & PHP_STREAM_IS_URI) != 0); rsrc = zend_register_resource(uwrap, le_protocols);