如何從 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

相關內容