Changeset View
Changeset View
Standalone View
Standalone View
src/toolset/ArcanistPrompt.php
- This file was added.
| <?php | |||||
| final class ArcanistPrompt | |||||
| extends Phobject { | |||||
| private $key; | |||||
| private $workflow; | |||||
| private $description; | |||||
| private $query; | |||||
| public function setKey($key) { | |||||
| $this->key = $key; | |||||
| return $this; | |||||
| } | |||||
| public function getKey() { | |||||
| return $this->key; | |||||
| } | |||||
| public function setWorkflow(ArcanistWorkflow $workflow) { | |||||
| $this->workflow = $workflow; | |||||
| return $this; | |||||
| } | |||||
| public function getWorkflow() { | |||||
| return $this->workflow; | |||||
| } | |||||
| public function setDescription($description) { | |||||
| $this->description = $description; | |||||
| return $this; | |||||
| } | |||||
| public function getDescription() { | |||||
| return $this->description; | |||||
| } | |||||
| public function setQuery($query) { | |||||
| $this->query = $query; | |||||
| return $this; | |||||
| } | |||||
| public function getQuery() { | |||||
| return $this->query; | |||||
| } | |||||
| public function execute() { | |||||
| $workflow = $this->getWorkflow(); | |||||
| if ($workflow) { | |||||
| $workflow_ok = $workflow->hasPrompt($this->getKey()); | |||||
| } else { | |||||
| $workflow_ok = false; | |||||
| } | |||||
| if (!$workflow_ok) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Prompt ("%s") is executing, but it is not properly bound to the '. | |||||
| 'invoking workflow. You may have called "newPrompt()" to execute a '. | |||||
| 'prompt instead of "getPrompt()". Use "newPrompt()" when defining '. | |||||
| 'prompts and "getPrompt()" when executing them.', | |||||
| $this->getKey())); | |||||
| } | |||||
| $query = $this->getQuery(); | |||||
| if (!strlen($query)) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Prompt ("%s") has no query text!', | |||||
| $this->getKey())); | |||||
| } | |||||
| $options = '[y/N]'; | |||||
| $default = 'N'; | |||||
| try { | |||||
| phutil_console_require_tty(); | |||||
| } catch (PhutilConsoleStdinNotInteractiveException $ex) { | |||||
| // TOOLSETS: Clean this up to provide more details to the user about how | |||||
| // they can configure prompts to be answered. | |||||
| // Throw after echoing the prompt so the user has some idea what happened. | |||||
| echo $query."\n"; | |||||
| throw $ex; | |||||
| } | |||||
| // NOTE: We're making stdin nonblocking so that we can respond to signals | |||||
| // immediately. If we don't, and you ^C during a prompt, the program does | |||||
| // not handle the signal until fgets() returns. | |||||
| $stdin = fopen('php://stdin', 'r'); | |||||
| if (!$stdin) { | |||||
| throw new Exception(pht('Failed to open stdin for reading.')); | |||||
| } | |||||
| $ok = stream_set_blocking($stdin, false); | |||||
| if (!$ok) { | |||||
| throw new Exception(pht('Unable to set stdin nonblocking.')); | |||||
| } | |||||
| echo "\n"; | |||||
| $result = null; | |||||
| while (true) { | |||||
| echo tsprintf( | |||||
| '**<bg:cyan> %s </bg>** %s %s ', | |||||
| '>>>', | |||||
| $query, | |||||
| $options); | |||||
| while (true) { | |||||
| $read = array($stdin); | |||||
| $write = array(); | |||||
| $except = array(); | |||||
| $ok = stream_select($read, $write, $except, 1); | |||||
| if ($ok === false) { | |||||
| throw new Exception(pht('stream_select() failed!')); | |||||
| } | |||||
| $response = fgets($stdin); | |||||
| if (!strlen($response)) { | |||||
| continue; | |||||
| } | |||||
| break; | |||||
| } | |||||
| $response = trim($response); | |||||
| if (!strlen($response)) { | |||||
| $response = $default; | |||||
| } | |||||
| if (phutil_utf8_strtolower($response) == 'y') { | |||||
| $result = true; | |||||
| break; | |||||
| } | |||||
| if (phutil_utf8_strtolower($response) == 'n') { | |||||
| $result = false; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (!$result) { | |||||
| throw new ArcanistUserAbortException(); | |||||
| } | |||||
| } | |||||
| } | |||||