skip to content

Statements & Expressions

A PHP script opens with <?php, keeps variables in function scope and compares with == or ===. Interviewers start here because loose comparison and scope rules still trip experienced developers.

part ofPHPoverview, primer and where to startread it →
on this pageshow

explore

questions

28

In PHP 8, what are the differences between a switch statement and a match expression?

level: juniorimportance: must knowfreq 74%

answer

  1. statement vs expression that returns
  2. == vs ===
  3. fall-through needs break; match never falls
  4. no arm matched: UnhandledMatchError
  5. comma-separated conditions, single expression per arm

basics

~20 s

switch is a statement that compares loosely (==) and falls through until a break. match, since PHP 8.0, is an expression that compares strictly (===), never falls through, returns a value and throws UnhandledMatchError when nothing matches.

solid answer

~40 s

`switch` is a **statement**: it compares its subject with each `case` using **loose `==`**, runs from the first matching case onward and keeps **falling through** into later cases until a `break`. With no matching case and no `default`, it silently does nothing. `match`, available since **PHP 8.0**, is an **expression** that evaluates to a value, compares with **strict `===`**, runs exactly one arm with no fall-through, and throws **`UnhandledMatchError`** when nothing matches and there is no `default`. Each `match` arm is a single expression, several conditions share one arm with commas (`2, 3 => 'In transit'`), and conditions are evaluated lazily, top to bottom. Use `match` to map a value to a result, such as a parcel status code to a label; keep `switch` for running several statements per case.

code

php · 25 lines
php
<?php
declare(strict_types=1);

$statusCode = 3;

switch ($statusCode) {            // loose ==, statement
    case 1:
        $label = 'Label created';
        break;
    case 2:
    case 3:                       // 2 and 3 share a body by falling through
        $label = 'In transit';
        break;
    default:
        $label = 'Unknown';
}

$label = match ($statusCode) {    // strict ===, expression
    1 => 'Label created',
    2, 3 => 'In transit',
    4 => 'Delivered',
    default => 'Unknown',
};

echo $label, PHP_EOL;             // In transit

go deeper

for a junior

Know that match returns a value, compares strictly with ===, needs no break and throws UnhandledMatchError when nothing matches.

for a middle

Explain fall-through in switch, lazy top-to-bottom evaluation of match conditions, comma-separated conditions and the match (true) idiom for ranges.

for a senior

Judge where the stricter comparison and exhaustiveness of match catch real bugs, such as unmapped status codes, and where input types must be normalised first.

for a principal

Set a codebase convention on when to use match, switch or polymorphism for branching on codes, weighing exhaustiveness errors against silent defaults.

## Two constructs for the same shape of problem Both `switch` and `match` take a **subject** value and compare it against a list of alternatives. That is where the similarity ends. `switch` is PHP's classic C-style statement; `match`, added in **PHP 8.0**, was designed to remove the traps of `switch` for the common case of "turn this value into that value". ## Side-by-side | Aspect | `switch` | `match` | |---|---|---| | Kind | statement | expression, evaluates to a value | | Comparison | loose `==` | strict `===` | | After a match | falls through to later cases until `break` | runs one arm only | | Nothing matches, no `default` | does nothing | throws `UnhandledMatchError` | | Arm body | any number of statements | a single expression | | Several values, one body | stacked `case 2: case 3:` | `2, 3 =>` | | Available since | always | PHP 8.0 | ## Loose versus strict comparison `switch` compares with `==`, so PHP's type-juggling rules apply. A string `"1"` from a query parameter matches `case 1:`; two numeric strings compare as numbers, so `"1"` even matches `case "01":` first if that case comes earlier. `match` compares with `===`: `"1"` does not match the arm `1 =>`. That makes `match` predictable, and it also means the types of your data must be right before you match. ## Fall-through In a `switch`, execution starts at the matching `case` and continues through every following `case` body until it hits `break` (or `return`, `continue`, `throw`). Forgetting a `break` is a classic bug: status 1 prints "Label created" *and* "In transit". Fall-through is sometimes deliberate, to share one body between cases, but it must be read carefully. `match` has no fall-through at all; shared bodies are written with commas. ## Exhaustiveness A `match` must handle its subject. If no condition is identical to the subject and there is no `default` arm, PHP throws **`UnhandledMatchError`**, a subclass of `Error`, with a message such as `Unhandled match case 5`. A `switch` without a `default` simply skips everything. The error is a feature: a new parcel status that nobody mapped fails loudly instead of producing an empty label. ## Expression semantics Because `match` is an expression, it is assigned or returned directly: ```php $label = match ($statusCode) { 1 => 'Label created', 2, 3 => 'In transit', 4 => 'Delivered', default => 'Unknown', }; ``` A few rules follow from being an expression: - Each arm holds **one expression**. For several steps, call a function, or use a `throw` expression (PHP 8.0 made `throw` an expression) to fail in an arm. - Conditions are evaluated **in order and lazily**: a condition is only evaluated if every earlier one failed, and only the winning arm's result is evaluated. - A `match` used as a statement on its own still needs a terminating semicolon. - Two `default` arms are a compile error. ## Ranges and other non-identity checks For conditions that are not "equals this value", the idiom is `match (true)` with boolean conditions: ```php $band = match (true) { $weightKg < 2 => 'small', $weightKg < 10 => 'medium', default => 'large', }; ``` The first arm whose condition is identical to `true` wins. With `switch (true)` the same trick works but compares loosely, so a truthy non-boolean case expression would also match. ## Choosing between them 1. Mapping a value to a value, such as a status code to a label: **`match`**. 2. Several statements per branch, or deliberate shared fall-through: **`switch`**, or `if`/`elseif`. 3. Input whose type is uncertain (strings from a request or a database): cast it first, then `match`; do not rely on `switch`'s loose comparison to paper over types. A PHP 8.5 detail on the old form: ending a `case` label with a semicolon (`case 1;`) instead of a colon was always accepted and is now deprecated.

  • What happens in PHP when a switch case has no break?
    Execution falls through: after the matching case's statements, PHP keeps running the next case bodies, without testing their labels, until it reaches a `break`, `return`, `continue`, `throw` or the end of the switch. That is useful for deliberately sharing a body, but an accidental missing `break` runs code meant for other values.
  • Can a match arm run several statements?
    Not directly: an arm's right-hand side is a single expression. Put the steps in a function or method and call it from the arm, or use an immediately invoked closure. To fail inside an arm, use a `throw` expression, which PHP 8.0 made legal in expression position.
  • How do you express a range check such as weight below 2 kg with match?
    Use `match (true)` and write boolean conditions as the arms: `$weight < 2 => 'small', $weight < 10 => 'medium', default => 'large'`. The first condition identical to `true` wins. Because the comparison is strict, a condition must evaluate to real `true`, not just a truthy value.

saying these in an interview costs you the question

  • Says match compares with == just like switch does.
  • Believes a switch without a default throws an error when nothing matches.
  • Claims match arms fall through to the next arm without a break.
  • Thinks match is available in PHP 7.4.
  • Says a match expression without default returns null when no arm matches.
open as a page

In PHP 8, what is the difference between the == and === operators, and why do most codebases default to ===?

level: juniorimportance: must knowfreq 88%

basics

~20 s

== compares values after type juggling, so an int and a string can be equal; === is true only when type and value both match. Loose rules still surprise after PHP 8.0, so === is the safe default.

open as a page

In PHP, what are the differences between echo and print, and why is neither of them a function?

level: juniorimportance: must knowfreq 62%

basics

~10 s

Both output strings and are language constructs, not functions. echo accepts several comma-separated arguments and returns nothing; print accepts one argument and always returns 1, so it can appear inside an expression.

open as a page

Why do PHP style guides say to omit the closing ?> tag in files that contain only PHP code?

level: juniorimportance: must knowfreq 55%

basics

~20 s

Anything after a closing ?> is output, and PHP swallows only the one newline right after the tag. A stray blank line in an included class file then corrupts responses or blocks later headers; omitting ?> removes the risk.

open as a page

In PHP, why can't a helper function read a config variable defined at the top of the script, and how should it get the value?

level: juniorimportance: must knowfreq 58%

basics

~20 s

PHP has function scope and global scope, and a function sees only its own variables, so an outer $config is undefined inside it. Pass the value as a parameter; global and $GLOBALS work but hide the dependency.

open as a page

In PHP, what is the difference between isset(), empty() and is_null(), and when does each return true?

level: juniorimportance: must knowfreq 72%

basics

~20 s

isset() is true when a variable exists and is not null. empty() is true when it is missing or falsy, such as 0, "0", "" or []. is_null() is a function, true only for null, and warns on undefined variables.

open as a page

In PHP, what is the difference between defining a global constant with define() and with the const keyword?

level: middleimportance: must knowfreq 62%

basics

~20 s

const is a top-level statement with a constant-expression value, prefixed with the current namespace. define() is a function call that runs anywhere, accepts any runtime value and uses its string name exactly as given, with no namespace prefix.

open as a page

In PHP, why does a foreach by value that reuses the variable of an earlier foreach by reference corrupt the last element?

level: middleimportance: must knowfreq 56%

basics

~20 s

After foreach ($arr as &$value), $value remains a reference to the last element. A later by-value foreach reusing $value writes each element into that slot, leaving it equal to the second-to-last value. unset($value) after the first loop prevents it.

open as a page

In PHP, how do the ??, ?:, ??= and ?-> operators differ, and when does each one fall back?

level: middleimportance: must knowfreq 70%

basics

~20 s

?? falls back when the left side is null or undefined, without a warning; ?: falls back on any falsy value; ??= assigns only when null or unset; ?-> returns null for a null object and skips the rest of the chain.

open as a page

In PHP, what are magic constants such as __DIR__, __CLASS__ and __METHOD__, and how do they differ from ordinary constants?

level: juniorimportance: should knowfreq 48%

basics

~20 s

Magic constants are compiler keywords replaced with facts about where they are written: file, directory, line, function, class, trait, method, namespace or property. They vary by location, are case-insensitive and are invisible to defined() and constant().

open as a page

In PHP, how do constant() and defined() resolve a constant name built at runtime inside a namespace, and what happens when it is missing?

level: middleimportance: should knowfreq 30%

basics

~20 s

constant() and defined() treat their string argument as a fully qualified name: the current namespace is not added and there is no fallback. constant() throws Error for a missing constant since PHP 8.0; defined() just returns false.

open as a page

In PHP, how do break N and continue N work in nested loops, and why does continue inside a switch behave like break?

level: middleimportance: should knowfreq 26%

basics

~20 s

break N leaves N enclosing loops or switches; continue N jumps to the next iteration of the Nth one. switch counts as a level, so a plain continue inside it acts like break, and PHP warns you to use continue 2.

open as a page

In PHP 7 and later, what happens when you add or change array elements inside a foreach loop over that same array?

level: middleimportance: should knowfreq 32%

basics

~20 s

By value, foreach iterates the array as it stood when the loop began, so changes made in the body are not visited. By reference, it follows the live array and visits appended elements. Since PHP 7.0 foreach never moves the internal pointer.

open as a page

In PHP, what does the <=> spaceship operator return, and how do you use it to sort by several fields?

level: middleimportance: should knowfreq 45%

basics

~20 s

$a <=> $b returns an int below, equal to or above zero when $a is less than, equal to or greater than $b. Comparing arrays of fields, [$a->x, $a->y] <=> [$b->x, $b->y], gives a multi-key comparator for usort().

open as a page

In a PHP template such as a restaurant menu, how does the colon-style alternative syntax (if: ... endif;) work, and what are its traps?

level: middleimportance: should knowfreq 32%

basics

~20 s

The opening brace becomes a colon and the closing brace an end keyword such as endif; or endforeach;, which keeps templates readable. Traps: write elseif, never mix braces and colons, and output nothing between switch: and its first case.

open as a page

In PHP, how do var_dump(), print_r() and var_export() differ, and when would you use each one?

level: middleimportance: should knowfreq 48%

basics

~20 s

var_dump() prints types, lengths and values and cannot return a string. print_r() prints a readable view, hides types, and can return a string. var_export() prints valid PHP code, can return it, and rejects circular references.

open as a page

In PHP, what is the difference between the <?= short echo tag and short open tags, and which one is safe to use?

level: middleimportance: should knowfreq 36%

basics

~10 s
<?= is shorthand for <?php echo and always works. The bare <? open tag works only when short_open_tag is enabled, which the shipped php.ini files turn off, so portable code uses <?php and <?=.
open as a page

In PHP, how does the global keyword work under the hood, and what did PHP 8.1 change about writing to $GLOBALS?

level: middleimportance: should knowfreq 38%

basics

~20 s

global $x creates a local reference to the global $x, like $x = &$GLOBALS['x'], so unsetting or rebinding the local name leaves the global alone. Since PHP 8.1, $GLOBALS cannot be written as a whole, only element by element.

open as a page

In PHP, what does a static local variable inside a function do, how long does it live, and what changed in PHP 8.1 and 8.3?

level: middleimportance: should knowfreq 42%

basics

~20 s

A static local keeps its value between calls, and its initialiser runs only on the first call; it lives until the request ends. PHP 8.3 allows any expression as initialiser; since 8.1 inherited methods share it with the parent.

open as a page

When upgrading legacy code to PHP 8.5, what happens when a script reads an undefined constant or declares a constant a second time?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Reading an undefined constant throws an Error since PHP 8.0; PHP 7 turned the bare word into a string. Redeclaring one keeps the first value, warns that PHP 9 will make it an error, and makes define() return false.

open as a page

After replacing a switch with match in a PHP 8 parcel tracker, status codes read from the database start throwing UnhandledMatchError. Why, and how do you fix it safely?

level: seniorimportance: should knowfreq 30%

basics

~20 s

switch compared the database string '2' loosely with case 2; match uses ===, so '2' matches no int arm and throws UnhandledMatchError. Validate and cast at the boundary, then decide deliberately how unknown codes should fail.

open as a page

A PHP checkout checks coupons with $input == $coupon->code and accepts codes that should fail; which loose-comparison rules cause it, and did PHP 8 fix them?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Loose == accepts numeric look-alikes ("1e2" == "100"), a JSON true against any ordinary code, and null against an empty code. PHP 8.0 fixed only int 0 matching a non-numeric code; require a string and compare with ===.

open as a page

In PHP, why does $ok = validate($code) and redeem($code); leave $ok holding only the validation result, and which other precedence traps catch developers?

level: seniorimportance: should knowfreq 38%

basics

~20 s

and, or and xor bind more loosely than =, so the line runs as ($ok = validate($code)) and redeem($code), discarding redeem's result. && and || bind tighter than =. Nested unparenthesised ternaries and == inside bitwise & are the other classic traps.

open as a page

In PHP, what does assignment by reference with =& do, and why can a reference taken to an array element leak changes into a copy of that array?

level: seniorimportance: should knowfreq 30%

basics

~20 s

$b = &$a makes two names share one value. A reference to an array element turns that slot into a shared reference, and copying the array copies the shared slot, so writing the copy changes the original.

open as a page

In PHP 8.5, what may appear on the right-hand side of a global const declaration, including the casts and closures added in 8.5?

level: middleimportance: nice to knowfreq 16%

basics

~20 s

A const value must be a constant expression: literals, arrays, operators, other constants, enum cases and, since 8.1, new. PHP 8.5 adds casts, static closures without use() and first-class callables; ordinary function calls and variables are still rejected.

open as a page

In PHP 8.5, what do 7 / 2, 6 / 2, 1 / 0 and -2 ** 2 evaluate to, and why?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

7 / 2 is float(3.5) and 6 / 2 is int(3), because / returns an int only when two ints divide exactly; 1 / 0 throws DivisionByZeroError since PHP 8.0; -2 ** 2 is int(-4) because ** binds tighter than unary minus.

open as a page

In PHP, what are variable variables such as $$name, what are their syntax rules, and why do most codebases avoid them?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

$$name uses the value of $name as the name of another variable, so $name = 'temp'; $$name = 21; sets $temp. Braces resolve array ambiguity. Codebases avoid them because tools cannot follow them and user-controlled names can overwrite any variable.

open as a page

When is PHP's var_export() the right tool for generating PHP code, such as a cached config file, and what are its limits?

level: seniorimportance: nice to knowfreq 16%

basics

~20 s

var_export($value, true) returns a PHP literal, so a file containing '<?php return ' plus that literal is a cache that include loads directly. It suits arrays, scalars, enums and stdClass; other objects need __set_state(), and cycles export as NULL.

open as a page