@title Command Execution @group exec This document describes best practices for executing system commands in PHP using libphutil. = Overview = PHP has several built-in mechanisms for executing system commands, like exec(), system(), and the backtick operator. However, these mechanisms often make it difficult to get all the information you need to handle error conditions, properly escaping commands is cumbersome, and they do not provide more advanced features like parallel execution and timeouts. This document describes how to use the APIs in libphutil to execute commands without encountering these problems. = Simple Commands: execx() and exec_manual() = @{function:execx} and @{function:exec_manual} are replacements for exec(), system(), shell_exec(), and the backtick operator. The APIs look like this: list($stdout, $stderr) = execx('ls %s', $path); list($err, $stdout, $stderr) = exec_manual('ls %s', $path); The major advantages of these methods over the exec() family are that you can easily check return codes, capture both stdout and stderr, and use a simple sprintf()-style formatting string to properly escape commands. @{function:execx} will throw a @{class:CommandException} if the command you execute terminates with a nonzero exit code, while @{function:exec_manual} returns the error code. If you use @{function:exec_manual}, you must manually check the error code. = Advanced Commands: ExecFutures = If you need more advanced features like parallel execution, command timeouts, and asynchronous I/O, use @{class:ExecFuture}. $future = new ExecFuture('ls %s', $path); list($stdout, $stderr) = $future->resolvex(); @{class:ExecFuture} is a @{class:Future}, and can be used with constructs like @{class:FutureIterator} to achieve and manage parallelism. See @{article:Using Futures} for general information on how to use futures in libphutil. In addition to futures-based parallelism, you can set a timeout on an ExecFuture, which will kill the command if it takes longer than the specified number of seconds to execute: $future->setTimeout(30); If the command runs longer than the timeout, the process will be killed and the future will resolve with a failure code (ExecFuture::TIMED_OUT_EXIT_CODE). You can also write to the stdin of a process by using the @{method:ExecFuture::write} method. $future = new ExecFuture('bc'); $future->write('2+2'); list($stdout) = $future->resolvex(); See @{class:ExecFuture} for complete capability documentation.