> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Eseguire processi esterni nei pacchetti e Process::fake

> Con la facade Process di Laravel 13 spiega gli argomenti dei comandi esterni, i timeout, le eccezioni di errore, i fake nei test e la corrispondenza dei comandi in forma di array.

Quando un pacchetto chiama comandi esterni come `ffmpeg` o `git`, le differenze di ambiente dell'applicazione che lo usa e gli errori diventano direttamente il comportamento del pacchetto. Invece di chiamare direttamente `shell_exec()` o `exec()`, esegui i comandi tramite la **facade Process** e sostituiscili con `Process::fake()` nei test.

> [!NOTE]
>
> Questa pagina riguarda Laravel 13 e verifica l'implementazione di Framework `v13.42.0`. Presuppone la conoscenza dei [processi](https://kawax.biz/it/processes.md) e dei [test dei pacchetti](https://kawax.biz/it/advanced/package-testing.md). La corrispondenza dei comandi in array trattata qui si basa su come Symfony Process, usato internamente da Process, converte gli argomenti.

## Perché evitare l'esecuzione diretta

Le chiamate dirette a funzioni non si possono sostituire nei test, e ogni chiamante gestisce timeout e codici di uscita in modo diverso. Passando dalla facade Process unifichi la registrazione delle esecuzioni, i fake, i timeout e le eccezioni in caso di errore.

```mermaid theme={null}
flowchart LR
    A["Classe wrapper<br>del pacchetto"] --> B["Process facade<br>PendingProcess"]
    B --> C{"Fake<br>registrato?"}
    C -->|Sì| D["FakeProcessResult<br>nessun comando esterno avviato"]
    C -->|No| E["Avvia il comando esterno"]
    E --> F["ProcessResult"]
    D --> F
    F --> G["throw() trasforma gli errori in eccezioni"]
```

## Passa i comandi come array

Un comando in forma di stringa viene interpretato da `sh`, quindi se include input dell'utente nascono problemi di quoting e rischi di injection. All'interno di un pacchetto, passa gli argomenti come array.

```php theme={null}
<?php

namespace Vendor\Package\Support;

use Illuminate\Support\Facades\Process;

final class VideoConverter
{
    public function toMp4(string $source, string $target): void
    {
        Process::timeout(120)
            ->run(['ffmpeg', '-y', '-i', $source, $target])
            ->throw();
    }
}
```

Il timeout predefinito di `PendingProcess` è di `60` secondi e il timeout di inattività è `null`. Per conversioni lunghe imposta esplicitamente `timeout()`, e per elaborazioni il cui output può bloccarsi aggiungi `idleTimeout()`.

## Eccezioni e messaggi in caso di errore

`ProcessResult::throw()` lancia `ProcessFailedException` quando il codice di uscita non indica successo. Il messaggio contiene il comando eseguito, il codice di uscita, l'output standard e l'output di errore.

> [!WARNING]
>
> Se inserisci segreti negli argomenti del comando, finiscono nel messaggio di `ProcessFailedException` e possono comparire nei log o a schermo. Non passare token come argomenti. Passali con `env()` oppure scrivili nel file di configurazione del comando.

I timeout sono rappresentati da `ProcessTimedOutException` e i timeout di inattività da `ProcessIdleTimedOutException`. Documenta per gli utenti del pacchetto quali eccezioni lanci e cosa possono aspettarsi quando le intercettano.

## Usa i fake nei test

Nei test, sostituisci i comandi esterni con `Process::fake()`. `Process::result()` permette di specificare output standard, output di errore e codice di uscita.

```php theme={null}
<?php

namespace Vendor\Package\Tests\Feature;

use Illuminate\Contracts\Process\ProcessResult;
use Illuminate\Process\Exceptions\ProcessFailedException;
use Illuminate\Process\PendingProcess;
use Illuminate\Support\Facades\Process;
use Vendor\Package\Support\VideoConverter;
use Vendor\Package\Tests\TestCase;

class VideoConverterTest extends TestCase
{
    public function test_it_runs_ffmpeg_with_the_expected_arguments(): void
    {
        Process::fake([
            '*' => Process::result(output: 'converted'),
        ]);

        (new VideoConverter)->toMp4('/tmp/in.mov', '/tmp/out.mp4');

        Process::assertRan(
            fn (PendingProcess $process, ProcessResult $result) => $process->command === ['ffmpeg', '-y', '-i', '/tmp/in.mov', '/tmp/out.mp4']
        );
    }

    public function test_it_fails_when_ffmpeg_exits_with_an_error(): void
    {
        Process::fake([
            '*' => Process::result(errorOutput: 'Invalid data', exitCode: 1),
        ]);

        $this->expectException(ProcessFailedException::class);

        (new VideoConverter)->toMp4('/tmp/in.mov', '/tmp/out.mp4');
    }
}
```

Passando una closure a `assertRan()` puoi verificare `command`, `timeout`, `environment` e altre proprietà di `PendingProcess`. I comandi in array vengono confrontati con `$process->command` per corrispondenza esatta, quindi i test rilevano modifiche all'ordine o ai valori degli argomenti.

## Chiavi di fake per comandi in array

Internamente Process confronta le chiavi di `fake()` con il risultato di `getCommandline()` tramite `Str::is()`. I comandi in array vengono convertiti in una stringa con virgolette per la shell, quindi un pattern stringa come `'ffmpeg*'` non corrisponde. Nei test di comandi in array usa uno dei seguenti approcci.

| Approccio | Quando usarlo | Attenzione |
| - | - | - |
| La chiave `'*'` | Restituire lo stesso risultato indipendentemente dal comando | È difficile accorgersi che è stato chiamato un altro comando |
| `Process::assertRan()` | Verificare gli argomenti dopo la chiamata | Prepara a parte la risposta del fake |
| Fake con closure | Cambiare il risultato in base agli argomenti | La logica si complica, quindi restringi lo scopo del test |

## Impedire comandi esterni imprevisti

Per rilevare comandi esterni che non dovrebbero essere eseguiti nei test, usa `preventStrayProcesses()`. I comandi che non corrispondono a nessun fake generano una `RuntimeException` e non vengono realmente avviati.

```php theme={null}
protected function setUp(): void
{
    parent::setUp();

    Process::preventStrayProcesses();
}
```

## Se usi una Factory iniettata

Se il costruttore del pacchetto riceve `Illuminate\Process\Factory`, verifica che lo stato di `Process::fake()` raggiunga l'istanza iniettata. La facade mette in cache l'istanza risolta e `swap()` sostituisce l'istanza nel container. Non puoi invece contare su una registrazione esplicita del framework perché `Factory` sia condivisa nel container, quindi verificalo in un test.

Se il fake non raggiunge l'istanza iniettata, sostituisci esplicitamente la Factory nel test oppure progetta il pacchetto in modo che passi dalla facade `Process`. In entrambi i casi, verificare con `assertRan()` che la chiamata sia stata registrata permette di individuare una sostituzione dimenticata.

## Verifiche da aggiungere per la manutenzione a lungo termine

| Verifica | Scopo |
| - | - |
| Test che fissa l'ordine degli argomenti dell'array | Accorgersi quando cambia la posizione di `-y` o di input e output |
| Verificare eccezione e messaggio in caso di errore | Mantenere invariata la gestione degli errori degli utenti |
| Verificare i valori predefiniti di `timeout()` e `idleTimeout()` | Trattare un cambio dei valori predefiniti come un cambio di comportamento per gli utenti |
| Abilitare `preventStrayProcesses()` | Bloccare nei test l'avvio di comandi esterni imprevisti |
| Verificare che nessun segreto venga passato come argomento | Evitare fughe nei messaggi di eccezione e nei log |

## Fonti primarie consultate

* [Documentazione ufficiale di Laravel: processi](https://github.com/laravel/docs/blob/13.x/processes.md)
* [Documentazione ufficiale di Laravel: Process::fake e assertRan](https://github.com/laravel/docs/blob/13.x/processes.md#faking-processes)
* [Process Factory: fake e assertRan](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Process/Factory.php)
* [PendingProcess: timeout predefiniti e corrispondenza dei fake](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Process/PendingProcess.php)
* [ProcessResult: comportamento di throw()](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Process/ProcessResult.php)
* [ProcessFailedException: struttura del messaggio](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Process/Exceptions/ProcessFailedException.php)
* [Facade: cache e swap()](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Support/Facades/Facade.php)


## Related topics

- [Argomenti avanzati](https://kawax.biz/it/advanced/index.md)
- [Processi](https://kawax.biz/it/processes.md)
- [Registrazione e test dei transport di posta nei pacchetti](https://kawax.biz/it/advanced/package-mail-transports.md)
- [Client HTTP e test nei pacchetti](https://kawax.biz/it/advanced/package-http-client.md)
- [Estendere le connessioni al database nei pacchetti](https://kawax.biz/it/advanced/package-database-connections.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.