php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #65181 Merging the "do {} while(cond)" and "while(cond){}" structures
Submitted: 2013-07-02 08:35 UTC Modified: 2013-07-02 14:00 UTC
Votes:2
Avg. Score:2.0 ± 1.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:1 (100.0%)
From: ducciogrun at gmail dot com Assigned:
Status: Wont fix Package: *General Issues
PHP Version: Irrelevant OS: Any
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2013-07-02 08:35 UTC] ducciogrun at gmail dot com
Description:
------------
An example directly from the Php documentation 
(http://php.net/manual/en/function.mysql-fetch-assoc.php):

    /* fetch associative array */
    while ($row = $result->fetch_assoc()) {
        printf ("%s (%s)\n", $row["Name"], $row["CountryCode"]);
    }

It's old, it's prone to errors and my IDE (Zend Studio) always reports a 
"assignment in condition" warning. And many times thw work to be done on the 
first iteration is a lot more complicated than that.

One solution is to duplicate code:

    /* fetch associative array */
    $row = $result->fetch_assoc();
    while ($row) {
        printf ("%s (%s)\n", $row["Name"], $row["CountryCode"]);
        $row = $result->fetch_assoc(); //Duplicate code
    }


But it is not the best, both to read and to mantain.

An elegant solution could be the following structure:

    /* fetch associative array */
    do {
        $row = $result->fetch_assoc();
    } while ($row) {
        printf ("%s (%s)\n", $row["Name"], $row["CountryCode"]);
    }

Merging "do { } while(cond)" and "while(cond){}" would come handy anytime there 
is code that should be run at the first iteration and at any subsequent 
iteration, before the condition is checked.

The same purpose could be achieved by 

    /* fetch associative array */
    do {
        $row = $result->fetch_assoc();
        if($row) {
            printf ("%s (%s)\n", $row["Name"], $row["CountryCode"]);
        }
    } while ($row);

But it adds a level of nesting and it forces php to check twice the condition.


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2013-07-02 08:45 UTC] ab@php.net
-Status: Open +Status: Feedback
 [2013-07-02 08:45 UTC] ab@php.net
What about while (false !== ($row = $result->fetch_assoc())); ?
 [2013-07-02 12:36 UTC] ducciogrun at gmail dot com
@ab: Assignments in condition are not nice to read but one can live with them, 
with or without the explicit true or false check. 

But sometimes one has to do more complicated tasks (to name some dumb ones 
related to that short piece of code, maybe check if the connection to the 
database is still alive before fetching new data, check if there is enough 
memory available, update the log to inform that the process is about to fetch 
another row, or whatever).

Which one is more readable: 

while (false !== ((memory_get_usage(true)<MYSCRIPT_MAX_MEMORY) && mysql_ping() 
&& fwrite($log, "Start fetching a new row...") && $row = $result-
>fetch_assoc()));

or 

do {
    if(memory_get_usage(true)<MYSCRIPT_MAX_MEMORY) {
        /* write to log the reaching of max memory usage */
        break;
    }

    if(mysql_ping()) {
        /* reestablish connection and send again the query */
    }
    
    fwrite($log, "Start fetching a new row...");

    $row = $result->fetch_assoc();

} while ($row) {
  
    /* do what you have to do with the row */

}

Now, let's be honest, there are several ways to do that, for example to create a 
specific do_prefetch_jobs_and_return_row() function, nesting "if" and other 
structures,  but IMHO they are all less elegant :-)

I opened this feature request to see if I'm the only one that would like to see 
this construct implemented, before jumping directly on the Php source code
 [2013-07-02 14:00 UTC] nikic@php.net
-Status: Feedback +Status: Wont fix
 [2013-07-02 14:00 UTC] nikic@php.net
I'm sure that your IDE provides a way to disable this warning (though I do wonder why they included it in the first place, as this is a pretty standard idiom in PHP.)

If the expression grows too complicated, the most obvious solution is to just put the code in a separate method / function.

This do { ... } while (...) { ... } structure is not present in any other language that I'm aware of and is not particularly intelligible.
 [2013-07-04 21:35 UTC] ducciogrun at gmail dot com
Please note that this is not a bug report/complaint/help request, but rather a 
new feature request. Though the assignment in condition is a common practice, it 
is prone to errors as it is easy to put too many or too few equal signs, $a = $b 
instead of $a == $b. There is a rationale behind signaling a warning for an 
assignment in condition. 

In my experience putting everything inside a function is not the best solution, 
as it makes the code less understandable 
(everything_that_must_be_done_before_the_condition_but_that_I_didnt_have_space_t
o_do_it_inline_in_the_condition() is not exactly a well defined function). Maybe 
closures would help in code readability, but what I always used was a "if(cond) 
break;" inside of an infinite loop. 

On a more abstract level, php forces you to make a choice, either the condition 
is at the beginning, or it is at the end. Must it be like that?

The for loop already has a predefined place for code to be executed before the 
condition check, and another place for code that goes after the condition check. 
But it's a quite specific loop, and the space for code before the condition is 
rather small. The while could be more flexible. And as far as I know, you're 
right, no other language has anything similar. 

For sure there is a need for executing some code before and some code after the 
condition. Can we live without a standardized structure that does it? Sure we 
can. In the past we also lived without the while and for loop, as the goto or 
even better the assembly JMP could handle each and every possible situation.

The need is there, what should be discussed if it is "better" to introduce a new 
structure or just live with one of the many "hacks" that php already offers 
(functions, closures, breaks inside infinite loops, nesting of structures).

I don't really have a clue whether the do{}while{} is the best choice or not. 
That was just my first idea.
 [2013-07-15 16:27 UTC] area at areasas dot net
.. I also had the same problem following the most common indication about the 
SQLite3 connection through PHP so that I propose two alternatives:

basic code:
===================================
$wp_sqlite = new SQLite3('../<dir>/<filename>');

$results = $wp_sqlite->query('SELECT * FROM test;');
===================================

alternative 1 (using "if(!empty..." to check if the $row has got a record):
===================================
$rows = array();
do {
	$row = $results->fetchArray(SQLITE3_ASSOC);
	if (!empty($row)) $rows[] = $row;
}
while ($row);
===================================

alternative 2 (using "array_pop" to just "pop" the last item of the array which 
is empty, due to the "do" construct):
===================================
$rows = array();
do {
	$row = $results->fetchArray(SQLITE3_ASSOC);
	$rows[] = $row;
}
while ($row);

array_pop($rows);
====================================


I had to do this way because the solution defined elegant (which really is 
elegant) is not applicable where you need an assignment so to have variable to 
be managed in the further code.

"alternative 2" is more efficient when tons of records has to be fetched.

I hope it helps.

Carmine Iaciofano
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 19 23:01:28 2024 UTC