Como abrir arquivo substituído por processo do php?

Como abrir arquivo substituído por processo do php?

Aqui está o que eu tentei fazer sozinho:

$ 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)

Eu testei em Debian 6( php-5.4.14, bash-4.1.5) e Arch Linux( php-5.4.12, bash-4.2.42).

Atualização

$ 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

Responder1

O problema é que você deseja que o php leia a entrada de um descritor de arquivo, mas você o força a ler como um arquivo normal.

Primeiro, tente isto:

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

então você pode processar lsa saída lendo /dev/fd/63. A substituição do processo retornará o file descriptor, que é utilizado por outro comando para ler sua saída.

No seu caso, você usa $_SERVER["argv"][1], o que significa que o php será interpretado assim:

file_get_contents(/dev/fd/63)

No manual do php, você pode ver o protótipo da file_get_contentsfunção:

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

Ops, o php será considerado /dev/fd/63um arquivo normal aqui, mas na verdade é um arquivo file descriptor.

Para acessar o descritor de arquivo, você deve usar php://fd, php://fd/63acessará o conteúdo do descritor de arquivo 63:

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

Você pode ver, agora o php pode processar conteúdo em arquivos /dev/fd/63. Mas nosso objetivo é ler o conteúdo do arquivo, que é fornecido por meio de substituição de processo (no meu exemplo, é test.txt). Não sei muito sobre php, então adiciono outro 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!
"

Eu uso echo -npara remover a nova linha da saída de eco, caso contrário, o php verá a saída "test.txt\n".

Observação

Para mais informações sobre como acessar o descritor de arquivo em php, você pode veraqui.

Responder2

Este é o problema:

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

Sem qualquer boa razão (IMHO) phptenta obter o nome real do destino do link. Infelizmente, o destino do link não faz parte do sistema de arquivos, portanto, a tentativa de acessar esse nome falha e causa esse erro. O link simbólico só pode ser aberto como tal. Considero isso um bug no php. Você poderia usar um FIFO:

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

Responder3

Esta é uma pergunta antiga, mas acabei de descobrir a resposta, então pensei em compartilhar. Talvez ajude alguém.

Você pode usar o php://stream wrapper para abrir o descritor de arquivo:

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

Então, em vez de abrir o /dev/fd/[nn]descritor de arquivo fornecido pelo sistema operacional. Você abrirá php://fd/[nn]o que funcionará. Não sei por que a abertura do descritor de arquivo falha em alguns sistemas, mas não em outros.

Este foi umbug antigoe deveria ter sido consertado.

Responder4

Aqui está o que acabei usando ...

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

Se você está tentando fazer outras coisas com stdin, isso obviamente não funcionará para você, e não é diferente de

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

o que me leva a pensar o que você estava realmente tentando fazer e por que está preso em file_get_contents? :)

Referência -http://php.net/manual/en/wrappers.php.php

informação relacionada