/**
* 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 [email protected]: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");
}
}