diff --git a/src/channel/PhutilChannelChannel.php b/src/channel/PhutilChannelChannel.php index d616ef0..89085dd 100644 --- a/src/channel/PhutilChannelChannel.php +++ b/src/channel/PhutilChannelChannel.php @@ -1,89 +1,94 @@ channel = $channel; } public function read() { return $this->channel->read(); } public function write($message) { $this->channel->write($message); return $this; } public function update() { return $this->channel->update(); } public function isOpen() { return $this->channel->isOpen(); } protected function readBytes() { $this->throwOnRawByteOperations(); } protected function writeBytes($bytes) { $this->throwOnRawByteOperations(); } protected function getReadSockets() { return $this->channel->getReadSockets(); } protected function getWriteSockets() { return $this->channel->getWriteSockets(); } protected function isReadBufferEmpty() { return $this->channel->isReadBufferEmpty(); } protected function isWriteBufferEmpty() { return $this->channel->isWriteBufferEmpty(); } + public function flush() { + $this->channel->flush(); + return $this; + } + private function throwOnRawByteOperations() { // NOTE: You should only be able to end up here if you subclass this class // and implement your subclass incorrectly, since the byte methods are // protected. throw new Exception( "Do not call readBytes() or writeBytes() directly on a ". "PhutilChannelChannel. Instead, call read() or write()."); } } diff --git a/src/console/PhutilConsoleServer.php b/src/console/PhutilConsoleServer.php index 673fb92..0400975 100644 --- a/src/console/PhutilConsoleServer.php +++ b/src/console/PhutilConsoleServer.php @@ -1,93 +1,95 @@ getData(); $type = $message->getType(); switch ($type) { case PhutilConsoleMessage::TYPE_CONFIRM: $ok = phutil_console_confirm($data['prompt'], !$data['default']); return $this->buildMessage( PhutilConsoleMessage::TYPE_INPUT, $ok); case PhutilConsoleMessage::TYPE_OUT: $this->writeText(STDOUT, $data); return null; case PhutilConsoleMessage::TYPE_ERR: $this->writeText(STDERR, $data); return null; default: if ($this->handler) { return call_user_func($this->handler, $message); } else { throw new Exception( "Received unknown console message of type '{$type}'."); } } } private function buildMessage($type, $data) { $response = new PhutilConsoleMessage(); $response->setType($type); $response->setData($data); return $response; } public function addExecFutureClient(ExecFuture $future) { $io_channel = new PhutilExecChannel($future); $protocol_channel = new PhutilPHPObjectProtocolChannel($io_channel); return $this->addClient($protocol_channel); } public function addClient(PhutilPHPObjectProtocolChannel $channel) { $this->clients[] = $channel; return $this; } public function run() { while ($this->clients) { PhutilChannel::waitForAny($this->clients); foreach ($this->clients as $key => $client) { + if (!$client->update()) { + // If the client has exited, remove it from the list of clients. + // We still need to process any remaining buffered I/O. + unset($this->clients[$key]); + } while ($message = $client->read()) { $response = $this->handleMessage($message); if ($response) { $client->write($response); } } - if (!$client->update()) { - unset($this->clients[$key]); - } } } } private function writeText($where, array $argv) { $text = call_user_func_array('phutil_console_format', $argv); fprintf($where, '%s', $text); } }