Statements

May 30, 2017 · View on GitHub

General

Syntax

  statement:
    function-static-declaration
    compound-statement
    labeled-statement
    expression-statement
    selection-statement
    iteration-statement
    jump-statement
    try-statement

Defined elsewhere

Compound Statements

Syntax

  compound-statement:
    {  statement-listopt  }

  statement-list:
    statement
    statement-list   statement

Defined elsewhere

Semantics

A compound statement allows a group of zero or more statements to be treated syntactically as a single statement. A compound statement is often referred to as a block.

Examples

if (condition)
{	// braces are needed as the true path has more than one statement
	// statement-1
	// statement-2
}
else
{	// braces are optional as the false path has only one statement
	// statement-3
}
// -----------------------------------------
while (condition)
{	// the empty block is equivalent to a null statement
}

Labeled Statements

Syntax

  labeled-statement:
    case-label
    default-label

  case-label:
    case   expression  :  statement

  default-label:
    default  :  statement

Defined elsewhere

Constraints

case-label and default-label must only occur inside a switch statement.

Semantics

See the switch statement.

Expression Statements

Syntax

   expression-statement:
     expressionopt  ;

Defined elsewhere

Semantics

If present, expression is evaluated for its side effects, if any, and any resulting value is discarded. If expression is omitted, the statement is a null statement, which has no effect on execution.

Examples

$i = 10;  // $i is assigned the value 10; result (10) is discarded
++$i; // $i is incremented; result (11) is discarded
$i++; // $i is incremented; result (11) is discarded
DoIt(); // function DoIt is called; result (return value) is discarded
// -----------------------------------------
$i;   // no side effects, result is discarded. Vacuous but permitted
123;  // likewise for this one and the two statements following
34.5 * 12.6 + 11.987;
true;

Selection Statements

General

Syntax

  selection-statement:
    if-statement
    switch-statement

Defined elsewhere

Semantics

Based on the value of a controlling expression, a selection statement selects among a set of statements.

The if Statement

Syntax

  if-statement:
    if   (   expression   )   statement   elseif-clauses-opt   else-clause-opt

  elseif-clauses:
    elseif-clause
    elseif-clauses   elseif-clause

  elseif-clause:
    elseif   (   expression   )   statement

  else-clause:
    else   statement

Defined elsewhere

Constraints

The controlling expression expression must have type bool or be implicitly convertible to that type.

Semantics

The two forms of the if statement are equivalent; they simply provide alternate styles.

If expression tests true, the statement that follows immediately is executed. Otherwise, if an elseif clause is present the statement immediately following the elseif is executed. Otherwise, any other elseif expressions are evaluated. If none of those tests true, if an else clause is present the statement immediately following the else is executed.

An else clause is associated with the lexically nearest preceding if or elseif that is permitted by the syntax.

Examples

if ($count > 0) {

} else {

}
// -----------------------------------------
if (1)

  if (0)

else  // this else does NOT go with the outer if


if (1) {

  if (0)

} else  // this else does go with the outer if

The switch Statement

Syntax

  switch-statement:
    switch  (  expression  )  compound-statement

Defined elsewhere

Constraints

The controlling expression expression must have scalar type.

There must be at most one default label.

Each label expression's type must be a subtype of the switch expression type.

The compound statement may be empty; if it is not, then the first statement in it must be a labeled statement.

Semantics

Based on the value of its expression, a switch statement transfers control to a case label; to a default label, if one exists; or to the statement immediately following the end of the switch statement. A case or default label is only reachable directly within its closest enclosing switch statement.

On entry to the switch statement, the controlling expression is evaluated and then compared with the value of the case-label-expression values, in lexical order. If one matches, control transfers to the statement following the corresponding case label. If there is no match, then if there is a default label, control transfers to the statement following that; otherwise, control transfers to the statement immediately following the end of the switch statement. If a switch contains more than one case label whose values compare equal to the controlling expression, the first in lexical order is considered the match.

An arbitrary number of statements can be associated with any case or default label. In the absence of a break statement at the end of a set of such statements, control drops through into any following case or default label. Thus, if all cases and the default end in break and there are no duplicate-valued case labels, the order of case and default labels is insignificant.

In no break statement is seen for a case or default before a subsequent case label, default label, or the switch-terminating } is encountered, an implementation might issue a warning. However, such a warning can be suppressed by placing a source line containing the special comment // FALLTHROUGH, at the end of that case or default statement group.

Case-label values can be runtime expressions, and the types of sibling case-label values need not be the same.

Switches may be nested, in which case, each switch has its own set of switch clauses.

Examples

$v = 10;
switch ($v) {
default:
  echo "default case: \$v is $v\n";
  break;    // break ends "group" of default statements
case 20:
  echo "case 20\n";
  break;    // break ends "group" of case 20 statements
case 10:
  echo "case 10\n"; // no break, so control drops into next label's "group"
  // FALLTHROUGH
case 30:
  echo "case 30\n"; // no break, but then none is really needed either
}
// -----------------------------------------
$v = 30;
switch ($v) {
case 30.0:  // <===== this case matches with 30
  echo "case 30.0\n";
  break;
default:
  echo "default case: \$v is $v\n";
  break;
case 30:    // <===== rather than this case matching with 30
  echo "case 30\n";
  break;
}
// -----------------------------------------
enum ControlStatus: int {
  Stopped = 0;
  Stopping = 1;
  Starting = 2;
  Started = 3;
}

switch ($p1) {
case ControlStatus::Stopped:
  echo "Stopped: $p1\n";
  break;

case ControlStatus::Started:
  echo "Started: $p1\n";
  break;
}

Iteration Statements

General

Syntax

  iteration-statement:
    while-statement
    do-statement
    for-statement
    foreach-statement

Defined elsewhere

The while Statement

Syntax

  while-statement:
    while  (  expression  )  statement

Defined elsewhere

Constraints

The controlling expression expression must have type bool or be implicitly convertible to that type.

Semantics

If expression tests true, the statement that follows immediately is executed, and the process is repeated. If expression tests false, control transfers to the point immediately following the end of the while statement. The loop body, statement, is executed zero or more times.

Examples

$i = 1;
while ($i <= 10) {
  echo "$i\t".($i * $i)."\n"; // output a table of squares
  ++$i;
}
// -----------------------------------------
while (true) {

  if ($done)
    break;  // break out of the while loop

}

The do Statement

Syntax

  do-statement:
    do  statement  while  (  expression  )  ;

Defined elsewhere

(Note: There is no :/enddo alternate syntax.)

Constraints

The controlling expression expression must have type bool or be implicitly convertible to that type.

Semantics

First, statement is executed and then expression is tested. If its value is true, the process is repeated. If expression tests false, control transfers to the point immediately following the end of the do statement. The loop body, statement, is executed one or more times.

Examples

$i = 1;
do {
  echo "$i\t".($i * $i)."\n"; // output a table of squares
  ++$i;
}
while ($i <= 10);

The for Statement

Syntax

  for-statement:
    for   (   for-initializeropt   ;   for-controlopt   ;   for-end-of-loopopt   )   statement

  for-initializer:
    for-expression-group

  for-control:
    for-expression-group

  for-end-of-loop:
    for-expression-group

  for-expression-group:
    expression
    for-expression-group   ,   expression

Defined elsewhere

Note: Unlike C/C++, Hack does not support a comma operator, per se. However, the syntax for the for statement has been extended from that of C/C++ to achieve the same results in this context.

Constraints

The controlling expression—the right-most expression in for-control—must have type bool or be implicitly convertible to that type.

Semantics

The group of expressions in for-initializer is evaluated once, left-to-right, for their side effects. Then the group of expressions in for-control is evaluated left-to-right (with all but the right-most one for their side effects only), with the right-most expression's value being tested. If that tests true, statement is executed, and the group of expressions in for-end-of-loop is evaluated left-to-right, for their side effects only. Then the process is repeated starting with for-control. If the right-most expression in for-control tests false, control transfers to the point immediately following the end of the for statement. The loop body, statement, is executed zero or more times.

If for-initializer is omitted, no action is taken at the start of the loop processing. If for-control is omitted, this is treated as if for-control was an expression with the value true. If for-end-of-loop is omitted, no action is taken at the end of each iteration.

Examples

for ($i = 1; $i <= 10; ++$i) {
  echo "$i\t".($i * $i)."\n"; // output a table of squares
}
// -----------------------------------------
// omit 1st and 3rd expressions

$i = 1;
for (; $i <= 10;) {
  echo "$i\t".($i * $i)."\n"; // output a table of squares
  ++$i;
}
// -----------------------------------------
// omit all 3 expressions

$i = 1;
for (;;) {
  if ($i > 10)
    break;
  echo "$i\t".($i * $i)."\n"; // output a table of squares
  ++$i;
}
// -----------------------------------------
//  use groups of expressions

for ($a = 100, $i = 1; ++$i, $i <= 10; ++$i, $a -= 10) {
  echo "$i\t$a\n";
}

The foreach Statement

Syntax

  foreach-statement:
    foreach  (  foreach-collection-name  as  foreach-keyopt  foreach-value  )   statement
    foreach  (  foreach-collection-name  await as  foreach-keyopt  foreach-value  )   statement

  foreach-collection-name:
    expression

  foreach-key:
    expression  =>

  foreach-value:
    expression
    list-intrinsic

Defined elsewhere

Constraints

The variable designated by foreach-collection-name must be a collection.

Each expression must designate a variable name.

For the “await as” form, the type of foreach-collection-name must implement interface AsyncIterator or interface AsyncKeyedIterator.

Semantics

The foreach statement iterates over the set of elements in the collection designated by foreach-collection-name, starting at the beginning, executing statement each iteration. On each iteration, the value of the current element is assigned to the corresponding variable designated by foreach-value, provided foreach-value’s expression is not ($_; otherwise, the value of the current element is ignored. The loop body, statement, is executed zero or more times.

If foreach-key is present and its expression is $_, the current element's key value is ignored. If foreach-key is present and expression is not $_, the variable designated by its expression is assigned the current element's key value.

In the list-intrinsic case, a value that is an array is split into individual elements.

Examples

$colors = array("red", "white", "blue");
foreach ($colors as $color) {

};
// -----------------------------------------
foreach ($colors as $key => $color) {

}
// -----------------------------------------
// Modify the local copy of an element's value

foreach ($colors as $color) {
  $color = "black";
}
// -----------------------------------------
  $a = array('a' => 10, 'f' => 30);
  foreach ($a as $key => $_) { // 10 and 30 are ignored

  }
// -----------------------------------------
async function countdown1(int $start): AsyncIterator<int> {
  for ($i = $start; $i >= 0; --$i) {
    await \HH\Asio\usleep(1000000); // Sleep for 1 second
    yield $i;
  }
}

async function use_countdown1(): Awaitable<void> {
  $async_gen = countdown1(3);
  foreach ($async_gen await as $value) {
    // $value is of type int here
    // …
  }
}

async function countdown2(int $start): AsyncKeyedIterator<int, string> {
  for ($i = $start; $i >= 0; --$i) {
    await \HH\Asio\usleep(1000000);
    yield $i => (string)$i;
  }
}

async function use_countdown2(): Awaitable<void> {
  foreach (countdown2(3) await as $num => $str) {
    // $num is of type int, $str is of type string
    // …
  }
}

Jump Statements

General

Syntax

  jump-statement:
    continue-statement
    break-statement
    return-statement
    throw-statement

Defined elsewhere

The continue Statement

Syntax

  continue-statement:
    continue  ;

Constraints

A continue statement must not attempt to break out of a finally-block.

Semantics

A continue statement terminates the execution of the innermost enclosing iteration or switch statement.

A continue statement may break out of a construct that is fully contained within a finally-block.

Examples

for ($i = 1; $i <= 5; ++$i) {
  if (($i % 2) == 0)
    continue;
  echo "$i is odd\n";
}

The break Statement

Syntax

  break-statement:
    break  ;

Constraints

A break statement must not attempt to break out of a finally-block.

Semantics

A break statement terminates the execution of one or more enclosing iteration or switch statements.

A break statement may break out of a construct that is fully contained within a finally-block.

Examples

$i = 1;
for (;;) {
  if ($i > 10)
    break;

  ++$i;
}

The return Statement

Syntax

  return-statement:
    return  expressionopt  ;

Defined elsewhere

Constraints

The expression in a return-statement in a generator function must be the literal null.

A return statement must not occur in a finally-block or in the compound-statement of a function-definition for a function with return-type noreturn.

For a non-async function, the type of expression (or any implicitly returned null) must be assignment-compatible with the return type of the enclosing function. For an async function, the type of expression must be a subtype of the parameter type of the Awaitable return-type for the enclosing function. However, if Awaitable’s parameter type is void, expression must be omitted.

Semantics

A return statement from within a function terminates the execution of that function normally. If expression is omitted, for a non-async function, no value is returned. For a sync function, an object of type Awaitable<void> is returned. If expression is present, for a non-async function, the value of expression is returned by value. For a sync function, the value of expression is wrapped in an object of type Awaitable<T> (where T is the type of expression), which is returned.

If execution flows into the closing brace (}) of a function, return; is implied.

A function may have any number of return statements, whose returned values may have different types.

A return statement is permitted in a try-block and a catch-block.

Returning from a constructor or destructor behaves just like returning from a function having a return type of void.

A return statement inside a generator function causes the generator to terminate.

Return statements can be used in the body of anonymous functions.

Examples

function f(): int { return 100; } // f explicitly returns a value
function h(): void { }    // h implicitly returns null
// -----------------------------------------
// j returns one of three dissimilarly-typed values
function j(int $x): mixed {
  if ($x > 0) {
    return "Positive";
  } else if ($x < 0) {
    return -1;
  }
  // for zero, implied return null
}
// -----------------------------------------
class Point {
  private static int $pointCount = 0;
  public static function getPointCount(): int {
    return self::$pointCount;
  }

}

Implementation Notes

Although expression is a full expression, and there is a sequence point at the end of that expression, as stated in §§, a side effect need not be executed if it can be decided that no other program code relies on its having happened. (For example, in the cases of return $a++; and return ++$a;, it is obvious what value must be returned in each case, but if $a is a variable local to the enclosing function, $a need not actually be incremented.

The throw Statement

Syntax

  throw-statement:
    throw  expression  ;

Defined elsewhere

Constraints

The type of expression must be [\Exception or a subclass of that class.

expression must be such that an alias to it can be created. 

Semantics

A throw statement throws an exception immediately and unconditionally. Control never reaches the statement immediately following the throw. See §§ and §§ for more details of throwing and catching exceptions, and how uncaught exceptions are dealt with.

Rather than handle an exception, a catch-block may (re-)throw the same exception that it caught, or it can throw an exception of a different type.

Examples

throw new Exception;
throw new Exception("Some message", 123);
class MyException extends Exception { ... }
throw new MyException;

The try Statement

Syntax

  try-statement:
    try  compound-statement   catch-clauses
    try  compound-statement   finally-clause
    try  compound-statement   catch-clauses   finally-clause

  catch-clauses:
    catch-clause
    catch-clauses   catch-clause

  catch-clause:
    catch  (  type-specifier  variable-name  )  compound-statement

  finally-clause:
    finally   compound-statement

Defined elsewhere

Constraints

In a catch-clause the type referred to by the type-specifier must be \Exception or a type derived from that class.

Semantics

In a catch-clause, the variable-name designates an exception variable passed in by value. This variable corresponds to a local variable with a scope that extends over the catch-block. During execution of the catch-block, the exception variable represents the exception currently being handled.

Once an exception is thrown, the Engine searches for the nearest catch-block that can handle the exception. The process begins at the current function level with a search for a try-block that lexically encloses the throw point. All catch-blocks associated with that try-block are considered in lexical order. If no catch-block is found that can handle the run-time type of the exception, the function that called the current function is searched for a lexically enclosing try-block that encloses the call to the current function. This process continues until a catch-block is found that can handle the current exception. 

If a matching catch-block is located, the Engine prepares to transfer control to the first statement of that catch-block. However, before execution of that catch-block can start, the Engine first executes, in order, any finally-blocks associated with try-blocks nested more deeply than the one that caught the exception. 

If no matching catch-block is found, the behavior is implementation-defined.

Examples

function getTextLines(string $filename): Continuation<string> {
  $infile = fopen($filename, 'r');
  if ($infile == false) { /* deal with an file-open failure */ }
  try {
    while ($textLine = fgets($infile)) {  // while not EOF
      yield $textLine;  // leave line terminator attached
    }
  } finally {
    fclose($infile);
  }
}
// -----------------------------------------
class DeviceException extends Exception { … }
class DiskException extends DeviceException { … }
class RemovableDiskException extends DiskException { … }
class FloppyDiskException extends RemovableDiskException { … }

try {
  process(); // call a function that might generate a disk-related exception
}
catch (FloppyDiskException $fde) { … }
catch (RemovableDiskException $rde) { … }
catch (DiskException $de) { … }
catch (DeviceException $dve) { … }
finally { … }