PHPOperator Precedence

Operator Precedence

When an expression mixes several operators, PHP does not evaluate them strictly left to right — it follows a precedence order, exactly like the "multiplication before addition" rule from arithmetic class. Operators of the same precedence are then resolved by associativity, which decides whether grouping happens left-to-right or right-to-left. Getting this wrong produces code that runs without error but computes the wrong answer, which is a far more dangerous kind of bug than one that crashes loudly.

Precedence with arithmetic

The example everyone recognizes from math also applies directly in PHP: multiplication and division bind tighter than addition and subtraction, so they are evaluated first even though addition appears first when read left to right.

Multiplication before addition

PHP
<?php
$result = 2 + 3 * 4; // not (2 + 3) * 4
echo $result; // 14, because 3 * 4 is evaluated first
14
Associativity

When two operators share the same precedence, associativity breaks the tie. Most arithmetic operators are left-associative (grouped left to right), but a few — like the exponentiation operator ** and the assignment operator = — are right-associative.

Left vs right associativity

PHP
<?php
echo 10 - 3 - 2; // 5, left-assoc: (10 - 3) - 2
echo "\n";
echo 2 ** 3 ** 2; // 512, right-assoc: 2 ** (3 ** 2), not (2 ** 3) ** 2
5
512
A reference table

This is not the complete PHP precedence table (the official one has dozens of entries once you include type casts, instanceof, and bitwise operators), but it covers the operators that come up in everyday code, ordered from highest precedence at the top to lowest at the bottom.

Precedence

Operators

Associativity

1 (highest)

**

Right

2

! ++ -- (unary)

Non-associative

3

* / %

Left

4

+ - (binary)

Left

5

.

Left

6

< <= > >=

Non-associative

7

== != === !== <=>

Non-associative

8

&&

Left

9

||

Left

10

??

Right

11

= += -= .= ??= etc.

Right

12

and

Left

13

xor

Left

14 (lowest)

or

Left

Real bugs caused by precedence

The Logical Operators page covers the and/or versus &&/|| trap in depth — it is the single most common real-world bug caused by precedence, because = binds tighter than and/or but looser than &&/||. A second common mistake mixes string concatenation with comparison or arithmetic without parentheses.

Concatenation vs addition without parentheses

PHP
<?php
$count = 3;

// Intent: print "Total: 8"
echo "Total: " . $count + 5; // TypeError in PHP 8!
. and + used to silently misbehave; PHP 8 now errors
In versions before PHP 8, `.` and `+`/`-` shared the same precedence and were evaluated left to right, so `"Total: " . $count + 5` concatenated first and then tried to add 5 to the resulting string, producing a confusing numeric result. PHP 8 fixed the underlying ambiguity by giving `+`/`-` **higher** precedence than `.`, so today this same line throws a `TypeError` because PHP tries to add `5` to a non-numeric string before concatenating. Either way, the fix is the same: use parentheses to say exactly what you mean.

Fixed with parentheses

PHP
<?php
$count = 3;
echo "Total: " . ($count + 5); // Total: 8
Total: 8
Ternary chaining

Nested ternary expressions are another classic precedence pitfall. As of PHP 8, chaining ternaries without explicit parentheses is a fatal compile-time error rather than a silent left-to-right guess, which is PHP forcing you to be explicit about something that used to be a common source of bugs.

Nested ternaries need parentheses in PHP 8

PHP
<?php
$score = 72;

// Must parenthesize nested ternaries explicitly in PHP 8+
$grade = ($score >= 90) ? "A" : (($score >= 70) ? "B" : "C");
echo $grade; // B
B
  • ** binds tighter than unary minus and is right-associative.

  • && binds tighter than ||, which binds tighter than ??.

  • and/or/xor sit below = in precedence — a frequent source of bugs.

  • PHP 8 requires explicit parentheses around nested ternary expressions.

Precedence tables describe defaults, not intent
Even when you know the precedence table by heart, a reader skimming your code usually does not have it memorized. Relying on precedence to make code shorter often makes it slower to review.
Tip
Add parentheses anywhere two different operator categories meet in the same expression — arithmetic mixed with concatenation, comparison mixed with logical operators, or any ternary nested inside another. It costs nothing at runtime and removes any doubt about what the code does, for you and for whoever reads it next.