Say I want to tell whether a string passed to fopen represents either a file path or a valid wrapper (e.g. "/home/user/example.txt"
vs "php://input"
). This is for the purpose of creating a tmpfile from what's in php://input
to work around fseek
ing limitations for PHP wrappers.
As shown below, file_exists
works for files, but not for wrapper URIs:
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"));
gives
bool(false)
bool(true)
bool(false)
bool(false)
The only one returning true is the file. I've found stream_get_wrappers() but I want to avoid complicating the check too much (such as using string comparison to try to detect a wrapper).
Using stream_get_meta_data does also seem to work, but it requires a call to fopen first, which would clog up error logs.
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+")));
produces
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
I can use the wrapper_type
from the array returned by stream_get_meta_data
, but it still will spew garbage into logs if the file or wrapper URI doesn't exist, which I want to avoid.
What's the best way to detect whether my input string (to be passed to fopen) contains either a valid file path for an existing file or a valid PHP wrapper, or neither?
Update: I found a workaround that solves the problem, at the expense of an extra fopen
call. I've put this in an answer below.