Repository navigation
Conversation
Adds wp_random_content_redirect(), hooked on template_redirect, which redirects ?random requests to a randomly selected published post. Disabled by default; enable with the enable_random_content_redirect filter. Picks via COUNT plus a random OFFSET instead of ORDER BY RAND(). See https://core.trac.wordpress.org/ticket/64498 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Check for the random parameter before evaluating the enable filter so disabled sites pay no filter lookup per request. Validate post type viewability unconditionally, prime the post cache for get_permalink(), skip the second query when the random offset is zero, and drop test teardown that the test framework already guarantees. See https://core.trac.wordpress.org/ticket/64498 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
ellatrix
left a comment
There was a problem hiding this comment.
Just some nits, leaving some time for others to see this and can commit if you're ready.
| } | ||
|
|
||
| // Temporary redirect, so clients do not cache the randomly picked target. | ||
| wp_safe_redirect( $link, 302 ); |
There was a problem hiding this comment.
Should we also call nocache_headers?
There was a problem hiding this comment.
I suppose the intent here is to not cache the redirection (hence 302), while allowing clients to cache the resolved page (i.e. the randomly selected post). No?
There was a problem hiding this comment.
Wouldn't it cache based on the URL? I'm not a page cache expert, hence my comment being formatted as a question 🙂
There was a problem hiding this comment.
Thanks both for the comments. I ended up adding a nocache_headers as we actually want both as much as possible:
- the 302 makes the redirect non-permanent
- stop intermediary caches (CDNs, full-page caches) from pinning the redirect, which would make ?random return the same post for everyone
Thanks! Hadn't considered this properly
| if ( isset( $_GET['random_cat_id'] ) ) { | ||
| $cat_id = absint( wp_unslash( $_GET['random_cat_id'] ) ); | ||
|
|
||
| if ( $cat_id < 1 ) { |
There was a problem hiding this comment.
absint always returns positive? Should it be (int) instead maybe?
There was a problem hiding this comment.
Great catch! I changed this and also on the same note changed the test that checked the negative value as it was generally failing (as it should) but mostly for the wrong reasons.
changed both on the last commit
| } | ||
|
|
||
| public function test_head_request_redirects() { | ||
| $_SERVER['REQUEST_METHOD'] = 'HEAD'; |
There was a problem hiding this comment.
I had the same question about superglobals and hooks, but I then saw that everything is saved and restored:
wordpress-develop/tests/phpunit/includes/abstract-testcase.php
Lines 119 to 123 in 2b2b6a9
There was a problem hiding this comment.
Oh wait, no, $_SERVER isn't included here, but my brain really wanted it to be:
wordpress-develop/tests/phpunit/includes/abstract-testcase.php
Lines 241 to 246 in 2b2b6a9
There was a problem hiding this comment.
Thanks for the second check @mcsf - if I am not mistaken $_SERVER variables are cleaned later on in (assuming WP_RUN_CORE_TESTS is set):
wordpress-develop/tests/phpunit/includes/functions.php
Lines 24 to 35 in 2b2b6a9
Would you prefer if I added a tear_down anyway?
There was a problem hiding this comment.
Thanks, @mlaetitia! OK, that looks good then, might not need explicit tearing down.
(I suggest verifying locally that the global is correctly reset, for good measure, but analytically it looks good.)
| return; | ||
| } | ||
|
|
||
| if ( isset( $_SERVER['REQUEST_METHOD'] ) && ! in_array( strtoupper( $_SERVER['REQUEST_METHOD'] ), array( 'GET', 'HEAD' ), true ) ) { |
There was a problem hiding this comment.
Not sure if by design, but if $_SERVER['REQUEST_METHOD'] isn't set, it will not return early.
There was a problem hiding this comment.
Generally it wouldn't (maybe some CLI scenarios?) , but I rewrote this to explicitly accept only GET or HEAD.
Note: I didn't include the new QUERY method since Core doesn't yet support it (so can't imagine a scenario where it would be sent)
| } | ||
|
|
||
| public function test_head_request_redirects() { | ||
| $_SERVER['REQUEST_METHOD'] = 'HEAD'; |
There was a problem hiding this comment.
I had the same question about superglobals and hooks, but I then saw that everything is saved and restored:
wordpress-develop/tests/phpunit/includes/abstract-testcase.php
Lines 119 to 123 in 2b2b6a9
| } | ||
|
|
||
| public function test_head_request_redirects() { | ||
| $_SERVER['REQUEST_METHOD'] = 'HEAD'; |
There was a problem hiding this comment.
Oh wait, no, $_SERVER isn't included here, but my brain really wanted it to be:
wordpress-develop/tests/phpunit/includes/abstract-testcase.php
Lines 241 to 246 in 2b2b6a9
|
@desrosj nudging here for when you have a chance to review. |
Add opt-in support for random content redirects, so that a page request with the `?random` query argument set will return a 302 redirect to a randomly selected published post.
The pool of eligible posts may be filtered with two additional query arguments:
- `random_post_type`
- `random_cat_id`
The feature may be opted into using:
{{{
add_filter( 'enable_random_content_redirect', '__return_true' );
}}}
Developed in: #13022
Props mlaetitia.
Fixes #64498.
git-svn-id: https://develop.svn.wordpress.org/trunk@63767 602fd350-edb4-49c9-b593-d223f7449a82
Add opt-in support for random content redirects, so that a page request with the `?random` query argument set will return a 302 redirect to a randomly selected published post.
The pool of eligible posts may be filtered with two additional query arguments:
- `random_post_type`
- `random_cat_id`
The feature may be opted into using:
{{{
add_filter( 'enable_random_content_redirect', '__return_true' );
}}}
Developed in: WordPress/wordpress-develop#13022
Props mlaetitia.
Fixes #64498.
Built from https://develop.svn.wordpress.org/trunk@63767
git-svn-id: http://core.svn.wordpress.org/trunk@62939 1a063a9b-81f0-0310-95a4-ce76da25c4cd
| wp_safe_redirect( $link, 302 ); | ||
| exit; |
There was a problem hiding this comment.
To facilitate unit test coverage of this line, this could instead be:
| wp_safe_redirect( $link, 302 ); | |
| exit; | |
| if ( wp_safe_redirect( $link, 302 ) ) { | |
| exit; | |
| } |
| */ | ||
| function wp_random_content_redirect() { | ||
| // The `random` parameter is a flag, present with or without a value. | ||
| if ( ! isset( $_GET['random'] ) ) { |
There was a problem hiding this comment.
Instead of reading from $_GET, shouldn't random be added to \WP::$public_query_vars and then here it can do:
| if ( ! isset( $_GET['random'] ) ) { | |
| if ( ! get_query_var( 'random' ) ) { |
| return; | ||
| } | ||
|
|
||
| $link = get_permalink( $query->posts[0] ); |
There was a problem hiding this comment.
Since plugins might filter the the_posts filter to remove items from the array, it may no longer be a list and $query->posts[0] could be undefined. It would be better to use array_first():
| $link = get_permalink( $query->posts[0] ); | |
| $link = get_permalink( array_first( $query->posts ) ); |
|
|
||
| $post_type = 'post'; | ||
|
|
||
| if ( isset( $_GET['random_post_type'] ) ) { |
There was a problem hiding this comment.
Also, this should ideally check if $_GET['random_post_type'] is a string. Someone could make a request to a page like ?random=1&random_post_type[]=error and then $_GET['random_post_type'] here would be array( 'error' ). This array would get passed into sanitize_key() as an array, which would then return an empty string so that avoids a fatal error. However, the sanitize_key filter includes a second param with the original key passed, so a callback could fatal if it is expecting a string but an array is passed.
Related: Core-60745.
| if ( isset( $_GET['random_post_type'] ) ) { | |
| if ( isset( $_GET['random_post_type'] ) && is_string( $_GET['random_post_type'] ) ) { |
| 'update_post_meta_cache' => false, | ||
| ); | ||
|
|
||
| if ( isset( $_GET['random_cat_id'] ) ) { |
There was a problem hiding this comment.
Same as above about using get_query_var() but also for type safety, which is less important here because of the (int) cast:
| if ( isset( $_GET['random_cat_id'] ) ) { | |
| if ( isset( $_GET['random_cat_id'] ) && is_string( $_GET['random_cat_id'] ) ) { |
| /** | ||
| * Filters the random content redirect URL. | ||
| * | ||
| * Returning a falsey value cancels the redirect. | ||
| * | ||
| * @since 7.2.0 | ||
| * | ||
| * @param string $link The redirect URL. | ||
| */ | ||
| $link = apply_filters( 'random_content_redirect_url', $link ); | ||
|
|
||
| if ( ! $link ) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
I wonder if we need this? There is already a filter applying in wp_safe_redirect() including wp_redirect. The use of this filter for unit testing can be replaced with using wp_redirect.
| 'order' => 'ASC', | ||
| 'ignore_sticky_posts' => true, | ||
| 'update_post_term_cache' => false, | ||
| 'update_post_meta_cache' => false, |
There was a problem hiding this comment.
More data is being queried than is needed:
| 'update_post_meta_cache' => false, | |
| 'update_post_meta_cache' => false, | |
| 'fields' => 'ids', |
Trac ticket: https://core.trac.wordpress.org/ticket/64498
This PR adds a minimal, opt-in implementation of random content redirects
What it does
Adds
wp_random_content_redirect()(inwp-includes/query.php, alongside its closest siblingwp_old_slug_redirect()), hooked ontemplate_redirect.When enabled, a request to
example.com/?randomissues a 302 redirect to a randomly selected published post.?random&random_post_type=pagerestricts the pick to a post type (viewable types only);?random&random_cat_id=<term ID>restricts it to a category. These parameter names match Matt's original Random Redirect plugin referenced in the ticket, so existing?randomlinks keep working.Disabled by default — per the ticket's request for an opt-in implementation with no user-facing settings or defaults, nothing happens unless a theme or plugin opts in:
A second filter,
random_content_redirect_url, allows short-circuiting or rewriting the redirect target (same pattern asold_slug_redirect_url).Implementation notes
WP_Querycalls) rather thanORDER BY RAND(), which randomizes and sorts every candidate row on each request and is uncached.$_GETontemplate_redirect, matching the original plugin's addressing. If pretty-URL support (e.g. a/random/rewrite endpoint) is desirable, the parameters should instead become registered query vars — happy to rework in that direction if preferred.Tests
tests/phpunit/tests/rewrite/randomContentRedirect.phpcovers: disabled-by-default behavior, no-op without therandomparameter, redirect to a published post, post type and category restriction, exclusion of password-protected/draft/private posts, invalid and non-viewable post types, nonexistent categories, empty result sets, and non-GET requests.Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Fable 5
Used for: Initial implementation and test suite, ported from the Jetpack Random Redirect module under my direction; reviewed, tested, and edited by me.
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.