Click the Castor logo or press Ctrl Alt T to change theme.
# Getting started Castor Process starts an external command and gives you three Castor IO streams for the process pipes: - `stdin` is a `Castor\Io\Writer` and `Castor\Io\Closer`. - `stdout` is a `Castor\Io\Reader` and `Castor\Io\Closer`. - `stderr` is a `Castor\Io\Reader` and `Castor\Io\Closer`. For the common case, `Process::run()` does all of that in one call and returns a result object. When you need to interact with the process while it is running, `Process::start()` gives you direct access to the three pipes. ## Installation ```bash composer config repositories.castor composer https://castor-labs.github.io/php-packages composer require castor/process ``` The package requires PHP 8.3 or newer and the `pcntl` extension. ## Run a command Use `Process::run()` when you want to execute a command to completion and collect its result. This is the easiest way to run small commands from application code, tests, or scripts. ```php <?php use Castor\Os\Process; $result = Process::run(['echo', 'Hello World']); var_dump($result->exitCode); // int(0) var_dump($result->stdout); // string(12) "Hello World\n" var_dump($result->stderr); // string(0) "" ``` The returned `Castor\Os\Process\Result` also contains the original command, process PID, duration, working directory, and termination signal metadata. A successful command conventionally returns `0`; command-specific failures usually return a non-zero value. Castor Process does not throw when a child command exits with a non-zero code. Use an argument list when you already know the executable and each argument separately. This avoids shell parsing and is the safest default for dynamic values. ## Limit total runtime Commands that hang can block an application or test suite indefinitely. Pass `timeout` when a command must finish within a known amount of time: ```php <?php use Castor\Os\Process; use Castor\Os\Process\TimeoutException; try { $result = Process::run(['php', '-r', 'sleep(10);'], timeout: 1.0); } catch (TimeoutException $error) { // The direct child was terminated. Streams remain available on the process. $stderr = $error->process->stderr->collect(); $error->process->close(); } ``` Timeouts are measured in seconds, may be fractional, and must be finite numbers greater than zero. They are total runtime limits, not idle-output limits: a process that keeps printing output still times out if it runs longer than the allowed time. ## Pass input through stdin `stdin` is a Castor IO writer. Write bytes to it, then close it when the child process should see end-of-input. ```php <?php use Castor\Os\Process; use function Castor\Io\read_all; $process = Process::start(['php', '-r', 'echo strtoupper(stream_get_contents(STDIN));']); $process->stdin->write('hello from stdin'); $process->stdin->close(); $exitCode = $process->wait(); $output = read_all($process->stdout); $process->close(); echo $exitCode; // 0 echo $output; // HELLO FROM STDIN ``` Closing `stdin` is important for commands that read until EOF. If you keep the pipe open, the child may keep waiting for more input and `wait()` may never return. For interactive, line-oriented protocols, `stdin` also has `sendLine()`. It writes the value you pass and appends a newline: ```php $process = Process::start(['php', '-r', 'echo trim(fgets(STDIN));']); $process->stdin->sendLine('hello'); $process->stdin->close(); $process->wait(); echo $process->stdout->collect(); // hello $process->close(); ``` ## Read stderr separately The process keeps standard output and standard error on separate readers. ```php <?php use Castor\Os\Process; use function Castor\Io\read_all; $process = Process::start(['php', '-r', 'fwrite(STDERR, "problem\\n"); exit(2);']); $exitCode = $process->wait(); $output = read_all($process->stdout); $errorOutput = read_all($process->stderr); $process->close(); var_dump($exitCode); // int(2) var_dump($output); // string(0) "" var_dump($errorOutput); // string(8) "problem\n" ``` A non-zero exit code and stderr output are application-level results from the child command. Decide in your own code whether they should become exceptions, log records, retries, or normal return values. ## Clean up explicitly Always call `close()` when you are done with a process. ```php $process = Process::start(['true']); try { $code = $process->wait(); } finally { $process->close(); } ``` The destructor also closes process resources, but explicit cleanup makes ownership clear and closes the three pipe streams immediately.
Castor ecosystem