PHP中如何判断字符串是否指向有效文件或包装?
How to determine whether a string points to a valid file or wrapper in PHP?
假设我想判断传递给 fopen 的字符串是表示文件路径还是表示有效的包装器(例如 "/home/user/example.txt"
与 "php://input"
)。这是为了根据 php://input
中的内容创建一个 tmp 文件,以解决 fseek
PHP wrappers 的限制。
如下所示,file_exists
适用于文件,但不适用于包装器 URI:
var_dump(file_exists("php://input"));
var_dump(file_exists("./exists.txt"));
var_dump(file_exists("./non_existent.txt"));
var_dump(file_exists("php://garbage"));
给予
bool(false)
bool(true)
bool(false)
bool(false)
唯一返回 true 的是文件。我找到了 stream_get_wrappers() 但我想避免使检查过于复杂(例如使用字符串比较来尝试检测包装器)。
使用 stream_get_meta_data 似乎也有效,但它需要先调用 fopen,这会阻塞错误日志。
var_dump(stream_get_meta_data(fopen("php://input","r+")));
var_dump(stream_get_meta_data(fopen("./exists.txt","r+")));
var_dump(stream_get_meta_data(fopen("./non_existent.txt","r+")));
var_dump(stream_get_meta_data(fopen("php://garbage","r+")));
产生
array(9) {
["timed_out"]=>
bool(false)
["blocked"]=>
bool(true)
["eof"]=>
bool(false)
["wrapper_type"]=>
string(3) "PHP"
["stream_type"]=>
string(5) "Input"
["mode"]=>
string(2) "rb"
["unread_bytes"]=>
int(0)
["seekable"]=>
bool(true)
["uri"]=>
string(11) "php://input"
}
array(9) {
["timed_out"]=>
bool(false)
["blocked"]=>
bool(true)
["eof"]=>
bool(false)
["wrapper_type"]=>
string(9) "plainfile"
["stream_type"]=>
string(5) "STDIO"
["mode"]=>
string(2) "r+"
["unread_bytes"]=>
int(0)
["seekable"]=>
bool(true)
["uri"]=>
string(10) "./exists.txt"
}
NULL
NULL
我可以使用 stream_get_meta_data
返回的数组中的 wrapper_type
,但如果文件或包装器 URI 不存在,它仍然会在日志中产生垃圾,这是我想避免的。
检测我的输入字符串(将传递给 fopen)是否包含现有文件有效文件路径或有效文件路径的最佳方法是什么PHP 包装器,或者两者都不是?
更新:我找到了解决问题的解决方法,代价是额外调用 fopen
。我已将其放在下面的答案中。
更新
我能够像这样解决它:
class example {
var $file;
function open($path) {
$testHandle = fopen($path,"rb");
if(!$testHandle) {
error_log("Error parsing file: could not open $path");
return false;
}
$wrapperType = stream_get_meta_data($testHandle)["wrapper_type"];
if ($wrapperType != "plainfile") {
$this->file = tmpfile();
fwrite($this->file,file_get_contents($path));
fclose($testHandle);
} else {
$this->file = $testHandle;
}
}
}
如果传入的$path
(如php://input
)不是直接打开的文件,会创建一个临时文件(withtmpfile()
)并写入流的内容进入该临时文件,然后关闭 $testHandle
。但是,如果它是一个从文件系统打开的文件(例如 /path/to/file
),它会简单地将 $this->file 设置为 $testHandle.
这确保我始终如一地使用文件句柄;它对我来说应该没问题,因为我正在阅读的文件中 none 将大于 1 兆字节左右。但是,我仍然希望能够放弃额外的 fopen 调用。
假设我想判断传递给 fopen 的字符串是表示文件路径还是表示有效的包装器(例如 "/home/user/example.txt"
与 "php://input"
)。这是为了根据 php://input
中的内容创建一个 tmp 文件,以解决 fseek
PHP wrappers 的限制。
如下所示,file_exists
适用于文件,但不适用于包装器 URI:
var_dump(file_exists("php://input"));
var_dump(file_exists("./exists.txt"));
var_dump(file_exists("./non_existent.txt"));
var_dump(file_exists("php://garbage"));
给予
bool(false)
bool(true)
bool(false)
bool(false)
唯一返回 true 的是文件。我找到了 stream_get_wrappers() 但我想避免使检查过于复杂(例如使用字符串比较来尝试检测包装器)。
使用 stream_get_meta_data 似乎也有效,但它需要先调用 fopen,这会阻塞错误日志。
var_dump(stream_get_meta_data(fopen("php://input","r+")));
var_dump(stream_get_meta_data(fopen("./exists.txt","r+")));
var_dump(stream_get_meta_data(fopen("./non_existent.txt","r+")));
var_dump(stream_get_meta_data(fopen("php://garbage","r+")));
产生
array(9) {
["timed_out"]=>
bool(false)
["blocked"]=>
bool(true)
["eof"]=>
bool(false)
["wrapper_type"]=>
string(3) "PHP"
["stream_type"]=>
string(5) "Input"
["mode"]=>
string(2) "rb"
["unread_bytes"]=>
int(0)
["seekable"]=>
bool(true)
["uri"]=>
string(11) "php://input"
}
array(9) {
["timed_out"]=>
bool(false)
["blocked"]=>
bool(true)
["eof"]=>
bool(false)
["wrapper_type"]=>
string(9) "plainfile"
["stream_type"]=>
string(5) "STDIO"
["mode"]=>
string(2) "r+"
["unread_bytes"]=>
int(0)
["seekable"]=>
bool(true)
["uri"]=>
string(10) "./exists.txt"
}
NULL
NULL
我可以使用 stream_get_meta_data
返回的数组中的 wrapper_type
,但如果文件或包装器 URI 不存在,它仍然会在日志中产生垃圾,这是我想避免的。
检测我的输入字符串(将传递给 fopen)是否包含现有文件有效文件路径或有效文件路径的最佳方法是什么PHP 包装器,或者两者都不是?
更新:我找到了解决问题的解决方法,代价是额外调用 fopen
。我已将其放在下面的答案中。
更新
我能够像这样解决它:
class example {
var $file;
function open($path) {
$testHandle = fopen($path,"rb");
if(!$testHandle) {
error_log("Error parsing file: could not open $path");
return false;
}
$wrapperType = stream_get_meta_data($testHandle)["wrapper_type"];
if ($wrapperType != "plainfile") {
$this->file = tmpfile();
fwrite($this->file,file_get_contents($path));
fclose($testHandle);
} else {
$this->file = $testHandle;
}
}
}
如果传入的$path
(如php://input
)不是直接打开的文件,会创建一个临时文件(withtmpfile()
)并写入流的内容进入该临时文件,然后关闭 $testHandle
。但是,如果它是一个从文件系统打开的文件(例如 /path/to/file
),它会简单地将 $this->file 设置为 $testHandle.
这确保我始终如一地使用文件句柄;它对我来说应该没问题,因为我正在阅读的文件中 none 将大于 1 兆字节左右。但是,我仍然希望能够放弃额外的 fopen 调用。