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. | |||||