如何从 php 打开进程替换文件?

如何从 php 打开进程替换文件?

这是我自己尝试做的:

$ type 1.sh
#!/bin/bash -eu
php -r 'var_dump(file_get_contents($_SERVER["argv"][1]));' -- <(echo 1)
$ ./1.sh
PHP Warning:  file_get_contents(/dev/fd/63): failed to open stream: No such file or directory in Command line code on line 1

Warning: file_get_contents(/dev/fd/63): failed to open stream: No such file or directory in Command line code on line 1
bool(false)

我在Debian 6( php-5.4.14, bash-4.1.5) 和Arch Linux( php-5.4.12, bash-4.2.42) 上进行了测试。

UPD

$ strace -f -e trace=file php -r 'var_dump(file_get_contents($_SERVER["argv"][1]));' -- <(echo 1)
...
open("/usr/lib/php5/20100525/mongo.so", O_RDONLY) = 3
lstat("/dev/fd/63", {st_mode=S_IFLNK|0500, st_size=64, ...}) = 0
readlink("/dev/fd/63", "pipe:[405116]"..., 4096) = 13
lstat("/dev/fd/pipe:[405116]", 0x7fff5ea44850) = -1 ENOENT (No such file or directory)
lstat("/dev/fd", {st_mode=S_IFLNK|0777, st_size=13, ...}) = 0
readlink("/dev/fd", "/proc/self/fd"..., 4096) = 13
lstat("/proc/self/fd", {st_mode=S_IFDIR|0500, st_size=0, ...}) = 0
lstat("/proc/self", {st_mode=S_IFLNK|0777, st_size=64, ...}) = 0
readlink("/proc/self", "31536"..., 4096) = 5
lstat("/proc/31536", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
lstat("/proc", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/31536/fd/pipe:[405116]", O_RDONLY) = -1 ENOENT (No such file or directory)
PHP Warning:  file_get_contents(/dev/fd/63): failed to open stream: No such file or directory in Command line code on line 1

Warning: file_get_contents(/dev/fd/63): failed to open stream: No such file or directory in Command line code on line 1
bool(false)

$ strace -f -e trace=file php <(echo 12)
...
open("/usr/lib/php5/20100525/mongo.so", O_RDONLY) = 3
open("/dev/fd/63", O_RDONLY)            = 3
lstat("/dev/fd/63", {st_mode=S_IFLNK|0500, st_size=64, ...}) = 0
readlink("/dev/fd/63", "pipe:[413359]", 4096) = 13
lstat("/dev/fd/pipe:[413359]", 0x7fffa69c3c00) = -1 ENOENT (No such file or directory)
lstat("/dev/fd/63", {st_mode=S_IFLNK|0500, st_size=64, ...}) = 0
readlink("/dev/fd/63", "pipe:[413359]", 4096) = 13
lstat("/dev/fd/pipe:[413359]", 0x7fffa69c19b0) = -1 ENOENT (No such file or directory)
lstat("/dev/fd", {st_mode=S_IFLNK|0777, st_size=13, ...}) = 0
readlink("/dev/fd", "/proc/self/fd"..., 4096) = 13
lstat("/proc/self/fd", {st_mode=S_IFDIR|0500, st_size=0, ...}) = 0
lstat("/proc/self", {st_mode=S_IFLNK|0777, st_size=64, ...}) = 0
readlink("/proc/self", "32214"..., 4096) = 5
lstat("/proc/32214", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
lstat("/proc", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
2

答案1

问题是您希望 php 从文件描述符读取输入,但您强制它像常规文件一样读取。

首先,试试这个:

$ echo <(ls)
/dev/fd/63

然后您可以ls通过读取来处理输出/dev/fd/63。进程替换将返回file descriptor,其他命令使用它来读取其输出。

在你的例子中,你使用$_SERVER["argv"][1],这意味着 php 将像这样解释:

file_get_contents(/dev/fd/63)

从php手册中,可以看到file_get_contents函数的原型:

string file_get_contents ( string $filename [, bool $use_include_path =
false [, resource $context [, int $offset = -1 [, int $maxlen ]]]] )

Ops,php/dev/fd/63在这里会被视为普通文件,但它实际上是一个file descriptor.

要访问文件描述符,必须使用php://fd,php://fd/63将访问文件描述符 63 的内容:

$ php -r 'var_dump(file_get_contents("php://".substr($_SERVER["argv"][1],-5)));' -- <(echo test.txt)
string(9) "test.txt
"

你可以看到,现在 php 可以处理/dev/fd/63.但我们的目的是读取文件内容,这是通过进程替换提供的(在我的示例中,它是test.txt)。我对php不太了解,所以我添加另一个file_get_contents

$ php -r 'var_dump(file_get_contents(file_get_contents("php://".substr($_SERVER["argv"][1],-5))));' -- <(echo -n test.txt)
string(13) "Hello world!
"

我用来echo -n从 echo 输出中删除换行符,否则,php 将看到输出“test.txt\n”。

笔记

有关php中访问文件描述符的更多信息,可以参见这里

答案2

这就是问题:

readlink("/dev/fd/63", "pipe:[405116]"..., 4096) = 13
lstat("/dev/fd/pipe:[405116]", 0x7fff5ea44850) = -1 ENOENT

没有任何充分的理由(恕我直言)php尝试获取链接目标的真实姓名。不幸的是,链接目标不是文件系统的一部分,因此尝试访问该名称失败并导致此错误。符号链接只能这样打开。我认为这是一个错误php。您可以使用 FIFO 来代替:

mkfifo /my/fifo; output_cmd >/my/fifo & php -r ... /my/fifo

答案3

这是一个老问题,但我刚刚找到答案,所以我想我会分享。也许它会对某人有所帮助。

您可以使用php://流包装器来打开文件描述符:

 $fd = $argv[1];
 $handle = fopen(str_replace('/dev/','php://',$fd)); 

所以不是打开/dev/fd/[nn]操作系统提供的文件描述符。你将打开php://fd/[nn]它,它将起作用。我不确定为什么打开文件描述符在某些系统上失败,而在其他系统上则不然。

这是一个老虫子应该已经修复了。

答案4

这是我最终使用的...

php -r "var_dump(file_get_contents('php://stdin'));" < <(echo this is a win)

如果您尝试使用 stdin 做其他事情,这显然对您不起作用,这与

php -r "var_dump(stream_get_contents(STDIN));" < <(echo this is a win)

这让我想知道你实际上想要做什么,为什么你被困在 file_get_contents 上? :)

参考 -http://php.net/manual/en/wrappers.php.php

相关内容