PageRenderTime 36ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/php/class-terminus.php

https://gitlab.com/Blueprint-Marketing/cli
PHP | 417 lines | 369 code | 25 blank | 23 comment | 8 complexity | ed17255d33c7e198601a7ac07ae5f287 MD5 | raw file
  1. <?php
  2. use \Terminus\Utils;
  3. use \Terminus\Dispatcher;
  4. use \Terminus\FileCache;
  5. /**
  6. * Various utilities for Terminus commands.
  7. */
  8. class Terminus {
  9. private static $configurator;
  10. private static $logger;
  11. private static $hooks = array(), $hooks_passed = array();
  12. /**
  13. * Set the logger instance.
  14. *
  15. * @param [object] $logger
  16. */
  17. static function set_logger($logger) {
  18. self::$logger = $logger;
  19. }
  20. static function get_configurator() {
  21. static $configurator;
  22. if(!$configurator) {
  23. $configurator = new Terminus\Configurator(TERMINUS_ROOT . '/php/config-spec.php');
  24. }
  25. return $configurator;
  26. }
  27. static function get_root_command() {
  28. static $root;
  29. if(!$root) {
  30. $root = new Dispatcher\RootCommand;
  31. }
  32. return $root;
  33. }
  34. static function get_runner() {
  35. // the entire process needs an exception wrapper
  36. try {
  37. static $runner;
  38. if(!$runner) {
  39. $runner = new Terminus\Runner;
  40. }
  41. return $runner;
  42. } catch(\Exception $e) {
  43. Terminus::error($e->getMessage());
  44. }
  45. }
  46. /**
  47. * @return FileCache
  48. */
  49. public static function get_cache() {
  50. static $cache;
  51. if(!$cache) {
  52. $home = getenv('HOME');
  53. if(!$home) {
  54. // sometime in windows $HOME is not defined
  55. $home = getenv('HOMEDRIVE') . '/' . getenv('HOMEPATH');
  56. }
  57. $dir = getenv('TERMINUS_CACHE_DIR') ? : "$home/.terminus/cache";
  58. // 6 months, 300mb
  59. $cache = new FileCache($dir, 86400, 314572800);
  60. }
  61. $cache->clean();
  62. return $cache;
  63. }
  64. static function colorize($string) {
  65. return \cli\Colors::colorize($string, self::get_runner()->in_color());
  66. }
  67. /**
  68. * Add a command to the terminus list of commands
  69. *
  70. * @param string $name The name of the command that will be used in the CLI
  71. * @param string $class The command implementation
  72. * @param array $args An associative array with additional parameters:
  73. * 'before_invoke' => callback to execute before invoking the command
  74. */
  75. static function add_command($name, $class, $args = array()) {
  76. $path = preg_split('/\s+/', $name);
  77. $leaf_name = array_pop($path);
  78. $full_path = $path;
  79. $command = self::get_root_command();
  80. while (!empty($path)) {
  81. $subcommand_name = $path[0];
  82. $subcommand = $command->find_subcommand($path);
  83. // create an empty container
  84. if(!$subcommand) {
  85. $subcommand = new Dispatcher\CompositeCommand($command, $subcommand_name,
  86. new \Terminus\DocParser(''));
  87. $command->add_subcommand($subcommand_name, $subcommand);
  88. }
  89. $command = $subcommand;
  90. }
  91. $leaf_command = Dispatcher\CommandFactory::create($leaf_name, $class, $command);
  92. if(! $command->can_have_subcommands()) {
  93. throw new Exception(sprintf("'%s' can't have subcommands.",
  94. implode(' ' , Dispatcher\get_path($command))));
  95. }
  96. $command->add_subcommand($leaf_name, $leaf_command);
  97. }
  98. /**
  99. * Prompt the user for input
  100. *
  101. * @param string $message
  102. */
  103. static function prompt($message = '', $params = array(), $default=null) {
  104. if(!empty($params)) {
  105. $message = vsprintf($message, $params);
  106. }
  107. $response = \cli\prompt($message);
  108. if(empty($response) AND $default) {
  109. $response = $default;
  110. }
  111. return $response;
  112. }
  113. /**
  114. * Display a message in the CLI and end with a newline
  115. *
  116. * @param string $message
  117. */
  118. static function line($message = '', $params = array()) {
  119. if(!empty($params)) {
  120. $message = vsprintf($message, $params);
  121. }
  122. echo \cli\line($message);
  123. }
  124. /**
  125. * Log an informational message.
  126. *
  127. * @param string $message
  128. */
  129. static function log($message, $params = array()) {
  130. if(!empty($params)) {
  131. $message = vsprintf($message, $params);
  132. }
  133. self::$logger->info($message);
  134. }
  135. /**
  136. * Display a success in the CLI and end with a newline
  137. *
  138. * @param string $message
  139. */
  140. static function success($message, $params = array()) {
  141. if(!empty($params)) {
  142. $message = vsprintf($message, $params);
  143. }
  144. self::$logger->success($message);
  145. }
  146. /**
  147. * Display a warning in the CLI and end with a newline
  148. *
  149. * @param string $message
  150. */
  151. static function warning($message, $params = array()) {
  152. if(!empty($params)) {
  153. $message = vsprintf($message, $params);
  154. }
  155. self::$logger->warning(self::error_to_string($message));
  156. }
  157. /**
  158. * Display an error in the CLI and end with a newline
  159. *
  160. * @param string $message
  161. */
  162. static function error($message, $params = array()) {
  163. if(!empty($params)) {
  164. $message = vsprintf($message, $params);
  165. }
  166. if(! isset(self::get_runner()->assoc_args[ 'completions' ])) {
  167. self::$logger->error(self::error_to_string($message));
  168. }
  169. exit(1);
  170. }
  171. /**
  172. * Ask for confirmation before running a destructive operation.
  173. */
  174. static function confirm($question, $assoc_args = array(), $params = array()) {
  175. if(\Terminus::get_config('yes')) return true;
  176. $question = vsprintf($question, $params);
  177. fwrite(STDOUT, $question . " [y/n] ");
  178. $answer = trim(fgets(STDIN));
  179. if('y' != $answer)
  180. exit;
  181. return true;
  182. }
  183. /**
  184. * Read value from a positional argument or from STDIN.
  185. *
  186. * @param array $args The list of positional arguments.
  187. * @param int $index At which position to check for the value.
  188. *
  189. * @return string
  190. */
  191. public static function get_value_from_arg_or_stdin($args, $index) {
  192. if(isset($args[ $index ])) {
  193. $raw_value = $args[ $index ];
  194. } else {
  195. // We don't use file_get_contents() here because it doesn't handle
  196. // Ctrl-D properly, when typing in the value interactively.
  197. $raw_value = '';
  198. while (($line = fgets(STDIN)) !== false) {
  199. $raw_value .= $line;
  200. }
  201. }
  202. return $raw_value;
  203. }
  204. /**
  205. * Read a value, from various formats.
  206. *
  207. * @param mixed $value
  208. * @param array $assoc_args
  209. */
  210. static function read_value($raw_value, $assoc_args = array()) {
  211. if(isset($assoc_args['format']) && 'json' == $assoc_args['format']) {
  212. $value = json_decode($raw_value, true);
  213. if(null === $value) {
  214. Terminus::error(sprintf('Invalid JSON: %s', $raw_value));
  215. }
  216. } else {
  217. $value = $raw_value;
  218. }
  219. return $value;
  220. }
  221. /**
  222. * Display a value, in various formats
  223. *
  224. * @param mixed $value
  225. * @param array $assoc_args
  226. */
  227. static function print_value($value, $assoc_args = array()) {
  228. if(isset($assoc_args['format']) && 'json' == $assoc_args['format']) {
  229. $value = json_encode($value);
  230. } elseif(is_array($value) || is_object($value)) {
  231. $value = var_export($value);
  232. }
  233. echo $value . "\n";
  234. }
  235. /**
  236. * Convert a error into a string
  237. *
  238. * @param mixed $errors
  239. * @return string
  240. */
  241. static function error_to_string($errors) {
  242. if(is_string($errors)) {
  243. return $errors;
  244. }
  245. if(is_object($errors) && is_a($errors, 'WP_Error')) {
  246. foreach($errors->get_error_messages() as $message) {
  247. if($errors->get_error_data())
  248. return $message . ' ' . $errors->get_error_data();
  249. else
  250. return $message;
  251. }
  252. }
  253. }
  254. /**
  255. * Launch an external process that takes over I/O.
  256. *
  257. * @param string Command to call
  258. * @param bool Whether to exit if the command returns an error status
  259. *
  260. * @return int The command exit status
  261. */
  262. static function launch($command, $exit_on_error = true) {
  263. $r = proc_close(proc_open($command, array(STDIN, STDOUT, STDERR), $pipes));
  264. if($r && $exit_on_error)
  265. exit($r);
  266. return $r;
  267. }
  268. /**
  269. * Launch another Terminus command using the runtime arguments for the current process
  270. *
  271. * @param string Command to call
  272. * @param array $args Positional arguments to use
  273. * @param array $assoc_args Associative arguments to use
  274. * @param bool Whether to exit if the command returns an error status
  275. *
  276. * @return int The command exit status
  277. */
  278. static function launch_self($command, $args = array(), $assoc_args = array(), $exit_on_error = true) {
  279. $reused_runtime_args = array(
  280. 'path',
  281. 'url',
  282. 'user',
  283. 'allow-root',
  284. );
  285. foreach($reused_runtime_args as $key) {
  286. if(array_key_exists($key, self::get_runner()->config))
  287. $assoc_args[ $key ] = self::get_runner()->config[$key];
  288. }
  289. $php_bin = self::get_php_binary();
  290. if(Terminus::is_test()) {
  291. $script_path = __DIR__.'/boot-fs.php';
  292. } else {
  293. $script_path = $GLOBALS['argv'][0];
  294. }
  295. $args = implode(' ', array_map('escapeshellarg', $args));
  296. $assoc_args = \Terminus\Utils\assoc_args_to_str($assoc_args);
  297. $full_command = "{$php_bin} {$script_path} {$command} {$args} {$assoc_args}";
  298. return self::launch($full_command, $exit_on_error);
  299. }
  300. private static function get_php_binary() {
  301. if(defined('PHP_BINARY'))
  302. return PHP_BINARY;
  303. if(getenv('TERMINUS_PHP_USED'))
  304. return getenv('TERMINUS_PHP_USED');
  305. if(getenv('TERMINUS_PHP'))
  306. return getenv('TERMINUS_PHP');
  307. return 'php';
  308. }
  309. static function get_config($key = null) {
  310. if(null === $key) {
  311. return self::get_runner()->config;
  312. }
  313. if(!isset(self::get_runner()->config[ $key ])) {
  314. self::warning("Unknown config option '$key'.");
  315. return null;
  316. }
  317. return self::get_runner()->config[ $key ];
  318. }
  319. static function set_config($key, $value) {
  320. self::get_runner()->config[ $key ] = $value;
  321. return self::get_runner()->config;
  322. }
  323. /**
  324. * @deprecated
  325. */
  326. static function menu($data, $default = null, $text = "Select one", $return_value=false) {
  327. echo PHP_EOL;
  328. $index = \cli\Streams::menu($data,$default,$text);
  329. if($return_value) {
  330. return $data[$index];
  331. }
  332. return $index;
  333. }
  334. /**
  335. * Run a given command.
  336. *
  337. * @param array
  338. * @param array
  339. */
  340. static function run_command($args, $assoc_args = array()) {
  341. self::get_runner()->run_command($args, $assoc_args);
  342. }
  343. /**
  344. * Terminus is in test mode
  345. */
  346. static function is_test() {
  347. if(defined('CLI_TEST_MODE') && (CLI_TEST_MODE !== false)) {
  348. return true;
  349. }
  350. $is_test = (boolean)getenv("CLI_TEST_MODE");
  351. return $is_test;
  352. }
  353. }