Changeset View
Changeset View
Standalone View
Standalone View
src/docs/article/command_execution.diviner
- This file was added.
@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 | |||||
@{class: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. |