W3docs

popen()

La funzione popen() di PHP esegue comandi shell e apre una pipe per leggerne l'output o scrivere dati in ingresso al processo.

Introduzione

La funzione popen() esegue un comando shell in un processo figlio separato e apre una pipe verso di esso — un flusso unidirezionale da cui è possibile leggere l'output del comando oppure scrivere dati in ingresso. È lo strumento PHP per trasmettere dati da o verso un programma esterno riga per riga, invece di attendere il completamento dell'intero comando.

Questa pagina illustra cosa restituisce popen(), le sue modalità r e w, perché è sempre necessario abbinarla a pclose(), in che cosa si differenzia da exec() e shell_exec(), e le regole di sicurezza da rispettare prima di passare qualsiasi input dell'utente alla shell.

Quando usare popen()

Ricorrere a popen() quando si ha bisogno di un flusso, non di un risultato istantaneo:

  • Modalità lettura ("r") — elaborare in modo incrementale l'output di un comando, ad esempio seguendo un log in tempo reale, leggendo un grande risultato di find, oppure estraendo righe da una CLI di database senza bufferizzare tutto in memoria.
  • Modalità scrittura ("w") — inviare dati all'input standard di un comando, ad esempio inviando testo a gzip, mail, o un filtro personalizzato.

Se si vuole semplicemente ottenere l'output completo di un comando come stringa, shell_exec() o exec() sono più semplici. Se è necessario leggere e scrivere nello stesso processo contemporaneamente, usare proc_open() — le pipe di popen() sono unidirezionali.

Sintassi

popen(string $command, string $mode): resource|false
  • $command — il comando shell da eseguire, esattamente come lo si digiterebbe in un terminale.
  • $mode — la direzione della pipe: "r" per leggere l'output standard del comando, oppure "w" per scrivere nel suo input standard. Su alcuni sistemi è possibile aggiungere "b" (binario) o "t" (testo), ad esempio "rb".

Valore restituito: una risorsa puntatore a file da passare a funzioni come fgets() e fwrite(), oppure false se la pipe non può essere aperta. Il puntatore non è un normale handle di file — deve essere chiuso con pclose(), mai con fclose().

Come funziona

Quando si chiama popen(), PHP crea un processo figlio che esegue $command tramite la shell e connette uno dei suoi flussi standard a una pipe:

  • In modalità "r", la pipe è collegata allo stdout del comando — si legge ciò che il comando stampa.
  • In modalità "w", la pipe è collegata allo stdin del comando — ciò che si scrive diventa l'input del comando.

Poiché opera in streaming, è possibile iniziare a elaborare l'output prima che il comando sia terminato, mantenendo costante l'utilizzo della memoria anche per output di grandi dimensioni.

Esempi

Esempio 1: Lettura dell'output di un 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 è stata raggiunta la fine del flusso, fgets() legge una riga alla volta, e pclose() chiude la pipe e attende che il processo figlio termini. Verificare sempre il valore restituito: se popen() fallisce, restituisce false, e leggere da false genera errori.

Su Windows, sostituire ls -l con il comando equivalente, ad esempio dir.

Esempio 2: Scrittura nell'input di un 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);

Qui fwrite() invia due righe all'input standard di grep. grep filtra per la parola example, quindi solo la prima riga viene stampata. pclose() chiude poi la pipe.

Esempio 3: Compressione dei dati al volo

<?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);
}

Questo invia i dati direttamente a gzip senza scrivere un file intermedio non compresso.

popen() vs. exec() e shell_exec()

FunzioneRestituisceStreaming?Direzione
popen()una risorsa pipesì (lettura o scrittura)unidirezionale (stdin o stdout)
exec()ultima riga + array di outputnosolo output
shell_exec()output completo come stringanosolo output
proc_open()risorsa processobidirezionale (stdin e stdout)

Usare popen() quando si vuole operare in streaming; usare le altre funzioni quando si ha bisogno solo del risultato finale.

Sicurezza: non passare mai input utente non elaborato

popen() esegue il suo argomento attraverso la shell, quindi qualsiasi input utente non precedentemente sanificato costituisce un rischio di command injection. Effettuare sempre l'escape degli argomenti prima di costruire un 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);

Usare escapeshellarg() per i singoli argomenti e escapeshellcmd() per i comandi interi. Meglio ancora, evitare del tutto di passare dati utente alla shell quando una funzione PHP nativa può svolgere il lavoro.

Errori comuni

  • Dimenticare pclose(). Lasciare la pipe aperta spreca la risorsa e non si ottiene mai lo stato di uscita del processo figlio. pclose() restituisce il codice di uscita del comando.
  • Usare fclose() invece di pclose(). Una risorsa popen() è una pipe di processo, non un semplice file — chiuderla con pclose().
  • Ignorare un valore restituito false. Se il comando non può avviarsi, popen() restituisce false; verificarlo prima di leggere o scrivere.
  • Confondere le direzioni. Una singola pipe popen() è di sola lettura o di sola scrittura. Per entrambe, usare proc_open().

Conclusione

popen() apre una pipe unidirezionale verso un comando shell per poterne trasmettere l'output ("r") oppure inviargli dati in ingresso ("w") senza bufferizzare tutto in memoria. Verificare sempre il valore restituito, chiudere la pipe con pclose() ed eseguire l'escape di qualsiasi input utente con escapeshellarg() prima che raggiunga la shell. Per conoscere gli helper di lettura e scrittura utilizzati con la pipe, vedere fgets() e fwrite().

Pratica

Pratica
Qual è la funzione del comando 'popen' in PHP?
Qual è la funzione del comando 'popen' in PHP?
Was this page helpful?