if / elseif / else
<?php
if ($x > 0) {
echo "positive";
} elseif ($x < 0) {
echo "negative";
} else {
echo "zero";
}
while
<?php
$i = 0;
while ($i < 10) {
echo $i;
$i++;
}
do…while
<?php
$i = 0;
do {
$i++;
} while ($i < 10);
for
<?php
for ($i = 0; $i < 10; $i++) {
echo $i;
}
foreach
<?php
$arr = [1, 2, 3];
foreach ($arr as $value) {
echo $value . "\n";
}
// With key binding (indexed arrays)
foreach ($arr as $i => $value) {
echo "$i: $value\n";
}
// With key binding (associative arrays)
$map = ["name" => "Alice", "age" => "30"];
foreach ($map as $key => $value) {
echo "$key = $value\n";
}
// By-reference value binding mutates the source array element.
$nums = [1, 2, 3];
foreach ($nums as &$value) {
$value *= 2;
}
Use foreach ($arr as $key => &$value) when both the key and a mutable
element reference are needed. The key itself cannot be bound by reference.
By-reference value binding is currently supported only for array sources;
foreach ($iterator as &$value) over Iterator, IteratorAggregate, or
iterable-typed values is rejected at compile time. Use an array source or
iterate by value when consuming Traversable objects.
Untyped, mixed, and union-typed sources are dispatched at runtime. If the
runtime value is an indexed or associative array, both by-value and by-reference
value binding are supported. If the runtime value is an Iterator or
IteratorAggregate, it is iterated by value; by-reference value binding over
Traversable objects is rejected. Non-iterable runtime values produce a fatal
diagnostic.
foreach also accepts any object that implements the built-in Iterator
interface (current, key, next, valid, rewind) or the
IteratorAggregate interface (getIterator(): Traversable):
<?php
class Range implements Iterator {
private int $current;
private int $end;
public function __construct(int $start, int $end) {
$this->current = $start;
$this->end = $end;
}
public function rewind(): void {}
public function valid(): bool { return $this->current < $this->end; }
public function current(): mixed { return $this->current; }
public function key(): mixed { return $this->current; }
public function next(): void { $this->current = $this->current + 1; }
}
foreach (new Range(0, 5) as $i) {
echo $i;
}
The loop calls rewind() once, then on each iteration: valid() to test
continuation, current() and key() to bind the loop variables, and
next() after the body. Method dispatch uses class vtables for concrete
iterator classes and interface metadata for Iterator/IteratorAggregate
typed values. The iterable pseudo-type accepts arrays and these Traversable
objects.
break / continue
<?php
for ($i = 0; $i < 100; $i++) {
if ($i == 5) { break; }
if ($i % 2 == 0) { continue; }
echo $i . " ";
}
// Output: 1 3
Multi-level exits are supported with positive integer literal depths:
<?php
for ($row = 0; $row < 3; $row++) {
for ($col = 0; $col < 3; $col++) {
if ($row == 1 && $col == 1) {
break 2; // leaves both loops
}
}
}
The level counts enclosing loops and switch statements, matching PHP. break;
and continue; are equivalent to break 1; and continue 1;.
Inside a finally block, break and continue may only target loops or
switches created inside that same finally; jumping out of finally is
rejected, matching PHP.
switch / case / default
Standard PHP switch with fall-through semantics. Use break to prevent fall-through.
<?php
$x = 2;
switch ($x) {
case 1:
echo "one";
break;
case 2:
echo "two";
break;
default:
echo "other";
break;
}
Fall-through example:
<?php
$x = 1;
switch ($x) {
case 1:
case 2:
echo "one or two";
break;
default:
echo "other";
}
match expression
PHP 8 style match. No fall-through, returns a value, uses strict comparison (===).
<?php
$x = 2;
$result = match($x) {
1 => "one",
2 => "two",
3 => "three",
default => "other",
};
echo $result; // two
If no arm matches and there is no default, elephc aborts with a fatal runtime error.
try / catch / finally / throw
<?php
class DivisionByZeroException extends Exception {}
function divide($left, $right) {
if ($right == 0) {
throw new DivisionByZeroException();
}
return intdiv($left, $right);
}
try {
echo divide(10, 2) . PHP_EOL;
echo divide(10, 0) . PHP_EOL;
} catch (DivisionByZeroException $e) {
echo "caught" . PHP_EOL;
} finally {
echo "cleanup" . PHP_EOL;
}
Supported subset:
- built-in
ErrorandExceptionclasses and theThrowableinterface are available without declaring them ErrorandExceptionprovide$message,$code,__construct($message = "", $code = 0),getMessage(), andgetCode()- the SPL exception hierarchy is built-in:
LogicException,BadFunctionCallException,BadMethodCallException,DomainException,InvalidArgumentException,LengthException,OutOfRangeException,RuntimeException,OutOfBoundsException,OverflowException,RangeException,UnderflowException,UnexpectedValueException. Each is a marker subclass that inherits the constructor,$message, andgetMessage()fromException. Catch a specific type (InvalidArgumentException), an intermediate parent (LogicException), or the root (Exception/Throwable) throw <expr>;where<expr>has an object type implementingThrowablethrow <expr>can also be used inside expressions such as??and ternariescatch (ClassName $e)andcatch (TypeA | TypeB $e)for multi-catchcatch (Exception),catch (Error), or another throwable type without binding the exception variable- catch types must extend or implement
Throwable - multiple
catchclauses try { ... } finally { ... }return,break, andcontinuerun enclosingfinallyblocks before leavingbreakandcontinuewritten inside afinallyblock cannot target an outer loop orswitch