/** * Install a WP-CLI package. * * Packages are required to be a valid Composer package, and can be * specified as: * * * Package name from WP-CLI's package index. * * Git URL accessible by the current shell user. * * Path to a directory on the local machine. * * Local or remote .zip file. * * When installing a local directory, WP-CLI simply registers a * reference to the directory. If you move or delete the directory, WP-CLI's * reference breaks. * * When installing a .zip file, WP-CLI extracts the package to * `~/.wp-cli/packages/local/<package-name>`. * * ## OPTIONS * * <name|git|path|zip> * : Name, git URL, directory path, or .zip file for the package to install. * Names can optionally include a version constraint * (e.g. wp-cli/server-command:@stable). * * ## EXAMPLES * * # Install the latest development version from the package index. * $ wp package install wp-cli/server-command * Installing package wp-cli/server-command (dev-master) * Updating /home/person/.wp-cli/packages/composer.json to require the package... * Using Composer to install the package... * --- * Loading composer repositories with package information * Updating dependencies * Resolving dependencies through SAT * Dependency resolution completed in 0.005 seconds * Analyzed 732 packages to resolve dependencies * Analyzed 1034 rules to resolve dependencies * - Installing package * Writing lock file * Generating autoload files * --- * Success: Package installed. * * # Install the latest stable version. * $ wp package install wp-cli/server-command:@stable * * # Install a package hosted at a git URL. * $ wp package install git@github.com:runcommand/hook.git * * # Install a package in a .zip file. * $ wp package install google-sitemap-generator-cli.zip */ public function install($args, $assoc_args) { list($package_name) = $args; $git_package = $dir_package = false; $version = 'dev-master'; if ('.git' === strtolower(substr($package_name, -4, 4))) { $git_package = $package_name; preg_match('#([^:\\/]+\\/[^\\/]+)\\.git#', $package_name, $matches); if (!empty($matches[1])) { $package_name = $matches[1]; } else { WP_CLI::error("Couldn't parse package name from expected path '<name>/<package>'."); } } else { if (false !== strpos($package_name, '://') && false !== stripos($package_name, '.zip') || pathinfo($package_name, PATHINFO_EXTENSION) === 'zip' && is_file($package_name)) { // Download the remote ZIP file to a temp directory if (false !== strpos($package_name, '://')) { $temp = Utils\get_temp_dir() . uniqid('package_') . ".zip"; $options = array('timeout' => 600, 'filename' => $temp); $response = Utils\http_request('GET', $package_name, null, array(), $options); if (20 != substr($response->status_code, 0, 2)) { WP_CLI::error("Couldn't download package."); } $package_name = $temp; } $dir_package = Utils\get_temp_dir() . uniqid('package_'); try { // Extract the package to get the package name Extractor::extract($package_name, $dir_package); list($package_name, $version) = self::get_package_name_and_version_from_dir_package($dir_package); // Move to a location based on the package name $local_dir = rtrim(WP_CLI::get_runner()->get_packages_dir_path(), '/') . '/local/'; $actual_dir_package = $local_dir . str_replace('/', '-', $package_name); Extractor::copy_overwrite_files($dir_package, $actual_dir_package); Extractor::rmdir($dir_package); // Behold, the extracted package $dir_package = $actual_dir_package; } catch (Exception $e) { WP_CLI::error($e->getMessage()); } } else { if (is_dir($package_name) && file_exists($package_name . '/composer.json')) { $dir_package = $package_name; if (!Utils\is_path_absolute($dir_package)) { $dir_package = getcwd() . DIRECTORY_SEPARATOR . $dir_package; } list($package_name, $version) = self::get_package_name_and_version_from_dir_package($dir_package); } else { if (false !== strpos($package_name, ':')) { list($package_name, $version) = explode(':', $package_name); } $package = $this->get_community_package_by_name($package_name); if (!$package) { WP_CLI::error("Invalid package."); } } } } WP_CLI::log(sprintf("Installing package %s (%s)", $package_name, $version)); $composer_json_obj = $this->get_composer_json(); // Add the 'require' to composer.json WP_CLI::log(sprintf("Updating %s to require the package...", $composer_json_obj->getPath())); $composer_backup = file_get_contents($composer_json_obj->getPath()); $json_manipulator = new JsonManipulator($composer_backup); $json_manipulator->addMainKey('name', 'wp-cli/wp-cli'); $json_manipulator->addMainKey('version', self::get_wp_cli_version_composer()); $json_manipulator->addLink('require', $package_name, $version); $json_manipulator->addConfigSetting('secure-http', true); if ($git_package) { WP_CLI::log(sprintf('Registering %s as a VCS repository...', $git_package)); $json_manipulator->addRepository($package_name, array('type' => 'vcs', 'url' => $git_package)); } else { if ($dir_package) { WP_CLI::log(sprintf('Registering %s as a path repository...', $dir_package)); $json_manipulator->addRepository($package_name, array('type' => 'path', 'url' => $dir_package)); } } $composer_backup_decoded = json_decode($composer_backup, true); // If the composer file does not contain the current package index repository, refresh the repository definition. if (empty($composer_backup_decoded['repositories']['wp-cli']['url']) || self::PACKAGE_INDEX_URL != $composer_backup_decoded['repositories']['wp-cli']['url']) { WP_CLI::log('Updating package index repository url...'); $json_manipulator->addRepository('wp-cli', array('type' => 'composer', 'url' => self::PACKAGE_INDEX_URL)); } file_put_contents($composer_json_obj->getPath(), $json_manipulator->getContents()); try { $composer = $this->get_composer(); } catch (Exception $e) { WP_CLI::error($e->getMessage()); } // Set up the EventSubscriber $event_subscriber = new \WP_CLI\PackageManagerEventSubscriber(); $composer->getEventDispatcher()->addSubscriber($event_subscriber); // Set up the installer $install = Installer::create(new ComposerIO(), $composer); $install->setUpdate(true); // Installer class will only override composer.lock with this flag $install->setPreferSource(true); // Use VCS when VCS for easier contributions. // Try running the installer, but revert composer.json if failed WP_CLI::log('Using Composer to install the package...'); WP_CLI::log('---'); $res = false; try { $res = $install->run(); } catch (Exception $e) { WP_CLI::warning($e->getMessage()); } WP_CLI::log('---'); if (0 === $res) { WP_CLI::success("Package installed."); } else { file_put_contents($composer_json_obj->getPath(), $composer_backup); WP_CLI::error("Package installation failed (Composer return code {$res}). Reverted composer.json"); } }
/** * Download core WordPress files. * * Downloads and extracts WordPress core files to the specified path. Uses * an archive file stored in cache if WordPress has been previously * downloaded. * * ## OPTIONS * * [--path=<path>] * : Specify the path in which to install WordPress. * * [--locale=<locale>] * : Select which language you want to download. * * [--version=<version>] * : Select which version you want to download. Accepts a version number, 'latest' or 'nightly' * * [--force] * : Overwrites existing files, if present. * * ## EXAMPLES * * $ wp core download --locale=nl_NL * Downloading WordPress 4.5.2 (nl_NL)... * md5 hash verified: c5366d05b521831dd0b29dfc386e56a5 * Success: WordPress downloaded. * * @when before_wp_load */ public function download($args, $assoc_args) { $download_dir = !empty($assoc_args['path']) ? $assoc_args['path'] : ABSPATH; $wordpress_present = is_readable($download_dir . 'wp-load.php'); if (!\WP_CLI\Utils\get_flag_value($assoc_args, 'force') && $wordpress_present) { WP_CLI::error('WordPress files seem to already be present here.'); } if (!is_dir($download_dir)) { if (!is_writable(dirname($download_dir))) { WP_CLI::error(sprintf("Insufficient permission to create directory '%s'.", $download_dir)); } WP_CLI::log(sprintf("Creating directory '%s'.", $download_dir)); $mkdir = \WP_CLI\Utils\is_windows() ? 'mkdir %s' : 'mkdir -p %s'; WP_CLI::launch(Utils\esc_cmd($mkdir, $download_dir)); } if (!is_writable($download_dir)) { WP_CLI::error(sprintf("'%s' is not writable by current user.", $download_dir)); } $locale = \WP_CLI\Utils\get_flag_value($assoc_args, 'locale', 'en_US'); if (isset($assoc_args['version']) && 'latest' !== $assoc_args['version']) { $version = $assoc_args['version']; $version = in_array(strtolower($version), array('trunk', 'nightly')) ? 'nightly' : $version; //nightly builds are only available in .zip format $ext = 'nightly' === $version ? 'zip' : 'tar.gz'; $download_url = $this->get_download_url($version, $locale, $ext); } else { $offer = $this->get_download_offer($locale); if (!$offer) { WP_CLI::error("The requested locale ({$locale}) was not found."); } $version = $offer['current']; $download_url = str_replace('.zip', '.tar.gz', $offer['download']); } if ('nightly' === $version && 'en_US' !== $locale) { WP_CLI::error('Nightly builds are only available for the en_US locale.'); } $from_version = ''; if (file_exists($download_dir . 'wp-includes/version.php')) { global $wp_version; require_once $download_dir . 'wp-includes/version.php'; $from_version = $wp_version; } WP_CLI::log(sprintf('Downloading WordPress %s (%s)...', $version, $locale)); $path_parts = pathinfo($download_url); $extension = 'tar.gz'; if ('zip' === $path_parts['extension']) { $extension = 'zip'; if (!class_exists('ZipArchive')) { WP_CLI::error('Extracting a zip file requires ZipArchive.'); } } $cache = WP_CLI::get_cache(); $cache_key = "core/wordpress-{$version}-{$locale}.{$extension}"; $cache_file = $cache->has($cache_key); $bad_cache = false; if ($cache_file) { WP_CLI::log("Using cached file '{$cache_file}'..."); try { Extractor::extract($cache_file, $download_dir); } catch (Exception $e) { WP_CLI::warning("Extraction failed, downloading a new copy..."); $bad_cache = true; } } if (!$cache_file || $bad_cache) { // We need to use a temporary file because piping from cURL to tar is flaky // on MinGW (and probably in other environments too). $temp = \WP_CLI\Utils\get_temp_dir() . uniqid('wp_') . ".{$extension}"; $headers = array('Accept' => 'application/json'); $options = array('timeout' => 600, 'filename' => $temp); $response = Utils\http_request('GET', $download_url, null, $headers, $options); if (404 == $response->status_code) { WP_CLI::error("Release not found. Double-check locale or version."); } else { if (20 != substr($response->status_code, 0, 2)) { WP_CLI::error("Couldn't access download URL (HTTP code {$response->status_code})."); } } if ('nightly' !== $version) { $md5_response = Utils\http_request('GET', $download_url . '.md5'); if (20 != substr($md5_response->status_code, 0, 2)) { WP_CLI::error("Couldn't access md5 hash for release (HTTP code {$response->status_code})."); } $md5_file = md5_file($temp); if ($md5_file === $md5_response->body) { WP_CLI::log('md5 hash verified: ' . $md5_file); } else { WP_CLI::error("md5 hash for download ({$md5_file}) is different than the release hash ({$md5_response->body})."); } } else { WP_CLI::warning('md5 hash checks are not available for nightly downloads.'); } try { Extractor::extract($temp, $download_dir); } catch (Exception $e) { WP_CLI::error("Couldn't extract WordPress archive. " . $e->getMessage()); } if ('nightly' !== $version) { $cache->import($cache_key, $temp); } unlink($temp); } if ($wordpress_present) { $this->cleanup_extra_files($from_version, $version, $locale); } WP_CLI::success('WordPress downloaded.'); }