/php/WP_CLI/Runner.php
PHP | 708 lines | 464 code | 123 blank | 121 comment | 106 complexity | 227c174ae3bf0217444d0758a62d7538 MD5 | raw file
- <?php
- namespace WP_CLI;
- use WP_CLI;
- use WP_CLI\Utils;
- use WP_CLI\Dispatcher;
- /**
- * Performs the execution of a command.
- *
- * @package WP_CLI
- */
- class Runner {
- private $global_config_path, $project_config_path;
- private $config, $extra_config;
- private $arguments, $assoc_args;
- private $_early_invoke = array();
- public function __get( $key ) {
- if ( '_' === $key[0] )
- return null;
- return $this->$key;
- }
- /**
- * Register a command for early invocation, generally before WordPress loads.
- *
- * @param string $when Named execution hook
- * @param WP_CLI\Dispatcher\Subcommand $command
- */
- public function register_early_invoke( $when, $command ) {
- $this->_early_invoke[ $when ][] = array_slice( Dispatcher\get_path( $command ), 1 );
- }
- /**
- * Perform the early invocation of a command.
- *
- * @param string $when Named execution hook
- */
- private function do_early_invoke( $when ) {
- if ( !isset( $this->_early_invoke[ $when ] ) )
- return;
- foreach ( $this->_early_invoke[ $when ] as $path ) {
- if ( $this->cmd_starts_with( $path ) ) {
- $this->_run_command();
- exit;
- }
- }
- }
- /**
- * Get the path to the global configuration YAML file.
- *
- * @return string|false
- */
- private static function get_global_config_path() {
- $config_path = getenv( 'WP_CLI_CONFIG_PATH' );
- if ( isset( $runtime_config['config'] ) ) {
- $config_path = $runtime_config['config'];
- }
- if ( !$config_path ) {
- $config_path = getenv( 'HOME' ) . '/.wp-cli/config.yml';
- }
- if ( !is_readable( $config_path ) )
- return false;
- return $config_path;
- }
- /**
- * Get the path to the project-specific configuration
- * YAML file.
- * wp-cli.local.yml takes priority over wp-cli.yml.
- *
- * @return string|false
- */
- private static function get_project_config_path() {
- $config_files = array(
- 'wp-cli.local.yml',
- 'wp-cli.yml'
- );
- // Stop looking upward when we find we have emerged from a subdirectory
- // install into a parent install
- return Utils\find_file_upward( $config_files, getcwd(), function ( $dir ) {
- static $wp_load_count = 0;
- $wp_load_path = $dir . DIRECTORY_SEPARATOR . 'wp-load.php';
- if ( file_exists( $wp_load_path ) ) {
- $wp_load_count += 1;
- }
- return $wp_load_count > 1;
- } );
- }
- /**
- * Attempts to find the path to the WP install inside index.php
- *
- * @param string $index_path
- * @return string|false
- */
- private static function extract_subdir_path( $index_path ) {
- $index_code = file_get_contents( $index_path );
- if ( !preg_match( '|^\s*require\s*\(?\s*(.+?)/wp-blog-header\.php([\'"])|m', $index_code, $matches ) ) {
- return false;
- }
- $wp_path_src = $matches[1] . $matches[2];
- $wp_path_src = Utils\replace_path_consts( $wp_path_src, $index_path );
- $wp_path = eval( "return $wp_path_src;" );
- if ( !Utils\is_path_absolute( $wp_path ) ) {
- $wp_path = dirname( $index_path ) . "/$wp_path";
- }
- return $wp_path;
- }
- /**
- * Find the directory that contains the WordPress files.
- * Defaults to the current working dir.
- *
- * @return string An absolute path
- */
- private function find_wp_root() {
- if ( !empty( $this->config['path'] ) ) {
- $path = $this->config['path'];
- if ( !Utils\is_path_absolute( $path ) )
- $path = getcwd() . '/' . $path;
- return $path;
- }
- if ( $this->cmd_starts_with( array( 'core', 'download' ) ) ) {
- return getcwd();
- }
- $dir = getcwd();
- while ( is_readable( $dir ) ) {
- if ( file_exists( "$dir/wp-load.php" ) ) {
- return $dir;
- }
- if ( file_exists( "$dir/index.php" ) ) {
- if ( $path = self::extract_subdir_path( "$dir/index.php" ) )
- return $path;
- }
- $parent_dir = dirname( $dir );
- if ( empty($parent_dir) || $parent_dir === $dir ) {
- break;
- }
- $dir = $parent_dir;
- }
- }
- /**
- * Set WordPress root as a given path.
- *
- * @param string $path
- */
- private static function set_wp_root( $path ) {
- define( 'ABSPATH', rtrim( $path, '/' ) . '/' );
- $_SERVER['DOCUMENT_ROOT'] = realpath( $path );
- }
- /**
- * Set a specific user context for WordPress.
- *
- * @param array $assoc_args
- */
- private static function set_user( $assoc_args ) {
- if ( isset( $assoc_args['user'] ) ) {
- $fetcher = new \WP_CLI\Fetchers\User;
- $user = $fetcher->get_check( $assoc_args['user'] );
- wp_set_current_user( $user->ID );
- } else {
- kses_remove_filters();
- }
- }
- /**
- * Guess which URL context WP-CLI has been invoked under.
- *
- * @param array $assoc_args
- * @return string|false
- */
- private static function guess_url( $assoc_args ) {
- if ( isset( $assoc_args['blog'] ) ) {
- $assoc_args['url'] = $assoc_args['blog'];
- }
- if ( isset( $assoc_args['url'] ) ) {
- $url = $assoc_args['url'];
- if ( true === $url ) {
- WP_CLI::warning( 'The --url parameter expects a value.' );
- }
- } elseif ( $wp_config_path = Utils\locate_wp_config() ) {
- // Try to find the blog parameter in the wp-config file
- $wp_config_file = file_get_contents( $wp_config_path );
- $hit = array();
- $re_define = "#.*define\s*\(\s*(['|\"]{1})(.+)(['|\"]{1})\s*,\s*(['|\"]{1})(.+)(['|\"]{1})\s*\)\s*;#iU";
- if ( preg_match_all( $re_define, $wp_config_file, $matches ) ) {
- foreach ( $matches[2] as $def_key => $def_name ) {
- if ( 'DOMAIN_CURRENT_SITE' == $def_name )
- $hit['domain'] = $matches[5][$def_key];
- if ( 'PATH_CURRENT_SITE' == $def_name )
- $hit['path'] = $matches[5][$def_key];
- }
- }
- if ( !empty( $hit ) && isset( $hit['domain'] ) ) {
- $url = $hit['domain'];
- if ( isset( $hit['path'] ) )
- $url .= $hit['path'];
- }
- }
- if ( isset( $url ) ) {
- return $url;
- }
- return false;
- }
- private function cmd_starts_with( $prefix ) {
- return $prefix == array_slice( $this->arguments, 0, count( $prefix ) );
- }
- /**
- * Given positional arguments, find the command to execute.
- *
- * @param array $args
- * @return array|string Command, args, and path on success; error message on failure
- */
- public function find_command_to_run( $args ) {
- $command = \WP_CLI::get_root_command();
- $cmd_path = array();
- while ( !empty( $args ) && $command->can_have_subcommands() ) {
- $cmd_path[] = $args[0];
- $full_name = implode( ' ', $cmd_path );
- $subcommand = $command->find_subcommand( $args );
- if ( !$subcommand ) {
- return sprintf(
- "'%s' is not a registered wp command. See 'wp help'.",
- $full_name
- );
- }
- if ( $this->is_command_disabled( $subcommand ) ) {
- return sprintf(
- "The '%s' command has been disabled from the config file.",
- $full_name
- );
- }
- $command = $subcommand;
- }
- return array( $command, $args, $cmd_path );
- }
- /**
- * Find the WP-CLI command to run given arguments,
- * and invoke it.
- *
- * @param array $args Positional arguments including command name
- * @param array $assoc_args
- */
- public function run_command( $args, $assoc_args = array() ) {
- $r = $this->find_command_to_run( $args );
- if ( is_string( $r ) ) {
- WP_CLI::error( $r );
- }
- list( $command, $final_args, $cmd_path ) = $r;
- $name = implode( ' ', $cmd_path );
- if ( isset( $this->extra_config[ $name ] ) ) {
- $extra_args = $this->extra_config[ $name ];
- } else {
- $extra_args = array();
- }
- try {
- $command->invoke( $final_args, $assoc_args, $extra_args );
- } catch ( WP_CLI\Iterators\Exception $e ) {
- WP_CLI::error( $e->getMessage() );
- }
- }
- private function _run_command() {
- $this->run_command( $this->arguments, $this->assoc_args );
- }
- /**
- * Check whether a given command is disabled by the config
- *
- * @return bool
- */
- public function is_command_disabled( $command ) {
- $path = implode( ' ', array_slice( \WP_CLI\Dispatcher\get_path( $command ), 1 ) );
- return in_array( $path, $this->config['disabled_commands'] );
- }
- /**
- * Returns wp-config.php code, skipping the loading of wp-settings.php
- *
- * @return string
- */
- public function get_wp_config_code() {
- $wp_config_path = Utils\locate_wp_config();
- $wp_config_code = explode( "\n", file_get_contents( $wp_config_path ) );
- $found_wp_settings = false;
- $lines_to_run = array();
- foreach ( $wp_config_code as $line ) {
- if ( preg_match( '/^\s*require.+wp-settings\.php/', $line ) ) {
- $found_wp_settings = true;
- continue;
- }
- $lines_to_run[] = $line;
- }
- if ( !$found_wp_settings ) {
- WP_CLI::error( 'Strange wp-config.php file: wp-settings.php is not loaded directly.' );
- }
- $source = implode( "\n", $lines_to_run );
- $source = Utils\replace_path_consts( $source, $wp_config_path );
- return preg_replace( '|^\s*\<\?php\s*|', '', $source );
- }
- /**
- * Transparently convert deprecated syntaxes
- *
- * @param array $args
- * @param array $assoc_args
- * @return array
- */
- private static function back_compat_conversions( $args, $assoc_args ) {
- $top_level_aliases = array(
- 'sql' => 'db',
- 'blog' => 'site'
- );
- if ( count( $args ) > 0 ) {
- foreach ( $top_level_aliases as $old => $new ) {
- if ( $old == $args[0] ) {
- $args[0] = $new;
- break;
- }
- }
- }
- // *-meta -> * meta
- if ( !empty( $args ) && preg_match( '/(post|comment|user|network)-meta/', $args[0], $matches ) ) {
- array_shift( $args );
- array_unshift( $args, 'meta' );
- array_unshift( $args, $matches[1] );
- }
- // core (multsite-)install --admin_name= -> --admin_user=
- if ( count( $args ) > 0 && 'core' == $args[0] && isset( $assoc_args['admin_name'] ) ) {
- $assoc_args['admin_user'] = $assoc_args['admin_name'];
- unset( $assoc_args['admin_name'] );
- }
- // site --site_id= -> site --network_id=
- if ( count( $args ) > 0 && 'site' == $args[0] && isset( $assoc_args['site_id'] ) ) {
- $assoc_args['network_id'] = $assoc_args['site_id'];
- unset( $assoc_args['site_id'] );
- }
- // {plugin|theme} update-all -> {plugin|theme} update --all
- if ( count( $args ) > 1 && in_array( $args[0], array( 'plugin', 'theme' ) )
- && $args[1] == 'update-all'
- ) {
- $args[1] = 'update';
- $assoc_args['all'] = true;
- }
- // plugin scaffold -> scaffold plugin
- if ( array( 'plugin', 'scaffold' ) == array_slice( $args, 0, 2 ) ) {
- list( $args[0], $args[1] ) = array( $args[1], $args[0] );
- }
- // foo --help -> help foo
- if ( isset( $assoc_args['help'] ) ) {
- array_unshift( $args, 'help' );
- unset( $assoc_args['help'] );
- }
- // {post|user} list --ids -> {post|user} list --format=ids
- if ( count( $args ) > 1 && in_array( $args[0], array( 'post', 'user' ) )
- && $args[1] == 'list'
- && isset( $assoc_args['ids'] )
- ) {
- $assoc_args['format'] = 'ids';
- unset( $assoc_args['ids'] );
- }
- // --json -> --format=json
- if ( isset( $assoc_args['json'] ) ) {
- $assoc_args['format'] = 'json';
- unset( $assoc_args['json'] );
- }
- // --{version|info} -> cli {version|info}
- if ( empty( $args ) ) {
- $special_flags = array( 'version', 'info' );
- foreach ( $special_flags as $key ) {
- if ( isset( $assoc_args[ $key ] ) ) {
- $args = array( 'cli', $key );
- unset( $assoc_args[ $key ] );
- break;
- }
- }
- }
- return array( $args, $assoc_args );
- }
- /**
- * Whether or not the output should be rendered in color
- *
- * @return bool
- */
- public function in_color() {
- return $this->colorize;
- }
- private function init_colorization() {
- if ( 'auto' === $this->config['color'] ) {
- $this->colorize = ( !\cli\Shell::isPiped() && !\WP_CLI\Utils\is_windows() );
- } else {
- $this->colorize = $this->config['color'];
- }
- }
- private function init_logger() {
- if ( $this->config['quiet'] )
- $logger = new \WP_CLI\Loggers\Quiet;
- else
- $logger = new \WP_CLI\Loggers\Regular( $this->in_color() );
- WP_CLI::set_logger( $logger );
- }
- /**
- * Do WordPress core files exist?
- *
- * @return bool
- */
- private function wp_exists() {
- return is_readable( ABSPATH . 'wp-includes/version.php' );
- }
- private function check_wp_version() {
- if ( !$this->wp_exists() ) {
- WP_CLI::error(
- "This does not seem to be a WordPress install.\n" .
- "Pass --path=`path/to/wordpress` or run `wp core download`." );
- }
- include ABSPATH . 'wp-includes/version.php';
- $minimum_version = '3.5.2';
- // @codingStandardsIgnoreStart
- if ( version_compare( $wp_version, $minimum_version, '<' ) ) {
- WP_CLI::error(
- "WP-CLI needs WordPress $minimum_version or later to work properly. " .
- "The version currently installed is $wp_version.\n" .
- "Try running `wp core download --force`."
- );
- }
- // @codingStandardsIgnoreEnd
- }
- private function init_config() {
- $configurator = \WP_CLI::get_configurator();
- // File config
- {
- $this->global_config_path = self::get_global_config_path();
- $this->project_config_path = self::get_project_config_path();
- $configurator->merge_yml( $this->global_config_path );
- $configurator->merge_yml( $this->project_config_path );
- }
- // Runtime config and args
- {
- list( $args, $assoc_args, $runtime_config ) = $configurator->parse_args(
- array_slice( $GLOBALS['argv'], 1 ) );
- list( $this->arguments, $this->assoc_args ) = self::back_compat_conversions(
- $args, $assoc_args );
- $configurator->merge_array( $runtime_config );
- }
- list( $this->config, $this->extra_config ) = $configurator->to_array();
- }
- private function check_root() {
- if ( $this->config['allow-root'] )
- return; # they're aware of the risks!
- if ( !function_exists( 'posix_geteuid') )
- return; # posix functions not available
- if ( posix_geteuid() !== 0 )
- return; # not root
- WP_CLI::error(
- "YIKES! It looks like you're running this as root. You probably meant to " .
- "run this as the user that your WordPress install exists under.\n" .
- "\n" .
- "If you REALLY mean to run this as root, we won't stop you, but just " .
- "bear in mind that any code on this site will then have full control of " .
- "your server, making it quite DANGEROUS.\n" .
- "\n" .
- "If you'd like to continue as root, please run this again, adding this " .
- "flag: --allow-root\n" .
- "\n" .
- "If you'd like to run it as the user that this site is under, you can " .
- "run the following to become the respective user:\n" .
- "\n" .
- " sudo -u USER -i -- wp <command>\n" .
- "\n"
- );
- }
- public function before_wp_load() {
- $this->init_config();
- $this->init_colorization();
- $this->init_logger();
- $this->check_root();
- if ( empty( $this->arguments ) )
- $this->arguments[] = 'help';
- // Protect 'cli info' from most of the runtime
- if ( 'cli' === $this->arguments[0] && ! empty( $this->arguments[1] ) && 'info' === $this->arguments[1] ) {
- $this->_run_command();
- exit;
- }
- // Load bundled commands early, so that they're forced to use the same
- // APIs as non-bundled commands.
- Utils\load_command( $this->arguments[0] );
- if ( isset( $this->config['require'] ) ) {
- foreach ( $this->config['require'] as $path ) {
- if ( ! file_exists( $path ) ) {
- WP_CLI::error( sprintf( "Required file '%s' doesn't exist", basename( $path ) ) );
- }
- Utils\load_file( $path );
- }
- }
- // Show synopsis if it's a composite command.
- $r = $this->find_command_to_run( $this->arguments );
- if ( is_array( $r ) ) {
- list( $command ) = $r;
- if ( $command->can_have_subcommands() ) {
- $command->show_usage();
- exit;
- }
- }
- // Handle --path parameter
- self::set_wp_root( $this->find_wp_root() );
- // First try at showing man page
- if ( 'help' === $this->arguments[0] && ( isset( $this->arguments[1] ) || !$this->wp_exists() ) ) {
- $this->_run_command();
- }
- // Handle --url parameter
- $url = self::guess_url( $this->config );
- if ( $url )
- \WP_CLI::set_url( $url );
- $this->do_early_invoke( 'before_wp_load' );
- $this->check_wp_version();
- if ( $this->cmd_starts_with( array( 'core', 'config' ) ) ) {
- $this->_run_command();
- exit;
- }
- if ( !Utils\locate_wp_config() ) {
- WP_CLI::error(
- "wp-config.php not found.\n" .
- "Either create one manually or use `wp core config`." );
- }
- if ( $this->cmd_starts_with( array( 'db' ) ) && !$this->cmd_starts_with( array( 'db', 'tables' ) ) ) {
- eval( $this->get_wp_config_code() );
- $this->_run_command();
- exit;
- }
- if ( $this->cmd_starts_with( array( 'core', 'is-installed' ) ) ) {
- define( 'WP_INSTALLING', true );
- }
- if (
- count( $this->arguments ) >= 2 &&
- $this->arguments[0] == 'core' &&
- in_array( $this->arguments[1], array( 'install', 'multisite-install' ) )
- ) {
- define( 'WP_INSTALLING', true );
- // We really need a URL here
- if ( !isset( $_SERVER['HTTP_HOST'] ) ) {
- $url = 'http://example.com';
- \WP_CLI::set_url( $url );
- }
- if ( 'multisite-install' == $this->arguments[1] ) {
- // need to fake some globals to skip the checks in wp-includes/ms-settings.php
- $url_parts = Utils\parse_url( $url );
- self::fake_current_site_blog( $url_parts );
- if ( !defined( 'COOKIEHASH' ) ) {
- define( 'COOKIEHASH', md5( $url_parts['host'] ) );
- }
- }
- }
- if ( $this->cmd_starts_with( array( 'import') ) ) {
- define( 'WP_LOAD_IMPORTERS', true );
- define( 'WP_IMPORTING', true );
- }
- if ( $this->cmd_starts_with( array( 'plugin' ) ) ) {
- $GLOBALS['pagenow'] = 'plugins.php';
- }
- }
- private static function fake_current_site_blog( $url_parts ) {
- global $current_site, $current_blog;
- if ( !isset( $url_parts['path'] ) ) {
- $url_parts['path'] = '/';
- }
- $current_site = (object) array(
- 'id' => 1,
- 'blog_id' => 1,
- 'domain' => $url_parts['host'],
- 'path' => $url_parts['path'],
- 'cookie_domain' => $url_parts['host'],
- 'site_name' => 'Fake Site',
- );
- $current_blog = (object) array(
- 'blog_id' => 1,
- 'site_id' => 1,
- 'domain' => $url_parts['host'],
- 'path' => $url_parts['path'],
- 'public' => '1',
- 'archived' => '0',
- 'mature' => '0',
- 'spam' => '0',
- 'deleted' => '0',
- 'lang_id' => '0',
- );
- }
- public function after_wp_load() {
- add_filter( 'filesystem_method', function() { return 'direct'; }, 99 );
- // Handle --user parameter
- if ( ! defined( 'WP_INSTALLING' ) ) {
- self::set_user( $this->config );
- }
- $this->_run_command();
- }
- }