summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@php.net>2008-03-27 10:33:40 +0000
committerDmitry Stogov <dmitry@php.net>2008-03-27 10:33:40 +0000
commit862f9ee3d227e75d0c9fa83a3d9b6c60fc9461e3 (patch)
tree3060773c29bf0ed6aadeb823d7d9d6dc03b64d68
parent17d22a50d9f318927bac81c1a33a9700a3ed0fd4 (diff)
downloadphp-git-862f9ee3d227e75d0c9fa83a3d9b6c60fc9461e3.tar.gz
Added ability to use stream wrappers in include_path
-rw-r--r--NEWS1
-rw-r--r--ext/standard/tests/file/include_streams.phpt130
-rw-r--r--main/fopen_wrappers.c63
-rwxr-xr-xmain/php_streams.h3
-rw-r--r--main/streams/plain_wrapper.c10
-rwxr-xr-xmain/streams/streams.c27
6 files changed, 227 insertions, 7 deletions
diff --git a/NEWS b/NEWS
index 17da2abde5..b02e9cc055 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ PHP NEWS
- Removed the experimental RPL (master/slave) functions from mysqli. (Andrey)
- Dropped zend.ze1_compatibility_mode (Dmitry)
+- Added ability to use stream wrappers in include_path (Gregory, Dmitry)
- Added concept of "delayed early binding" that allows opcode caches to perform
class declaration (early and/or run-time binding) in exactly the same order
as vanila php. (Dmitry)
diff --git a/ext/standard/tests/file/include_streams.phpt b/ext/standard/tests/file/include_streams.phpt
new file mode 100644
index 0000000000..4c7d4bb082
--- /dev/null
+++ b/ext/standard/tests/file/include_streams.phpt
@@ -0,0 +1,130 @@
+--TEST--
+Stream wrappers in include_path
+--FILE--
+<?php
+$data1 = $data2 = $data3 = $data4 = $data5 = $data6 = <<<'EOD'
+<?php echo __FILE__ . "\n";?>
+
+EOD;
+
+class mystream
+{
+ public $path;
+ public $mode;
+ public $options;
+
+ public $position;
+ public $varname;
+
+ function url_stat($path, $flags) {
+ return array();
+ }
+
+ function stream_stat() {
+ return array();
+ }
+
+ function stream_open($path, $mode, $options, &$opened_path)
+ {
+ $this->path = $path;
+ $this->mode = $mode;
+ $this->options = $options;
+
+ $split = parse_url($path);
+ if ($split["host"] !== "GLOBALS" ||
+ empty($split["path"]) ||
+ empty($GLOBALS[substr($split["path"],1)])) {
+ return false;
+ }
+ $this->varname = substr($split["path"],1);
+
+ if (strchr($mode, 'a'))
+ $this->position = strlen($GLOBALS[$this->varname]);
+ else
+ $this->position = 0;
+
+ return true;
+ }
+
+ function stream_read($count)
+ {
+ $ret = substr($GLOBALS[$this->varname], $this->position, $count);
+ $this->position += strlen($ret);
+ return $ret;
+ }
+
+ function stream_tell()
+ {
+ return $this->position;
+ }
+
+ function stream_eof()
+ {
+ return $this->position >= strlen($GLOBALS[$this->varname]);
+ }
+
+ function stream_seek($offset, $whence)
+ {
+ switch($whence) {
+ case SEEK_SET:
+ if ($offset < strlen($GLOBALS[$this->varname]) && $offset >= 0) {
+ $this->position = $offset;
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ case SEEK_CUR:
+ if ($offset >= 0) {
+ $this->position += $offset;
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ case SEEK_END:
+ if (strlen($GLOBALS[$this->varname]) + $offset >= 0) {
+ $this->position = strlen($GLOBALS[$this->varname]) + $offset;
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+
+}
+
+if (!stream_wrapper_register("test", "mystream")) {
+ die("test wrapper registration failed");
+}
+
+echo file_get_contents("test://GLOBALS/data1");
+include("test://GLOBALS/data1");
+include_once("test://GLOBALS/data2");
+include_once("test://GLOBALS/data2");
+$include_path = get_include_path();
+set_include_path($include_path . PATH_SEPARATOR . "test://GLOBALS");
+echo file_get_contents("data3", true);
+include("data3");
+include_once("data4");
+include_once("data4");
+set_include_path("test://GLOBALS" . PATH_SEPARATOR . $include_path);
+echo file_get_contents("data5", true);
+include("data5");
+include_once("data6");
+include_once("data6");
+?>
+--EXPECTF--
+<?php echo __FILE__ . "\n";?>
+test://GLOBALS/data1
+test://GLOBALS/data2
+<?php echo __FILE__ . "\n";?>
+test://GLOBALS/data3
+test://GLOBALS/data4
+<?php echo __FILE__ . "\n";?>
+test://GLOBALS/data5
+test://GLOBALS/data6
+
diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c
index 9f9b0e7c16..4acc47e4f2 100644
--- a/main/fopen_wrappers.c
+++ b/main/fopen_wrappers.c
@@ -447,14 +447,22 @@ PHPAPI char *php_resolve_path(const char *filename, int filename_length, const c
char resolved_path[MAXPATHLEN];
char trypath[MAXPATHLEN];
const char *ptr, *end, *p;
+ char *actual_path;
+ php_stream_wrapper *wrapper;
if (!filename) {
return NULL;
}
- /* Don't resolve paths which contain protocol */
+ /* Don't resolve paths which contain protocol (except of file://) */
for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
if ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/')) {
+ wrapper = php_stream_locate_url_wrapper(filename, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC);
+ if (wrapper == &php_plain_files_wrapper) {
+ if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) {
+ return estrdup(resolved_path);
+ }
+ }
return NULL;
}
@@ -473,7 +481,18 @@ PHPAPI char *php_resolve_path(const char *filename, int filename_length, const c
ptr = path;
while (ptr && *ptr) {
- end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
+ /* Check for stream wrapper */
+ int is_stream_wrapper = 0;
+
+ for (p = ptr; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
+ if ((*p == ':') && (p - ptr > 1) && (p[1] == '/') && (p[2] == '/')) {
+ /* .:// or ..:// is not a stream wrapper */
+ if (p[-1] != '.' || p[-2] != '.' || p - 2 != ptr) {
+ p += 3;
+ is_stream_wrapper = 1;
+ }
+ }
+ end = strchr(p, DEFAULT_DIR_SEPARATOR);
if (end) {
if ((end-ptr) + 1 + filename_length + 1 >= MAXPATHLEN) {
ptr = end + 1;
@@ -494,7 +513,23 @@ PHPAPI char *php_resolve_path(const char *filename, int filename_length, const c
memcpy(trypath+len+1, filename, filename_length+1);
ptr = NULL;
}
- if (tsrm_realpath(trypath, resolved_path TSRMLS_CC)) {
+ actual_path = trypath;
+ if (is_stream_wrapper) {
+ wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC);
+ if (!wrapper) {
+ continue;
+ } else if (wrapper != &php_plain_files_wrapper) {
+ if (wrapper->wops->url_stat) {
+ php_stream_statbuf ssb;
+
+ if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, 0, &ssb, NULL TSRMLS_CC)) {
+ return estrdup(trypath);
+ }
+ }
+ continue;
+ }
+ }
+ if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) {
return estrdup(resolved_path);
}
} /* end provided path */
@@ -511,7 +546,27 @@ PHPAPI char *php_resolve_path(const char *filename, int filename_length, const c
exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) {
memcpy(trypath, exec_fname, exec_fname_length + 1);
memcpy(trypath+exec_fname_length + 1, filename, filename_length+1);
- if (tsrm_realpath(trypath, resolved_path TSRMLS_CC)) {
+ actual_path = trypath;
+
+ /* Check for stream wrapper */
+ for (p = trypath; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
+ if ((*p == ':') && (p - trypath > 1) && (p[1] == '/') && (p[2] == '/')) {
+ wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC);
+ if (!wrapper) {
+ return NULL;
+ } else if (wrapper != &php_plain_files_wrapper) {
+ if (wrapper->wops->url_stat) {
+ php_stream_statbuf ssb;
+
+ if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, 0, &ssb, NULL TSRMLS_CC)) {
+ return estrdup(trypath);
+ }
+ }
+ return NULL;
+ }
+ }
+
+ if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) {
return estrdup(resolved_path);
}
}
diff --git a/main/php_streams.h b/main/php_streams.h
index d013214773..91e09f8f2d 100755
--- a/main/php_streams.h
+++ b/main/php_streams.h
@@ -511,6 +511,9 @@ END_EXTERN_C()
/* don't check allow_url_fopen and allow_url_include */
#define STREAM_DISABLE_URL_PROTECTION 0x00002000
+/* assume the path passed in exists and is fully expanded, avoiding syscalls */
+#define STREAM_ASSUME_REALPATH 0x00004000
+
/* Antique - no longer has meaning */
#define IGNORE_URL_WIN 0
diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c
index 484150e2a7..e35ffcb168 100644
--- a/main/streams/plain_wrapper.c
+++ b/main/streams/plain_wrapper.c
@@ -892,9 +892,13 @@ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, cha
}
return NULL;
}
-
- if ((realpath = expand_filepath(filename, NULL TSRMLS_CC)) == NULL) {
- return NULL;
+
+ if (options & STREAM_ASSUME_REALPATH) {
+ realpath = estrdup(filename);
+ } else {
+ if ((realpath = expand_filepath(filename, NULL TSRMLS_CC)) == NULL) {
+ return NULL;
+ }
}
if (persistent) {
diff --git a/main/streams/streams.c b/main/streams/streams.c
index 03607cdb5d..614470fc7b 100755
--- a/main/streams/streams.c
+++ b/main/streams/streams.c
@@ -1754,6 +1754,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio
php_stream_wrapper *wrapper = NULL;
char *path_to_open;
int persistent = options & STREAM_OPEN_PERSISTENT;
+ char *resolved_path = NULL;
char *copy_of_path = NULL;
@@ -1765,11 +1766,24 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio
return NULL;
}
+ if (options & USE_PATH) {
+ resolved_path = php_resolve_path(path, strlen(path), PG(include_path) TSRMLS_CC);
+ if (resolved_path) {
+ path = resolved_path;
+ /* we've found this file, don't re-check include_path or run realpath */
+ options |= STREAM_ASSUME_REALPATH;
+ options &= ~USE_PATH;
+ }
+ }
+
path_to_open = path;
wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC);
if (options & STREAM_USE_URL && (!wrapper || !wrapper->is_url)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "This function may only be used against URLs");
+ if (resolved_path) {
+ efree(resolved_path);
+ }
return NULL;
}
@@ -1798,6 +1812,10 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio
}
if (stream) {
+ if (opened_path && !*opened_path && resolved_path) {
+ *opened_path = resolved_path;
+ resolved_path = NULL;
+ }
if (stream->orig_path) {
pefree(stream->orig_path, persistent);
}
@@ -1816,12 +1834,18 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio
(options & STREAM_WILL_CAST)
? PHP_STREAM_PREFER_STDIO : PHP_STREAM_NO_PREFERENCE)) {
case PHP_STREAM_UNCHANGED:
+ if (resolved_path) {
+ efree(resolved_path);
+ }
return stream;
case PHP_STREAM_RELEASED:
if (newstream->orig_path) {
pefree(newstream->orig_path, persistent);
}
newstream->orig_path = pestrdup(path, persistent);
+ if (resolved_path) {
+ efree(resolved_path);
+ }
return newstream;
default:
php_stream_close(stream);
@@ -1860,6 +1884,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio
pefree(copy_of_path, persistent);
}
#endif
+ if (resolved_path) {
+ efree(resolved_path);
+ }
return stream;
}
/* }}} */