Il componente Process

Il componente Process esegue i comandi nei sotto-processi.

Installazione

Si può installare il componente in due modi:

Quindi, richiedere il file vendor/autoload.php per abilitare il meccanismo di auto-caricamento fornito da Composer. Altrimenti, l’applicazione non sarà in grado di trovare le classi di questo componente di Symfony.

Uso

La classe Symfony\Component\Process\Process consente di eseguire un comando in un sotto-processo:

use Symfony\Component\Process\Process;

$process = new Process('ls -lsa');
$process->run();

// eseguito deopo la fine del comando
if (!$process->isSuccessful()) {
    throw new \RuntimeException($process->getErrorOutput());
}

print $process->getOutput();

Il metodo si prende cura delle sottili differenze tra le varie piattaforme, durante l’esecuzione del comando.

Il metodo getOutput() restituisce sempre l’intero contenuto dell’output standard del comando, mentre getErrorOutput() restituisce l’intero contenuto dell’output di errore. In alternativa, i metodi getIncrementalOutput() e getIncrementalErrorOutput() restituiscono i nuovi output dall’ultima chiamata.

Nuovo nella versione 2.4: I metodi flushOutput() e flushErrorOutput() sono stati aggiunti in Symfony 2.4.

Il metodo flushOutput() aggiorna il contenuto dell’output e flushErrorOutput() aggiorna il contenuto degli errori.

Output del processo in tempo reale

Quando si esegue un comando che gira a lungo (come la sincronizzazione di file con un server remoto), si può dare un feedback all’utente finale in tempo reale, passando una funzione anonima al metodo run():

use Symfony\Component\Process\Process;

$process = new Process('ls -lsa');
$process->run(function ($type, $buffer) {
    if (Process::ERR === $type) {
        echo 'ERR > '.$buffer;
    } else {
        echo 'OUT > '.$buffer;
    }
});

Esecuzione asincrona dei processi

Si può anche iniziare il sotto-processo e lasciarlo girare in modo asincrono, recuperando l’output e lo stato nel processo principale, quando occorre. Usare il metodo start() per iniziare un processo asincrono, il metodo isRunning() per verificare che il processo sia finito e il metodo getOutput() per ottenere l’output:

$process = new Process('ls -lsa');
$process->start();

while ($process->isRunning()) {
    // aspetta che il processo finisca
}

echo $process->getOutput();

Si può anche aspettare che un processo finisca, se è stato fatto partire in modo asincrono e si sta facendo altro:

$process = new Process('ls -lsa');
$process->start();

// ... fare altre cose

$process->wait(function ($type, $buffer) {
    if (Process::ERR === $type) {
        echo 'ERR > '.$buffer;
    } else {
        echo 'OUT > '.$buffer;
    }
});

Nota

Il metodo wait() è bloccante, il che vuol dire che il codice si fermerà a quella linea, finché il processo esterno non sarà finito.

Fermare un processo

Nuovo nella versione 2.3: Il parametro signal del metodo stop è stato aggiunto in Symfony 2.3.

Ogni processo asincrono può essere fermato in qualsiasi momento, con il metodo stop(). Questo metodo accetta due parametri: una scadenza e un segnale. Una volta raggiunta la scadenza, il segnale viene inviato al processo in esecuzione. Il segnale predefinito inviato al processo è SIGKILL. Si legga la documentazione sui segnali per approfondire la gestione dei segnali nel componente Process:

$process = new Process('ls -lsa');
$process->start();

// ... fare altre cose

$process->stop(3, SIGINT);

Eseguire codice PHP in isolamento

Se si vuole eseguire del codice PHP in isolamento, usare invece PhpProcess:

use Symfony\Component\Process\PhpProcess;

$process = new PhpProcess(<<<EOF
    <?php echo 'Ciao mondo'; ?>
EOF
);
$process->run();

Per far funzionare meglio il proprio codice su tutte le piattaforme, potrebbe essere preferibile usare la classe Symfony\Component\Process\ProcessBuilder:

use Symfony\Component\Process\ProcessBuilder;

$builder = new ProcessBuilder(array('ls', '-lsa'));
$builder->getProcess()->run();

Nuovo nella versione 2.3: Il metodo ProcessBuilder::setPrefix è stato aggiunto in Symfony 2.3.

Se si sta costruendo un driver binario, si può usare il metodo setPrefix() per prefissare tutti i comandi del processo generato.

L’esempio seguente genererà due comandi di processo per un adattatore binario di tar:

use Symfony\Component\Process\ProcessBuilder;

$builder = new ProcessBuilder();
$builder->setPrefix('/usr/bin/tar');

// '/usr/bin/tar' '--list' '--file=archive.tar.gz'
echo $builder
    ->setArguments(array('--list', '--file=archive.tar.gz'))
    ->getProcess()
    ->getCommandLine();

// '/usr/bin/tar' '-xzf' 'archive.tar.gz'
echo $builder
    ->setArguments(array('-xzf', 'archive.tar.gz'))
    ->getProcess()
    ->getCommandLine();

Scadenza del processo

Si può limitare il tempo a disposizione di un processo per essere completato, impostando una scadenza (in secondi):

use Symfony\Component\Process\Process;

$process = new Process('ls -lsa');
$process->setTimeout(3600);
$process->run();

Se questo tempo viene raggiunto, viene lanciata una Symfony\Process\Exception\RuntimeException.

Per comandi che richiedono molto tempo, è responsabilità dello sviluppatore controllare il timeout a intervalli regolari:

$process->setTimeout(3600);
$process->start();

while ($condition) {
    // ...

    // verifica se è stato raggiunto il timeout
    $process->checkTimeout();

    usleep(200000);
}

Scadenza del processo inattivo

Nuovo nella versione 2.4: Il metodo setIdleTimeout() è stato aggiunto in Symfony 2.4.

Contrariamente alla scadenza vista nel paragrafo precedente, la scadenza inattiva considera solo il tempo dall’ultimo output prodotto dal processo:

use Symfony\Component\Process\Process;

$process = new Process('qualcosa-con-runtime-variabile');
$process->setTimeout(3600);
$process->setIdleTimeout(60);
$process->run();

In questo caso, si considera scaduto un processo se il tempo totale eccede 3600 secondi o se il processo non produce output per 60 secondi.

Segnali di processo

Nuovo nella versione 2.3: Il metodo signal è stato aggiunto in Symfony 2.3.

Durante l’esecuzione di un programma asincrono, si possono inviare segnali posix, con il metodo signal():

use Symfony\Component\Process\Process;

$process = new Process('find / -name "rabbit"');
$process->start();

// invierà un SIGKILL al processo
$process->signal(SIGKILL);

Attenzione

A causa di alcune limitazioni in PHP, se si usano segnali con il componente Process, potrebbe essere necessario prefissare i comandi con exec. Si leggano la issue #5759 di Symfony e il bug #39992 di PHP per capire perché questo accada.

I segnali POSIX non sono disponibili su piattaforme Windows, si faccia riferimento alla documentazione di PHP per i segnali disponibili.

Pid del processo

Nuovo nella versione 2.3: Il metodo getPid è stato aggiunto in Symfony 2.3.

Si può avere accesso al pid di un processo in esecuzione, con il metodo getPid().

use Symfony\Component\Process\Process;

$process = new Process('/usr/bin/php worker.php');
$process->start();

$pid = $process->getPid();

Attenzione

A causa di alcune limitazioni in PHP, se si vuole ottenere il pid di un processo, potrebbe essere necessario prefissare i comandi con exec. Si legga la issue #5759 di Symfony per capire perché questo accada.

Disabling Output

Nuovo nella versione 2.5: I metodi disableOutput() e enableOutput() sono stati introdotti in Symfony 2.5.

Poiché l’output standard e l’output di errore sono sempre recuperati dal processo sottostante, in alcuni casi potrebbe essere conveniente disabilitare l’output, per risparmiare memoria. Usare disableOutput() e enableOutput() per abilitare questa caratteristica:

use Symfony\Component\Process\Process;

$process = new Process('/usr/bin/php worker.php');
$process->disableOutput();
$process->run();

Attenzione

Non si può abilitare o disabilitare l’output mentre il processo sta girando.

Se si disabilita l’output, non si può accedere a getOutput, getIncrementalOutput, getErrorOutput o getIncrementalErrorOutput. Inoltre, non si può passare un callback ai metodi start, run o mustRun né usare setIdleTimeout.