] * : Specify the path in which to install WordPress. * * [--locale=] * : Select which language you want to download. * * [--version=] * : Select which version you want to download. * * [--force] * : Overwrites existing files, if present. * * ## EXAMPLES * * wp core download --version=3.3 * * @when before_wp_load */ public function download( $args, $assoc_args ) { if ( !isset( $assoc_args['force'] ) && is_readable( ABSPATH . 'wp-load.php' ) ) WP_CLI::error( 'WordPress files seem to already be present here.' ); if ( !is_dir( ABSPATH ) ) { WP_CLI::log( sprintf( 'Creating directory %s', ABSPATH ) ); WP_CLI::launch( Utils\esc_cmd( 'mkdir -p %s', ABSPATH ) ); } if ( isset( $assoc_args['locale'] ) && isset( $assoc_args['version'] ) ) { $download_url = sprintf( 'https://%s.wordpress.org/wordpress-%s-%s.tar.gz', substr( $assoc_args['locale'], 0, 2 ), $assoc_args['version'], $assoc_args['locale'] ); WP_CLI::log( sprintf( 'Downloading WordPress %s (%s)...', $assoc_args['version'], $assoc_args['locale'] ) ); } else if ( isset( $assoc_args['locale'] ) ) { $offer = $this->get_download_offer( $assoc_args['locale'] ); $download_url = str_replace( '.zip', '.tar.gz', $offer['download'] ); WP_CLI::log( sprintf( 'Downloading WordPress %s (%s)...', $offer['current'], $offer['locale'] ) ); } elseif ( isset( $assoc_args['version'] ) ) { $download_url = 'https://wordpress.org/wordpress-' . $assoc_args['version'] . '.tar.gz'; WP_CLI::log( sprintf( 'Downloading WordPress %s (%s)...', $assoc_args['version'], 'en_US' ) ); } else { $download_url = 'https://wordpress.org/latest.tar.gz'; WP_CLI::log( sprintf( 'Downloading latest WordPress (%s)...', 'en_US' ) ); } // We need to use a temporary file because piping from cURL to tar is flaky // on MinGW (and probably in other environments too). $temp = sys_get_temp_dir() . '/' . uniqid('wp_') . '.tar.gz'; $headers = array('Accept' => 'application/json'); $options = array( 'timeout' => 600, // 10 minutes ought to be enough for everybody 'filename' => $temp ); self::_request( 'GET', $download_url, $headers, $options ); self::_extract( $temp, ABSPATH ); WP_CLI::success( 'WordPress downloaded.' ); } private static function _extract( $tarball, $dest ) { if ( ! class_exists( 'PharData' ) ) { $cmd = "tar xz --strip-components=1 --directory=%s -f $tarball && rm $tarball"; WP_CLI::launch( Utils\esc_cmd( $cmd, $dest ) ); return; } $phar = new PharData( $tarball ); $tempdir = implode( DIRECTORY_SEPARATOR, Array ( dirname( $tarball ), basename( $tarball, '.tar.gz' ), $phar->getFileName() ) ); $phar->extractTo( dirname( $tempdir ), null, true ); self::_copy_overwrite_files( $tempdir, $dest ); self::_rmdir( dirname( $tempdir ) ); } private static function _copy_overwrite_files( $source, $dest ) { $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $source, RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::SELF_FIRST); foreach ( $iterator as $item ) { if ( $item->isDir() ) { $dest_path = $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName(); if ( !is_dir( $dest_path ) ) { mkdir( $dest_path ); } } else { copy( $item, $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName() ); } } } private static function _rmdir( $dir ) { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir, RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST ); foreach ( $files as $fileinfo ) { $todo = $fileinfo->isDir() ? 'rmdir' : 'unlink'; $todo( $fileinfo->getRealPath() ); } } private static function _request( $method, $url, $headers = array(), $options = array() ) { // cURL can't read Phar archives if ( 0 === strpos( WP_CLI_ROOT, 'phar://' ) ) { $options['verify'] = sys_get_temp_dir() . '/wp-cli-cacert.pem'; copy( WP_CLI_ROOT . '/vendor/rmccue/requests/library/Requests/Transport/cacert.pem', $options['verify'] ); } try { return Requests::get( $url, $headers, $options ); } catch( Requests_Exception $ex ) { // Handle SSL certificate issues gracefully WP_CLI::warning( $ex->getMessage() ); $options['verify'] = false; try { return Requests::get( $url, $headers, $options ); } catch( Requests_Exception $ex ) { WP_CLI::error( $ex->getMessage() ); } } } private static function _read( $url ) { $headers = array('Accept' => 'application/json'); return self::_request( 'GET', $url, $headers )->body; } private function get_download_offer( $locale ) { $out = unserialize( self::_read( 'https://api.wordpress.org/core/version-check/1.6/?locale=' . $locale ) ); return $out['offers'][0]; } private static function get_initial_locale() { include ABSPATH . '/wp-includes/version.php'; // @codingStandardsIgnoreStart if ( isset( $wp_local_package ) ) return $wp_local_package; // @codingStandardsIgnoreEnd return ''; } /** * Set up a wp-config.php file. * * ## OPTIONS * * --dbname= * : Set the database name. * * --dbuser= * : Set the database user. * * [--dbpass=] * : Set the database user password. * * [--dbhost=] * : Set the database host. Default: 'localhost' * * [--dbprefix=] * : Set the database table prefix. Default: 'wp_' * * [--dbcharset=] * : Set the database charset. Default: 'utf8' * * [--dbcollate=] * : Set the database collation. Default: '' * * [--locale=] * : Set the WPLANG constant. Defaults to $wp_local_package variable. * * [--extra-php] * : If set, the command reads additional PHP code from STDIN. * * [--skip-salts] * : If set, keys and salts won't be generated, but, instead, should be passed via --extra-php. * * ## EXAMPLES * * # Standard wp-config.php file * wp core config --dbname=testing --dbuser=wp --dbpass=securepswd --locale=ro_RO * * # Enable WP_DEBUG and WP_DEBUG_LOG * wp core config --dbname=testing --dbuser=wp --dbpass=securepswd --extra-php < 'localhost', 'dbpass' => '', 'dbprefix' => 'wp_', 'dbcharset' => 'utf8', 'dbcollate' => '', 'locale' => self::get_initial_locale() ); $assoc_args = array_merge( $defaults, $assoc_args ); if ( preg_match( '|[^a-z0-9_]|i', $assoc_args['dbprefix'] ) ) WP_CLI::error( '--dbprefix can only contain numbers, letters, and underscores.' ); // Check DB connection Utils\run_mysql_command( 'mysql --no-defaults', array( 'execute' => ';', 'host' => $assoc_args['dbhost'], 'user' => $assoc_args['dbuser'], 'pass' => $assoc_args['dbpass'], ) ); if ( isset( $assoc_args['extra-php'] ) ) { $assoc_args['extra-php'] = file_get_contents( 'php://stdin' ); } // TODO: adapt more resilient code from wp-admin/setup-config.php if ( ! isset( $assoc_args['skip-salts'] ) ) { $assoc_args['keys-and-salts'] = self::_read( 'https://api.wordpress.org/secret-key/1.1/salt/' ); } $out = Utils\mustache_render( 'wp-config.mustache', $assoc_args ); $bytes_written = file_put_contents( ABSPATH . 'wp-config.php', $out ); if ( ! $bytes_written ) { WP_CLI::error( 'Could not create new wp-config.php file.' ); } else { WP_CLI::success( 'Generated wp-config.php file.' ); } } /** * Determine if the WordPress tables are installed. * * ## EXAMPLES * * if ! $(wp core is-installed); then * wp core install * fi * * @subcommand is-installed */ public function is_installed() { if ( is_blog_installed() ) { exit( 0 ); } else { exit( 1 ); } } /** * Create the WordPress tables in the database. * * ## OPTIONS * * --url= * : The address of the new site. * * --title= * : The title of the new site. * * --admin_user= * : The name of the admin user. * * --admin_password= * : The password for the admin user. * * --admin_email= * : The email address for the admin user. */ public function install( $args, $assoc_args ) { if ( $this->_install( $assoc_args ) ) { WP_CLI::success( 'WordPress installed successfully.' ); } else { WP_CLI::log( 'WordPress is already installed.' ); } } /** * Transform a single-site install into a multi-site install. * * ## OPTIONS * * [--title=] * : The title of the new network. * * [--base=] * : Base path after the domain name that each site url will start with. * Default: '/' * * [--subdomains] * : If passed, the network will use subdomains, instead of subdirectories. * * @subcommand multisite-convert * @alias install-network */ public function multisite_convert( $args, $assoc_args ) { if ( is_multisite() ) WP_CLI::error( 'This already is a multisite install.' ); $assoc_args = self::_set_multisite_defaults( $assoc_args ); if ( !isset( $assoc_args['title'] ) ) { $assoc_args['title'] = sprintf( _x('%s Sites', 'Default network name' ), get_option( 'blogname' ) ); } if ( $this->_multisite_convert( $assoc_args ) ) { WP_CLI::success( "Network installed. Don't forget to set up rewrite rules." ); } } /** * Install multisite from scratch. * * ## OPTIONS * * [--url=] * : The address of the new site. * * [--base=] * : Base path after the domain name that each site url in the network will start with. * Default: '/' * * [--subdomains] * : If passed, the network will use subdomains, instead of subdirectories. * * --title= * : The title of the new site. * * --admin_user= * : The name of the admin user. Default: 'admin' * * --admin_password= * : The password for the admin user. * * --admin_email= * : The email address for the admin user. * * @subcommand multisite-install */ public function multisite_install( $args, $assoc_args ) { if ( $this->_install( $assoc_args ) ) { WP_CLI::log( 'Created single site database tables.' ); } else { WP_CLI::log( 'Single site database tables already present.' ); } $assoc_args = self::_set_multisite_defaults( $assoc_args ); $assoc_args['title'] = sprintf( _x('%s Sites', 'Default network name' ), $assoc_args['title'] ); // Overwrite runtime args, to avoid mismatches. $consts_to_args = array( 'SUBDOMAIN_INSTALL' => 'subdomains', 'PATH_CURRENT_SITE' => 'base', 'SITE_ID_CURRENT_SITE' => 'site_id', 'BLOG_ID_CURRENT_SITE' => 'blog_id', ); foreach ( $consts_to_args as $const => $arg ) { if ( defined( $const ) ) { $assoc_args[ $arg ] = constant( $const ); } } if ( !$this->_multisite_convert( $assoc_args ) ) { return; } // Do the steps that were skipped by populate_network(), // which checks is_multisite(). if ( is_multisite() ) { $site_user = get_user_by( 'email', $assoc_args['admin_email'] ); self::add_site_admins( $site_user ); $domain = self::get_clean_basedomain(); self::create_initial_blog( $assoc_args['site_id'], $assoc_args['blog_id'], $domain, $assoc_args['base'], $assoc_args['subdomains'], $site_user ); } WP_CLI::success( "Network installed. Don't forget to set up rewrite rules." ); } private static function _set_multisite_defaults( $assoc_args ) { $defaults = array( 'subdomains' => false, 'base' => '/', 'site_id' => 1, 'blog_id' => 1, ); return array_merge( $defaults, $assoc_args ); } private function _install( $assoc_args ) { if ( is_blog_installed() ) { return false; } require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); extract( wp_parse_args( $assoc_args, array( 'title' => '', 'admin_user' => '', 'admin_email' => '', 'admin_password' => '' ) ), EXTR_SKIP ); // Support prompting for the `--url=`, // which is normally a runtime argument if ( isset( $assoc_args['url'] ) ) $url_parts = WP_CLI::set_url( $assoc_args['url'] ); $public = true; // @codingStandardsIgnoreStart $result = wp_install( $title, $admin_user, $admin_email, $public, '', $admin_password ); if ( is_wp_error( $result ) ) { WP_CLI::error( 'Installation failed (' . WP_CLI::error_to_string($result) . ').' ); } // @codingStandardsIgnoreEnd return true; } private function _multisite_convert( $assoc_args ) { global $wpdb; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); // need to register the multisite tables manually for some reason foreach ( $wpdb->tables( 'ms_global' ) as $table => $prefixed_table ) $wpdb->$table = $prefixed_table; install_network(); $domain = self::get_clean_basedomain(); $result = populate_network( $assoc_args['site_id'], $domain, get_option( 'admin_email' ), $assoc_args['title'], $assoc_args['base'], $assoc_args['subdomains'] ); if ( true === $result ) { WP_CLI::log( 'Set up multisite database tables.' ); } else if ( is_wp_error( $result ) ) { switch ( $result->get_error_code() ) { case 'siteid_exists': WP_CLI::log( $result->get_error_message() ); return false; case 'no_wildcard_dns': WP_CLI::warning( __( 'Wildcard DNS may not be configured correctly.' ) ); break; default: WP_CLI::error( $result ); } } if ( !is_multisite() ) { ob_start(); ?> define('MULTISITE', true); define('SUBDOMAIN_INSTALL', ); $base = ''; define('DOMAIN_CURRENT_SITE', ''); define('PATH_CURRENT_SITE', ''); define('SITE_ID_CURRENT_SITE', 1); define('BLOG_ID_CURRENT_SITE', 1); domain = $domain; $current_site->path = $path; $current_site->site_name = ucfirst( $domain ); $wpdb->insert( $wpdb->blogs, array( 'site_id' => $network_id, 'domain' => $domain, 'path' => $path, 'registered' => current_time( 'mysql' ) ) ); $current_site->blog_id = $blog_id = $wpdb->insert_id; update_user_meta( $site_user->ID, 'source_domain', $domain ); update_user_meta( $site_user->ID, 'primary_blog', $blog_id ); if ( $subdomain_install ) $wp_rewrite->set_permalink_structure( '/%year%/%monthnum%/%day%/%postname%/' ); else $wp_rewrite->set_permalink_structure( '/blog/%year%/%monthnum%/%day%/%postname%/' ); flush_rewrite_rules(); } // copied from populate_network() private static function add_site_admins( $site_user ) { $site_admins = array( $site_user->user_login ); $users = get_users( array( 'fields' => array( 'ID', 'user_login' ) ) ); if ( $users ) { foreach ( $users as $user ) { if ( is_super_admin( $user->ID ) && !in_array( $user->user_login, $site_admins ) ) $site_admins[] = $user->user_login; } } update_site_option( 'site_admins', $site_admins ); } private static function modify_wp_config( $content ) { $wp_config_path = Utils\locate_wp_config(); $token = "/* That's all, stop editing!"; list( $before, $after ) = explode( $token, file_get_contents( $wp_config_path ) ); file_put_contents( $wp_config_path, $before . $content . $token . $after ); } private static function get_clean_basedomain() { $domain = preg_replace( '|https?://|', '', get_option( 'siteurl' ) ); if ( $slash = strpos( $domain, '/' ) ) $domain = substr( $domain, 0, $slash ); return $domain; } /** * Display the WordPress version. * * ## OPTIONS * * [--extra] * : Show extended version information. * * @when before_wp_load */ public function version( $args = array(), $assoc_args = array() ) { $versions_path = ABSPATH . 'wp-includes/version.php'; if ( !is_readable( $versions_path ) ) { WP_CLI::error( "This does not seem to be a WordPress install.\n" . "Pass --path=`path/to/wordpress` or run `wp core download`." ); } include $versions_path; // @codingStandardsIgnoreStart if ( isset( $assoc_args['extra'] ) ) { if ( preg_match( '/(\d)(\d+)-/', $tinymce_version, $match ) ) { $human_readable_tiny_mce = $match[1] . '.' . $match[2]; } else { $human_readable_tiny_mce = ''; } echo \WP_CLI\Utils\mustache_render( 'versions.mustache', array( 'wp-version' => $wp_version, 'db-version' => $wp_db_version, 'mce-version' => ( $human_readable_tiny_mce ? "$human_readable_tiny_mce ($tinymce_version)" : $tinymce_version ) ) ); } else { WP_CLI::line( $wp_version ); } // @codingStandardsIgnoreEnd } /** * Update WordPress. * * ## OPTIONS * * [] * : Path to zip file to use, instead of downloading from wordpress.org. * * [--version=] * : Update to this version, instead of to the latest version. * * [--force] * : Will update even when current WP version < passed version. Use with * caution. * * ## EXAMPLES * * wp core update * * wp core update --version=3.4 ../latest.zip * * wp core update --version=3.1 --force * * @alias upgrade */ function update( $args, $assoc_args ) { global $wp_version; $update = $from_api = null; $upgrader = 'Core_Upgrader'; if ( empty( $assoc_args['version'] ) ) { wp_version_check(); $from_api = get_site_transient( 'update_core' ); if ( empty( $from_api->updates ) ) $update = false; else list( $update ) = $from_api->updates; } else if ( version_compare( $wp_version, $assoc_args['version'], '<' ) || isset( $assoc_args['force'] ) ) { $new_package = null; if ( empty( $args[0] ) ) { $new_package = 'https://wordpress.org/wordpress-' . $assoc_args['version'] . '.zip'; WP_CLI::log( sprintf( 'Downloading WordPress %s (%s)...', $assoc_args['version'], 'en_US' ) ); } else { $new_package = $args[0]; $upgrader = 'WP_CLI\\NonDestructiveCoreUpgrader'; } $update = (object) array( 'response' => 'upgrade', 'current' => $assoc_args['version'], 'download' => $new_package, 'packages' => (object) array ( 'partial' => null, 'new_bundled' => null, 'no_content' => null, 'full' => $new_package, ), ); } else { WP_CLI::success( 'WordPress is up to date.' ); return; } require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); $result = Utils\get_upgrader( $upgrader )->upgrade( $update ); if ( is_wp_error($result) ) { $msg = WP_CLI::error_to_string( $result ); if ( 'up_to_date' != $result->get_error_code() ) { WP_CLI::error( $msg ); } else { WP_CLI::success( $msg ); } } else { WP_CLI::success( 'WordPress updated successfully.' ); } } /** * Update the WordPress database. * * @subcommand update-db */ function update_db() { require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); wp_upgrade(); WP_CLI::success( 'WordPress database upgraded successfully.' ); } /** * Get main WordPress info: blog name, site url, version, update version, plugins, themes. * * ## OPTIONS * * [--plugin-fields=] * : Limit the plugins output to specific object fields. Defaults to name,status,update,version,title,description,update version. * * [--theme-fields=] * : Limit the themes output to specific object fields. Defaults to name,status,update,version,title,description,update version. * * [--format=] * : The format to use when printing the user; acceptable values: * * **table**: Outputs all fields of the user as a table. * * **json**: Outputs all fields in JSON format. * * [--check-updates=] * : Check updates for instance: * * **true**: check updates for instance. * **false**: do not check updates for instance. * * ## EXAMPLES * * wp core info * wp core info --format=json * wp core info --plugin-fields=name,status,version --theme-fields=name,title,description --format=json * wp core info --plugin-fields=name,status,version --theme-fields=name,title,description --format=json --check-updates=true */ public function info( $_, $assoc_args ) { global $wp_version; $info['name'] = get_option( 'blogname' ); $info['url'] = get_option( 'siteurl' ); $info['version'] = $wp_version; $checkUpdates = isset( $assoc_args['check-updates'] ) ? $assoc_args['check-updates'] : 'true'; $info['update_version'] = ('true' === $checkUpdates) ? $this->_get_update_version() : null; $plugin_fields = isset( $assoc_args['plugin-fields'] ) ? $assoc_args['plugin-fields'] : 'name,status,version,title,description,update_version'; ob_start(); WP_CLI::run_command( array( 'plugin', 'list' ), array( 'fields' => $plugin_fields, 'check-updates' => $checkUpdates, 'format' => 'json' ) ); $info['plugins'] = ob_get_clean(); $theme_fields = isset( $assoc_args['theme-fields'] ) ? $assoc_args['theme-fields'] : 'name,status,version,title,description,update_version'; ob_start(); WP_CLI::run_command( array( 'theme', 'list' ), array( 'fields' => $theme_fields, 'check-updates' => $checkUpdates, 'format' => 'json' ) ); $info['themes'] = ob_get_clean(); $formatter = new \WP_CLI\Formatter( $assoc_args ); $formatter->display_item( $info ); } private function _get_update_version() { global $wp_version; wp_version_check(); $from_api = get_site_transient( 'update_core' ); if ( isset( $from_api->updates ) && is_array( $from_api->updates ) ) { list( $update ) = $from_api->updates; if ( isset( $update->response ) && 'latest' != $update->response && 'development' != $update->response && version_compare($update->current, $wp_version, '!=')) { return $update->current; } } return null; } } WP_CLI::add_command( 'core', 'Core_Command' );