Ordering posts in get_posts

You can set the orderby to post__in. Here is a superior post loop:

$args = [
    'post__in' => [ 1, 2, 3, 4 ],
    'orderby'  => 'post__in',
];

$q = new \WP_Query( $args );
if ( $q->have_posts() ) {
    while ( $q->have_posts() ) {
        $q->the_post();
        ?>
        <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
            <?php the_title(); ?>
        </article>
        <?php
    }
    wp_reset_postdata();
} else {
    echo '<p>No posts found</p>';
}

Important changes:

  • get_posts does not fire all the loop lifecycle filters, bypasses caches by default ( suppress_filters is set to true in get_posts ), and doesn’t set the current post for you. It also uses WP_Query internally, so I cut out the middleman and used WP_Query directly. This loop is both faster, more expandable, and more compatible
  • I removed numberposts and post_type, these are unnecessary and would actually slow down the query if WP used them. WP knows which posts you want via post__in so it’ll just fetch those
  • orderby is set to post__in so posts will appear in the same order they’re requested
  • I used a PHP array instead of using explode on a string ( which gives the same result so why do the extra work? )
  • I used the_ID and post_class so the inner loops tags would have the appropriate classes

I strongly recommend you read through this document: https://developer.wordpress.org/reference/classes/wp_query/ it will save a lot of time in the future and has a lot of examples.