Almost everything that produces a value in PHP is an expression. Literals, variables,
calls and assignments are all expressions, and so since PHP 8.0 are
throw and match.
Operator precedence
Operators are listed from highest to lowest precedence, following the manual’s precedence table. Operators in the same row have equal precedence.
| Associativity | Operators | Kind |
|---|---|---|
| none | clone new | Object creation |
| right | ** | Exponent |
| none | + - ++ -- ~ (int) (float) (string) (array) (object) (bool) @ | Unary, casts and error control |
| left | instanceof | Type test |
| none | ! | Logical not |
| left | * / % | Multiplicative |
| left | + - | Additive |
| left | << >> | Shift |
| left | . | Concatenation |
| left | |> | Pipe (8.5) |
| non-associative | < <= > >= | Relational |
| non-associative | == != === !== <> <=> | Equality |
| left | & | Bitwise and references |
| left | ^ | Bitwise xor |
| left | | | Bitwise or |
| left | && | Logical and |
| left | || | Logical or |
| right | ?? | Null coalescing |
| non-associative | ? : | Ternary (left-associative before 8.0) |
| right | = += -= *= **= /= .= %= &= |= ^= <<= >>= ??= | Assignment |
| none | yield from | Generators |
| none | => | Yield with a key |
| none | yield | Generators |
| none | print | Output |
| left | and | Logical and, low precedence |
| left | xor | Logical exclusive or |
| left | or | Logical or, low precedence |
| none | include include_once require require_once | Inclusion |
| none | fn => | Arrow function body |
| none | throw | Throw expression |
- Non-associative means the operators cannot be chained without
parentheses.
1 < 2 < 3and1 == 2 == 3are parse errors.a ? b : c ? d : eis a compile error. - Concatenation. Since PHP 8.0
.binds less tightly than+,-,<<and>>, so"x" . 1 + 2is"x3". Before 8.0 it had the same precedence as+and-and a higher one than the shifts. andandorare lower than=.$a = true and false;assignstrue, because it is($a = true) and false. Use&&and||when an expression includes assignment.- Assignment inside prefix operators.
!$a = f()parses as!($a = f()), because the left side of=must be a variable. - Exponent.
**binds more tightly than unary minus:-2 ** 2is-4. It is right-associative:2 ** 3 ** 2is512. - Evaluation order. Precedence groups the operands but does not fix
the order in which they are evaluated. Avoid writing expressions such as
$i + $i++, whose result is not defined.
Arithmetic operators
| Expression | Result |
|---|---|
+$a, -$a | Identity (converts to a number) and negation |
$a + $b | Sum, or union of two arrays |
$a - $b, $a * $b | Difference and product |
$a / $b | Quotient. An int if both operands are integers and the division is exact, otherwise a float. Division by zero throws DivisionByZeroError (PHP 8.0) |
$a % $b | Remainder. Operands are converted to integers. The sign is that of the dividend, so -7 % 3 is -1. Zero as divisor throws DivisionByZeroError |
$a ** $b | Power (PHP 5.6). 2 ** -1 is 0.5. Zero to a negative power is deprecated since 8.4 |
Integer results that do not fit become floats. intdiv() gives integer
division. It throws DivisionByZeroError on zero and
ArithmeticError for PHP_INT_MIN divided by
-1. Non-numeric operands behave as under
Numeric strings.
Increment and decrement
++$a and --$a change and return the new value;
$a++ and $a-- return the old value.
| Operand | ++ | -- |
|---|---|---|
| Integer or float | Adds 1 | Subtracts 1 |
null | Becomes 1 | Stays null, with a warning |
| Boolean | Unchanged, with a warning | Unchanged, with a warning |
| Numeric string | As the number | As the number |
| Non-numeric string | Perl-style text increment: "Az" becomes "Ba", "zz" becomes "aaa", "a9" becomes "b0". Soft-deprecated in 8.3 and deprecated in 8.5; use str_increment() | No effect. Deprecated since 8.3 |
| Array, object or resource | TypeError | TypeError |
Assignment
$a = 1is an expression whose value is what was assigned, so$a = $b = 1works from right to left.$a = &$bassigns by reference.- Compound forms, such as
$a += 1, read and then write the variable. They exist for every arithmetic, bitwise and string operator, plus??=(PHP 7.4), which assigns only if the left side is not set or isnull. - The left side can be a destructuring pattern. See Destructuring.
Comparison operators
| Expression | True when |
|---|---|
$a == $b | Equal after conversion |
$a === $b | Equal and the same type |
$a != $b, $a <> $b | Not equal after conversion |
$a !== $b | Not equal or not the same type |
$a < $b, <=, >, >= | Ordering |
$a <=> $b | Spaceship (PHP 7.0): an int less than, equal to or greater than zero as $a is less than, equal to or greater than $b |
The conversion rules are given in full under Comparison.
Logical operators
&&, ||, and and or evaluate
the right operand only if the left does not already decide the result.
! negates. xor is true when exactly one operand is true. Every
operator yields a bool.
Bitwise operators
| Expression | Result |
|---|---|
$a & $b, $a | $b, $a ^ $b | And, or and xor, bit by bit. If both operands are strings, they are applied byte by byte and the result is a string |
~$a | Not |
$a << $b, $a >> $b | Shifts. >> is arithmetic and keeps the sign. A negative count throws ArithmeticError. A count larger than the integer width gives 0, or -1 for >> on a negative value |
Ternary and null coalescing
$a ? $b : $cevaluates only the chosen branch. Nesting without parentheses is an error since PHP 8.0.$a ?: $b(PHP 5.3) gives$aif it is truthy, otherwise$b, evaluating$aonce. A chain of these is stable.$a ?? $b(PHP 7.0) gives$aif it exists and is notnull, otherwise$b. It behaves likeisset(), so an undefined variable, key or property on the left raises no warning. Its precedence is low, so parenthesise it inside a larger expression:'Mr ' . ($name ?? 'Anon').
Other operators
| Form | Meaning |
|---|---|
$a . $b | Concatenation; both operands are converted to strings |
$x instanceof Y | True if $x is an instance of class or interface Y. The right side may be a variable holding a name or an object, or a parenthesised expression (PHP 8.0). A left operand that is not an object gives false, and an unknown class is not an error |
new Foo(...) | Creates an object. The parentheses can be left out when there are no arguments. A parenthesised expression may give the class name (PHP 8.0): new ($name) |
new Foo()->bar() | Member access directly on new, without surrounding parentheses, since PHP 8.4 |
clone $x | Copies an object |
@$x | Evaluates with error reporting suppressed. Since PHP 8.0 it no longer hides fatal errors |
`command` | Runs a shell command and gives its output, like shell_exec(). Deprecated since PHP 8.5 |
print $x | Outputs a string and evaluates to 1 |
throw $e | Throws, usable as an expression since PHP 8.0 |
yield, yield from | Generator operators |
empty($x) | True if $x does not exist or converts to false; no warning |
isset($a, $b) | True if every argument exists and is not null |
Array operators
| Expression | Meaning |
|---|---|
$a + $b | Union. Keys from $a are kept; keys only in $b are added |
$a == $b | The same key and value pairs, in any order, values compared with == |
$a === $b | The same key and value pairs, in the same order and of the same types |
$a != $b, $a <> $b, $a !== $b | The inverses |
Nullsafe operator
$a?->b (PHP 8.0)
behaves like $a->b unless $a is null, when
the whole chain gives null.
$city = $user?->address?->city; // null if $user or the address is null
$name = $user?->getName(makeDefault()); // makeDefault() is not called if $user is null
It is for reading only: $a?->b = 1 is a compile error. It applies only
to null. Calling a method on another non-object still throws. It works
inside isset() and empty().
Match
match (PHP 8.0)
is an expression that selects one arm using strict comparison.
match ( subject ) {
condition {, condition} [,] => expression ,
default => expression [,]
}
$label = match ($code) {
200, 201 => 'ok',
404 => 'not found',
default => throw new RuntimeException("Unknown code $code"),
};
- The subject is compared with
===, so"1"does not match1.switchuses==. - Conditions are tried in order, and only until one matches. Only the matching arm’s result is evaluated.
- An arm may have several conditions separated by commas. There is no fall-through,
and there is no
break. - With no match and no
default, anUnhandledMatchErroris thrown. Only onedefaultis allowed. - The body of an arm is a single expression. Use a closure or function for several
statements.
throwis an expression, so it can be an arm. - The subject can be omitted by writing
match (true)and using boolean conditions. They must be exactlytrueorfalse. - As a statement, a
matchneeds a semicolon after its closing brace.
Arrays
An array is an ordered list of key and value pairs. Short syntax
(5.4 for literals) and the
older array() are the same thing.
$list = [1, 2, 3,]; // the trailing comma is allowed
$map = ['a' => 1, 'b' => 2];
$old = array(1, 2);
$map['c'] = 3;
$list[] = 4; // appends with the next key
echo $map['a'], $list[0];
| Key as written | Key stored |
|---|---|
A string that is a decimal integer, without a plus sign | That int: "8" is 8, "08" stays a string |
A float | Truncated: 8.7 is 8. A fractional float is deprecated since 8.1 |
A bool | 1 or 0 |
null | "". As an offset it is deprecated since 8.5 |
An array or object | Not allowed: an “Illegal offset type” error |
- Writing the same key twice keeps the last value.
$a[] = $vuses one more than the largest integer key the array has held since it was last reindexed, even if that key is gone now. An empty array starts at 0.- Since PHP 8.3, after a negative key
n, the next key isn + 1rather than 0. - Assigning to a key of a variable that is
nullor undefined creates an array. Doing it tofalseis deprecated since 8.1. Doing it to any other scalar throwsError. - Elements of an array are ordered by insertion, not by key.
Destructuring
An array pattern on the left of an assignment takes values out of an array. The
short form [...] was added in PHP 7.1 and
list() is the older equivalent. The two forms cannot be mixed in one
pattern.
[$a, $b] = [1, 2]; // $a = 1, $b = 2
[, , $third] = [1, 2, 3]; // skip positions
['x' => $x, 'y' => $y] = $point; // by key
[[$p, $q], $r] = [[1, 2], 3]; // nested
[$a, $b] = [$b, $a]; // swap
foreach ($rows as ['id' => $id, 'name' => $name]) {}
- A missing key gives
nullwith a warning (a notice before 8.0). - Keys and positions cannot be mixed in one pattern.
- A pattern element may be a reference,
[&$a, $b] = $arr;, since PHP 7.3. - Spreading into a pattern is not supported.
...is not allowed in assignments. - Destructuring a value that is not an array, other than
null, warns since PHP 8.5.
Spread
... in an array literal unpacks an array or Traversable
(PHP 7.4). It may be
used several times and mixed with ordinary elements. It behaves like
array_merge(): string keys later override earlier ones, and integer keys
are renumbered. Before PHP 8.1 an array with string keys could not be unpacked.
$a = [1, 2];
$b = [0, ...$a, 3]; // [0, 1, 2, 3]
$c = [...['x' => 1], ...['x' => 2, 'y' => 3]]; // ['x' => 2, 'y' => 3]
echo max(...$b); // 3
In a function call, ... unpacks the arguments; see
Argument unpacking.
Pipe operator
$value |> $callable
(PHP 8.5) passes the value on the
left to the callable on the right and gives its result. It is left-associative, so
calls chain from top to bottom.
$slug = ' PHP 8.5 Released '
|> trim(...)
|> strtolower(...)
|> (fn($s) => str_replace(' ', '-', $s));
// "php-8.5-released"
- The right side must be a callable, or an expression that evaluates to one. Anything
else throws
Error. - The callable receives exactly one argument. A function with a further required parameter fails as if it had been called with too few arguments.
- A function that takes its parameter by reference is not allowed.