Why is it necessary to call rewind_posts() when using the loop more than once? [duplicate]

Global rewind_posts

File: wp-includes/query.php
784: /**
785:  * Rewind the loop posts.
786:  *
787:  * @since 1.5.0
788:  *
789:  * @global WP_Query $wp_query Global WP_Query instance.
790:  */
791: function rewind_posts() {
792:    global $wp_query;
793:    $wp_query->rewind_posts();
794: }

and rewind_posts from WP_Query class.

File: wp-includes/class-wp-query.php
3144:    * Rewind the posts and reset post index.
3145:    *
3146:    * @since 1.5.0
3147:    * @access public
3148:    */
3149:   public function rewind_posts() {
3150:       $this->current_post = -1;
3151:       if ( $this->post_count > 0 ) {
3152:           $this->post = $this->posts[0];
3153:       }
3154:   }

can help us understand we are actually in the context of $wp_query object when we are rewinding using the global rewind_posts.


The codex pointer you set:

// main loop
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<?php the_content(); ?>
<?php endwhile; endif; ?>

// rewind
<?php rewind_posts(); ?>

// new loop
<?php while (have_posts()) : the_post(); ?>
<?php the_content(); ?>
<?php endwhile; ?>

suggests that this has to do something with the $the_post method.

So, what is the nature of the_post()?

File: /class-wp-query.php
3095:   public function the_post() {
3096:       global $post;
3097:       $this->in_the_loop = true;
3098: 
3099:       if ( $this->current_post == -1 ) // loop has just started
3100:           /**
3101:            * Fires once the loop is started.
3102:            *
3103:            * @since 2.0.0
3104:            *
3105:            * @param WP_Query &$this The WP_Query instance (passed by reference).
3106:            */
3107:           do_action_ref_array( 'loop_start', array( &$this ) );
3108: 
3109:       $post = $this->next_post();
3110:       $this->setup_postdata( $post );
3111:   }

enter image description here

At least we can understand that there is a global $post object being set each time we iterate the while loop.

The key for understanding is the next_post() method.

File: wp-includes/class-wp-query.php
3068:   /**
3069:    * Set up the next post and iterate current post index.
3070:    *
3071:    * @since 1.5.0
3072:    * @access public
3073:    *
3074:    * @return WP_Post Next post.
3075:    */
3076:   public function next_post() {
3077: 
3078:       $this->current_post++;
3079: 
3080:       $this->post = $this->posts[$this->current_post];
3081:       return $this->post;
3082:   }

That method finally explains we increment the current post pointer:

$this->current_post++;

Note that in the expression above the whole variable $this->current_post will be incremented.

($this->current_post)++;

This is equivalent to:

$this->current_post = $this->current_post + 1;

for those that hate to admit the syntax $this->current_post++; is pretty cool to increment a variable.


To recap:

Since we called the_post() and increased the $this->current_post pointer we need now to set this pointer to -1, if we plan to loop again.

3149:   public function rewind_posts() {
3150:       $this->current_post = -1;
3151:       if ( $this->post_count > 0 ) {
3152:           $this->post = $this->posts[0];
3153:       }
3154:   }

Strange enought, but it works.

Leave a Comment