: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Displays the edit post link for post.
* @since 4.4.0 The `$css_class` argument was added.
* @param string $text Optional. Anchor text. If null, default is 'Edit This'. Default null.
* @param string $before Optional. Display before edit link. Default empty.
* @param string $after Optional. Display after edit link. Default empty.
* @param int|WP_Post $post Optional. Post ID or post object. Default is the global `$post`.
* @param string $css_class Optional. Add custom class to link. Default 'post-edit-link'.
function edit_post_link( $text = null, $before = '', $after = '', $post = 0, $css_class = 'post-edit-link' ) {
$post = get_post( $post );
$url = get_edit_post_link( $post->ID );
$text = __( 'Edit This' );
$link = '<a class="' . esc_attr( $css_class ) . '" href="' . esc_url( $url ) . '">' . $text . '</a>';
* Filters the post edit link anchor tag.
* @param string $link Anchor tag for the edit link.
* @param int $post_id Post ID.
* @param string $text Anchor text.
echo $before . apply_filters( 'edit_post_link', $link, $post->ID, $text ) . $after;
* Retrieves the delete posts link for post.
* Can be used within the WordPress loop or outside of it, with any post type.
* @param int|WP_Post $post Optional. Post ID or post object. Default is the global `$post`.
* @param string $deprecated Not used.
* @param bool $force_delete Optional. Whether to bypass Trash and force deletion. Default false.
* @return string|void The delete post link URL for the given post.
function get_delete_post_link( $post = 0, $deprecated = '', $force_delete = false ) {
if ( ! empty( $deprecated ) ) {
_deprecated_argument( __FUNCTION__, '3.0.0' );
$post = get_post( $post );
$post_type_object = get_post_type_object( $post->post_type );
if ( ! $post_type_object ) {
if ( ! current_user_can( 'delete_post', $post->ID ) ) {
$action = ( $force_delete || ! EMPTY_TRASH_DAYS ) ? 'delete' : 'trash';
$delete_link = add_query_arg( 'action', $action, admin_url( sprintf( $post_type_object->_edit_link, $post->ID ) ) );
* Filters the post delete link.
* @param string $link The delete link.
* @param int $post_id Post ID.
* @param bool $force_delete Whether to bypass the Trash and force deletion. Default false.
return apply_filters( 'get_delete_post_link', wp_nonce_url( $delete_link, "$action-post_{$post->ID}" ), $post->ID, $force_delete );
* Retrieves the edit comment link.
* @param int|WP_Comment $comment_id Optional. Comment ID or WP_Comment object.
* @return string|void The edit comment link URL for the given comment.
function get_edit_comment_link( $comment_id = 0 ) {
$comment = get_comment( $comment_id );
if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) ) {
$location = admin_url( 'comment.php?action=editcomment&c=' ) . $comment->comment_ID;
* Filters the comment edit link.
* @param string $location The edit link.
return apply_filters( 'get_edit_comment_link', $location );
* Displays the edit comment link with formatting.
* @param string $text Optional. Anchor text. If null, default is 'Edit This'. Default null.
* @param string $before Optional. Display before edit link. Default empty.
* @param string $after Optional. Display after edit link. Default empty.
function edit_comment_link( $text = null, $before = '', $after = '' ) {
$comment = get_comment();
if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) ) {
$text = __( 'Edit This' );
$link = '<a class="comment-edit-link" href="' . esc_url( get_edit_comment_link( $comment ) ) . '">' . $text . '</a>';
* Filters the comment edit link anchor tag.
* @param string $link Anchor tag for the edit link.
* @param string $comment_id Comment ID as a numeric string.
* @param string $text Anchor text.
echo $before . apply_filters( 'edit_comment_link', $link, $comment->comment_ID, $text ) . $after;
* Displays the edit bookmark link.
* @param int|stdClass $link Optional. Bookmark ID. Default is the ID of the current bookmark.
* @return string|void The edit bookmark link URL.
function get_edit_bookmark_link( $link = 0 ) {
$link = get_bookmark( $link );
if ( ! current_user_can( 'manage_links' ) ) {
$location = admin_url( 'link.php?action=edit&link_id=' ) . $link->link_id;
* Filters the bookmark edit link.
* @param string $location The edit link.
* @param int $link_id Bookmark ID.
return apply_filters( 'get_edit_bookmark_link', $location, $link->link_id );
* Displays the edit bookmark link anchor content.
* @param string $link Optional. Anchor text. If empty, default is 'Edit This'. Default empty.
* @param string $before Optional. Display before edit link. Default empty.
* @param string $after Optional. Display after edit link. Default empty.
* @param int $bookmark Optional. Bookmark ID. Default is the current bookmark.
function edit_bookmark_link( $link = '', $before = '', $after = '', $bookmark = null ) {
$bookmark = get_bookmark( $bookmark );
if ( ! current_user_can( 'manage_links' ) ) {
$link = __( 'Edit This' );
$link = '<a href="' . esc_url( get_edit_bookmark_link( $bookmark ) ) . '">' . $link . '</a>';
* Filters the bookmark edit link anchor tag.
* @param string $link Anchor tag for the edit link.
* @param int $link_id Bookmark ID.
echo $before . apply_filters( 'edit_bookmark_link', $link, $bookmark->link_id ) . $after;
* Retrieves the edit user link.
* @param int $user_id Optional. User ID. Defaults to the current user.
* @return string URL to edit user page or empty string.
function get_edit_user_link( $user_id = null ) {
$user_id = get_current_user_id();
if ( empty( $user_id ) || ! current_user_can( 'edit_user', $user_id ) ) {
$user = get_userdata( $user_id );
if ( get_current_user_id() === $user->ID ) {
$link = get_edit_profile_url( $user->ID );
$link = add_query_arg( 'user_id', $user->ID, self_admin_url( 'user-edit.php' ) );
* Filters the user edit link.
* @param string $link The edit link.
* @param int $user_id User ID.
return apply_filters( 'get_edit_user_link', $link, $user->ID );
* Retrieves the previous post that is adjacent to the current post.
* @param bool $in_same_term Optional. Whether post should be in the same taxonomy term.
* @param int[]|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs.
* @param string $taxonomy Optional. Taxonomy, if `$in_same_term` is true. Default 'category'.
* @return WP_Post|null|string Post object if successful. Null if global `$post` is not set.
* Empty string if no corresponding post exists.
function get_previous_post( $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
return get_adjacent_post( $in_same_term, $excluded_terms, true, $taxonomy );
* Retrieves the next post that is adjacent to the current post.
* @param bool $in_same_term Optional. Whether post should be in the same taxonomy term.
* @param int[]|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs.
* @param string $taxonomy Optional. Taxonomy, if `$in_same_term` is true. Default 'category'.
* @return WP_Post|null|string Post object if successful. Null if global `$post` is not set.
* Empty string if no corresponding post exists.
function get_next_post( $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
return get_adjacent_post( $in_same_term, $excluded_terms, false, $taxonomy );
* Retrieves the adjacent post.
* Can either be next or previous post.
* @global wpdb $wpdb WordPress database abstraction object.
* @param bool $in_same_term Optional. Whether post should be in the same taxonomy term.
* @param int[]|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs.
* @param bool $previous Optional. Whether to retrieve previous post.
* @param string $taxonomy Optional. Taxonomy, if `$in_same_term` is true. Default 'category'.
* @return WP_Post|null|string Post object if successful. Null if global `$post` is not set.
* Empty string if no corresponding post exists.
function get_adjacent_post( $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'category' ) {
if ( ! $post || ! taxonomy_exists( $taxonomy ) ) {
$current_post_date = $post->post_date;
$adjacent = $previous ? 'previous' : 'next';
if ( ! empty( $excluded_terms ) && ! is_array( $excluded_terms ) ) {
// Back-compat, $excluded_terms used to be $excluded_categories with IDs separated by " and ".
if ( str_contains( $excluded_terms, ' and ' ) ) {
/* translators: %s: The word 'and'. */
__( 'Use commas instead of %s to separate excluded terms.' ),
$excluded_terms = explode( ' and ', $excluded_terms );
$excluded_terms = explode( ',', $excluded_terms );
$excluded_terms = array_map( 'intval', $excluded_terms );
* Filters the IDs of terms excluded from adjacent post queries.
* The dynamic portion of the hook name, `$adjacent`, refers to the type
* of adjacency, 'next' or 'previous'.
* Possible hook names include:
* - `get_next_post_excluded_terms`
* - `get_previous_post_excluded_terms`
* @param int[]|string $excluded_terms Array of excluded term IDs. Empty string if none were provided.
$excluded_terms = apply_filters( "get_{$adjacent}_post_excluded_terms", $excluded_terms );
if ( $in_same_term || ! empty( $excluded_terms ) ) {
$join .= " INNER JOIN $wpdb->term_relationships AS tr ON p.ID = tr.object_id INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id";
$where .= $wpdb->prepare( 'AND tt.taxonomy = %s', $taxonomy );
if ( ! is_object_in_taxonomy( $post->post_type, $taxonomy ) ) {
$term_array = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'ids' ) );
// Remove any exclusions from the term array to include.
$term_array = array_diff( $term_array, (array) $excluded_terms );
$term_array = array_map( 'intval', $term_array );
if ( ! $term_array || is_wp_error( $term_array ) ) {
$where .= ' AND tt.term_id IN (' . implode( ',', $term_array ) . ')';
if ( ! empty( $excluded_terms ) ) {
$where .= " AND p.ID NOT IN ( SELECT tr.object_id FROM $wpdb->term_relationships tr LEFT JOIN $wpdb->term_taxonomy tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id) WHERE tt.term_id IN (" . implode( ',', array_map( 'intval', $excluded_terms ) ) . ') )';
// 'post_status' clause depends on the current user.
if ( is_user_logged_in() ) {
$user_id = get_current_user_id();
$post_type_object = get_post_type_object( $post->post_type );
if ( empty( $post_type_object ) ) {
$post_type_cap = $post->post_type;
$read_private_cap = 'read_private_' . $post_type_cap . 's';
$read_private_cap = $post_type_object->cap->read_private_posts;
* Results should include private posts belonging to the current user, or private posts where the
* current user has the 'read_private_posts' cap.
$private_states = get_post_stati( array( 'private' => true ) );
$where .= " AND ( p.post_status = 'publish'";
foreach ( $private_states as $state ) {
if ( current_user_can( $read_private_cap ) ) {
$where .= $wpdb->prepare( ' OR p.post_status = %s', $state );
$where .= $wpdb->prepare( ' OR (p.post_author = %d AND p.post_status = %s)', $user_id, $state );
$where .= " AND p.post_status = 'publish'";
$op = $previous ? '<' : '>';
$order = $previous ? 'DESC' : 'ASC';
* Filters the JOIN clause in the SQL for an adjacent post query.
* The dynamic portion of the hook name, `$adjacent`, refers to the type
* of adjacency, 'next' or 'previous'.
* Possible hook names include:
* - `get_previous_post_join`
* @since 4.4.0 Added the `$taxonomy` and `$post` parameters.
* @param string $join The JOIN clause in the SQL.
* @param bool $in_same_term Whether post should be in the same taxonomy term.
* @param int[]|string $excluded_terms Array of excluded term IDs. Empty string if none were provided.
* @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true.
* @param WP_Post $post WP_Post object.
$join = apply_filters( "get_{$adjacent}_post_join", $join, $in_same_term, $excluded_terms, $taxonomy, $post );
* Filters the WHERE clause in the SQL for an adjacent post query.
* The dynamic portion of the hook name, `$adjacent`, refers to the type
* of adjacency, 'next' or 'previous'.
* Possible hook names include:
* - `get_next_post_where`
* - `get_previous_post_where`
* @since 4.4.0 Added the `$taxonomy` and `$post` parameters.
* @param string $where The `WHERE` clause in the SQL.
* @param bool $in_same_term Whether post should be in the same taxonomy term.
* @param int[]|string $excluded_terms Array of excluded term IDs. Empty string if none were provided.
* @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true.
* @param WP_Post $post WP_Post object.
$where = apply_filters( "get_{$adjacent}_post_where", $wpdb->prepare( "WHERE p.post_date $op %s AND p.post_type = %s $where", $current_post_date, $post->post_type ), $in_same_term, $excluded_terms, $taxonomy, $post );
* Filters the ORDER BY clause in the SQL for an adjacent post query.
* The dynamic portion of the hook name, `$adjacent`, refers to the type
* of adjacency, 'next' or 'previous'.
* Possible hook names include:
* - `get_previous_post_sort`
* @since 4.4.0 Added the `$post` parameter.
* @since 4.9.0 Added the `$order` parameter.
* @param string $order_by The `ORDER BY` clause in the SQL.
* @param WP_Post $post WP_Post object.
* @param string $order Sort order. 'DESC' for previous post, 'ASC' for next.
$sort = apply_filters( "get_{$adjacent}_post_sort", "ORDER BY p.post_date $order LIMIT 1", $post, $order );
$query = "SELECT p.ID FROM $wpdb->posts AS p $join $where $sort";
$last_changed = wp_cache_get_last_changed( 'posts' );
if ( $in_same_term || ! empty( $excluded_terms ) ) {
$last_changed .= wp_cache_get_last_changed( 'terms' );
$cache_key = "adjacent_post:$key:$last_changed";
$result = wp_cache_get( $cache_key, 'post-queries' );
if ( false !== $result ) {