* : The new permalink structure to apply.
*
* [--category-base=]
* : Set the base for category permalinks, i.e. '/category/'.
*
* [--tag-base=]
* : Set the base for tag permalinks, i.e. '/tag/'.
*
* [--hard]
* : Perform a hard flush - update `.htaccess` rules as well as rewrite rules in database.
*
* ## EXAMPLES
*
* $ wp rewrite structure '/%year%/%monthnum%/%postname%/'
* Success: Rewrite structure set.
*/
public function structure( $args, $assoc_args ) {
global $wp_rewrite;
// copypasta from /wp-admin/options-permalink.php
$blog_prefix = '';
$prefix = $blog_prefix;
if ( is_multisite() && ! is_subdomain_install() && is_main_site() ) {
$blog_prefix = '/blog';
}
$permalink_structure = ( 'default' === $args[0] ) ? '' : $args[0];
if ( ! empty( $permalink_structure ) ) {
$permalink_structure = preg_replace( '#/+#', '/', '/' . str_replace( '#', '', $permalink_structure ) );
if ( $prefix && $blog_prefix ) {
$permalink_structure = $prefix . preg_replace( '#^/?index\.php#', '', $permalink_structure );
} else {
$permalink_structure = $blog_prefix . $permalink_structure;
}
}
$wp_rewrite->set_permalink_structure( $permalink_structure );
// Update category or tag bases
if ( isset( $assoc_args['category-base'] ) ) {
$category_base = $assoc_args['category-base'];
if ( ! empty( $category_base ) ) {
$category_base = $blog_prefix . preg_replace( '#/+#', '/', '/' . str_replace( '#', '', $category_base ) );
}
$wp_rewrite->set_category_base( $category_base );
}
if ( isset( $assoc_args['tag-base'] ) ) {
$tag_base = $assoc_args['tag-base'];
if ( ! empty( $tag_base ) ) {
$tag_base = $blog_prefix . preg_replace( '#/+#', '/', '/' . str_replace( '#', '', $tag_base ) );
}
$wp_rewrite->set_tag_base( $tag_base );
}
// make sure we detect mod_rewrite if configured in apache_modules in config
self::apache_modules();
WP_CLI::success( 'Rewrite structure set.' );
// Launch a new process to flush rewrites because core expects flush
// to happen after rewrites are set
$new_assoc_args = [];
$cmd = 'rewrite flush';
if ( Utils\get_flag_value( $assoc_args, 'hard' ) ) {
$cmd .= ' --hard';
$new_assoc_args['hard'] = true;
if ( ! in_array( 'mod_rewrite', (array) WP_CLI::get_config( 'apache_modules' ), true ) ) {
WP_CLI::warning( 'Regenerating a .htaccess file requires special configuration. See usage docs.' );
}
}
$process_run = WP_CLI::runcommand( $cmd );
if ( ! empty( $process_run->stderr ) ) {
// Strip "Warning: "
WP_CLI::warning( substr( $process_run->stderr, 9 ) );
}
}
/**
* Gets a list of the current rewrite rules.
*
* ## OPTIONS
*
* [--match=]
* : Show rewrite rules matching a particular URL.
*
* [--source=]
* : Show rewrite rules from a particular source.
*
* [--fields=]
* : Limit the output to specific fields. Defaults to match,query,source.
*
* [--format=]
* : Render output in a particular format.
* ---
* default: table
* options:
* - table
* - csv
* - json
* - count
* - yaml
* ---
*
* ## EXAMPLES
*
* $ wp rewrite list --format=csv
* match,query,source
* ^wp-json/?$,index.php?rest_route=/,other
* ^wp-json/(.*)?,index.php?rest_route=/$matches[1],other
* category/(.+?)/feed/(feed|rdf|rss|rss2|atom)/?$,index.php?category_name=$matches[1]&feed=$matches[2],category
* category/(.+?)/(feed|rdf|rss|rss2|atom)/?$,index.php?category_name=$matches[1]&feed=$matches[2],category
* category/(.+?)/embed/?$,index.php?category_name=$matches[1]&embed=true,category
*
* @subcommand list
*/
public function list_( $args, $assoc_args ) {
global $wp_rewrite;
$rules = get_option( 'rewrite_rules' );
if ( ! $rules ) {
$rules = [];
WP_CLI::warning( 'No rewrite rules.' );
}
self::check_skip_plugins_themes();
$defaults = [
'source' => '',
'match' => '',
'format' => 'table',
'fields' => 'match,query,source',
];
$assoc_args = array_merge( $defaults, $assoc_args );
if ( ! empty( $assoc_args['match'] ) ) {
if ( 0 === stripos( $assoc_args['match'], 'http://' )
|| 0 === stripos( $assoc_args['match'], 'https://' ) ) {
$bits = WP_CLI\Utils\parse_url( $assoc_args['match'] );
$assoc_args['match'] = ( isset( $bits['path'] ) ? $bits['path'] : '' )
. ( isset( $bits['query'] ) ? '?' . $bits['query'] : '' );
}
}
$rewrite_rules_by_source = [];
$rewrite_rules_by_source['post'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->permalink_structure, EP_PERMALINK );
$rewrite_rules_by_source['date'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->get_date_permastruct(), EP_DATE );
$rewrite_rules_by_source['root'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->root . '/', EP_ROOT );
$rewrite_rules_by_source['comments'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->root . $wp_rewrite->comments_base, EP_COMMENTS, true, true, true, false );
$rewrite_rules_by_source['search'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->get_search_permastruct(), EP_SEARCH );
$rewrite_rules_by_source['author'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->get_author_permastruct(), EP_AUTHORS );
$rewrite_rules_by_source['page'] = $wp_rewrite->page_rewrite_rules();
// Extra permastructs including tags, categories, etc.
foreach ( $wp_rewrite->extra_permastructs as $permastructname => $permastruct ) {
if ( is_array( $permastruct ) ) {
$rewrite_rules_by_source[ $permastructname ] = $wp_rewrite->generate_rewrite_rules( $permastruct['struct'], $permastruct['ep_mask'], $permastruct['paged'], $permastruct['feed'], $permastruct['forcomments'], $permastruct['walk_dirs'], $permastruct['endpoints'] );
} else {
$rewrite_rules_by_source[ $permastructname ] = $wp_rewrite->generate_rewrite_rules( $permastruct, EP_NONE );
}
}
// Apply the filters used in core just in case
foreach ( $rewrite_rules_by_source as $source => $source_rules ) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Can't prefix dynamic hooks here, calling hooks for custom permastructs.
$rewrite_rules_by_source[ $source ] = apply_filters( $source . '_rewrite_rules', $source_rules );
if ( 'post_tag' === $source ) {
if ( Utils\wp_version_compare( '3.1.0', '>=' ) ) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Calling native WordPress hook.
$rewrite_rules_by_source[ $source ] = apply_filters( 'post_tag_rewrite_rules', $source_rules );
} else {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Calling native WordPress hook.
$rewrite_rules_by_source[ $source ] = apply_filters( 'tag_rewrite_rules', $source_rules );
}
}
}
$rule_list = [];
foreach ( $rules as $match => $query ) {
if ( ! empty( $assoc_args['match'] ) && ! preg_match( "!^$match!", trim( $assoc_args['match'], '/' ) ) ) {
continue;
}
$source = 'other';
foreach ( $rewrite_rules_by_source as $rules_source => $source_rules ) {
if ( array_key_exists( $match, $source_rules ) ) {
$source = $rules_source;
}
}
if ( ! empty( $assoc_args['source'] ) && $source !== $assoc_args['source'] ) {
continue;
}
$rule_list[] = compact( 'match', 'query', 'source' );
}
Utils\format_items( $assoc_args['format'], $rule_list, explode( ',', $assoc_args['fields'] ) );
}
/**
* Exposes apache modules if present in config
*
* Implementation Notes: This function exposes a global function
* apache_get_modules and also sets the $is_apache global variable.
*
* This is so that flush_rewrite_rules will actually write out the
* .htaccess file for apache WordPress installations. There is a check
* to see:
*
* 1. if the $is_apache variable is set.
* 2. if the mod_rewrite module is returned from the apache_get_modules
* function.
*
* To get this to work with wp-cli you'll need to add the mod_rewrite module
* to your config.yml. For example
*
* ```
* apache_modules:
* - mod_rewrite
* ```
*
* If this isn't done then the .htaccess rewrite rules won't be flushed out
* to disk.
*/
private static function apache_modules() {
$mods = WP_CLI::get_config( 'apache_modules' );
if ( ! empty( $mods ) && ! function_exists( 'apache_get_modules' ) ) {
global $is_apache;
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$is_apache = true;
// needed for get_home_path() and .htaccess location
$_SERVER['SCRIPT_FILENAME'] = ABSPATH;
function apache_get_modules() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
return WP_CLI::get_config( 'apache_modules' );
}
}
}
/**
* Displays a warning if --skip-plugins or --skip-themes are in use.
*
* Skipping the loading of plugins or themes can mean some rewrite rules
* are unregistered, which may cause erroneous behavior.
*/
private static function check_skip_plugins_themes() {
$skipped = [];
if ( WP_CLI::get_config( 'skip-plugins' ) ) {
$skipped[] = 'plugins';
}
if ( WP_CLI::get_config( 'skip-themes' ) ) {
$skipped[] = 'themes';
}
if ( empty( $skipped ) ) {
return;
}
$skipped = implode( ' and ', $skipped );
WP_CLI::warning( sprintf( "Some rewrite rules may be missing because %s weren't loaded.", $skipped ) );
}
}
WP_CLI::add_command( 'rewrite', 'Rewrite_Command' );