-1

假设我想知道传递给 fopen 的字符串是否代表文件路径或有效的包装器(例如 "/home/user/example.txt"vs "php://input")。这是为了从内容中创建一个 tmpfilephp://input以解决fseekPHP包装器的限制。

如下所示,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

我可以使用wrapper_type由返回的数组中的stream_get_meta_data,但如果文件或包装器 URI 不存在,它仍会将垃圾放入日志中,我想避免这种情况。

检测我的输入字符串(要传递给 fopen)是否包含现有文件的有效文件路径有效的 PHP 包装器,或者两者都不包含的最佳方法是什么?

更新:我找到了解决问题的解决方法,但代价是额外fopen调用。我把这个放在下面的答案中。

4

1 回答 1

1

更新

我能够像这样解决它:

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)不是直接打开的文件,它将创建一个临时文件(带有tmpfile())并将流的内容写入该临时文件,然后关闭$testHandle。然而,如果它是从文件系统打开的文件,(例如/path/to/file)它会简单地将 $this->file 设置为 $testHandle。

这可确保我始终使用文件句柄;它对我来说应该很好,因为我正在阅读的文件都不会超过一兆字节左右。但是,我仍然希望能够放弃额外的 fopen 调用。

于 2017-05-13T02:54:02.333 回答