Delete all posts from WordPress except latest X posts

The offset parameter is ignored with posts_per_page set to -1 in WP_Query. If you look at the source code in the WP_Query class, posts_per_page=-1 sets nopaging to true.

if ( !isset($q['nopaging']) ) {
    if ( $q['posts_per_page'] == -1 ) {
        $q['nopaging'] = true;
    } else {
        $q['nopaging'] = false;
    }
}

This in turn will not append the LIMIT to the SQL query (empty($q['nopaging'] === false which fail the conditional statement) meaning that the whole pagination/offset is ignored and all posts are returned regardless

if ( empty($q['nopaging']) && !$this->is_singular ) {
    $page = absint($q['paged']);
    if ( !$page )
        $page = 1;

    if ( empty($q['offset']) ) {
        $pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
    } else { // we're ignoring $page and using 'offset'
        $q['offset'] = absint($q['offset']);
        $pgstrt = $q['offset'] . ', ';
    }
    $limits="LIMIT " . $pgstrt . $q['posts_per_page'];
}

I think the best workaround here is to make use of normal PHP (array_slice())after getting all the posts. You want to get only post ID’s here as wp_delete_post is quite expensive to run and only really need the post ID, so we don’t need any other post info.

In short, your query will look like this: (NOTE: This is all untested and needs PHP 5.4+)

add_action( 'publish_post', function ( $post_ID ) 
{
    $args = [
        'nopaging' => true,
        'fields'   => 'ids'
    ];
    $posts_array = get_posts( $args );
    // Make sure that we have post ID's returned
    if ( $posts_array ) {
        // Get all post ids after the 10th post using array_slice
        $delete_posts = array_slice( $posts_array, 10 );
        // Make sure that we actually have post ID's in the $delete_posts array
        if ( $delete_posts ) {
            foreach ( $delete_posts as $delete_post )
                wp_delete_post( $delete_post, true );
        }
    }
});

EDIT

Just before I forget, you can also define posts_per_page as an unlikely integer value instead of -1. This will also work with your code with the offset parameter set

Leave a Comment