Skip to content

Processes

Introduction

LaraGram provides an expressive, minimal API around the Symfony Process component, allowing you to conveniently invoke external processes from your LaraGram application. LaraGram's process features are focused on the most common use cases and a wonderful developer experience.

Invoking Processes

To invoke a process, you may use the run and start methods offered by the Process facade. The run method will invoke a process and wait for the process to finish executing, while the start method is used for asynchronous process execution. We'll examine both approaches within this documentation. First, let's examine how to invoke a basic, synchronous process and inspect its result:

php
use LaraGram\Support\Facades\Process;

$result = Process::run('ls -la');

return $result->output();

Of course, the LaraGram\Contracts\Process\ProcessResult instance returned by the run method offers a variety of helpful methods that may be used to inspect the process result:

php
$result = Process::run('ls -la');

$result->successful();
$result->failed();
$result->exitCode();
$result->output();
$result->errorOutput();

Throwing Exceptions

If you have a process result and would like to throw an instance of LaraGram\Process\Exceptions\ProcessFailedException if the exit code is greater than zero (thus indicating failure), you may use the throw and throwIf methods. If the process did not fail, the process result instance will be returned:

php
$result = Process::run('ls -la')->throw();

$result = Process::run('ls -la')->throwIf($condition);

Process Options

Of course, you may need to customize the behavior of a process before invoking it. Thankfully, LaraGram allows you to tweak a variety of process features, such as the working directory, timeout, and environment variables.

Working Directory Path

You may use the path method to specify the working directory of the process. If this method is not invoked, the process will inherit the working directory of the currently executing PHP script:

php
$result = Process::path(__DIR__)->run('ls -la');

Input

You may provide input via the "standard input" of the process using the input method:

php
$result = Process::input('Hello World')->run('cat');

Timeouts

By default, processes will throw an instance of LaraGram\Process\Exceptions\ProcessTimedOutException after executing for more than 60 seconds. However, you can customize this behavior via the timeout method:

php
$result = Process::timeout(120)->run('bash import.sh');

Or, if you would like to disable the process timeout entirely, you may invoke the forever method:

php
$result = Process::forever()->run('bash import.sh');

The idleTimeout method may be used to specify the maximum number of seconds the process may run without returning any output:

php
$result = Process::timeout(60)->idleTimeout(30)->run('bash import.sh');

Environment Variables

Environment variables may be provided to the process via the env method. The invoked process will also inherit all of the environment variables defined by your system:

php
$result = Process::forever()
    ->env(['IMPORT_PATH' => __DIR__])
    ->run('bash import.sh');

If you wish to remove an inherited environment variable from the invoked process, you may provide that environment variable with a value of false:

php
$result = Process::forever()
    ->env(['LOAD_PATH' => false])
    ->run('bash import.sh');

TTY Mode

The tty method may be used to enable TTY mode for your process. TTY mode connects the input and output of the process to the input and output of your program, allowing your process to open an editor like Vim or Nano as a process:

php
Process::forever()->tty()->run('vim');

Process Output

As previously discussed, process output may be accessed using the output (stdout) and errorOutput (stderr) methods on a process result:

php
use LaraGram\Support\Facades\Process;

$result = Process::run('ls -la');

echo $result->output();
echo $result->errorOutput();

However, output may also be gathered in real-time by passing a closure as the second argument to the run method. The closure will receive two arguments: the "type" of output (stdout or stderr) and the output string itself:

php
$result = Process::run('ls -la', function (string $type, string $output) {
    echo $output;
});

LaraGram also offers the seeInOutput and seeInErrorOutput methods, which provide a convenient way to determine if a given string was contained in the process' output:

php
if (Process::run('ls -la')->seeInOutput('laragram')) {
    // ...
}

Disabling Process Output

If your process is writing a significant amount of output that you are not interested in, you can conserve memory by disabling output retrieval entirely. To accomplish this, invoke the quietly method while building the process:

php
use LaraGram\Support\Facades\Process;

$result = Process::quietly()->run('bash import.sh');

Pipelines

Sometimes you may want to make the output of one process the input of another process. This is often referred to as "piping" the output of a process into another. The pipe method provided by the Process facades makes this easy to accomplish. The pipe method will execute the piped processes synchronously and return the process result for the last process in the pipeline:

php
use LaraGram\Process\Pipe;
use LaraGram\Support\Facades\Process;

$result = Process::pipe(function (Pipe $pipe) {
    $pipe->command('cat example.txt');
    $pipe->command('grep -i "laragram"');
});

if ($result->successful()) {
    // ...
}

If you do not need to customize the individual processes that make up the pipeline, you may simply pass an array of command strings to the pipe method:

php
$result = Process::pipe([
    'cat example.txt',
    'grep -i "laragram"',
]);

The process output may be gathered in real-time by passing a closure as the second argument to the pipe method. The closure will receive two arguments: the "type" of output (stdout or stderr) and the output string itself:

php
$result = Process::pipe(function (Pipe $pipe) {
    $pipe->command('cat example.txt');
    $pipe->command('grep -i "laragram"');
}, function (string $type, string $output) {
    echo $output;
});

LaraGram also allows you to assign string keys to each process within a pipeline via the as method. This key will also be passed to the output closure provided to the pipe method, allowing you to determine which process the output belongs to:

php
$result = Process::pipe(function (Pipe $pipe) {
    $pipe->as('first')->command('cat example.txt');
    $pipe->as('second')->command('grep -i "laragram"');
})->start(function (string $type, string $output, string $key) {
    // ...
});

Asynchronous Processes

While the run method invokes processes synchronously, the start method may be used to invoke a process asynchronously. This allows your application to continue performing other tasks while the process runs in the background. Once the process has been invoked, you may utilize the running method to determine if the process is still running:

php
$process = Process::timeout(120)->start('bash import.sh');

while ($process->running()) {
    // ...
}

$result = $process->wait();

As you may have noticed, you may invoke the wait method to wait until the process is finished executing and retrieve the process result instance:

php
$process = Process::timeout(120)->start('bash import.sh');

// ...

$result = $process->wait();

Process IDs and Signals

The id method may be used to retrieve the operating system assigned process ID of the running process:

php
$process = Process::start('bash import.sh');

return $process->id();

You may use the signal method to send a "signal" to the running process. A list of predefined signal constants can be found within the PHP documentation:

php
$process->signal(SIGUSR2);

Asynchronous Process Output

While an asynchronous process is running, you may access its entire current output using the output and errorOutput methods; however, you may utilize the latestOutput and latestErrorOutput to access the output from the process that has occurred since the output was last retrieved:

php
$process = Process::timeout(120)->start('bash import.sh');

while ($process->running()) {
    echo $process->latestOutput();
    echo $process->latestErrorOutput();

    sleep(1);
}

Like the run method, output may also be gathered in real-time from asynchronous processes by passing a closure as the second argument to the start method. The closure will receive two arguments: the "type" of output (stdout or stderr) and the output string itself:

php
$process = Process::start('bash import.sh', function (string $type, string $output) {
    echo $output;
});

$result = $process->wait();

Instead of waiting until the process has finished, you may use the waitUntil method to stop waiting based on the output of the process. LaraGram will stop waiting for the process to finish when the closure given to the waitUntil method returns true:

php
$process = Process::start('bash import.sh');

$process->waitUntil(function (string $type, string $output) {
    return $output === 'Ready...';
});

Asynchronous Process Timeouts

While an asynchronous process is running, you may verify that the process has not timed out using the ensureNotTimedOut method. This method will throw a timeout exception if the process has timed out:

php
$process = Process::timeout(120)->start('bash import.sh');

while ($process->running()) {
    $process->ensureNotTimedOut();

    // ...

    sleep(1);
}

Concurrent Processes

LaraGram also makes it a breeze to manage a pool of concurrent, asynchronous processes, allowing you to easily execute many tasks simultaneously. To get started, invoke the pool method, which accepts a closure that receives an instance of LaraGram\Process\Pool.

Within this closure, you may define the processes that belong to the pool. Once a process pool is started via the start method, you may access the collection of running processes via the running method:

php
use LaraGram\Process\Pool;
use LaraGram\Support\Facades\Process;

$pool = Process::pool(function (Pool $pool) {
    $pool->path(__DIR__)->command('bash import-1.sh');
    $pool->path(__DIR__)->command('bash import-2.sh');
    $pool->path(__DIR__)->command('bash import-3.sh');
})->start(function (string $type, string $output, int $key) {
    // ...
});

while ($pool->running()->isNotEmpty()) {
    // ...
}

$results = $pool->wait();

As you can see, you may wait for all of the pool processes to finish executing and resolve their results via the wait method. The wait method returns an array accessible object that allows you to access the process result instance of each process in the pool by its key:

php
$results = $pool->wait();

echo $results[0]->output();

Or, for convenience, the concurrently method may be used to start an asynchronous process pool and immediately wait on its results. This can provide particularly expressive syntax when combined with PHP's array destructuring capabilities:

php
[$first, $second, $third] = Process::concurrently(function (Pool $pool) {
    $pool->path(__DIR__)->command('ls -la');
    $pool->path(app_path())->command('ls -la');
    $pool->path(storage_path())->command('ls -la');
});

echo $first->output();

Naming Pool Processes

Accessing process pool results via a numeric key is not very expressive; therefore, LaraGram allows you to assign string keys to each process within a pool via the as method. This key will also be passed to the closure provided to the start method, allowing you to determine which process the output belongs to:

php
$pool = Process::pool(function (Pool $pool) {
    $pool->as('first')->command('bash import-1.sh');
    $pool->as('second')->command('bash import-2.sh');
    $pool->as('third')->command('bash import-3.sh');
})->start(function (string $type, string $output, string $key) {
    // ...
});

$results = $pool->wait();

return $results['first']->output();

Pool Process IDs and Signals

Since the process pool's running method provides a collection of all invoked processes within the pool, you may easily access the underlying pool process IDs:

php
$processIds = $pool->running()->each->id();

And, for convenience, you may invoke the signal method on a process pool to send a signal to every process within the pool:

php
$pool->signal(SIGUSR2);

Released under the MIT License.