PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/php/class-wp-cli.php

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