: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Updates post author user caches for a list of post objects.
* @param WP_Post[] $posts Array of post objects.
function update_post_author_caches( $posts ) {
* cache_users() is a pluggable function so is not available prior
* to the `plugins_loaded` hook firing. This is to ensure against
* fatal errors when the function is not available.
if ( ! function_exists( 'cache_users' ) ) {
$author_ids = wp_list_pluck( $posts, 'post_author' );
$author_ids = array_map( 'absint', $author_ids );
$author_ids = array_unique( array_filter( $author_ids ) );
cache_users( $author_ids );
* Updates parent post caches for a list of post objects.
* @param WP_Post[] $posts Array of post objects.
function update_post_parent_caches( $posts ) {
$parent_ids = wp_list_pluck( $posts, 'post_parent' );
$parent_ids = array_map( 'absint', $parent_ids );
$parent_ids = array_unique( array_filter( $parent_ids ) );
if ( ! empty( $parent_ids ) ) {
_prime_post_caches( $parent_ids, false );
* Updates metadata cache for a list of post IDs.
* Performs SQL query to retrieve the metadata for the post IDs and updates the
* metadata cache for the posts. Therefore, the functions, which call this
* function, do not need to perform SQL queries on their own.
* @param int[] $post_ids Array of post IDs.
* @return array|false An array of metadata on success, false if there is nothing to update.
function update_postmeta_cache( $post_ids ) {
return update_meta_cache( 'post', $post_ids );
* Will clean the attachment in the cache.
* Cleaning means delete from the cache. Optionally will clean the term
* object cache associated with the attachment ID.
* This function will not run if $_wp_suspend_cache_invalidation is not empty.
* @global bool $_wp_suspend_cache_invalidation
* @param int $id The attachment ID in the cache to clean.
* @param bool $clean_terms Optional. Whether to clean terms cache. Default false.
function clean_attachment_cache( $id, $clean_terms = false ) {
global $_wp_suspend_cache_invalidation;
if ( ! empty( $_wp_suspend_cache_invalidation ) ) {
wp_cache_delete( $id, 'posts' );
wp_cache_delete( $id, 'post_meta' );
clean_object_term_cache( $id, 'attachment' );
* Fires after the given attachment's cache is cleaned.
* @param int $id Attachment ID.
do_action( 'clean_attachment_cache', $id );
* Hook for managing future post transitions to published.
* @see wp_clear_scheduled_hook()
* @global wpdb $wpdb WordPress database abstraction object.
* @param string $new_status New post status.
* @param string $old_status Previous post status.
* @param WP_Post $post Post object.
function _transition_post_status( $new_status, $old_status, $post ) {
if ( 'publish' !== $old_status && 'publish' === $new_status ) {
// Reset GUID if transitioning to publish and it is empty.
if ( '' === get_the_guid( $post->ID ) ) {
$wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post->ID ) ), array( 'ID' => $post->ID ) );
* Fires when a post's status is transitioned from private to published.
* @deprecated 2.3.0 Use {@see 'private_to_publish'} instead.
* @param int $post_id Post ID.
do_action_deprecated( 'private_to_published', array( $post->ID ), '2.3.0', 'private_to_publish' );
// If published posts changed clear the lastpostmodified cache.
if ( 'publish' === $new_status || 'publish' === $old_status ) {
foreach ( array( 'server', 'gmt', 'blog' ) as $timezone ) {
wp_cache_delete( "lastpostmodified:$timezone", 'timeinfo' );
wp_cache_delete( "lastpostdate:$timezone", 'timeinfo' );
wp_cache_delete( "lastpostdate:$timezone:{$post->post_type}", 'timeinfo' );
if ( $new_status !== $old_status ) {
wp_cache_delete( _count_posts_cache_key( $post->post_type ), 'counts' );
wp_cache_delete( _count_posts_cache_key( $post->post_type, 'readable' ), 'counts' );
// Always clears the hook in case the post status bounced from future to draft.
wp_clear_scheduled_hook( 'publish_future_post', array( $post->ID ) );
* Hook used to schedule publication for a post marked for the future.
* The $post properties used and must exist are 'ID' and 'post_date_gmt'.
* @param int $deprecated Not used. Can be set to null. Never implemented. Not marked
* as deprecated with _deprecated_argument() as it conflicts with
* wp_transition_post_status() and the default filter for _future_post_hook().
* @param WP_Post $post Post object.
function _future_post_hook( $deprecated, $post ) {
wp_clear_scheduled_hook( 'publish_future_post', array( $post->ID ) );
wp_schedule_single_event( strtotime( get_gmt_from_date( $post->post_date ) . ' GMT' ), 'publish_future_post', array( $post->ID ) );
* Hook to schedule pings and enclosures when a post is published.
* Uses XMLRPC_REQUEST and WP_IMPORTING constants.
* @param int $post_id The ID of the post being published.
function _publish_post_hook( $post_id ) {
if ( defined( 'XMLRPC_REQUEST' ) ) {
* Fires when _publish_post_hook() is called during an XML-RPC request.
* @param int $post_id Post ID.
do_action( 'xmlrpc_publish_post', $post_id );
if ( defined( 'WP_IMPORTING' ) ) {
if ( get_option( 'default_pingback_flag' ) ) {
add_post_meta( $post_id, '_pingme', '1', true );
add_post_meta( $post_id, '_encloseme', '1', true );
$to_ping = get_to_ping( $post_id );
if ( ! empty( $to_ping ) ) {
add_post_meta( $post_id, '_trackbackme', '1' );
if ( ! wp_next_scheduled( 'do_pings' ) ) {
wp_schedule_single_event( time(), 'do_pings' );
* Returns the ID of the post's parent.
* @since 5.9.0 The `$post` parameter was made optional.
* @param int|WP_Post|null $post Optional. Post ID or post object. Defaults to global $post.
* @return int|false Post parent ID (which can be 0 if there is no parent),
* or false if the post does not exist.
function wp_get_post_parent_id( $post = null ) {
$post = get_post( $post );
if ( ! $post || is_wp_error( $post ) ) {
return (int) $post->post_parent;
* Checks the given subset of the post hierarchy for hierarchy loops.
* Prevents loops from forming and breaks those that it finds. Attached
* to the {@see 'wp_insert_post_parent'} filter.
* @see wp_find_hierarchy_loop()
* @param int $post_parent ID of the parent for the post we're checking.
* @param int $post_id ID of the post we're checking.
* @return int The new post_parent for the post, 0 otherwise.
function wp_check_post_hierarchy_for_loops( $post_parent, $post_id ) {
// Nothing fancy here - bail.
// New post can't cause a loop.
// Can't be its own parent.
if ( $post_parent == $post_id ) {
// Now look for larger loops.
$loop = wp_find_hierarchy_loop( 'wp_get_post_parent_id', $post_id, $post_parent );
return $post_parent; // No loop.
// Setting $post_parent to the given value causes a loop.
if ( isset( $loop[ $post_id ] ) ) {
// There's a loop, but it doesn't contain $post_id. Break the loop.
foreach ( array_keys( $loop ) as $loop_member ) {
* Sets the post thumbnail (featured image) for the given post.
* @param int|WP_Post $post Post ID or post object where thumbnail should be attached.
* @param int $thumbnail_id Thumbnail to attach.
* @return int|bool True on success, false on failure.
function set_post_thumbnail( $post, $thumbnail_id ) {
$post = get_post( $post );
$thumbnail_id = absint( $thumbnail_id );
if ( $post && $thumbnail_id && get_post( $thumbnail_id ) ) {
if ( wp_get_attachment_image( $thumbnail_id, 'thumbnail' ) ) {
return update_post_meta( $post->ID, '_thumbnail_id', $thumbnail_id );
return delete_post_meta( $post->ID, '_thumbnail_id' );
* Removes the thumbnail (featured image) from the given post.
* @param int|WP_Post $post Post ID or post object from which the thumbnail should be removed.
* @return bool True on success, false on failure.
function delete_post_thumbnail( $post ) {
$post = get_post( $post );
return delete_post_meta( $post->ID, '_thumbnail_id' );
* Deletes auto-drafts for new posts that are > 7 days old.
* @global wpdb $wpdb WordPress database abstraction object.
function wp_delete_auto_drafts() {
// Cleanup old auto-drafts more than 7 days old.
$old_posts = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_status = 'auto-draft' AND DATE_SUB( NOW(), INTERVAL 7 DAY ) > post_date" );
foreach ( (array) $old_posts as $delete ) {
wp_delete_post( $delete, true );
* Queues posts for lazy-loading of term meta.
* @param WP_Post[] $posts Array of WP_Post objects.
function wp_queue_posts_for_term_meta_lazyload( $posts ) {
$post_type_taxonomies = array();
$prime_post_terms = array();
foreach ( $posts as $post ) {
if ( ! ( $post instanceof WP_Post ) ) {
if ( ! isset( $post_type_taxonomies[ $post->post_type ] ) ) {
$post_type_taxonomies[ $post->post_type ] = get_object_taxonomies( $post->post_type );
foreach ( $post_type_taxonomies[ $post->post_type ] as $taxonomy ) {
$prime_post_terms[ $taxonomy ][] = $post->ID;
if ( $prime_post_terms ) {
foreach ( $prime_post_terms as $taxonomy => $post_ids ) {
$cached_term_ids = wp_cache_get_multiple( $post_ids, "{$taxonomy}_relationships" );
if ( is_array( $cached_term_ids ) ) {
$cached_term_ids = array_filter( $cached_term_ids );
foreach ( $cached_term_ids as $_term_ids ) {
// Backward compatibility for if a plugin is putting objects into the cache, rather than IDs.
foreach ( $_term_ids as $term_id ) {
if ( is_numeric( $term_id ) ) {
$term_ids[] = (int) $term_id;
} elseif ( isset( $term_id->term_id ) ) {
$term_ids[] = (int) $term_id->term_id;
$term_ids = array_unique( $term_ids );
wp_lazyload_term_meta( $term_ids );
* Updates the custom taxonomies' term counts when a post's status is changed.
* For example, default posts term counts (for custom taxonomies) don't include
* @param string $new_status New post status.
* @param string $old_status Old post status.
* @param WP_Post $post Post object.
function _update_term_count_on_transition_post_status( $new_status, $old_status, $post ) {
// Update counts for the post's terms.
foreach ( (array) get_object_taxonomies( $post->post_type ) as $taxonomy ) {
$tt_ids = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'tt_ids' ) );
wp_update_term_count( $tt_ids, $taxonomy );
* Adds any posts from the given IDs to the cache that do not already exist in cache.
* @since 6.1.0 This function is no longer marked as "private".
* @see update_post_cache()
* @see update_postmeta_cache()
* @see update_object_term_cache()
* @global wpdb $wpdb WordPress database abstraction object.
* @param int[] $ids ID list.
* @param bool $update_term_cache Optional. Whether to update the term cache. Default true.
* @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
function _prime_post_caches( $ids, $update_term_cache = true, $update_meta_cache = true ) {
$non_cached_ids = _get_non_cached_ids( $ids, 'posts' );
if ( ! empty( $non_cached_ids ) ) {
$fresh_posts = $wpdb->get_results( sprintf( "SELECT $wpdb->posts.* FROM $wpdb->posts WHERE ID IN (%s)", implode( ',', $non_cached_ids ) ) );
// Despite the name, update_post_cache() expects an array rather than a single post.
update_post_cache( $fresh_posts );
if ( $update_meta_cache ) {
update_postmeta_cache( $ids );
if ( $update_term_cache ) {
$post_types = array_map( 'get_post_type', $ids );
$post_types = array_unique( $post_types );
update_object_term_cache( $ids, $post_types );
* Prime the cache containing the parent ID of various post objects.
* @global wpdb $wpdb WordPress database abstraction object.
* @param int[] $ids ID list.
function _prime_post_parent_id_caches( array $ids ) {
$ids = array_filter( $ids, '_validate_cache_id' );
$ids = array_unique( array_map( 'intval', $ids ), SORT_NUMERIC );
foreach ( $ids as $id ) {
$cache_keys[ $id ] = 'post_parent:' . (string) $id;
$cached_data = wp_cache_get_multiple( array_values( $cache_keys ), 'posts' );
$non_cached_ids = array();
foreach ( $cache_keys as $id => $cache_key ) {
if ( false === $cached_data[ $cache_key ] ) {
if ( ! empty( $non_cached_ids ) ) {
$fresh_posts = $wpdb->get_results( sprintf( "SELECT $wpdb->posts.ID, $wpdb->posts.post_parent FROM $wpdb->posts WHERE ID IN (%s)", implode( ',', $non_cached_ids ) ) );
$post_parent_data = array();
foreach ( $fresh_posts as $fresh_post ) {
$post_parent_data[ 'post_parent:' . (string) $fresh_post->ID ] = (int) $fresh_post->post_parent;
wp_cache_add_multiple( $post_parent_data, 'posts' );