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-statement
- expression-statement
- function-static-declaration
- iteration-statement
- jump-statement
- labeled-statement
- selection-statement
- try-statement
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 { … }