Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

PHP (8.5)  Module 3 – Control flow

if, else, elseif and the ternary operator

Branch with PHP's if, elseif and else, return early with guard clauses, write the ternary operator safely and use the colon syntax in HTML templates.

Free lesson, Pro answers: 15 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Write if, elseif and else chains that test their conditions in the right order
  • Refactor nested conditions into guard clauses that return early
  • Use the ternary operator, and parenthesise or replace a nested one
  • Write template branches in the colon syntax, with elseif in one word
  • Avoid the precedence trap of and and or in assignments

Before you start

What you’ll learn · Before you start

What you will learn

  • Write if, elseif and else chains that test their conditions in the right order
  • Refactor nested conditions into guard clauses that return early
  • Use the ternary operator, and parenthesise or replace a nested one
  • Write template branches in the colon syntax, with elseif in one word
  • Avoid the precedence trap of and and or in assignments

Before you start

On this page

A shop delivers free once an order reaches 999 rupees, and otherwise charges 40 rupees in a metro city and 70 elsewhere. That makes three rules, and for every order PHP must run exactly one of them: the job of if, elseif and else, which test their conditions from the top and run the first block whose condition is true.

This lesson writes that choice in four forms and runs each on PHP 8.5.11: an elseif chain, guard clauses that return early, the ternary operator where one of two values is enough, and the colon syntax of HTML templates. It also shows three mistakes that look right: tests in the wrong order, a nested ternary without brackets, which PHP 8 refuses to compile, and and written where && was meant.

elseif tests conditions in order, and the first true one wins

deliveryFee() below decides the fee in one chain. PHP evaluates the condition after if and turns it into a bool, as the lesson on booleans showed. When it is true, PHP runs that block and skips every elseif and else after it. When it is false, PHP moves down to the next elseif, and else, which has no condition, runs only when nothing above it was true. Step through four orders and watch which test decides each one:

Interactive · step by step Four orders through one if, elseif and else Step through the four orders of delivery_fee.php. Each step names the test PHP is at and what it found.
Four orders through one if, elseif and else, step 7 of 7. Showing if: 99,900 or more?, $fee = 0, elseif: $isMetro?, $fee = 4_000, else, $fee = 7_000. In focus: if: 99,900 or more?, $fee = 0.truefalsetruefalseif: 99,900 or more?paise in the order$fee = 0free deliveryelseif: $isMetro?a metro city$fee = 4_00040 rupeeselseevery other order$fee = 7_00070 rupeestrue

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. Order 1: 500.00, metro. deliveryFee(50_000, true) starts at the top. The if asks whether 50,000 paise is at least 99,900. It is not, so PHP skips the block under the if.
  2. Order 1: the elseif. PHP moves down to the elseif and tests $isMetro. This order goes to a metro city, so the condition is true.
  3. Order 1: fee 40.00. The first true condition wins: $fee becomes 4,000 paise, 40 rupees. PHP skips the else without looking at it and leaves the chain.
  4. Order 2: 998.99, non-metro. deliveryFee(99_899, false) is one paisa short of the free-delivery mark, so the if is false, and $isMetro is false too.
  5. Order 2: the else runs. No condition above it was true, so the else runs: $fee becomes 7,000 paise, 70 rupees. The else has no condition of its own.
  6. Order 3: 999.00, non-metro. deliveryFee(99_900, false) is exactly on the mark. >= includes the boundary, so the first test is true and $fee becomes 0.
  7. Order 4: 1,500.00, metro. deliveryFee(150_000, true) makes both the first test and $isMetro true. Only the first true branch runs: the fee is 0, and the elseif is never even tested.

The program behind the figure prints the same four orders:

delivery_fee.php: four orders through one if, elseif and else PHP · delivery_fee.php
<?php

// Amounts are whole paise (100 paise = 1 rupee). Orders of 999 rupees or more ship free.
function deliveryFee(int $orderPaise, bool $isMetro): int
{
    if ($orderPaise >= 99_900) {
        $fee = 0;
    } elseif ($isMetro) {
        $fee = 4_000;
    } else {
        $fee = 7_000;
    }

    return $fee;
}

function rupees(int $paise): string
{
    return number_format(intdiv($paise, 100)) . sprintf('.%02d', $paise % 100);
}

function show(int $orderPaise, bool $isMetro, string $city): void
{
    printf("Order %8s, %-10s fee %5s\n", rupees($orderPaise), $city, rupees(deliveryFee($orderPaise, $isMetro)));
}

show(50_000, true, 'metro:');
show(99_899, false, 'non-metro:');
show(99_900, false, 'non-metro:');
show(150_000, true, 'metro:');

Output

Order   500.00, metro:     fee 40.00
Order   998.99, non-metro: fee 70.00
Order   999.00, non-metro: fee  0.00
Order 1,500.00, metro:     fee  0.00

Recorded with PHP 8.5.11 (cli) (NTS) on macOS 26 arm64. To run it yourself: php delivery_fee.php

The last order, 1,500 rupees to a metro city, satisfies both the first test and $isMetro, and its fee is 0.00, because only the first true branch runs. That rule makes the order of the tests part of the logic: when one condition is wider than another, the narrow one must come first. This program asks the same two discount questions in both orders:

discount_order.php: the same two tests in two orders PHP · discount_order.php
<?php

// 10 % off from 5,000 rupees, 5 % off from 2,000 rupees (amounts in paise).
function discountRight(int $totalPaise): int
{
    if ($totalPaise >= 500_000) {
        $percent = 10;
    } elseif ($totalPaise >= 200_000) {
        $percent = 5;
    } else {
        $percent = 0;
    }

    return $percent;
}

// The same tests, the wider one first.
function discountWrong(int $totalPaise): int
{
    if ($totalPaise >= 200_000) {
        $percent = 5;
    } elseif ($totalPaise >= 500_000) {
        $percent = 10;
    } else {
        $percent = 0;
    }

    return $percent;
}

function show(int $totalPaise): void
{
    printf(
        "%8s rupees: right order %2d %%, wrong order %2d %%\n",
        number_format(intdiv($totalPaise, 100)),
        discountRight($totalPaise),
        discountWrong($totalPaise),
    );
}

show(150_000);
show(200_000);
show(499_900);
show(500_000);
show(800_000);

Output

   1,500 rupees: right order  0 %, wrong order  0 %
   2,000 rupees: right order  5 %, wrong order  5 %
   4,999 rupees: right order  5 %, wrong order  5 %
   5,000 rupees: right order 10 %, wrong order  5 %
   8,000 rupees: right order 10 %, wrong order  5 %

Recorded with PHP 8.5.11 (cli) (NTS) on macOS 26 arm64. To run it yourself: php discount_order.php

With >= 200_000 first, every total of 5,000 rupees or more stops at the 5 % branch, so the 10 % branch can never run, and PHP gives no warning about it. The two versions agree below 5,000 rupees and differ from exactly 5,000 up, which is why tests belong at the boundaries: one value just below each threshold and one exactly on it.

An else that belongs to another if

Without braces, an else belongs to the nearest if, whatever the indentation says. In if ($a) if ($b) echo 'b'; else echo 'c'; the else is part of if ($b), so nothing prints when $a is false. PSR-12 asks for braces around every block, which removes the question.

Checkpoint

Check: if, elseif and else, in order

  1. Question 1 of 2 In delivery_fee.php, which branch sets the fee for deliveryFee(150_000, true), an order of 1,500 rupees to a metro city?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The if: $fee = 0

    PHP tests the conditions from the top and stops at the first true one. The figure's last step and the program's last line both show a fee of 0.00 for this order.

  2. Question 2 of 2 A shop gives free gift wrap from 3,000 rupees and a free gift from 10,000 rupees, and one order gets one of them at most. Which chain gives a 12,000-rupee order the free gift?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: if ($total >= 1_000_000) { … gift } elseif ($total >= 300_000) { … wrap }

    When one condition is wider than another, test the narrow one first, or write conditions that cannot overlap. The wrong order gives no warning, as discount_order.php showed.

Guard clauses return early instead of nesting

Rules pile up. Once the shop also refuses orders of 0 paise, a natural first draft puts one if inside another, and the fee ends up three levels deep. A guard clause: An if at the top of a function that handles one special case, such as an invalid input, and leaves at once with return or throw, so the main rule can follow without nesting. handles one special case at the top of the function and leaves at once with return or throw, so the main rule comes last, with no nesting at all. Both versions below make the same four calls; compare their shape:

The same fee rules, nested and with guard clauses

Nested · fee_nested.php

<?php

function deliveryFee(int $orderPaise, bool $isMetro): int
{
    if ($orderPaise > 0) {
        if ($orderPaise < 99_900) {
            if ($isMetro) {
                return 4_000;
            } else {
                return 7_000;
            }
        } else {
            return 0;
        }
    } else {
        throw new InvalidArgumentException("An order must cost more than 0 paise, got $orderPaise");
    }
}

echo deliveryFee(99_899, true), "\n";
echo deliveryFee(99_899, false), "\n";
echo deliveryFee(99_900, false), "\n";
echo deliveryFee(0, true), "\n";

Output (exit status 255)

4000
7000
0

Fatal error: Uncaught InvalidArgumentException: An order must cost more than 0 paise, got 0 in fee_nested.php:16
Stack trace:
#0 fee_nested.php(23): deliveryFee(0, true)
#1 {main}
  thrown in fee_nested.php on line 16

Recorded with PHP 8.5.11 (cli) (NTS) on macOS 26 arm64. To run it yourself: php fee_nested.php

Guard clauses · fee_guards.php

<?php

function deliveryFee(int $orderPaise, bool $isMetro): int
{
    if ($orderPaise <= 0) {
        throw new InvalidArgumentException("An order must cost more than 0 paise, got $orderPaise");
    }
    if ($orderPaise >= 99_900) {
        return 0;
    }
    if ($isMetro) {
        return 4_000;
    }

    return 7_000;
}

echo deliveryFee(99_899, true), "\n";
echo deliveryFee(99_899, false), "\n";
echo deliveryFee(99_900, false), "\n";
echo deliveryFee(0, true), "\n";

Output (exit status 255)

4000
7000
0

Fatal error: Uncaught InvalidArgumentException: An order must cost more than 0 paise, got 0 in fee_guards.php:6
Stack trace:
#0 fee_guards.php(21): deliveryFee(0, true)
#1 {main}
  thrown in fee_guards.php on line 6

Recorded with PHP 8.5.11 (cli) (NTS) on macOS 26 arm64. To run it yourself: php fee_guards.php

Both printed 4000, 7000 and 0, then stopped at the fourth call with the same InvalidArgumentException: the refusal of an empty order. In the nested version the throw sits at the bottom, far from the test it answers, and the else branches push every rule further right. In the guard version each rule is one if with its result beside it. An else after a return is never needed, because the function has already left by then.

The flat version is also easier to test. Each guard is one boundary: 0 paise, 99,900 paise and 99,899 paise to a metro city exercise one guard each, and 99,899 paise anywhere else reaches the last line.

Key idea

Handle the special cases first and leave the function as soon as its answer is known: what is left at the bottom is the normal case, read without any nesting.

Checkpoint

Check: Guard clauses that return early

  1. Question 1 of 2 The shop now refuses delivery to blocked pin codes. Where does that check go in the guard version of deliveryFee()?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: At the top, with the other refusal, as its own if that throws

    Each guard clause handles one special case at the top and returns or throws at once; the main fee rules come last.

  2. Question 2 of 2 Why does the guard version of deliveryFee() need no else?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Each if ends with return or throw, so the code after it runs only when its condition was false

    Returning early makes "otherwise" the next line of the function. Both tabs printed 4000, 7000 and 0, then the same exception.

The ternary operator picks one of two values

When each branch only produces a value, a whole if and else block is more than you need. The ternary operator: The operator condition ? a : b, an expression that evaluates to a when the condition is true and to b otherwise. Its short form a ?: b gives a itself when a counts as true. condition ? a : b is an expression: Code that produces a value, such as 2 + 3, $price * 2, a function call or a ternary. A statement such as if or echo does something instead, so it cannot stand on the right of =.: it evaluates to a when the condition is true and to b otherwise, so it can stand inside an echo or on the right of =. Its short form a ?: b gives a itself when a counts as true, which suits a default for an empty name:

ternary.php: three choices made inline PHP · ternary.php
<?php

$items = 1;
echo "$items ", $items === 1 ? 'item' : 'items', " in your cart\n";

$items = 3;
echo "$items ", $items === 1 ? 'item' : 'items', " in your cart\n";

$nickname = '';
echo 'Hello, ', $nickname ?: 'guest', "\n";

$stock = 0;
$label = $stock === 0 ? 'Sold out' : ($stock < 5 ? 'Low stock' : 'In stock');
echo $label, "\n";

Output

1 item in your cart
3 items in your cart
Hello, guest
Sold out

Recorded with PHP 8.5.11 (cli) (NTS) on macOS 26 arm64. To run it yourself: php ternary.php

The ternary and the two shorthands that look like it differ in what they test:

Written Gives Falls back when the left side is
$cond ? $a : $b $a or $b $cond is false (any falsy value)
$a ?: $b $a or $b $a is falsy: 0, '', '0', null, [], false
$a ?? $b $a or $b $a is null or does not exist, with no warning

Stacking ternaries is where PHP differs from most languages. Before PHP 8.0, a chain without brackets grouped from the left, so a ? b : c ? d : e meant (a ? b : c) ? d : e, which few readers expect. PHP 7.4 deprecated the form, and PHP 8.0 refuses to compile it:

nested_ternary.php: two ternaries without brackets PHP · nested_ternary.php
<?php

$stock = 0;
$label = $stock === 0 ? 'Sold out' : $stock < 5 ? 'Low stock' : 'In stock';
echo $label, "\n";

Output (exit status 255)


Fatal error: Unparenthesized `a ? b : c ? d : e` is not supported. Use either `(a ? b : c) ? d : e` or `a ? b : (c ? d : e)` in nested_ternary.php on line 4
Stack trace:
#0 {main}

Recorded with PHP 8.5.11 (cli) (NTS) on macOS 26 arm64. To run it yourself: php nested_ternary.php

The message names both fixes. The last line of ternary.php used the second one, brackets around the inner ternary, and printed Sold out. Nothing of nested_ternary.php ran, not even the line before the faulty one, because PHP found the mistake while compiling the file. For three or more outcomes a match expression, the subject of the next lesson, reads better than any chain of ternaries.

Nested ternaries in older code

PHP 7.4 deprecated nested ternaries without brackets, and PHP 8.0 made the operator non-associative, so such code no longer compiles. Code written for PHP 7.3 or older may still contain them: add the brackets the message suggests, in the grouping the code meant.

Checkpoint

Check: The ternary operator and nesting

  1. Question 1 of 2 What does this print?

    Read the code, then choose one answer.

    <?php
    $count = 0;
    echo $count, ' ', $count === 1 ? 'seat' : 'seats', "\n";
    Show the answer: Question 1 of 2

    Answer: 0 seats

    condition ? a : b gives b when the condition is false. ternary.php printed "3 items" the same way.

  2. Question 2 of 2 PHP 8.5 refuses $a ? 'x' : $b ? 'y' : 'z'. Which version compiles and means "x if $a is true, else y if $b is true, else z"?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: $a ? 'x' : ($b ? 'y' : 'z')

    Since PHP 8.0 the ternary operator is non-associative, so a nested one needs brackets. For three or more outcomes a match expression reads better.

Templates use the colon syntax, with elseif in one word

In a file that is mostly HTML, closing braces get lost between the tags. PHP’s alternative syntax: The colon form of if, while, for, foreach and switch that templates use: the opening brace becomes a colon, and the closing brace becomes endif;, endwhile;, endfor;, endforeach; or endswitch;. replaces the opening brace with a colon and the closing brace with a keyword that names the block: endif;, endforeach;, endwhile;, endfor; or endswitch;. This template prints a card for each of three products. foreach, which the loops lesson of this module covers, repeats the card, and the if inside it picks one of three lines:

cards.php: product cards with if, elseif and else in a template PHP · cards.php
<?php

$products = [
    ['name' => 'Steel bottle', 'price' => 54_900, 'stock' => 0, 'offer' => 0],
    ['name' => 'Desk lamp', 'price' => 129_900, 'stock' => 8, 'offer' => 10],
    ['name' => 'Notebook & pen', 'price' => 24_900, 'stock' => 40, 'offer' => 0],
];

function rupees(int $paise): string
{
    return number_format(intdiv($paise, 100)) . sprintf('.%02d', $paise % 100);
}
?>
<?php foreach ($products as $p): ?>
<article class="card">
  <h2><?= htmlspecialchars($p['name']) ?></h2>
<?php if ($p['stock'] === 0): ?>
  <p class="status">Sold out</p>
<?php elseif ($p['offer'] > 0): ?>
  <p class="price"><?= rupees(intdiv($p['price'] * (100 - $p['offer']), 100)) ?> (<?= $p['offer'] ?> % off)</p>
<?php else: ?>
  <p class="price"><?= rupees($p['price']) ?></p>
<?php endif; ?>
</article>
<?php endforeach; ?>

Output

<article class="card">
  <h2>Steel bottle</h2>
  <p class="status">Sold out</p>
</article>
<article class="card">
  <h2>Desk lamp</h2>
  <p class="price">1,169.10 (10 % off)</p>
</article>
<article class="card">
  <h2>Notebook &amp; pen</h2>
  <p class="price">249.00</p>
</article>

Recorded with PHP 8.5.11 (cli) (NTS) on macOS 26 arm64. To run it yourself: php cards.php

Each card took exactly one branch: the bottle is sold out, the lamp shows its offer price and the notebook its plain price. <?= … ?> prints a value, and htmlspecialchars() turned the & of “Notebook & pen” into &amp;, so a browser shows it as text rather than reading it as markup. One rule of the colon syntax catches people who learned the brace form first:

colon_else_if.php: else if in the colon syntax PHP · colon_else_if.php
<?php $stock = 0; ?>
<?php if ($stock > 5): ?>
<p>In stock</p>
<?php else if ($stock > 0): ?>
<p>Low stock</p>
<?php else: ?>
<p>Sold out</p>
<?php endif; ?>

Output (exit status 255)


Parse error: syntax error, unexpected token "if", expecting ":" in colon_else_if.php on line 4

Recorded with PHP 8.5.11 (cli) (NTS) on macOS 26 arm64. To run it yourself: php colon_else_if.php

With braces, else if is an else whose statement happens to be another if, and it behaves exactly like elseif. In the colon syntax PHP expects else: and finds if, so the file does not compile. Write elseif in one word in both syntaxes, as PSR-12 recommends, and never mix braces and colons in one block, which the manual says is not supported.

Checkpoint

Check: The colon syntax in templates

  1. Question 1 of 2 Why does <?php else if ($stock > 0): ?> fail in a template written in the colon syntax?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: In the colon syntax PHP expects else: and finds if; only the one-word elseif works there

    With braces, else if is an else whose statement is another if. In the colon syntax that reading is not possible, so write elseif in one word everywhere.

  2. Question 2 of 2 A template opens a block with <?php if ($user): ?> and closes it with <?php } ?>. What happens?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: A parse error: a block opened with a colon must close with endif;

    Close each colon-syntax block with its own keyword: endif;, endforeach;, endwhile;, endfor; or endswitch;.

and, or and xor bind after the assignment

PHP has two spellings for two of its logical operators: && and and, || and or. They compute the same truth values but sit at very different places in the precedence table, and the gap matters as soon as an assignment is in the line:

and_or.php: one condition with and, with && and with brackets PHP · and_or.php
<?php

$inStock = true;
$addressOk = false;

$canShip = $inStock and $addressOk;
var_dump($canShip);

$canShip = $inStock && $addressOk;
var_dump($canShip);

$canShip = ($inStock and $addressOk);
var_dump($canShip);

Output

bool(true)
bool(false)
bool(false)

Recorded with PHP 8.5.11 (cli) (NTS) on macOS 26 arm64. To run it yourself: php and_or.php

The first var_dump() printed bool(true) although the address check failed. operator precedence: The order in which PHP groups the operators of an expression: an operator that binds more tightly takes its operands first, as * does before +, and = does before and. decides which operator takes its operands first, and = comes before and, so PHP ran ($canShip = $inStock) and $addressOk: it stored true, then computed true and false and threw that result away. && comes before =, so the second line stored the value of the whole condition, false, and so did the third, where brackets set the grouping by hand. The operators of this lesson, in the order they bind:

Order Operators, from the first to bind to the last
1 !
2 comparisons such as <, >=, == and ===
3 &&, then ||
4 ??, then ? :
5 = and the assignments such as +=
6 and, then xor, then or

The manual’s own example shows the same with or: after $f = false or true; the variable holds false. Use && and || in conditions and assignments. or survives mostly in the old idiom $file = fopen($path, 'r') or die('…');, which works only because the assignment happens first and or then looks at what was stored. Lines like that one are why PHP-CS-Fixer, whose logical_operators rule rewrites and and or as && and ||, marks the rule as risky: check each line it changes.

Checkpoint

Check: and, or and operator precedence

  1. Question 1 of 2 What does this print?

    Read the code, then choose one answer.

    <?php
    $ok = false or true;
    var_dump($ok);
    Show the answer: Question 1 of 2

    Answer: bool(false)

    The PHP Manual shows exactly this: $f = false or true acts like ($f = false) or true. and_or.php showed the same with and.

  2. Question 2 of 2 Why does $file = fopen($path, 'r') or die('No file'); do what its author meant, while $canShip = $inStock and $addressOk; does not?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: In both, the assignment happens first; for fopen that is the intent, because or then tests what was stored

    and, or and xor bind after =, so the left side is assigned first. That grouping suits "do this, or else stop", and breaks "store the result of both conditions".

Practise: a delivery fee you can test

Add a remote-area rule to deliveryFee()

On your own computer, copy the guard-clause version of deliveryFee() from the tabs above and give it a third parameter, bool $isRemote: an order to a remote area pays 12,000 paise, whatever its total. Decide where the new guard goes, then check it with php -n: 150,000 paise to a remote area must cost 12,000, 150,000 to a metro city 0, 99,899 to a metro city 4,000, and 0 paise must still throw. Then move the new guard below the test for 99,900 paise and run it again: the remote order now costs 0, and PHP prints no warning.

Finish with the self-check. It opens on whichever of the five ideas you have shown least so far, and a wrong pick names the slip behind it:

Practice that adapts to you

Practise this lesson

11 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 11 When does the else block of an if, elseif and else chain run?

    Choose one answer.

    Show the answer: Question 1 of 11

    Answer: Only when the if and every elseif before it were false

    The PHP Manual says the else runs only if the if expression was false, and any elseif expressions too.

  2. Question 2 of 11 What fee, in paise, does deliveryFee(99_900, true) from delivery_fee.php return?

    Type a number.

    A hint

    Which test comes first, and is 99,900 at least 99,900?

    Show the answer: Question 2 of 11

    Answer: 0 paise

    The first test is $orderPaise >= 99_900, and >= includes the boundary, so the order ships free whatever the city. The run printed 0.00 for 999.00 rupees.

  3. Question 3 of 11 What does this print?

    Read the code, then choose one answer.

    <?php
    $a = true;
    $b = false;
    if ($a)
        if ($b)
            echo 'b';
    else
        echo 'c';
    Show the answer: Question 3 of 11

    Answer: c

    Without braces an else is always associated with the nearest if. Braces around every block make the pairing visible.

  4. Question 4 of 11 What is a guard clause?

    Choose one answer.

    Show the answer: Question 4 of 11

    Answer: An if at the top of a function that handles one special case and leaves at once with return or throw

    Guard clauses put the special cases first and return early, so the normal case is the last line, without nesting.

  5. Question 5 of 11 Which set of calls runs every line that returns or throws in the guard version of deliveryFee()?

    Choose one answer.

    Show the answer: Question 5 of 11

    Answer: (0, true), (99_900, false), (99_899, true) and (99_899, false)

    Each guard is one boundary, so a flat function is tested with one call per guard plus one for the normal case.

  6. Question 6 of 11 A form sends a quantity of 0. What does this print?

    Read the code, then choose one answer.

    <?php
    $qty = 0;
    echo $qty ?: 1, "\n";
    Show the answer: Question 6 of 11

    Answer: 1

    The short ternary treats every falsy value as missing. Use ?? when 0, '' or '0' are real values.

  7. Question 7 of 11 Which of these can stand on the right side of $label = …;?

    Choose one answer.

    Show the answer: Question 7 of 11

    Answer: $stock === 0 ? 'Sold out' : 'In stock'

    An expression produces a value; a statement does something. The ternary operator is an expression, which is why it fits in assignments and echo.

  8. Question 8 of 11 Which line closes <?php if ($p['stock'] === 0): ?> in a template?

    Choose one answer.

    Show the answer: Question 8 of 11

    Answer: <?php endif; ?>

    Each colon-syntax block ends with a keyword that names it, which keeps templates readable.

  9. Question 9 of 11 A template has twelve <?php } ?> lines between its HTML tags. What does rewriting it in the colon syntax change?

    Choose one answer.

    Show the answer: Question 9 of 11

    Answer: Each block ends with a keyword such as endif; or endforeach;, so a reader sees which block ends where

    The alternative syntax is about reading: a closing keyword per block instead of a brace that could close anything.

  10. Question 10 of 11 What does this print?

    Read the code, then choose one answer.

    <?php
    $both = true xor true;
    var_dump($both);
    Show the answer: Question 10 of 11

    Answer: bool(true)

    and, xor and or all bind after the assignment, so the variable keeps the left operand.

  11. Question 11 of 11 Which rewrite of $canShip = $inStock and $addressOk; stores false when the address check fails?

    Choose one answer.

    Show the answer: Question 11 of 11

    Answer: $canShip = $inStock && $addressOk;

    Use && and || whenever the result is stored or returned; brackets around an and expression work too.

Interview questions

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher · Warm-up

    Are elseif and else if the same in PHP?

    With braces they behave the same. else if is an else whose statement happens to be another if, while elseif is one keyword, and both run the first branch whose condition is true and skip the rest. They differ in the colon syntax of templates: after if (...): PHP accepts elseif (...):, but else if (...): is a parse error, "unexpected token "if", expecting ":"", because after else PHP expects the colon that opens the else block. Since one file may use both syntaxes, write elseif in one word everywhere. PSR-12 recommends it so that every control keyword is a single word, and it means code moved into a template keeps compiling.

  2. Fresher to mid

    A checkout function prices delivery with if statements nested five levels deep: member or not, coupon or not, metro or not, order size, weekend or not. Members sometimes pay the non-member price. How do you find the cause, and how do you restructure the function so that the next mistake is easy to see?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Fresher to mid

    Reading a signup handler, you find $valid = $hasEmail and $hasPhone;, and its tests pass. What does the line really store, why did the tests miss it, and how do you find and fix similar lines in a large codebase?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • if, elseif and else test their conditions from the top and run only the first block whose condition is true, so a narrow condition must come before a wider one.
  • Guard clauses handle the special cases first and return or throw at once, which leaves the main rule at the bottom, without nesting, and gives each boundary its own test.
  • The ternary operator is an expression that picks one of two values; nested ternaries need brackets since PHP 8.0, and match reads better for three or more outcomes.
  • Templates use the colon syntax with endif; and its relatives, where only the one-word elseif compiles.
  • and and or bind after =, so $x = $a and $b; stores $a; conditions and assignments use && and ||.

Terms in this lesson

alternative syntax
The colon form of if, while, for, foreach and switch that templates use: the opening brace becomes a colon, and the closing brace becomes endif;, endwhile;, endfor;, endforeach; or endswitch;.
expression
Code that produces a value, such as 2 + 3, $price * 2, a function call or a ternary. A statement such as if or echo does something instead, so it cannot stand on the right of =.
guard clause
An if at the top of a function that handles one special case, such as an invalid input, and leaves at once with return or throw, so the main rule can follow without nesting.
operator precedence
The order in which PHP groups the operators of an expression: an operator that binds more tightly takes its operands first, as * does before +, and = does before and.
ternary operator
The operator condition ? a : b, an expression that evaluates to a when the condition is true and to b otherwise. Its short form a ?: b gives a itself when a counts as true.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.