I’m trying to dynamically add a rewrite rule that allows one to access any page under an alternate permalink structure of my choosing.
So for example, if the alternate permalink structure includes the slug foo/
in the first postion, I want to add a rewrite rule such that:
example.com/foo/my-page
rewrites to example.com/my-page
example.com/foo/my-cool/blog/post
rewrites to example.com/my-cool/blog/post
So basically, no matter whether the query is for a page, a post, or any other type of custom content, I want this content to be accessible under the /foo/
slug as well as the original non-foo URL structure.
I have written a working function that accomplishes this by..
- reading the URI
- removing the slug from he URI
- looping through all the existing rewrite rules
- matching the URI with the slug removed to an existing rewrite rule
- grabbing the matching groups for matched the rewrite rule
- compiling a new rewrite rule with the slugged URI as the source and the matched rewrite rule endpoint with matching groups replaced with their values as the destination.
Here is the code:
add_action('generate_rewrite_rules', function($wp_rewrite) {
$slug = 'foo';
// get the request URI
$uri = $_SERVER['REQUEST_URI'];
// determine whether URI has slug in the first position
if(preg_match('/^(/' . $slug . ')/', $uri)){
// get the base URI by removing the slug
$base_uri = str_replace('/goo','',$uri);
// loop through existing rewrite rules
foreach($wp_rewrite->rules as $src => $dest){
// find the rewrite rule for which the base URI would have matched
$regex_to_match = '/' . str_replace('/','/',$src) . '/';
preg_match_all($regex_to_match, $base_uri, $matches, PREG_SET_ORDER);
if(count($matches) > 0){
// get the specific matching groups
$matches = $matches[0];
// compile valid regex from URI with slug to create new rewrite source
$new_src = ($uri[0] == '/' ? substr($uri, 1) : $uri) . '?$';
// replace match variables with their string values to create new rewrite destination
for($i=1; $i<count($matches)+1; $i++){
$replacement = isset($matches[$i]) ? $matches[$i] : '';
$dest = str_replace('$matches[' . $i . ']', $replacement, $dest);
}
$new_dest = $dest;
// add new rewrite rule to $wp_rewrite rules array
$wp_rewrite->rules[$new_src] = $new_dest;
// add the write rule
add_rewrite_rule($new_src,$new_dest,'top');
break;
}
}
}
return $wp_rewrite->rules;
});
This works but for some reason the generate_rewrite_rules
hook is not run on every request and the callback function never fires. It only seems to run one time after I flush the permalinks. I have the Query Monitor plugin installed and am able to seem my new rewrite and verify that it is matched, but after I reload the page, it is no longer there.
I suspect it has to do with caching, and the hook is not firing after the rewrites have been initially generated.
How can I force the generate_rewrite_rules
hook to fire on every request, such that my action is run?