File: //usr/local/wp/php/commands/src/CLI_Alias_Command.php
<?php
/**
* Retrieves, sets and updates aliases for WordPress Installations.
*/
use Mustangostang\Spyc;
use WP_CLI\ExitException;
use WP_CLI\Utils;
/**
* Retrieves, sets and updates aliases for WordPress Installations.
*
* Aliases are shorthand references to WordPress installs. For instance,
* `@dev` could refer to a development install and `@prod` could refer to a production install.
* This command gives you and option to add, update and delete, the registered aliases you have available.
*
* ## EXAMPLES
*
* # List alias information.
* $ wp cli alias list
* list
* ---
* @all: Run command against every registered alias.
* @local:
* user: wpcli
* path: /Users/wpcli/sites/testsite
*
* # Get alias information.
* $ wp cli alias get @dev
* ssh: dev@somedeve.env:12345/home/dev/
*
* # Add alias.
* $ wp cli alias add @prod --set-ssh=login@host --set-path=/path/to/wordpress/install/ --set-user=wpcli
* Success: Added '@prod' alias.
*
* # Update alias.
* $ wp cli alias update @prod --set-user=newuser --set-path=/new/path/to/wordpress/install/
* Success: Updated 'prod' alias.
*
* # Delete alias.
* $ wp cli alias delete @prod
* Success: Deleted '@prod' alias.
*
* @package wp-cli
* @when before_wp_load
*/
class CLI_Alias_Command extends WP_CLI_Command {
/**
* Lists available WP-CLI aliases.
*
* ## OPTIONS
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: yaml
* options:
* - yaml
* - json
* - var_export
* ---
*
* ## EXAMPLES
*
* # List all available aliases.
* $ wp cli alias list
* ---
* @all: Run command against every registered alias.
* @prod:
* ssh: runcommand@runcommand.io~/webapps/production
* @dev:
* ssh: vagrant@192.168.50.10/srv/www/runcommand.dev
* @both:
* - @prod
* - @dev
*
* @subcommand list
*/
public function list_( $args, $assoc_args ) {
WP_CLI::print_value( WP_CLI::get_runner()->aliases, $assoc_args );
}
/**
* Gets the value for an alias.
*
* ## OPTIONS
*
* <key>
* : Key for the alias.
*
* ## EXAMPLES
*
* # Get alias.
* $ wp cli alias get @prod
* ssh: dev@somedeve.env:12345/home/dev/
*/
public function get( $args, $assoc_args ) {
list( $alias ) = $args;
$aliases = WP_CLI::get_runner()->aliases;
if ( empty( $aliases[ $alias ] ) ) {
WP_CLI::error( "No alias found with key '{$alias}'." );
}
foreach ( $aliases[ $alias ] as $key => $value ) {
WP_CLI::log( "{$key}: {$value}" );
}
}
/**
* Creates an alias.
*
* ## OPTIONS
*
* <key>
* : Key for the alias.
*
* [--set-user=<user>]
* : Set user for alias.
*
* [--set-url=<url>]
* : Set url for alias.
*
* [--set-path=<path>]
* : Set path for alias.
*
* [--set-ssh=<ssh>]
* : Set ssh for alias.
*
* [--set-http=<http>]
* : Set http for alias.
*
* [--grouping=<grouping>]
* : For grouping multiple aliases.
*
* [--config=<config>]
* : Config file to be considered for operations.
* ---
* default: global
* options:
* - global
* - project
* ---
*
* ## EXAMPLES
*
* # Add alias to global config.
* $ wp cli alias add @prod --set-ssh=login@host --set-path=/path/to/wordpress/install/ --set-user=wpcli
* Success: Added '@prod' alias.
*
* # Add alias to project config.
* $ wp cli alias add @prod --set-ssh=login@host --set-path=/path/to/wordpress/install/ --set-user=wpcli --config=project
* Success: Added '@prod' alias.
*
* # Add group of aliases.
* $ wp cli alias add @multiservers --grouping=servera,serverb
* Success: Added '@multiservers' alias.
*/
public function add( $args, $assoc_args ) {
$config = ( ! empty( $assoc_args['config'] ) ? $assoc_args['config'] : 'global' );
list( $config_path, $aliases ) = $this->get_aliases_data( $config, '', true );
$this->validate_config_file( $config_path );
$alias = $args[0];
$grouping = Utils\get_flag_value( $assoc_args, 'grouping' );
$this->validate_input( $assoc_args, $grouping );
if ( isset( $aliases[ $alias ] ) ) {
WP_CLI::error( "Key '{$alias}' exists already." );
}
if ( null === $grouping ) {
$aliases = $this->build_aliases( $aliases, $alias, $assoc_args, false );
} else {
$aliases = $this->build_aliases( $aliases, $alias, $assoc_args, true, $grouping );
}
$this->process_aliases( $aliases, $alias, $config_path, 'Added' );
}
/**
* Deletes an alias.
*
* ## OPTIONS
*
* <key>
* : Key for the alias.
*
* [--config=<config>]
* : Config file to be considered for operations.
* ---
* options:
* - global
* - project
* ---
*
* ## EXAMPLES
*
* # Delete alias.
* $ wp cli alias delete @prod
* Success: Deleted '@prod' alias.
*
* # Delete project alias.
* $ wp cli alias delete @prod --config=project
* Success: Deleted '@prod' alias.
*/
public function delete( $args, $assoc_args ) {
list( $alias ) = $args;
$config = ( ! empty( $assoc_args['config'] ) ? $assoc_args['config'] : '' );
list( $config_path, $aliases ) = $this->get_aliases_data( $config, $alias );
$this->validate_config_file( $config_path );
if ( empty( $aliases[ $alias ] ) ) {
WP_CLI::error( "No alias found with key '{$alias}'." );
}
unset( $aliases[ $alias ] );
$this->process_aliases( $aliases, $alias, $config_path, 'Deleted' );
}
/**
* Updates an alias.
*
* ## OPTIONS
*
* <key>
* : Key for the alias.
*
* [--set-user=<user>]
* : Set user for alias.
*
* [--set-url=<url>]
* : Set url for alias.
*
* [--set-path=<path>]
* : Set path for alias.
*
* [--set-ssh=<ssh>]
* : Set ssh for alias.
*
* [--set-http=<http>]
* : Set http for alias.
*
* [--grouping=<grouping>]
* : For grouping multiple aliases.
*
* [--config=<config>]
* : Config file to be considered for operations.
* ---
* options:
* - global
* - project
* ---
*
* ## EXAMPLES
*
* # Update alias.
* $ wp cli alias update @prod --set-user=newuser --set-path=/new/path/to/wordpress/install/
* Success: Updated 'prod' alias.
*
* # Update project alias.
* $ wp cli alias update @prod --set-user=newuser --set-path=/new/path/to/wordpress/install/ --config=project
* Success: Updated 'prod' alias.
*/
public function update( $args, $assoc_args ) {
$config = ( ! empty( $assoc_args['config'] ) ? $assoc_args['config'] : '' );
$alias = $args[0];
$grouping = Utils\get_flag_value( $assoc_args, 'grouping' );
list( $config_path, $aliases ) = $this->get_aliases_data( $config, $alias, true );
$this->validate_config_file( $config_path );
$this->validate_input( $assoc_args, $grouping );
if ( empty( $aliases[ $alias ] ) ) {
WP_CLI::error( "No alias found with key '{$alias}'." );
}
if ( null === $grouping ) {
$aliases = $this->build_aliases( $aliases, $alias, $assoc_args, false, '', true );
} else {
$aliases = $this->build_aliases( $aliases, $alias, $assoc_args, true, $grouping, true );
}
$this->process_aliases( $aliases, $alias, $config_path, 'Updated' );
}
/**
* Check whether an alias is a group.
*
* ## OPTIONS
*
* <key>
* : Key for the alias.
*
* ## EXAMPLES
*
* # Checks whether the alias is a group; exit status 0 if it is, otherwise 1.
* $ wp cli alias is-group @prod
* $ echo $?
* 1
*
* @subcommand is-group
*/
public function is_group( $args, $assoc_args = array() ) {
$alias = $args[0];
$aliases = WP_CLI::get_runner()->aliases;
if ( empty( $aliases[ $alias ] ) ) {
WP_CLI::error( "No alias found with key '{$alias}'." );
}
// how do we know the alias is a group?
// + array keys are numeric
// + array values begin with '@'
$first_item = $aliases[ $alias ];
$first_item_key = key( $first_item );
$first_item_value = $first_item[ $first_item_key ];
if ( is_numeric( $first_item_key ) && substr( $first_item_value, 0, 1 ) === '@' ) {
WP_CLI::halt( 0 );
}
WP_CLI::halt( 1 );
}
/**
* Get config path and aliases data based on config type.
*
* @param string $config Type of config to get data from.
* @param string $alias Alias to be used for Add/Update/Delete.
* @param bool $create_config_file Optional. If a config file doesn't exist,
* should it be created? Defaults to false.
*
* @return array Config Path and Aliases in it.
* @throws ExitException
*/
private function get_aliases_data( $config, $alias, $create_config_file = false ) {
$global_config_path = WP_CLI::get_runner()->get_global_config_path( $create_config_file );
$global_aliases = Spyc::YAMLLoad( $global_config_path );
$project_config_path = WP_CLI::get_runner()->get_project_config_path();
$project_aliases = Spyc::YAMLLoad( $project_config_path );
if ( 'global' === $config ) {
$config_path = $global_config_path;
$aliases = $global_aliases;
} elseif ( 'project' === $config ) {
$config_path = $project_config_path;
$aliases = $project_aliases;
} else {
$is_global_alias = array_key_exists( $alias, $global_aliases );
$is_project_alias = array_key_exists( $alias, $project_aliases );
if ( $is_global_alias && $is_project_alias ) {
WP_CLI::error( "Key '{$alias}' found in more than one path. Please pass --config param." );
} elseif ( $is_global_alias ) {
$config_path = $global_config_path;
$aliases = $global_aliases;
} else {
$config_path = $project_config_path;
$aliases = $project_aliases;
}
}
return [ $config_path, $aliases ];
}
/**
* Check if the config file exists and is writable.
*
* @param string $config_path Path to config file.
*
* @return void
*/
private function validate_config_file( $config_path ) {
if ( ! file_exists( $config_path ) || ! is_writable( $config_path ) ) {
WP_CLI::error( "Config file does not exist: {$config_path}" );
}
}
/**
* Return aliases array.
*
* @param array $aliases Current aliases data.
* @param string $alias Name of alias.
* @param array $assoc_args Associative arguments.
* @param bool $is_grouping Check if its a grouping operation.
* @param string $grouping Grouping value.
* @param bool $is_update Is this an update operation?
*
* @return mixed
*/
private function build_aliases( $aliases, $alias, $assoc_args, $is_grouping, $grouping = '', $is_update = false ) {
$alias = $this->normalize_alias( $alias );
if ( $is_grouping ) {
$valid_assoc_args = [ 'config', 'grouping' ];
$invalid_args = array_diff( array_keys( $assoc_args ), $valid_assoc_args );
// Check for invalid args.
if ( ! empty( $invalid_args ) ) {
$args_info = implode( ',', $invalid_args );
WP_CLI::error( "--grouping argument works alone. Found invalid arg(s) '$args_info'." );
}
}
if ( $is_update ) {
$this->validate_alias_type( $aliases, $alias, $assoc_args, $grouping );
}
if ( ! $is_grouping ) {
foreach ( $assoc_args as $key => $value ) {
if ( strpos( $key, 'set-' ) !== false ) {
$alias_key_info = explode( '-', $key );
$alias_key = empty( $alias_key_info[1] ) ? '' : $alias_key_info[1];
if ( ! empty( $alias_key ) && ! empty( $value ) ) {
$aliases[ $alias ][ $alias_key ] = $value;
}
}
}
} elseif ( ! empty( $grouping ) ) {
$group_alias_list = explode( ',', $grouping );
$group_alias = array_map(
function ( $current_alias ) {
return '@' . ltrim( $current_alias, '@' );
},
$group_alias_list
);
$aliases[ $alias ] = $group_alias;
}
return $aliases;
}
/**
* Validate input of passed arguments.
*
* @param array $assoc_args Arguments array.
* @param string $grouping Grouping argument value.
*
* @throws ExitException
*/
private function validate_input( $assoc_args, $grouping ) {
// Check if valid arguments were passed.
$arg_match = preg_grep( '/^set-(\w+)/i', array_keys( $assoc_args ) );
// Verify passed-arguments.
if ( empty( $grouping ) && empty( $arg_match ) ) {
WP_CLI::error( 'No valid arguments passed.' );
}
// Check whether passed arguments contain value or not.
$assoc_arg_values = array_filter( array_intersect_key( $assoc_args, array_flip( $arg_match ) ) );
if ( empty( $grouping ) && empty( $assoc_arg_values ) ) {
WP_CLI::error( 'No value passed to arguments.' );
}
}
/**
* Validate alias type before update.
*
* @param array $aliases Existing aliases data.
* @param string $alias Alias Name.
* @param array $assoc_args Arguments array.
* @param string $grouping Grouping argument value.
*
* @throws ExitException
*/
private function validate_alias_type( $aliases, $alias, $assoc_args, $grouping ) {
$alias_data = $aliases[ $alias ];
$group_aliases_match = preg_grep( '/^@(\w+)/i', $alias_data );
$arg_match = preg_grep( '/^set-(\w+)/i', array_keys( $assoc_args ) );
if ( ! empty( $group_aliases_match ) && ! empty( $arg_match ) ) {
WP_CLI::error( 'Trying to update group alias with invalid arguments.' );
} elseif ( empty( $group_aliases_match ) && ! empty( $grouping ) ) {
WP_CLI::error( 'Trying to update simple alias with invalid --grouping argument.' );
}
}
/**
* Save aliases data to config file.
*
* @param array $aliases Current aliases data.
* @param string $alias Name of alias.
* @param string $config_path Path to config file.
* @param string $operation Current operation string fro message.
*/
private function process_aliases( $aliases, $alias, $config_path, $operation = '' ) {
$alias = $this->normalize_alias( $alias );
// Convert data to YAML string.
$yaml_data = Spyc::YAMLDump( $aliases );
// Add data in config file.
if ( file_put_contents( $config_path, $yaml_data ) ) {
WP_CLI::success( "$operation '{$alias}' alias." );
}
}
/**
* Normalize the alias to an expected format.
*
* - Add @ if not present.
*
* @param string $alias Name of alias.
*/
private function normalize_alias( $alias ) {
// Check if the alias starts with the @.
// See: https://github.com/wp-cli/wp-cli/issues/5391
if ( strpos( $alias, '@' ) !== 0 ) {
$alias = '@' . ltrim( $alias, '@' );
}
return $alias;
}
}