This RFC proposes an optional default control block for loops, that is executed in the event that the loop is not entered.

It is often desirable to perform a set of actions if the conditions to enter a loop are not met. This can require a significant amount of boilerplate code if the conditions are complex.

Pre-condition loops (for, foreach and while) will be allowed to have an optional default block (using the existing or keyword) after their loop body, which is executed when the body of the loop is not entered (condition failed, empty array, etc.).

Usage:

while ( $cond ) { // loop body } or { // did not enter while loop } for ( $i = 0 , $j = $k ; $i < 4 && $j < 65536 ; $i ++, $j <<= 1 ) { // loop body } or { // did not enter for loop } foreach ( generator ( ) as $k => $v ) { // loop body } or { // did not enter foreach loop }

In the case of for and foreach loops it's immediately obvious that separate tracking variables would be required to monitor whether the loop had been entered. This proposal does away with that necessity and increases performance as a side-effect.

do {} while(); loops have been deliberately excluded from this behaviour as they always enter the loop at least once. The only available behaviour for a default block on a do while loop is to have it execute if the loop only iterates once, which feels inconsistent.

Alternate syntax loops also gain this functionality with a or: clause.

This type of behaviour has been suggested before typically using the else keyword. There are several bug report feature requests for this, and several templating engines (i.e. Twig and Smarty) emulate this functionality for their users.

The originally conceived alternative was to use the default keyword however this can break switch when using a semi-colon after the default keyword (thanks @ Paul Crovella). However using else especially has several drawbacks that make or more attractive:

* Not backwards compatible by default due to dangling else

if ( $something ) while ( $cond ) // loop body else // this now belongs to the while loop

* Making it backwards compatible leads to inconsistent behaviour

if ( $something ) while ( $cond ) // loop body else // this still belongs to the if statement

* It breaks familiarity with similar behaviour in other languages

while ( $cond ) { // loop body } else { // In Python this will always execute unless break; is used in the loop body }

Using default is problematic in the following scenario

switch ( $x ) { case 1 : while ( $y ) // stuff default ; // stuff }

These scenarios could be solved by introducing a new keyword, however to maintain backward compatibility as far as possible it is more sane to borrow an existing keyword with a similar semantic meaning, in this case or , loop or do otherwise.

The intention is to implement this by duplicating loop prologues to avoid the requirement for tracking variables, and keep performance on-par with pre-patch looping.

As an example here is the opcode dump of a pre-patch basic while loop.

$i = 3; while ($i--) { print 'loop'; } line # * op fetch ext return operands --------------------------------------------------------------------------------- 3 0 > ASSIGN !0, 3 4 1 > POST_DEC ~1 !0 2 > JMPZ ~1, ->6 5 3 > PRINT ~2 'loop' 4 FREE ~2 6 5 > JMP ->1 6 > > RETURN 1

And a post-patch basic while loop with default block (labels added to help visualise flow)

$i = 0; while ($i--) { print 'loop'; } or { print 'or'; } # * op fetch ext return operands --------------------------------------------------------------------------------- 0 > ASSIGN !0, 0 cond_1: 1 > POST_DEC ~1 !0 2 > JMPZNZ loop ~1, ->or cond_2: 3 > POST_DEC ~1 !0 4 > JMPZ ~1, ->nxt_op loop: 5 > PRINT ~2 'loop' 6 FREE ~2 7 > JMP ->cond_2 or: 8 > PRINT ~3 'or' 9 FREE ~3 nxt_op: 10 > > RETURN 1