customization - add_feed rewrite overwriting standard permalinks

admin2025-04-19  0

By doing a simple:

add_feed( 'schedule', 'my_schedule_feed' );

I have created a feed at /feed/schedule/... easy.

But when I go to /schedule/ (which is a page) it is now showing the feed!

Looking at the rewrite rules I see this:

[feed/(feed|rdf|rss|rss2|atom|schedule)/?$] => index.php?&feed=$matches[1]
[(feed|rdf|rss|rss2|atom|schedule)/?$] => index.php?&feed=$matches[1]

This seems really odd. Why would WordPress overwrite existing page rules with the feed, instead of just using only the /feed/ base as expected?

Update: I have tried this as suggested by this answer, and flushing the permalinks, but while it shows up in the rewrites, it does not show the feed at /feed/schedule/

function add_feed_rewrites( $wp_rewrite ) {
    $feednames = array( 'schedule', 'calendar' );
    $feeds = implode( '|', $feednames);
    $feed_rule = array(
        'feed/(' . $feeds . ')/?$' => 'index.php?&feed=$matches[1]'
    );
    $wp_rewrite->rules = array_merge( $wp_rewrite->rules, $feed_rule );    
}
add_filter( 'generate_rewrite_rules', 'add_feed_rewrites' );

add_action( 'do_feed_schedule', 'my_schedule', 10, 2 );
add_action( 'do_feed_calendar', 'my_calendar', 10, 2 );

Any ideas on how to get this working without the initial conflict?

By doing a simple:

add_feed( 'schedule', 'my_schedule_feed' );

I have created a feed at /feed/schedule/... easy.

But when I go to /schedule/ (which is a page) it is now showing the feed!

Looking at the rewrite rules I see this:

[feed/(feed|rdf|rss|rss2|atom|schedule)/?$] => index.php?&feed=$matches[1]
[(feed|rdf|rss|rss2|atom|schedule)/?$] => index.php?&feed=$matches[1]

This seems really odd. Why would WordPress overwrite existing page rules with the feed, instead of just using only the /feed/ base as expected?

Update: I have tried this as suggested by this answer, and flushing the permalinks, but while it shows up in the rewrites, it does not show the feed at /feed/schedule/

function add_feed_rewrites( $wp_rewrite ) {
    $feednames = array( 'schedule', 'calendar' );
    $feeds = implode( '|', $feednames);
    $feed_rule = array(
        'feed/(' . $feeds . ')/?$' => 'index.php?&feed=$matches[1]'
    );
    $wp_rewrite->rules = array_merge( $wp_rewrite->rules, $feed_rule );    
}
add_filter( 'generate_rewrite_rules', 'add_feed_rewrites' );

add_action( 'do_feed_schedule', 'my_schedule', 10, 2 );
add_action( 'do_feed_calendar', 'my_calendar', 10, 2 );

Any ideas on how to get this working without the initial conflict?

Share Improve this question edited Oct 30, 2019 at 16:28 majick asked Oct 30, 2019 at 16:06 majickmajick 5,1412 gold badges18 silver badges30 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

If you don't want to use add_feed(), then you can use add_rewrite_rule():

add_action( 'init', function () {
    add_rewrite_rule(
        '^feed/(schedule|calendar)/?$',
        'index.php?feed=$matches[1]',
        'top'
    );
} );

add_action( 'do_feed_schedule', function () {
    header( 'Content-Type: text/plain' ); // dummy type just for testing
    echo 'Yay, it works!';
} );

But regarding the original question about add_feed()..

Why would WordPress overwrite existing page rules with the feed, instead of just using only the /feed/ base as expected?

So that example/schedule would serve your custom feed, just like standard feed URLs such as example/feed. But WordPress does create the (proper) rules having the feed/ base for custom feed URLs like example/feed/schedule.

Therefore you shouldn't use schedule as the slug of your Page, or if you have/need to, then you can alter the feed rewrite so that the example/schedule will serve the Page instead of your feed:

  • If you know the exact rule/RegEx:

    add_filter( 'rewrite_rules_array', function ( $rules ) {
        $rules2 = [];
        foreach ( $rules as $regex => $query ) {
            if ( '(feed|rdf|rss|rss2|atom|schedule)/?$' === $regex ) { // it has 'schedule'
                $rules2['(feed|rdf|rss|rss2|atom)/?$'] = $query;       // so let's remove it
            } else {
                $rules2[ $regex ] = $query;
            }
        }
        return $rules2;
    } );
    
  • Otherwise, try something dynamic like this:

    add_filter( 'rewrite_rules_array', function ( $rules ) {
        global $wp_rewrite;
    
        $feeds = $wp_rewrite->feeds;         // original feeds
        $pages = [ 'schedule', 'calendar' ]; // page slugs list
        $feeds2 = array_diff( $feeds, $pages );
    
        $rules2 = [];
        $feeds = implode( '|', $feeds );
        $feeds2 = implode( '|', $feeds2 );
    
        foreach ( $rules as $regex => $query ) {
            if ( '(' . $feeds . ')/?$' === $regex ) {
                $rules2[ '(' . $feeds2 . ')/?$' ] = $query;
            } else {
                $rules2[ $regex ] = $query;
            }
        }
    
        return $rules2;
    } );
    

Note that I use the foreach so that the rule stays in its original position. And another possible option is that just move the rule to a position after the Page rewrite rules. But I haven't yet tried that and the above code should be enough as a workaround..


And if I'm not mistaken, here's the code which WordPress use to generate the rule that's being altered in the above function:

// Create query for /(feed|atom|rss|rss2|rdf) (see comment near creation of $feedregex).
$feedmatch2 = $match . $feedregex2;
$feedquery2 = $feedindex . '?' . $query . '&feed=' . $this->preg_index( $num_toks + 1 );

See the source on Trac.

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745041516a281570.html

最新回复(0)