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

# Externe Prozesse in Paketen ausführen und Process::fake

> Erklärt mit der Process-Facade von Laravel 13 Argumente externer Befehle, Timeouts, Fehler-Exceptions, Fakes in Tests und den Abgleich von Array-Befehlen.

Wenn ein Paket externe Befehle wie `ffmpeg` oder `git` aufruft, werden Unterschiede in der Umgebung der nutzenden Anwendung und Fehlschläge direkt zum Verhalten des Pakets. Rufen Sie `shell_exec()` oder `exec()` nicht direkt auf, sondern führen Sie Befehle über die **Process-Facade** aus und ersetzen Sie sie in Tests durch `Process::fake()`.

> [!NOTE]
>
> Diese Seite richtet sich an Laravel 13 und prüft die Implementierung von Framework `v13.42.0`. Sie setzt [Prozesse](https://kawax.biz/de/processes.md) und [Pakete testen](https://kawax.biz/de/advanced/package-testing.md) voraus. Der hier behandelte Abgleich von Array-Befehlen beruht darauf, wie Symfony Process, das Process intern verwendet, Argumente umwandelt.

## Warum Sie die direkte Ausführung vermeiden sollten

Direkte Funktionsaufrufe lassen sich in Tests nicht austauschen, und jeder Aufrufer behandelt Timeouts und Exit-Codes anders. Über die Process-Facade vereinheitlichen Sie das Protokollieren der Ausführung, Fakes, Timeouts und Exceptions bei Fehlschlägen.

```mermaid theme={null}
flowchart LR
    A["Wrapper-Klasse<br>des Pakets"] --> B["Process facade<br>PendingProcess"]
    B --> C{"Fake<br>registriert?"}
    C -->|Ja| D["FakeProcessResult<br>kein externer Befehl wird gestartet"]
    C -->|Nein| E["Externen Befehl starten"]
    E --> F["ProcessResult"]
    D --> F
    F --> G["throw() macht Fehlschläge zu Exceptions"]
```

## Befehle als Array übergeben

Ein Befehl als String wird von `sh` interpretiert. Enthält er Benutzereingaben, entstehen Probleme mit Anführungszeichen und Raum für Injection. Übergeben Sie Argumente innerhalb eines Pakets als 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();
    }
}
```

Der Standard-Timeout von `PendingProcess` beträgt `60` Sekunden, der Idle-Timeout ist `null`. Setzen Sie bei langen Konvertierungen `timeout()` explizit und kombinieren Sie es bei Verarbeitungen, deren Ausgabe stocken kann, mit `idleTimeout()`.

## Exceptions und Meldungen bei Fehlschlägen

`ProcessResult::throw()` wirft eine `ProcessFailedException`, wenn der Exit-Code keinen Erfolg anzeigt. Die Meldung enthält den ausgeführten Befehl, den Exit-Code sowie die Standardausgabe und die Standardfehlerausgabe.

> [!WARNING]
>
> Wenn Sie Geheimnisse in Befehlsargumente schreiben, landen sie in der Meldung der `ProcessFailedException` und können in Logs oder auf dem Bildschirm erscheinen. Übergeben Sie Tokens nicht als Argumente, sondern mit `env()`, oder schreiben Sie sie in die Konfigurationsdatei des Befehls.

Timeouts werden durch `ProcessTimedOutException` dargestellt, Idle-Timeouts durch `ProcessIdleTimedOutException`. Dokumentieren Sie für die Nutzer Ihres Pakets, welche Exceptions Sie werfen und was sie beim Abfangen erwarten können.

## In Tests faken

Ersetzen Sie externe Befehle in Tests durch `Process::fake()`. Mit `Process::result()` legen Sie Standardausgabe, Standardfehlerausgabe und Exit-Code fest.

```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');
    }
}
```

Wenn Sie `assertRan()` eine Closure übergeben, können Sie `command`, `timeout`, `environment` und weitere Eigenschaften von `PendingProcess` prüfen. Array-Befehle werden mit `$process->command` auf exakte Gleichheit verglichen, sodass Tests Änderungen an Reihenfolge oder Werten der Argumente erkennen.

## Fake-Schlüssel für Array-Befehle

Intern gleicht Process die Schlüssel von `fake()` mit `Str::is()` gegen das Ergebnis von `getCommandline()` ab. Array-Befehle werden in einen für die Shell in Anführungszeichen gesetzten String umgewandelt, daher passt ein String-Muster wie `'ffmpeg*'` nicht. Verwenden Sie in Tests für Array-Befehle eine der folgenden Möglichkeiten.

| Methode | Einsatzzweck | Hinweis |
| - | - | - |
| Der Schlüssel `'*'` | Unabhängig vom Befehl dasselbe Ergebnis zurückgeben | Ein anderer aufgerufener Befehl fällt kaum auf |
| `Process::assertRan()` | Argumente nach dem Aufruf prüfen | Die Fake-Antwort separat vorbereiten |
| Fake mit Closure | Ergebnis abhängig von den Argumenten ändern | Die Logik wird komplex, daher den Zweck des Tests eng halten |

## Unerwartete externe Befehle verhindern

Um externe Befehle zu erkennen, die in Tests nicht ausgeführt werden sollen, verwenden Sie `preventStrayProcesses()`. Befehle, die zu keinem Fake passen, führen zu einer `RuntimeException` und werden nicht tatsächlich gestartet.

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

    Process::preventStrayProcesses();
}
```

## Wenn Sie eine injizierte Factory verwenden

Wenn der Konstruktor Ihres Pakets `Illuminate\Process\Factory` entgegennimmt, prüfen Sie, ob der Zustand von `Process::fake()` bis zur injizierten Instanz reicht. Die Facade cacht die aufgelöste Instanz, und `swap()` ersetzt die Instanz im Container. Ob `Factory` im Container geteilt wird, können Sie dagegen nicht einer expliziten Registrierung durch das Framework überlassen. Prüfen Sie es daher tatsächlich in einem Test.

Reicht der Fake nicht bis zur injizierten Instanz, tauschen Sie die Factory im Test explizit aus oder gestalten Sie das Paket so, dass es über die `Process`-Facade läuft. Prüfen Sie in beiden Fällen mit `assertRan()`, dass der Aufruf protokolliert wurde. So erkennen Sie einen vergessenen Austausch.

## Zusätzliche Prüfungen für die langfristige Wartung

| Prüfung | Zweck |
| - | - |
| Test, der die Reihenfolge der Array-Argumente festschreibt | Bemerken, wenn sich die Position von `-y` oder von Ein- und Ausgabe ändert |
| Exception und Meldung bei Fehlschlägen prüfen | Die Fehlerbehandlung der Nutzer unverändert halten |
| Standardwerte von `timeout()` und `idleTimeout()` prüfen | Eine Änderung der Standardwerte als Verhaltensänderung für Nutzer behandeln |
| `preventStrayProcesses()` aktivieren | Unerwartete Starts externer Befehle in Tests stoppen |
| Prüfen, dass keine Geheimnisse als Argumente übergeben werden | Lecks in Exception-Meldungen und Logs verhindern |

## Verwendete Primärquellen

* [Offizielle Laravel-Dokumentation: Prozesse](https://github.com/laravel/docs/blob/13.x/processes.md)
* [Offizielle Laravel-Dokumentation: Process::fake und assertRan](https://github.com/laravel/docs/blob/13.x/processes.md#faking-processes)
* [Process Factory: fake und assertRan](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Process/Factory.php)
* [PendingProcess: Standard-Timeouts und Abgleich von Fakes](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Process/PendingProcess.php)
* [ProcessResult: Verhalten von throw()](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Process/ProcessResult.php)
* [ProcessFailedException: Aufbau der Meldung](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Process/Exceptions/ProcessFailedException.php)
* [Facade: Caching und swap()](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Support/Facades/Facade.php)


## Related topics

- [Weiterführende Themen](https://kawax.biz/de/advanced/index.md)
- [Prozesse](https://kawax.biz/de/processes.md)
- [Queue-Jobs in Paketen und Kompatibilität bei Updates](https://kawax.biz/de/advanced/package-queue-jobs.md)
- [HTTP-Client und Tests in Paketen](https://kawax.biz/de/advanced/package-http-client.md)
- [Event-Listener in Paketen registrieren und cachen](https://kawax.biz/de/advanced/package-events.md)


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