diff --git a/composer.json b/composer.json index ec7ad4e1..e2318963 100644 --- a/composer.json +++ b/composer.json @@ -41,6 +41,7 @@ "bundled": true, "commands": [ "config", + "config add", "config edit", "config delete", "config create", @@ -50,7 +51,8 @@ "config list", "config path", "config set", - "config shuffle-salts" + "config shuffle-salts", + "config update" ] }, "autoload": { diff --git a/features/config-add.feature b/features/config-add.feature new file mode 100644 index 00000000..90acbddf --- /dev/null +++ b/features/config-add.feature @@ -0,0 +1,138 @@ +Feature: Add a constant or variable to wp-config.php file + + Scenario: Add a new constant to wp-config.php + Given a WP install + + When I run `wp config add NEW_CONSTANT constant_value` + Then STDOUT should be: + """ + Success: Added the constant 'NEW_CONSTANT' to the 'wp-config.php' file with the value 'constant_value'. + """ + + When I run `wp config get NEW_CONSTANT` + Then STDOUT should be: + """ + constant_value + """ + + Scenario: Add a new variable to wp-config.php + Given a WP install + + When I run `wp config add new_variable variable_value --type=variable` + Then STDOUT should be: + """ + Success: Added the variable 'new_variable' to the 'wp-config.php' file with the value 'variable_value'. + """ + + When I run `wp config get new_variable` + Then STDOUT should be: + """ + variable_value + """ + + Scenario: Add a raw constant to wp-config.php + Given a WP install + + When I run `wp config add WP_CUSTOM_CONSTANT true --raw` + Then STDOUT should be: + """ + Success: Added the constant 'WP_CUSTOM_CONSTANT' to the 'wp-config.php' file with the raw value 'true'. + """ + + When I run `wp config list WP_CUSTOM_CONSTANT --strict --format=json` + Then STDOUT should contain: + """ + {"name":"WP_CUSTOM_CONSTANT","value":true,"type":"constant"} + """ + + Scenario: Fail when trying to add an existing constant + Given a WP install + + When I run `wp config add TEST_CONSTANT test_value` + Then STDOUT should be: + """ + Success: Added the constant 'TEST_CONSTANT' to the 'wp-config.php' file with the value 'test_value'. + """ + + When I try `wp config add TEST_CONSTANT another_value` + Then STDERR should be: + """ + Error: The constant 'TEST_CONSTANT' already exists in the 'wp-config.php' file. + """ + + Scenario: Fail when trying to add an existing variable + Given a WP install + + When I run `wp config add test_variable test_value --type=variable` + Then STDOUT should be: + """ + Success: Added the variable 'test_variable' to the 'wp-config.php' file with the value 'test_value'. + """ + + When I try `wp config add test_variable another_value --type=variable` + Then STDERR should be: + """ + Error: The variable 'test_variable' already exists in the 'wp-config.php' file. + """ + + @custom-config-file + Scenario: Add a new constant to wp-custom-config.php + Given an empty directory + And WP files + + When I run `wp config create {CORE_CONFIG_SETTINGS} --skip-check --config-file='wp-custom-config.php'` + Then STDOUT should contain: + """ + Generated 'wp-custom-config.php' file. + """ + + When I run `wp config add NEW_CONSTANT constant_value --config-file='wp-custom-config.php'` + Then STDOUT should be: + """ + Success: Added the constant 'NEW_CONSTANT' to the 'wp-custom-config.php' file with the value 'constant_value'. + """ + + When I run `wp config get NEW_CONSTANT --config-file='wp-custom-config.php'` + Then STDOUT should be: + """ + constant_value + """ + + Scenario: Additions can be properly placed in wp-config.php + Given a WP install + And a wp-config.php file: + """ + define( 'CONST_A', 'val-a' ); + /** ANCHOR */ + define( 'CONST_B', 'val-b' ); + require_once( ABSPATH . 'wp-settings.php' ); + """ + + When I run `wp config add SOME_NAME some_value --anchor="/** ANCHOR */" --placement=before --separator="\n"` + Then STDOUT should be: + """ + Success: Added the constant 'SOME_NAME' to the 'wp-config.php' file with the value 'some_value'. + """ + And the wp-config.php file should be: + """ + define( 'CONST_A', 'val-a' ); + define( 'SOME_NAME', 'some_value' ); + /** ANCHOR */ + define( 'CONST_B', 'val-b' ); + require_once( ABSPATH . 'wp-settings.php' ); + """ + + When I run `wp config add ANOTHER_NAME another_value --anchor="/** ANCHOR */" --placement=after --separator="\n"` + Then STDOUT should be: + """ + Success: Added the constant 'ANOTHER_NAME' to the 'wp-config.php' file with the value 'another_value'. + """ + And the wp-config.php file should be: + """ + define( 'CONST_A', 'val-a' ); + define( 'SOME_NAME', 'some_value' ); + /** ANCHOR */ + define( 'ANOTHER_NAME', 'another_value' ); + define( 'CONST_B', 'val-b' ); + require_once( ABSPATH . 'wp-settings.php' ); + """ diff --git a/features/config-update.feature b/features/config-update.feature new file mode 100644 index 00000000..f666f40e --- /dev/null +++ b/features/config-update.feature @@ -0,0 +1,188 @@ +Feature: Update or add a constant or variable in wp-config.php file + + Scenario: Update an existing constant in wp-config.php + Given a WP install + + When I run `wp config update DB_HOST db.example.com` + Then STDOUT should be: + """ + Success: Updated the constant 'DB_HOST' in the 'wp-config.php' file with the value 'db.example.com'. + """ + + When I run `wp config get DB_HOST` + Then STDOUT should be: + """ + db.example.com + """ + + Scenario: Add a new constant when it doesn't exist + Given a WP install + + When I run `wp config update NEW_CONSTANT constant_value` + Then STDOUT should be: + """ + Success: Added the constant 'NEW_CONSTANT' to the 'wp-config.php' file with the value 'constant_value'. + """ + + When I run `wp config get NEW_CONSTANT` + Then STDOUT should be: + """ + constant_value + """ + + Scenario: Update an existing constant then add it again + Given a WP install + + When I run `wp config update TEST_CONSTANT first_value` + Then STDOUT should be: + """ + Success: Added the constant 'TEST_CONSTANT' to the 'wp-config.php' file with the value 'first_value'. + """ + + When I run `wp config update TEST_CONSTANT second_value` + Then STDOUT should be: + """ + Success: Updated the constant 'TEST_CONSTANT' in the 'wp-config.php' file with the value 'second_value'. + """ + + When I run `wp config get TEST_CONSTANT` + Then STDOUT should be: + """ + second_value + """ + + Scenario: Update a variable with --type=variable + Given a WP install + + When I run `wp config update new_variable variable_value --type=variable` + Then STDOUT should be: + """ + Success: Added the variable 'new_variable' to the 'wp-config.php' file with the value 'variable_value'. + """ + + When I run `wp config update new_variable updated_value --type=variable` + Then STDOUT should be: + """ + Success: Updated the variable 'new_variable' in the 'wp-config.php' file with the value 'updated_value'. + """ + + When I run `wp config get new_variable` + Then STDOUT should be: + """ + updated_value + """ + + Scenario: Update raw values in wp-config.php + Given a WP install + + When I run `wp config update WP_DEBUG true --raw` + Then STDOUT should be: + """ + Success: Updated the constant 'WP_DEBUG' in the 'wp-config.php' file with the raw value 'true'. + """ + + When I run `wp config list WP_DEBUG --strict --format=json` + Then STDOUT should contain: + """ + {"name":"WP_DEBUG","value":true,"type":"constant"} + """ + + When I run `wp config update WP_DEBUG false --raw` + Then STDOUT should be: + """ + Success: Updated the constant 'WP_DEBUG' in the 'wp-config.php' file with the raw value 'false'. + """ + + When I run `wp config list WP_DEBUG --strict --format=json` + Then STDOUT should contain: + """ + {"name":"WP_DEBUG","value":false,"type":"constant"} + """ + + @custom-config-file + Scenario: Update a constant in wp-custom-config.php + Given an empty directory + And WP files + + When I run `wp config create {CORE_CONFIG_SETTINGS} --skip-check --config-file='wp-custom-config.php'` + Then STDOUT should contain: + """ + Generated 'wp-custom-config.php' file. + """ + + When I run `wp config update DB_HOST db.example.com --config-file='wp-custom-config.php'` + Then STDOUT should be: + """ + Success: Updated the constant 'DB_HOST' in the 'wp-custom-config.php' file with the value 'db.example.com'. + """ + + When I run `wp config get DB_HOST --config-file='wp-custom-config.php'` + Then STDOUT should be: + """ + db.example.com + """ + + Scenario: Ambiguous update requests throw errors + Given a WP install + + When I run `wp config update SOME_NAME some_value --type=constant` + Then STDOUT should be: + """ + Success: Added the constant 'SOME_NAME' to the 'wp-config.php' file with the value 'some_value'. + """ + + When I run `wp config update SOME_NAME some_value --type=variable` + Then STDOUT should be: + """ + Success: Added the variable 'SOME_NAME' to the 'wp-config.php' file with the value 'some_value'. + """ + + When I run `wp config list --fields=name,type SOME_NAME --strict` + Then STDOUT should be a table containing rows: + | name | type | + | SOME_NAME | constant | + | SOME_NAME | variable | + + When I try `wp config update SOME_NAME some_value` + Then STDERR should be: + """ + Error: Found both a constant and a variable 'SOME_NAME' in the 'wp-config.php' file. Use --type= to disambiguate. + """ + + Scenario: Update with placement options for new constants + Given a WP install + And a wp-config.php file: + """ + define( 'CONST_A', 'val-a' ); + /** ANCHOR */ + define( 'CONST_B', 'val-b' ); + require_once( ABSPATH . 'wp-settings.php' ); + """ + + When I run `wp config update SOME_NAME some_value --anchor="/** ANCHOR */" --placement=before --separator="\n"` + Then STDOUT should be: + """ + Success: Added the constant 'SOME_NAME' to the 'wp-config.php' file with the value 'some_value'. + """ + And the wp-config.php file should be: + """ + define( 'CONST_A', 'val-a' ); + define( 'SOME_NAME', 'some_value' ); + /** ANCHOR */ + define( 'CONST_B', 'val-b' ); + require_once( ABSPATH . 'wp-settings.php' ); + """ + + When I run `wp config update SOME_NAME updated_value --anchor="/** ANCHOR */" --placement=before --separator="\n"` + Then STDOUT should be: + """ + Success: Updated the constant 'SOME_NAME' in the 'wp-config.php' file with the value 'updated_value'. + """ + And the wp-config.php file should be: + """ + define( 'CONST_A', 'val-a' ); + define( 'SOME_NAME', 'updated_value' ); + /** ANCHOR */ + define( 'CONST_B', 'val-b' ); + require_once( ABSPATH . 'wp-settings.php' ); + """ diff --git a/src/Config_Command.php b/src/Config_Command.php index 9fd9d0b7..5b01f6a1 100644 --- a/src/Config_Command.php +++ b/src/Config_Command.php @@ -34,6 +34,14 @@ * $ wp config get table_prefix * wp_ * + * # Add a new constant to the wp-config.php file. + * $ wp config add WP_DEBUG true --raw + * Success: Added the constant 'WP_DEBUG' to the 'wp-config.php' file with the raw value 'true'. + * + * # Update or add a constant to the wp-config.php file. + * $ wp config update WP_DEBUG false --raw + * Success: Updated the constant 'WP_DEBUG' in the 'wp-config.php' file with the raw value 'false'. + * * # Set the WP_DEBUG constant to true. * $ wp config set WP_DEBUG true --raw * Success: Updated the constant 'WP_DEBUG' in the 'wp-config.php' file with the raw value 'true'. @@ -739,6 +747,255 @@ public function set( $args, $assoc_args ) { WP_CLI::success( $message ); } + /** + * Adds a new constant or variable to the wp-config.php file. + * + * ## OPTIONS + * + * + * : Name of the wp-config.php constant or variable. + * + * + * : Value to set the wp-config.php constant or variable to. + * + * [--raw] + * : Place the value into the wp-config.php file as is, instead of as a quoted string. + * + * [--anchor=] + * : Anchor string where additions of new values are anchored around. + * Defaults to "/* That's all, stop editing!". + * The special case "EOF" string uses the end of the file as the anchor. + * + * [--placement=] + * : Where to place the new values in relation to the anchor string. + * --- + * default: 'before' + * options: + * - before + * - after + * --- + * + * [--separator=] + * : Separator string to put between an added value and its anchor string. + * The following escape sequences will be recognized and properly interpreted: '\n' => newline, '\r' => carriage return, '\t' => tab. + * Defaults to a single EOL ("\n" on *nix and "\r\n" on Windows). + * + * [--type=] + * : Type of the config value to add. Defaults to 'constant'. + * --- + * default: constant + * options: + * - constant + * - variable + * --- + * + * [--config-file=] + * : Specify the file path to the config file to be modified. Defaults to the root of the + * WordPress installation and the filename "wp-config.php". + * + * ## EXAMPLES + * + * # Add the WP_DEBUG constant to the wp-config.php file. + * $ wp config add WP_DEBUG true --raw + * Success: Added the constant 'WP_DEBUG' to the 'wp-config.php' file with the raw value 'true'. + * + * @when before_wp_load + */ + public function add( $args, $assoc_args ) { + $path = $this->get_config_path( $assoc_args ); + $wp_config_file_name = basename( $path ); + list( $name, $value ) = $args; + + /** + * @var string $type + */ + $type = Utils\get_flag_value( $assoc_args, 'type', 'constant' ); + + $options = []; + + $option_flags = [ + 'raw' => false, + 'anchor' => null, + 'placement' => null, + 'separator' => null, + ]; + + foreach ( $option_flags as $option => $default ) { + /** + * @var string|null $option_value + */ + $option_value = Utils\get_flag_value( $assoc_args, $option, $default ); + if ( null !== $option_value ) { + /** + * @var array $options + */ + $options[ $option ] = $option_value; + if ( 'separator' === $option ) { + $options['separator'] = $this->parse_separator( $options['separator'] ); + } + } + } + + // add command always adds, so we set the 'add' option to true + $options['add'] = true; + + try { + $config_transformer = new WPConfigTransformer( $path ); + + // Check if the constant/variable already exists + if ( $config_transformer->exists( $type, $name ) ) { + WP_CLI::error( "The {$type} '{$name}' already exists in the '{$wp_config_file_name}' file." ); + } + + $config_transformer->update( $type, $name, $value, $options ); + + } catch ( Exception $exception ) { + WP_CLI::error( "Could not process the '{$wp_config_file_name}' transformation.\nReason: {$exception->getMessage()}" ); + } + + $raw = $options['raw'] ? 'raw ' : ''; + $message = "Added the {$type} '{$name}' to the '{$wp_config_file_name}' file with the {$raw}value '{$value}'."; + + WP_CLI::success( $message ); + } + + /** + * Updates or adds a constant or variable in the wp-config.php file. + * + * This command will add the constant or variable if it doesn't exist, or update it if it does. + * + * ## OPTIONS + * + * + * : Name of the wp-config.php constant or variable. + * + * + * : Value to set the wp-config.php constant or variable to. + * + * [--raw] + * : Place the value into the wp-config.php file as is, instead of as a quoted string. + * + * [--anchor=] + * : Anchor string where additions of new values are anchored around. + * Defaults to "/* That's all, stop editing!". + * The special case "EOF" string uses the end of the file as the anchor. + * + * [--placement=] + * : Where to place the new values in relation to the anchor string. + * --- + * default: 'before' + * options: + * - before + * - after + * --- + * + * [--separator=] + * : Separator string to put between an added value and its anchor string. + * The following escape sequences will be recognized and properly interpreted: '\n' => newline, '\r' => carriage return, '\t' => tab. + * Defaults to a single EOL ("\n" on *nix and "\r\n" on Windows). + * + * [--type=] + * : Type of the config value to update. Defaults to 'all'. + * --- + * default: all + * options: + * - constant + * - variable + * - all + * --- + * + * [--config-file=] + * : Specify the file path to the config file to be modified. Defaults to the root of the + * WordPress installation and the filename "wp-config.php". + * + * ## EXAMPLES + * + * # Update or add the WP_DEBUG constant to the wp-config.php file. + * $ wp config update WP_DEBUG true --raw + * Success: Updated the constant 'WP_DEBUG' in the 'wp-config.php' file with the raw value 'true'. + * + * @when before_wp_load + */ + public function update( $args, $assoc_args ) { + $path = $this->get_config_path( $assoc_args ); + $wp_config_file_name = basename( $path ); + list( $name, $value ) = $args; + + /** + * @var string $type + */ + $type = Utils\get_flag_value( $assoc_args, 'type', 'all' ); + + $options = []; + + $option_flags = [ + 'raw' => false, + 'anchor' => null, + 'placement' => null, + 'separator' => null, + ]; + + foreach ( $option_flags as $option => $default ) { + /** + * @var string|null $option_value + */ + $option_value = Utils\get_flag_value( $assoc_args, $option, $default ); + if ( null !== $option_value ) { + /** + * @var array $options + */ + $options[ $option ] = $option_value; + if ( 'separator' === $option ) { + $options['separator'] = $this->parse_separator( $options['separator'] ); + } + } + } + + // update command always adds if not exists, so we set the 'add' option to true + $options['add'] = true; + + $adding = false; + try { + $config_transformer = new WPConfigTransformer( $path ); + + switch ( $type ) { + case 'all': + $has_constant = $config_transformer->exists( 'constant', $name ); + $has_variable = $config_transformer->exists( 'variable', $name ); + if ( $has_constant && $has_variable ) { + WP_CLI::error( "Found both a constant and a variable '{$name}' in the '{$wp_config_file_name}' file. Use --type= to disambiguate." ); + } + if ( ! $has_constant && ! $has_variable ) { + $type = 'constant'; + $adding = true; + } else { + $type = $has_constant ? 'constant' : 'variable'; + } + break; + case 'constant': + case 'variable': + if ( ! $config_transformer->exists( $type, $name ) ) { + $adding = true; + } + break; + } + + $config_transformer->update( $type, $name, $value, $options ); + + } catch ( Exception $exception ) { + WP_CLI::error( "Could not process the '{$wp_config_file_name}' transformation.\nReason: {$exception->getMessage()}" ); + } + + $raw = $options['raw'] ? 'raw ' : ''; + if ( $adding ) { + $message = "Added the {$type} '{$name}' to the '{$wp_config_file_name}' file with the {$raw}value '{$value}'."; + } else { + $message = "Updated the {$type} '{$name}' in the '{$wp_config_file_name}' file with the {$raw}value '{$value}'."; + } + + WP_CLI::success( $message ); + } + /** * Deletes a specific constant or variable from the wp-config.php file. *