popen()
A função popen() do PHP executa comandos shell e abre um pipe para interagir com eles, permitindo leitura ou escrita de dados de forma eficiente.
Introdução
A função popen() executa um comando shell em um processo filho separado e abre um pipe para ele — um fluxo unidirecional do qual você pode ler a saída do comando ou para o qual pode escrever dados de entrada. É a ferramenta do PHP para transmitir dados de ou para um programa externo linha por linha, em vez de esperar que o comando inteiro termine.
Esta página aborda o que popen() retorna, seus modos r e w, por que você deve sempre combiná-la com pclose(), como ela difere de exec() e shell_exec(), e as regras de segurança que você deve seguir antes de passar qualquer entrada do usuário para um shell.
Quando Usar popen()
Use popen() quando precisar de um fluxo, não de um resultado único:
- Modo leitura (
"r") — processar a saída de um comando de forma incremental, por exemplo, acompanhar um log, ler uma listagem grande dofind, ou transmitir linhas de um CLI de banco de dados sem armazenar tudo na memória. - Modo escrita (
"w") — enviar dados para a entrada padrão de um comando, por exemplo, transmitir texto paragzip,mail, ou um filtro personalizado.
Se você apenas quer a saída completa de um comando como string, shell_exec() ou exec() é mais simples. Se você precisar ler e escrever no mesmo processo ao mesmo tempo, use proc_open() — os pipes do popen() são unidirecionais.
Sintaxe
popen(string $command, string $mode): resource|false$command— o comando shell a executar, exatamente como você digitaria em um terminal.$mode— a direção do pipe:"r"para ler a saída padrão do comando, ou"w"para escrever em sua entrada padrão. Em alguns sistemas você pode adicionar"b"(binário) ou"t"(texto), por exemplo"rb".
Valor de retorno: um recurso de ponteiro de arquivo que você passa para funções como fgets() e fwrite(), ou false se o pipe não pôde ser aberto. O ponteiro não é um identificador de arquivo normal — você deve fechá-lo com pclose(), nunca com fclose().
Como Funciona
Quando você chama popen(), o PHP cria um processo filho que executa $command através do shell e conecta um de seus fluxos padrão a um pipe:
- No modo
"r", o pipe é conectado ao stdout do comando — você lê o que o comando imprime. - No modo
"w", o pipe é conectado ao stdin do comando — o que você escreve torna-se a entrada do comando.
Por transmitir em fluxo, você pode começar a processar a saída antes que o comando termine, o que mantém o uso de memória constante mesmo para saídas grandes.
Exemplos
Exemplo 1: Lendo a Saída de um Comando
<?php
// Read mode: stream the output of a directory listing line by line.
$handle = popen('ls -l', 'r');
if ($handle === false) {
exit("Could not open the pipe.\n");
}
while (!feof($handle)) {
$line = fgets($handle);
echo $line;
}
pclose($handle);feof() verifica se o fim do fluxo foi atingido, fgets() lê uma linha de cada vez, e pclose() fecha o pipe e aguarda o término do processo filho. Sempre verifique o valor de retorno: se popen() falhar, retorna false, e ler de false dispara erros.
No Windows, substitua
ls -lpelo comando equivalente, por exemplodir.
Exemplo 2: Escrevendo na Entrada de um Comando
<?php
// Write mode: pipe a line of text into grep's standard input.
$handle = popen('grep "example"', 'w');
if ($handle === false) {
exit("Could not open the pipe.\n");
}
fwrite($handle, "This line has the word example.\n");
fwrite($handle, "This line does not match.\n");
pclose($handle);Aqui fwrite() envia duas linhas para a entrada padrão do grep. O grep filtra pela palavra example, então apenas a primeira linha é impressa. pclose() então fecha o pipe.
Exemplo 3: Comprimindo Dados em Tempo Real
<?php
// Stream text straight into gzip and save a compressed file.
$handle = popen('gzip > output.txt.gz', 'w');
if ($handle !== false) {
fwrite($handle, "Some data to compress.\n");
pclose($handle);
}Isso transmite dados diretamente para o gzip sem gravar um arquivo intermediário não comprimido.
popen() vs. exec() e shell_exec()
| Função | Retorna | Streaming? | Direção |
|---|---|---|---|
popen() | um recurso de pipe | sim (leitura ou escrita) | unidirecional (stdin ou stdout) |
exec() | última linha + array de saída | não | somente saída |
shell_exec() | saída completa como string | não | somente saída |
proc_open() | recurso de processo | sim | bidirecional (stdin e stdout) |
Use popen() quando quiser transmitir em fluxo; use as outras quando precisar apenas do resultado final.
Segurança: Nunca Passe Entrada Bruta do Usuário
popen() executa seu argumento através do shell, portanto qualquer entrada do usuário não escapada é um risco de injeção de comando. Sempre escape os argumentos antes de construir um comando:
<?php
$userInput = $_GET['name'] ?? 'world';
// escapeshellarg() wraps the value in quotes and neutralizes shell metacharacters.
$command = 'echo Hello ' . escapeshellarg($userInput);
$handle = popen($command, 'r');
echo fgets($handle);
pclose($handle);Use escapeshellarg() para argumentos individuais e escapeshellcmd() para comandos inteiros. Melhor ainda, evite passar dados do usuário para o shell quando uma função nativa do PHP puder fazer o trabalho.
Armadilhas Comuns
- Esquecer o
pclose(). Deixar o pipe aberto vaza o recurso e você nunca obtém o status de saída do filho.pclose()retorna o código de saída do comando. - Usar
fclose()em vez depclose(). Um recurso depopen()é um pipe de processo, não um arquivo simples — feche-o compclose(). - Ignorar um retorno
false. Se o comando não puder iniciar,popen()retornafalse; verifique isso antes de ler ou escrever. - Misturar direções. Um único pipe
popen()é somente leitura ou somente escrita. Para ambas, useproc_open().
Conclusão
popen() abre um pipe unidirecional para um comando shell para que você possa transmitir sua saída ("r") ou alimentá-lo com entrada ("w") sem armazenar tudo na memória. Sempre verifique o valor de retorno, feche o pipe com pclose(), e escape qualquer entrada do usuário com escapeshellarg() antes que ela chegue ao shell. Para aprender os auxiliares de leitura e escrita usados com o pipe, consulte fgets() e fwrite().