Commit 5c91d3ac59 for wordpress.org

commit 5c91d3ac5934540a3d437873d987776b413141f0
Author: jorbin <jorbin@git.wordpress.org>
Date:   Tue Nov 3 20:35:58 2026 +0000

    Rewrites: Avoid fatal errors when flushing rules for random content.

    `wp_random_content_flush_rewrite_rules_for_post()` runs on the `wp_after_insert_post` and `after_delete_post` actions, which third-party code may fire with a missing or incorrectly typed `$post`. Also make the `$post` parameter of `_update_posts_count_on_delete()` optional for the same single-argument `after_delete_post` calls.

    Developed in: https://github.com/WordPress/wordpress-develop/pull/14356

    Follow-up to r64402.

    Props jorbin, bradshawtm, therssoftware.
    Fixes #64498.

    Built from https://develop.svn.wordpress.org/trunk@65250


    git-svn-id: http://core.svn.wordpress.org/trunk@64399 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/ms-blogs.php b/wp-includes/ms-blogs.php
index adb20da267..d791dd2f45 100644
--- a/wp-includes/ms-blogs.php
+++ b/wp-includes/ms-blogs.php
@@ -889,12 +889,13 @@ function _update_blog_date_on_post_delete( $post_id ) {
  *
  * @since 4.0.0
  * @since 6.2.0 Added the `$post` parameter.
+ * @since 7.2.0 The `$post` parameter isn't always guaranteed to be provided.
  *
- * @param int     $post_id Post ID.
- * @param WP_Post $post    Post object.
+ * @param int          $post_id Post ID.
+ * @param WP_Post|null $post    Optional. Post object. Default null.
  */
-function _update_posts_count_on_delete( $post_id, $post ) {
-	if ( ! $post || 'publish' !== $post->post_status || 'post' !== $post->post_type ) {
+function _update_posts_count_on_delete( $post_id, $post = null ) {
+	if ( ! $post instanceof WP_Post || 'publish' !== $post->post_status || 'post' !== $post->post_type ) {
 		return;
 	}

diff --git a/wp-includes/query.php b/wp-includes/query.php
index 4cafca3d7c..40a9a285c9 100644
--- a/wp-includes/query.php
+++ b/wp-includes/query.php
@@ -1513,6 +1513,12 @@ function wp_random_content_redirect(): void {
  * unpublished, renamed, moved or deleted.
  *
  * Runs on the {@see 'wp_after_insert_post'} and {@see 'after_delete_post'} actions.
+ * Third-party code may fire these actions with unexpected values, so the parameters are
+ * not type declared. When `$post` is omitted it is looked up from `$post_id`. Passing
+ * anything other than a `WP_Post` as `$post`, or a post that cannot be looked up, is
+ * incorrect usage and nothing is flushed. `$post_before` is only considered on
+ * `wp_after_insert_post`, where passing anything other than a `WP_Post` or null is
+ * incorrect usage and the value is ignored.
  *
  * @since 7.2.0
  *
@@ -1520,21 +1526,59 @@ function wp_random_content_redirect(): void {
  * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
  *
  * @param int          $post_id     Post ID.
- * @param WP_Post      $post        Post object.
+ * @param WP_Post|null $post        Optional. Post object. Looked up from `$post_id` when null. Default null.
  * @param bool         $update      Optional. Whether this is an existing post being updated. Default false.
  * @param WP_Post|null $post_before Optional. Post object before the update, null for new posts. Default null.
  */
-function wp_random_content_flush_rewrite_rules_for_post( int $post_id, WP_Post $post, bool $update = false, ?WP_Post $post_before = null ): void {
+function wp_random_content_flush_rewrite_rules_for_post( $post_id, $post = null, $update = false, $post_before = null ): void {
 	global $wp_rewrite;

+	// Only look up a real ID: get_post() falls back to the global post for an empty value.
+	if ( null === $post && (int) $post_id > 0 ) {
+		$post = get_post( (int) $post_id );
+	}
+
+	if ( ! $post instanceof WP_Post ) {
+		_doing_it_wrong(
+			__FUNCTION__,
+			sprintf(
+				/* translators: 1: $post, 2: WP_Post */
+				__( 'The %1$s argument must be a %2$s object.' ),
+				'<code>$post</code>',
+				'<code>WP_Post</code>'
+			),
+			'7.2.0'
+		);
+		return;
+	}
+
+	$posts = array( $post );
+
+	// Only wp_after_insert_post passes the previous post object, which matters when it was renamed or moved.
+	if ( 'wp_after_insert_post' === current_action() ) {
+		if ( $post_before instanceof WP_Post ) {
+			$posts[] = $post_before;
+		} elseif ( null !== $post_before ) {
+			_doing_it_wrong(
+				__FUNCTION__,
+				sprintf(
+					/* translators: 1: $post_before, 2: WP_Post */
+					__( 'The %1$s argument must be a %2$s object or null.' ),
+					'<code>$post_before</code>',
+					'<code>WP_Post</code>'
+				),
+				'7.2.0'
+			);
+		}
+	}
+
 	if ( ! $wp_rewrite->using_permalinks() || ! wp_is_random_content_redirect_enabled() ) {
 		return;
 	}

-	foreach ( array( $post, $post_before ) as $page ) {
+	foreach ( $posts as $page ) {
 		if (
-			$page instanceof WP_Post
-			&& $wp_rewrite->get_random_base() === $page->post_name
+			$wp_rewrite->get_random_base() === $page->post_name
 			&& (
 				( 'page' === $page->post_type && 0 === (int) $page->post_parent )
 				|| ( 'post' === $page->post_type && $wp_rewrite->post_permalinks_share_random_base() )
diff --git a/wp-includes/version.php b/wp-includes/version.php
index ff06770a58..3c3d36e029 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-beta4-65249';
+$wp_version = '7.2-beta4-65250';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.