PHP Language Basics

Operators And Expressions

An expression is PHP code that produces a value. A literal, variable read, function call, calculation, comparison, and conditional choice can all be expressions.

An operator takes one or more expressions and produces another value. Operators let a program calculate totals, compare states, combine boolean rules, join text, choose a default, and update stored values.

The main skill is not memorising symbols. It is being able to state the value and type produced by each expression before using it in a larger rule.

Build Arithmetic From Named Units

The common arithmetic operators are:

  • + for addition;
  • - for subtraction;
  • * for multiplication;
  • / for division;
  • % for remainder;
  • ** for exponentiation.

A small order calculation can use integer cents:

PHP example
<?php

$priceCents = 1299;
$quantity = 3;
$discountCents = 500;

$subtotalCents = $priceCents * $quantity;
$totalCents = $subtotalCents - $discountCents;

var_dump($subtotalCents, $totalCents);

Output:

int(3897)
int(3397)

Each right-hand expression is evaluated first, and assignment stores the result. The Cents suffix keeps the unit visible throughout the calculation.

Do not combine incompatible units merely because both values are integers. Adding 30 seconds to 500 cents is valid syntax and invalid domain logic.

Understand Division And Remainders

The / operator performs division and normally produces a float even when both operands are integers:

PHP example
<?php

$average = 7 / 2;

var_dump($average);

Output:

float(3.5)

Use intdiv() when the contract explicitly requires integer division:

PHP example
<?php

$completeBoxes = intdiv(17, 5);
$leftOverItems = 17 % 5;

var_dump($completeBoxes, $leftOverItems);

Output:

int(3)
int(2)

The remainder operator % answers what remains after whole groups are removed. It is useful for alternating rows, page offsets, clock-style wrapping, and divisibility checks.

Division or remainder by zero fails. Validate or establish a non-zero divisor before evaluating the expression; do not suppress that failure.

Use Parentheses To State The Intended Formula

Multiplication and division bind more tightly than addition and subtraction:

PHP example
<?php

$result = 10 + 5 * 2;

var_dump($result);

The result is 20, because PHP groups it as 10 + (5 * 2).

Parentheses change the grouping:

PHP example
<?php

$result = (10 + 5) * 2;

var_dump($result);

The result is 30.

Even when PHP's default precedence already matches the formula, parentheses can document a business rule:

PHP example
<?php

$subtotalCents = 2400;
$taxCents = 480;
$creditCents = 300;

$amountDueCents = ($subtotalCents + $taxCents) - $creditCents;

A reviewer can see that tax is added before credit is removed without consulting a precedence table.

Join Text With The Concatenation Operator

PHP uses . to concatenate strings:

PHP example
<?php

$firstName = 'Ada';
$lastName = 'Lovelace';

$fullName = $firstName . ' ' . $lastName;

echo $fullName, PHP_EOL;

Do not use + to join text. It belongs to arithmetic.

When concatenation and arithmetic appear together, group the calculation:

PHP example
<?php

$stock = 4;

echo 'Stock after sale: ' . ($stock - 1) . PHP_EOL;

Since PHP 8.0, string concatenation has lower precedence than addition and subtraction, but explicit parentheses remain clearer and make intent portable across code review and maintenance contexts.

The concatenating assignment operator .= appends to the current string:

PHP example
<?php

$message = 'Order';
$message .= ' accepted';

var_dump($message);

Output:

string(14) "Order accepted"

Use incremental string building when it improves clarity. For fixed output, one literal or interpolation is often simpler.

Separate Assignment From Comparison

Assignment stores a value:

PHP example
<?php

$status = 'paid';

Comparison produces a boolean:

PHP example
<?php

$status = 'paid';

var_dump($status === 'paid');

Output:

bool(true)

Confusing = with === changes state instead of asking a question. Keep assignments as clear standalone statements rather than hiding them inside larger boolean expressions.

Compound assignment reads, operates, and stores:

PHP example
<?php

$stock = 10;
$stock -= 2;
$stock += 5;

var_dump($stock);

The final value is 13. $stock -= 2 is equivalent to $stock = $stock - 2 for this simple case.

PHP also has ++ and --. They are concise for increments and decrements, but prefix and postfix forms produce different expression values. Prefer a standalone $count++ or an explicit $count += 1; avoid calculations that depend on when a side-effecting increment is evaluated.

Prefer Strict Equality

Comparison operators include:

  • === identical value and type;
  • !== not identical;
  • == equal after PHP's comparison conversions;
  • != not equal after conversions;
  • <, <=, >, and >= for ordering.
PHP example
<?php

$submittedQuantity = '3';
$availableStock = 3;

var_dump($submittedQuantity == $availableStock);
var_dump($submittedQuantity === $availableStock);

Output:

bool(true)
bool(false)

Loose equality allows type conversion, so the string and integer compare equal. Strict equality keeps the boundary visible.

Validate external text before numeric comparison:

PHP example
<?php

$quantityInput = '3';
$availableStock = 5;
$quantity = filter_var(
    $quantityInput,
    FILTER_VALIDATE_INT,
    ['options' => ['min_range' => 1]],
);

$canFitInStock = $quantity !== false && $quantity <= $availableStock;

var_dump($quantity, $canFitInStock);

The validation result must not be false, and the integer must fit within stock. This is stronger than casting unknown text or relying on loose comparison.

Combine Boolean Rules With Logical Operators

The common logical operators are:

  • && for and;
  • || for or;
  • ! for not.
PHP example
<?php

$isPaid = true;
$hasStock = true;
$isFraudCheckPassed = false;

$canShip = $isPaid && $hasStock && $isFraudCheckPassed;
$needsReview = !$isFraudCheckPassed;

var_dump($canShip, $needsReview);

Output:

bool(false)
bool(true)

Name intermediate boolean expressions when they represent separate rules:

PHP example
<?php

$quantity = 2;
$stock = 5;
$accountIsActive = true;

$quantityIsPositive = $quantity > 0;
$stockIsAvailable = $quantity <= $stock;
$orderCanProceed = $accountIsActive
    && $quantityIsPositive
    && $stockIsAvailable;

var_dump($orderCanProceed);

The names make the final rule reviewable.

Understand Short-Circuit Evaluation

For &&, PHP stops once a false left-hand result determines that the full expression is false. For ||, it stops once a true left-hand result determines that the full expression is true.

This expression safely compares stock only after validation succeeds:

PHP example
$canFitInStock = $quantity !== false && $quantity <= $availableStock;

If $quantity !== false is false, the right side is not evaluated. The ordering therefore carries meaning: establish the prerequisite before the expression that depends on it.

Do not hide important writes, network calls, or other side effects on the right side of a logical operator. Short-circuit behavior can prevent that code from running.

Avoid and And or In Assignments

PHP also provides the words and and or, but they have much lower precedence than && and ||.

PHP example
<?php

$expected = true && false;
$surprising = true and false;

var_dump($expected, $surprising);

Output:

bool(false)
bool(true)

The second assignment groups as ($surprising = true) and false, so $surprising receives true. Use && and || for normal boolean expressions and add parentheses where grouping would otherwise be unclear.

Choose Defaults With ??

The null coalescing operator chooses its right-hand value when the left side is missing or null:

PHP example
<?php

$requestedLocale = null;
$locale = $requestedLocale ?? 'en_GB';

var_dump($locale);

Output:

string(5) "en_GB"

A present non-null value is preserved:

PHP example
<?php

$requestedLocale = 'cy_GB';
$locale = $requestedLocale ?? 'en_GB';

var_dump($locale);

?? is a defaulting operator, not validation. An empty string is present and not null, so it does not trigger the fallback. Validate content separately when an empty or malformed value is unacceptable.

??= assigns a default only when the variable is missing or null:

PHP example
<?php

$locale = null;
$locale ??= 'en_GB';

var_dump($locale);

Use it when updating the same variable is genuinely clearer than creating separate requested and resolved names.

Choose Between Two Values With Ternary

The ternary operator takes three expressions:

condition ? value_when_true : value_when_false
PHP example
<?php

$totalCents = 7500;
$shippingLabel = $totalCents >= 5000
    ? 'free shipping'
    : 'standard shipping';

var_dump($shippingLabel);

Use ternary for a short value choice. The next lesson teaches if, elseif, else, and match for control flow. If either branch needs several statements or the condition needs a paragraph of explanation, a control structure is clearer.

Nested ternaries require explicit parentheses and are difficult to scan. Prefer named expressions or ordinary control flow.

Do Not Assume General Evaluation Order

Precedence determines grouping; it does not promise a universal left-to-right order for every side effect. Avoid expressions that both read and modify the same variable in multiple operands:

$result = $count + $count++;

Split state changes into separate statements:

PHP example
<?php

$count = 2;
$originalCount = $count;
$count += 1;
$result = $originalCount + $count;

var_dump($count, $result);

Separate statements make the order explicit and testable.

Common Operator Mistakes

Symptom Likely cause
State changes during a "check" = used instead of comparison
String and integer compare equal unexpectedly Loose == converted types
Invalid text becomes a plausible integer Cast used instead of validation
Total uses the wrong grouping Missing parentheses around the intended formula
Text joining causes a type error + used instead of .
Assigned boolean differs from expectation and or or precedence interacted with =
Default does not replace empty text ?? only handles missing or null values
Later operation does not run Earlier logical operand short-circuited the expression

What You Should Be Able To Do

After this lesson, you should be able to calculate with clear units, use division and remainder deliberately, concatenate strings, update values with compound assignment, prefer strict comparisons, validate before numeric comparison, combine named boolean rules, explain short-circuit behavior, use ?? and ternary for small value choices, and add parentheses that make precedence explicit.

Official references include PHP's operator overview, operator precedence, comparison operators, and logical operators.

Practice

Task: Calculate An Order And Packing Summary
PHP example
$unitPriceCents = 1499;
$quantity = 2;
$discountCents = 300;
$deliveryCents = 499;
$itemsToPack = 17;
$itemsPerBox = 5;

Calculate:

  • line subtotal as unit price multiplied by quantity
  • discounted subtotal as line subtotal minus discount
  • amount due as discounted subtotal plus delivery
  • complete boxes with intdiv()
  • unpacked remainder with %

Print exactly:

Line subtotal: 2998 cents
After discount: 2698 cents
Amount due: 3197 cents
Packing: 3 full boxes and 2 remaining items

Requirements:

  • keep all money as integer cents
  • use descriptive intermediate variables
  • use parentheses in the amount-due expression to make the intended grouping explicit
  • predict every result before running the file
  • lint and execute the script

Afterward, explain why / is not used for the complete-box count and what would be invalid about setting $itemsPerBox to zero.

Show solution
PHP example
<?php

$unitPriceCents = 1499;
$quantity = 2;
$discountCents = 300;
$deliveryCents = 499;
$itemsToPack = 17;
$itemsPerBox = 5;

$lineSubtotalCents = $unitPriceCents * $quantity;
$discountedSubtotalCents = $lineSubtotalCents - $discountCents;
$amountDueCents = ($discountedSubtotalCents + $deliveryCents);
$completeBoxes = intdiv($itemsToPack, $itemsPerBox);
$remainingItems = $itemsToPack % $itemsPerBox;

echo "Line subtotal: $lineSubtotalCents cents\n";
echo "After discount: $discountedSubtotalCents cents\n";
echo "Amount due: $amountDueCents cents\n";
echo "Packing: $completeBoxes full boxes and $remainingItems remaining items\n";

Output:

Line subtotal: 2998 cents
After discount: 2698 cents
Amount due: 3197 cents
Packing: 3 full boxes and 2 remaining items

intdiv(17, 5) returns the three complete groups without a fractional box. The remainder expression 17 % 5 returns the two items left after those groups are removed.

A zero box size would make both division operations undefined and PHP would throw a division-by-zero error. A real boundary must establish a positive divisor before these expressions run.

Task: Predict Validated Comparisons
PHP example
<?php

$quantityInput = '3';
$invalidQuantityInput = '3 items';
$availableStock = 3;
$minimumQuantity = 1;

$quantity = filter_var(
    $quantityInput,
    FILTER_VALIDATE_INT,
    ['options' => ['min_range' => 1]],
);
$invalidQuantity = filter_var(
    $invalidQuantityInput,
    FILTER_VALIDATE_INT,
    ['options' => ['min_range' => 1]],
);

var_dump($quantityInput === $availableStock);
var_dump($quantity === $availableStock);
var_dump($quantity !== false && $quantity >= $minimumQuantity);
var_dump($quantity !== false && $quantity <= $availableStock);
var_dump($invalidQuantity !== false && $invalidQuantity <= $availableStock);

Task

  • record five predicted boolean values in order
  • explain the type mismatch in the first comparison
  • explain why the second comparison is strict and true
  • explain which final right-hand comparison is skipped through short-circuit evaluation
  • lint and run the script

Do not replace validation with an integer cast or loose equality.

Show solution
bool(false)
bool(true)
bool(true)
bool(true)
bool(false)

The runnable script is unchanged:

PHP example
<?php

$quantityInput = '3';
$invalidQuantityInput = '3 items';
$availableStock = 3;
$minimumQuantity = 1;

$quantity = filter_var(
    $quantityInput,
    FILTER_VALIDATE_INT,
    ['options' => ['min_range' => 1]],
);
$invalidQuantity = filter_var(
    $invalidQuantityInput,
    FILTER_VALIDATE_INT,
    ['options' => ['min_range' => 1]],
);

var_dump($quantityInput === $availableStock);
var_dump($quantity === $availableStock);
var_dump($quantity !== false && $quantity >= $minimumQuantity);
var_dump($quantity !== false && $quantity <= $availableStock);
var_dump($invalidQuantity !== false && $invalidQuantity <= $availableStock);

The raw string '3' is not identical to integer 3. Validation produces integer 3, so the second strict comparison is true. That integer also satisfies the minimum and stock limits.

The malformed input validates to false. In the final expression, $invalidQuantity !== false is false, so && does not evaluate $invalidQuantity <= $availableStock. The prerequisite check protects the dependent comparison.

Task: Build A Shipping Expression
PHP example
$totalCents = 6400;
$freeShippingThresholdCents = 5000;
$requestedCountry = null;
$isAccountActive = true;

Build expressions that:

  • resolve $country from $requestedCountry, defaulting to 'GB' only when it is null
  • create $meetsTotalThreshold
  • create $shipsInsideFreeRegion using strict comparison with 'GB'
  • create $qualifiesForFreeShipping requiring active account, threshold, and country rules
  • choose Free shipping or Standard shipping with a ternary
  • print the resolved country and label

Run the script three times, changing only $requestedCountry:

Value Expected country Expected label
null GB Free shipping
'US' US Standard shipping
'' empty Standard shipping

Afterward, explain why the empty string does not trigger ?? and why naming the three boolean rules is clearer than one long expression.

Show solution
PHP example
<?php

$totalCents = 6400;
$freeShippingThresholdCents = 5000;
$requestedCountry = null;
$isAccountActive = true;

$country = $requestedCountry ?? 'GB';
$meetsTotalThreshold = $totalCents >= $freeShippingThresholdCents;
$shipsInsideFreeRegion = $country === 'GB';
$qualifiesForFreeShipping = $isAccountActive
    && $meetsTotalThreshold
    && $shipsInsideFreeRegion;
$shippingLabel = $qualifiesForFreeShipping
    ? 'Free shipping'
    : 'Standard shipping';

echo "Country: $country\n";
echo "Shipping: $shippingLabel\n";

With null, the output is:

Country: GB
Shipping: Free shipping

Changing $requestedCountry produces:

Value Country output Shipping output
null GB Free shipping
'US' US Standard shipping
'' empty after Country: Standard shipping

?? selects its fallback only for missing or null values. An empty string is a present string, so it remains the country value and then fails the strict 'GB' comparison.

The named booleans separate the account, total, and regional rules. A reviewer can inspect each result independently before checking the final && expression.