WordPress.org

Make WordPress Core

Opened 10 years ago

Closed 10 years ago

#11698 closed defect (bug) (wontfix)

have_posts should not auto rewind

Reported by: mwillberg Owned by:
Milestone: Priority: normal
Severity: minor Version:
Component: General Keywords: dev-feedback 2nd-opinion
Focuses: Cc:
PR Number:

Description

When the loop has been exhausted the have_posts will auto rewind itself. ALL documentation and examples are not valid as they claim that have_posts() will return false when there are no more posts in query.

Example, this will incorrectly go thru the loop twice:

while (have_posts()) {the_post();the_title();}
while (have_posts()) {the_post();the_title();}

the later loop should not do anything as the query has already been walked thru.

Suggested FIX, to remove the rewind_posts();

query.php / function have_posts() {
...
do_action_ref_array('loop_end', array(&$this));
// Do some cleaning up after the loop
// $this->rewind_posts();
...
}

Life after the fix:

If someone really wants to revert to old behaviour, it is already possible to do easily. So AFTER the issue has been fixed (ie. rewinding has been removed):

while (have_posts()) {the_post();}

if (!have_posts()) {rewind_posts();}
while (have_posts()) {the_post();}

Change History (9)

#1 follow-up: @mwillberg
10 years ago

Naturally the "loop_end" hook needs some kind of flag to make sure it runs only once and subsequent calls to not activate it.

#2 @dd32
10 years ago

  • Keywords dev-feedback 2nd-opinion added
  • Milestone changed from Unassigned to Future Release
  • Version set to 2.9

while (have_posts()) {the_post();the_title();}
while (have_posts()) {the_post();the_title();}
}}}

Another way to look at that, Is that that itself, is wanting for the loop to be repeated.

Naturally the "loop_end" hook needs some kind of flag to make sure it runs only once and subsequent calls to not activate it.

IMHO, The while block is the loop, So the loop_end hook should be fired afterwards no matter what, you're ending a' loop.

#3 in reply to: ↑ 1 @azaozz
10 years ago

  • Milestone Future Release deleted
  • Resolution set to invalid
  • Status changed from new to closed
  • Version 2.9 deleted

Agree with dd32. The actual loop is the while(){} part, if you repeat it, it runs twice.

Replying to mwillberg:

Naturally the "loop_end" hook needs some kind of flag to make sure it runs only once...

It already has one: did_action('loop_end').

#4 in reply to: ↑ description ; follow-up: @filosofo
10 years ago

Replying to mwillberg:

documentation and examples are not valid as they claim that have_posts() will return false when there are no more posts in query.

have_posts() does return false once the Loop is completed. Perhaps the documentation you mention could be improved. Can you link to an example that is in error?

Example, this will incorrectly go thru the loop twice:


while (have_posts()) {the_post();the_title();}
while (have_posts()) {the_post();the_title();}

Restoring the global variables to their pre-Loop state is intended behavior. See #977 and #1518

#5 in reply to: ↑ 4 @mwillberg
10 years ago

  • Resolution invalid deleted
  • Status changed from closed to reopened

More details, I dislike multiquoting...

The current loop defeats the only usefull thing I could try to make it to do:

$tmp=5;
while (have_posts() && ($tmp > 0)) {
  the_post();
  // output the post and decorate it with blinking lights
  $tmp--;
}

// Now we CONTINUE iterating the REST of the loop or naturally SKIP it
// if the have_posts() routine would work AS IT SHOULD
while (have_posts()) {
  the_post();
  // output normally
}

So it the query has less than 6 posts they are output twice.

I have so limited free time with a baby that I just checked briefly the referenced bug IDs.

977 seem to indicate that some people are using some loop-only golbal variables when the loop has ened. Funny that when the loop has ended they are not set to null which makes than actually unusable. I smell some kludges there and it would have been correct way to point programmer to reset the loop when he wants to do so.

1518 talks about in_the_loop, that I was not awere of, which is as useless as have_posts in determinind if there actually are anything left on query.

I also like how Codex has has nearly everywhere if+while construction

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>

which is totally redudant and useless use of "if", but more importantly has the exact problem that I am here crying about: two subsequent calls to have_posts() will in certain case have DIFFERENT return values.

I would say copy+paste coders have high probability of doing somehting like this.

I do not know how I could explain this bizarre behavior more clearly. This issue feels like some routine would mangle a object before you have finished using it or in file operations EOF detection will work only ONCE after that EOF will just happily state that there is more data available.

If the current way of operation contiunues the documentation needs to add some warnings:

"have_posts() command returns false when there are no more items in the loop and loop will be reset. After this subsequent calls to the function will return INVALID results.

#6 @TobiasBg
10 years ago

You can achieve the same output as in your example with one loop:

$tmp=5;
while ( have_posts() ) {
  if ( $tmp > 0 ) {
    the_post();
    // output the post and decorate it with blinking lights
    $tmp--;
  } else {
    the_post();
    // output normally
  }
}

#7 @filosofo
10 years ago

Just change

while (have_posts() && ($tmp > 0)) {

to

while (($tmp > 0) && have_posts()) {

so have_posts() isn't called when you don't want it to be.

#8 @purewhite
10 years ago

  • Cc purewhite added

This issue affects other people who couldn't work out how to fix it. As stated above, if you read an EOF from a file handler, you should not then be able to keep reading data from the file unless you manually perform a rewind. Having it rewind automatically is a bug.
For example, I wish to do something similar to making the first post "fancy" except I want to make the last post fancy.
Keeping to "conventions", calling a function that "gets" information should not modify any information. Another modifying function should do that.

#9 @nacin
10 years ago

  • Resolution set to wontfix
  • Status changed from reopened to closed
Note: See TracTickets for help on using tickets.