---
title: "Tricks for Running Commands with Laravel Process"
description: "Laravel 10 comes with the Process facade. Let's see how to use it properly."
---

# Tricks for Running Commands with Laravel Process

![waiting for a long process to complete](https://fly.io/laravel-bytes/run-commands-with-laravel-process/assets/run-process-cover.webp)

[Annie Ruygt](https://annieruygtillustration.com/)

Fly.io is a great place to run both apps and workers! Deploy now on Fly.io, and get your [Laravel app running](https://docs.fly.io/laravel) globally in a few commands!

Laravel 10 has released the [Laravel’s Process facade](https://laravel.com/docs/10.x/processes), which make running external commands super easily.

```
$result = Process::run("php -v");
echo $result->output();
```

This uses [Symfony Process](https://symfony.com/doc/current/components/process.html) under the hood, and adds a TON of quality-of-life improvements.

What might you run commands for? I’ve personally used it to run `docker` commands on an early version of [Chipper CI](https://chipperci.com), and recently have seen it used to run `ffmpeg` commands to edit media files. There’s a lot of times when it makes sense to reach out beyond PHP!

Let’s see how to run commands (processes), and learn some tricks along the way.

## The Process Component

Some basics first! Let’s run the `ls -lah` command to list out files in the current directory:

```
$result = Process::run('ls -lah')
```

The `$result` is an instance of the [`Illuminate\Contracts\Process\ProcessResult` interface](https://github.com/laravel/framework/blob/10.x/src/Illuminate/Contracts/Process/ProcessResult.php).

If you check that out, we’ll see a few handy methods available to us:

```
# See if it was successful
$result->successful(); // zero exit code
$result->failed(); // non-zero exit code

$result->exitCode(); // the actual exit code

$result->output(); // stdout
$result->errorOutput(); // stderr
```

### Stdout and stderr

Output from a command will come in 2 flavors: `stderr` and `stdout`. They’re sort of like different channels that a command can use to report information, errors, or important data.

Did you know that `stderr` isn’t just error messages? There’s some important stuff to know!

In general, `stderr` output will be human-readable output meant to inform users about errors *or just general information* .

The `stdout` output is generally meant to be something either human-readable or machine readable. It might be something you’d ask some code to parse.

Let’s see that in action.

If we run `fly apps list -j` to get a JSON format list of apps and their information, we may see this:

![flyctl output showing information message and json output](https://fly.io/laravel-bytes/run-commands-with-laravel-process/assets/run_a_process_stdout_stderr.png)

The informational message about the version of `flyctl` and how to update *is output to `stderr`* . The real content (the list of apps) is sent to `stdout`.

The `stdout` output is what we care about. Luckily, if I wanted to pipe the JSON output to `jq` for some extra parsing, I could! Even though the command outputs an informational message, *sending output through a pipe will only pass along the `stdout` content* .

```
# Find the status of each app
# The pipe `|` only get stdout content
fly apps list -j | jq '.[].Status'
```

That lists the status of each Fly.io app listed.

### Parsing Output in PHP

We can use this knowledge to parse command output in PHP.

```
// Get our list of apps as JSON and parse it in PHP
$result = Process::run("fly apps list -j");

if ($result->successful()) {
    $apps = json_decode($result->output(), true);

    var_dump($apps);
}
```

Here’s a fun issue with the above code: The `flyctl` output has some debug information sent through `stdout`, so we can’t directly parse the JSON outout!

![flyctl output mixing debug information with stdout](https://fly.io/laravel-bytes/run-commands-with-laravel-process/assets/run_a_process_haha.png)

It turns out that when running the above in a tinker session, the Laravel environment has `LOG_LEVEL=debug` set. While that is a Laravel-specific environment variable coming from the `.env` file, `flyctl` happens to use it also!

Why is the `fly` command seeing that? This brings us to our next topic!

### Environment Variables

Processes typically inherit their parent-processes environment variables. Since our PHP tinker session has environment variable `LOG_LEVEL` set, that’s getting passed to the `fly` command we ran. The `fly` command just so happens to use that environment variable, and so we get some debug information output (to `stdout` in this case).

Luckily, we can unset that environment variable by setting it to `false`:

```
# Unset the LOG_LEVEL env var when 
# running the process:
$result = Process::env(['LOG_LEVEL' => false])
    ->run("fly apps list -j");

if ($result->successful()) {
    $apps = json_decode($result, true);

    // todo: Ensure JSON was parsed
    foreach($apps as $app) {
        echo $app['Name']."\n";
    }
}
```

Now our output doesn’t contain the debug statements in `stdout` and we can parse the JSON in PHP!

We’re not limited to unsetting environment variables. We can pass additional ones (or overwrite others) by passing more things to that array:

```
# If you want to see a TON of debug
# output from the `fly` command:
$result = Process::env([
        'LOG_LEVEL' => 'debug',
        'DEV' => '1',
    ])->run("fly apps list -j");
```

## Fly.io ❤️ Laravel

Fly your servers close to your users—and keep your apps speedy. Deploy globally on Fly in minutes!

[Deploy your Laravel app! →](https://docs.fly.io/laravel)

### Streaming Output

What if we run a command that does a bunch of stuff over time, like deploying a Fly app?

We can actually get a command’s output in “real time”. We just pass the `run` method a closure:

```
$result = Process::env('LOG_LEVEL' => false,)
    ->path("/path/to/dir/containing/a/fly.toml-file")
    ->run("fly deploy", function(string $type, string $output) {
        // We'll ignore stderr for now
        if ($type == 'stdout') {
            doSomethingWithThisChunkOfOutput($output);
        }
     });
```

I’ve used this to stream output to users watching in the browser (with the help of Pusher and websockets).

## Security

I’m not going to talk about sanitizing user input and the dangers of allowing user input to define what commands you might run (yeah, probably don’t do that).

Instead, I have something specific to Fly.io and Laravel! If you ran `fly launch` to generate a Dockerfile, we have something to tweak here.

By default, the `open_basedir` setting is set in the [`php-fpm` configuration](https://github.com/fly-apps/laravel-docker/blob/main/src/fpm/pool.d/www.conf#L509). This limits what directories PHP can see, which prohibits the ability for PHP to see (and run) commands in `/usr/local/bin` or similar.

You change that, you can either remove that configuration or append a directory containing a command you’ll be using.

In file `.fly/fpm/poold/www.conf`, I can append directory `/usr/local/bin`:

```
; @fly settings:
; Security measures
php_admin_value[open_basedir] = /var/www/html:/dev/stdout:/tmp:/usr/local/bin
php_admin_flag[session.cookie_secure] = true
```

That change should get pulled in in your next deployment.

## There’s More

There’s a bunch more you can do! Check out the [docs](https://laravel.com/docs/10.x/processes) and [examples here](https://beyondco.de/blog/laravel-10-new-process-facade)!
