Click the Castor logo or press Ctrl Alt T to change theme.
# Waiting and exit codes Starting a process only opens the child process. The command result is known after the child exits. If you do not need to interact with the process while it runs, `Process::run()` waits for you and returns a `Castor\Os\Process\Result` with the captured output. ## Poll running state Use `isRunning()` to ask PHP for the current process status: ```php <?php use Castor\Os\Process; $process = Process::start(['sleep', '1']); while ($process->isRunning()) { usleep(10_000); } $code = $process->getExitCode(); $process->close(); ``` `pid()` returns the process identifier reported by `proc_get_status()`. Before status is available it falls back to `-1`, but a started process normally has a PID immediately after the first status update. ```php $pid = $process->pid(); ``` ## Run and collect a result `Process::run()` is the shortest path when you want the final exit code, stdout, and stderr: ```php $result = Process::run(['php', '-r', 'fwrite(STDERR, "bad"); exit(7);']); $result->exitCode; // 7 $result->stdout; // "" $result->stderr; // bad ``` The result has `successful()` and `failed()` helpers, but it does not throw for non-zero exit codes. Your application decides what a non-zero command result means. ## Wait for completion When you use `Process::start()`, `wait()` repeatedly checks `isRunning()` until the process exits and then returns `getExitCode()`. ```php $process = Process::start(['php', '-r', 'exit(7);']); $code = $process->wait(); $process->close(); var_dump($code); // int(7) ``` `wait()` does not throw on non-zero exit codes. A non-zero code is a normal command result. ## Wait with a timeout Use a timeout when a process should have a total runtime limit. You can set the default on `start()` or pass it directly to `wait()`: ```php <?php use Castor\Os\Process; use Castor\Os\Process\TimeoutException; $process = Process::start(['php', '-r', 'echo "starting"; sleep(10);'], timeout: 1.0); try { $process->wait(); } catch (TimeoutException $error) { echo $error->process->stdout->collect(); // starting } finally { $process->close(); } ``` `wait(timeout: ...)` overrides the timeout passed to `start()`. Calling `wait()` with `null` uses the default from `start()` when one was configured. When the timeout expires, Castor Process sends `SIGTERM` to the direct child, waits briefly, and then sends `SIGKILL` if the child is still running. It does not try to kill a whole process tree. The timeout is a total elapsed-time limit measured from process start. It is not an idle timeout based on whether stdout or stderr have been quiet. ## Exit code values `getExitCode()` returns: - `0` when the command exits successfully by convention. - `1` through `255` for command-specific failures or other normal exits. - `-1` while the process is still running or when it was terminated by an external signal instead of exiting normally. ```php if ($process->wait() !== 0) { $stderr = Castor\Io\read_all($process->stderr); throw new RuntimeException('Command failed: ' . $stderr); } ``` Read the child program documentation to understand what each non-zero code means. Castor Process intentionally does not translate command exit codes into exceptions. ## Signals and termination Use `terminate()` to ask PHP to terminate the child process. The default signal is `SIGTERM`. ```php <?php use Castor\Os\Process; $process = Process::start(['php', '-r', 'while (true) { sleep(1); }']); $process->terminate(); while ($process->isRunning()) { usleep(10_000); } $wasSignaled = $process->isSignaled(); $signal = $process->getTermSignal(); $code = $process->getExitCode(); $process->close(); ``` After signal termination, `isSignaled()` reports whether PHP marked the process as signaled, `getTermSignal()` returns the terminating signal number when available, and `getExitCode()` returns `-1`. `terminate()` does not close `stdin`, `stdout`, or `stderr`. Processes often print final output when they receive a termination signal, so keep reading any remaining output you care about before calling `close()`. ## Stopped processes `isStopped()` reports the `stopped` status from `proc_get_status()`. Most simple command execution code only needs `isRunning()`, but `isStopped()` is available when your platform and signal model can stop and resume child processes. ## Process status caching PHP versions before 8.3 had `proc_get_status()` behavior that could lose the original exit code after repeated status checks. Castor Process preserves the first known exit code for those older PHP runtimes. The package requires PHP 8.3, but this compatibility logic keeps the class safe if it is inspected in broader dependency contexts. ## Close after waiting `wait()` waits for process completion, but it does not close the process resource or streams. Call `close()` afterwards: ```php $process = Process::start(['true']); try { $code = $process->wait(); } finally { $process->close(); } ``` Calling `close()` more than once is safe. The second and later calls return without doing anything.
Castor ecosystem