Page MenuHomePhorge

final class PhutilExecChannel
Arcanist Technical Documentation ()

Channel on an underlying ExecFuture. For a description of channels, see PhutilChannel.

For example, you can open a channel on nc like this:

$future = new ExecFuture('nc example.com 80');
$channel = new PhutilExecChannel($future);

$channel->write("GET / HTTP/1.0\n\n");
while (true) {
  echo $channel->read();

  PhutilChannel::waitForAny(array($channel));
  if (!$channel->update()) {
    // Break out of the loop when the channel closes.
    break;
  }
}

This script makes an HTTP request to "example.com". This example is heavily contrived. In most cases, ExecFuture and other futures constructs offer a much easier way to solve problems which involve system commands, and HTTPFuture and other HTTP constructs offer a much easier way to solve problems which involve HTTP.

PhutilExecChannel is generally useful only when a program acts like a server but performs I/O on stdin/stdout, and you need to act like a client or interact with the program at the same time as you manage traditional socket connections. Examples are Mercurial operating in "cmdserve" mode, git operating in "receive-pack" mode, etc. It is unlikely that any reasonable use of this class is concise enough to make a short example out of, so you get a contrived one instead.

See also PhutilSocketChannel, for a similar channel that uses sockets for I/O.

Since ExecFuture already supports buffered I/O and socket selection, the implementation of this class is fairly straightforward.

Tasks

Reading and Writing

  • public function read() — Read from the channel. A channel defines the format of data that is read from it, so this method may return strings, objects, or anything else.
  • public function write($bytes)

Waiting for Activity

  • public static function waitForAny($channels, $options) — Wait for any activity on a list of channels. Convenience wrapper around @{method:waitForActivity}.
  • public static function waitForActivity($reads, $writes, $options) — Wait (using select()) for channels to become ready for reads or writes. This method blocks until some channel is ready to be updated.

Responding to Activity

Channel Implementation

Construction

  • public function __construct($future) — Construct an exec channel from a @{class:ExecFuture}. The future should **NOT** have been started yet (e.g., with `isReady()` or `start()`), because @{class:ExecFuture} closes stdin by default when futures start. If stdin has been closed, you will be unable to write on the channel.

Other Methods

  • public function __get($name)
  • public function __set($name, $value)
  • public function current()
  • public function key()
  • public function next()
  • public function rewind()
  • public function valid()
  • private function throwOnAttemptedIteration()
  • public function getPhobjectClassConstant($key, $byte_limit) — Read the value of a class constant.
  • public function __destruct()
  • public function setStderrHandler($handler) — If the wrapped @{class:ExecFuture} outputs data to stderr, we normally throw an exception. Instead, you can provide a callback handler that will be invoked and passed the data. It should have this signature:

Methods

public function __get($name)
Inherited

This method is not documented.
Parameters
$name
Return
wild

public function __set($name, $value)
Inherited

This method is not documented.
Parameters
$name
$value
Return
wild

public function current()
Inherited

This method is not documented.
Return
wild

public function key()
Inherited

This method is not documented.
Return
wild

public function next()
Inherited

This method is not documented.
Return
wild

public function rewind()
Inherited

This method is not documented.
Return
wild

public function valid()
Inherited

This method is not documented.
Return
wild

private function throwOnAttemptedIteration()
Inherited

This method is not documented.
Return
wild

public function getPhobjectClassConstant($key, $byte_limit)
Inherited

Phobject

Read the value of a class constant.

This is the same as just typing self::CONSTANTNAME, but throws a more useful message if the constant is not defined and allows the constant to be limited to a maximum length.

Parameters
string$keyName of the constant.
int|null$byte_limitMaximum number of bytes permitted in the value.
Return
stringValue of the constant.

public function __construct($future)

Construct an exec channel from a ExecFuture. The future should NOT have been started yet (e.g., with isReady() or start()), because ExecFuture closes stdin by default when futures start. If stdin has been closed, you will be unable to write on the channel.

Return
this//Implicit.//

public function read()
Inherited

PhutilChannel

Read from the channel. A channel defines the format of data that is read from it, so this method may return strings, objects, or anything else.

The default implementation returns bytes.

Return
wildData from the channel, normally bytes.

public function write($bytes)

PhutilChannel

Write to the channel. A channel defines what data format it accepts, so this method may take strings, objects, or anything else.

The default implementation accepts bytes.

PhutilExecChannel
This method is not documented.
Parameters
wild$bytesData to write to the channel, normally bytes.
Return
this

public static function waitForAny($channels, $options)
Inherited

PhutilChannel

Wait for any activity on a list of channels. Convenience wrapper around waitForActivity().

Parameters
list<PhutilChannel>$channelsA list of channels to wait for.
dict$optionsOptions, see above.
Return
void

public static function waitForActivity($reads, $writes, $options)
Inherited

PhutilChannel

Wait (using select()) for channels to become ready for reads or writes. This method blocks until some channel is ready to be updated.

It does not provide a way to determine which channels are ready to be updated. The expectation is that you'll just update every channel. This might change eventually.

Available options are:

  • 'read' (list<stream>) Additional streams to select for read.
  • 'write' (list<stream>) Additional streams to select for write.
  • 'except' (list<stream>) Additional streams to select for except.
  • 'timeout' (float) Select timeout, defaults to 1.
NOTE: Extra streams must be streams, not sockets, because this method uses stream_select(), not socket_select().
Parameters
list<PhutilChannel>$readsList of channels to wait for reads on.
list<PhutilChannel>$writesList of channels to wait for writes on.
array$options
Return
void

public function update()

PhutilChannel

Updates the channel, filling input buffers and flushing output buffers. Returns false if the channel has closed.

PhutilExecChannel
This method is not documented.
Return
boolTrue if the channel is still open.

public function setName($name)
Inherited

PhutilChannel

Set a channel name. This is primarily intended to allow you to debug channel code more easily, by naming channels something meaningful.

Parameters
string$nameChannel name.
Return
this

public function getName()
Inherited

PhutilChannel

Get the channel name, as set by setName().

Return
stringName of the channel.

public function isOpen()

PhutilChannel

Test if the channel is open: active, can be read from and written to, etc.

PhutilExecChannel
This method is not documented.
Return
boolTrue if the channel is open.

public function closeWriteChannel()

PhutilChannel

Close the channel for writing.

PhutilExecChannel
This method is not documented.
Return
void

public function isOpenForReading()
Inherited

PhutilChannel

Test if the channel is open for reading.

Return
boolTrue if the channel is open for reading.

public function isOpenForWriting()
Inherited

PhutilChannel

Test if the channel is open for writing.

Return
boolTrue if the channel is open for writing.

protected function readBytes($length)

PhutilChannel

Read from the channel's underlying I/O.

PhutilExecChannel
This method is not documented.
Parameters
int$lengthMaximum number of bytes to read.
Return
stringBytes, if available.

protected function writeBytes($bytes)

PhutilChannel

Write to the channel's underlying I/O.

PhutilExecChannel
This method is not documented.
Parameters
string$bytesBytes to write.
Return
intNumber of bytes written.

protected function getReadSockets()

PhutilChannel

Get sockets to select for reading.

PhutilExecChannel
This method is not documented.
Return
list<stream>Read sockets.

protected function getWriteSockets()

PhutilChannel

Get sockets to select for writing.

PhutilExecChannel
This method is not documented.
Return
list<stream>Write sockets.

public function setReadBufferSize($size)

PhutilChannel

Set the maximum size of the channel's read buffer. Reads will artificially block once the buffer reaches this size until the in-process buffer is consumed.

PhutilExecChannel
This method is not documented.
Parameters
int|null$sizeMaximum read buffer size, or `null` for a limitless buffer.
Return
this

public function isReadBufferEmpty()

PhutilChannel

Test state of the read buffer.

PhutilExecChannel
This method is not documented.
Return
boolTrue if the read buffer is empty.

public function isWriteBufferEmpty()

PhutilChannel

Test state of the write buffer.

PhutilExecChannel
This method is not documented.
Return
boolTrue if the write buffer is empty.

public function getWriteBufferSize()

PhutilChannel

Get the number of bytes we're currently waiting to write.

PhutilExecChannel
This method is not documented.
Return
intNumber of waiting bytes.

public function flush()
Inherited

PhutilChannel

Wait for any buffered writes to complete. This is a blocking call. When the call returns, the write buffer will be empty.

Return
wild

public function __destruct()

This method is not documented.
Return
wild

public function setStderrHandler($handler)

If the wrapped ExecFuture outputs data to stderr, we normally throw an exception. Instead, you can provide a callback handler that will be invoked and passed the data. It should have this signature:

function f(PhutilExecChannel $channel, $stderr) {
  // ...
}

The $channel will be this channel object, and $stderr will be a string with bytes received over stderr.

You can set a handler which does nothing to effectively ignore and discard any output on stderr.

Parameters
callable$handlerHandler to invoke when stderr data is received.
Return
this