0

I have a php file named test.php that outputs the page querystring parameter.

I want to use add_rewrite_rule to serve my php file, while the WordPress pages which exist already or will be created should have priority: for example, if I catch bar, baz and foo-* in my regex, and then I add a page named baz (living under the /baz url) and another one named /foo-bar, both should be served from the database (by WordPress), not by my php file.

This is my custom rewrite in functions.php:

function custom_rewrite() {
  add_rewrite_rule( '^(bar|baz|foo-.*)', 'test.php?page=$1', 'top');
}
add_action('init', 'custom_rewrite');

This works fine: it catches bar, baz, foo-* urls. But when I create a page named bar it still uses my test.php to serve the content.

I tried to use bottom instead of top as the third argument in the add_rewrite_rule call, but it does not make any change.

After each edit in functions.php I made sure to click the Save changes button in Permalink Settings.

How can I fix this?

I want something like this:

Client ---- [does the WP page exist?] --- YES -> Serve it from DB
                        \----- NO -- [is the url matched by my regex?]
                                         |            |
                                        YES           NO
                                         |            |
                                         v            |
                                      test.php        404

Obviously, if there are other rules, check them first and then show 404.

  • you have an external rule, it's always going to win over internal rules. you have to point rules to index.php to get an internal rule. – Milo Mar 14 '16 at 5:45
  • @Milo How can I do that? I'm not sure what you mean. – Ionică Bizău Mar 14 '16 at 6:10
2

When you point a rule to anything other than index.php, it gets interpreted as external and written to the .htaccess file. After external rules are parsed, requests get directed to WordPress, which parses the internal rules in php. This is the internal version:

function custom_rewrite() {
  add_rewrite_rule(
    '^(bar|baz|foo-.*)',
    'index.php?my_var=$matches[1]',
    'top'
  );
}
add_action( 'init', 'custom_rewrite' );

You'll also need to register the custom query var you use in the rule. Note that page is already in use by WordPress

function wpd_query_vars( $qvars ) {
  $qvars[] = 'my_var';
  return $qvars;
}
add_filter( 'query_vars', 'wpd_query_vars' , 10, 1 );

This alone won't do much, you'll have to check if a page that matches your rule exists, and load your file if it doesn't. We can do that on the parse_request action.

function wpd_parse_request( $request ) {
    // if the rule was matched, the query var will be set
    if( isset( $request->query_vars['my_var'] ) ){
        // check if a page exists, reset query vars to load that page if it does
        if( get_page_by_path( $request->query_vars['my_var'] ) ){
            $request->query_vars['pagename'] = $request->query_vars['my_var'];
            unset( $request->query_vars['my_var'] );
        // else load the file
        } else {
            include 'test.php';
            die();
        }
    }
    return $request;
}
add_action( 'parse_request', 'wpd_parse_request' );
  • Oh, thanks for the nice answer! It worked. You have a missing semicolon on the include 'test.php' line. – Ionică Bizău Mar 14 '16 at 8:17
  • Can you improve this to handle posts as well? For example, if there is a post that exists on that url, render it. – Ionică Bizău Apr 1 '16 at 3:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.