Expressions
May 30, 2017 · View on GitHub
General
An expression involves one or more terms and zero or more operators.
A full expression is an expression that is not part of another expression.
A value side effect is an action that changes the state of the execution environment. (Examples of such actions are modifying a variable, writing to a device or file, or calling a function that performs such operations.) Throughout this specification, this term is shortened to side effect, which should not be confused with type side effect.
When an expression is evaluated, it produces a result. It might also
produce a side effect. Only a few operators produce side effects. (For
example, given the expression statement $v = 10; the
expression 10 is evaluated to the result 10, and there is no side
effect. Then the assignment operator is executed, which results in the
side effect of $v being modified. The result of the whole expression is
the value of $v after the assignment has taken place. However, that
result is never used. Similarly, given the expression statement ++$v;
the expression is evaluated to the result incremented-value-of-$v, and
the side effect is that $v is actually incremented. Again, the result
is never used.)
The occurrence of value computation and side effects is delimited by
sequence points, places in a program's execution at which all the
computations and side effects previously promised are complete, and no
computations or side effects of future operations have yet begun. There
is a sequence point at the end of each full expression. The logical and, logical or, conditional, and function-call operators each contain a sequence point. (For example, in the
following series of expression statements, $a = 10; ++$a; $b = $a;,
there is sequence point at the end of each full expression, so the
assignment to a is incremented, and the increment is completed before the assignment to $b`.)
When an expression contains multiple operators, the precedence of
those operators controls the order in which those operators are applied.
(For example, the expression $a - $b / $c is evaluated as
$a - ($b / $c) because the / operator has higher precedence than the
binary - operator.) The precedence of an operator is defined by the
definition of its associated grammar production.
If an operand occurs between two operators having the same precedence,
the order in which the operations are performed is defined by those
operators' associativity. With left-associative operators,
operations are performed left-to-right. (For example, $a + $b - $c is
evaluated as ($a + $b) - $c.) With right-associative operators,
operations are performed right-to-left. (For example, $a = $b = $c is
evaluated as $a = ($b = $c).)
Precedence and associativity can be controlled using grouping
parentheses. (For example, in the expression ($a - $b) / $c, the
subtraction is done before the division. Without the grouping
parentheses, the division would take place first.)
While precedence, associativity, and grouping parentheses control the
order in which operators are applied, they do not control the order of
evaluation of the terms themselves. Unless stated explicitly in this
specification, the order in which the operands in an expression are
evaluated relative to each other is unspecified. See the discussion
above about the operators that contain sequence points. (For example, in
the full expression $list1[$i] = $list2[$i++], whether the value
of $i on the left-hand side is the old or new $i, is unspecified.
Similarly, in the full expression $j = $i + $i++, whether the value
of $i is the old or new $i, is unspecified. Finally, in the full
expression f() + g() * h(), the order in which the three functions are
called, is unspecified.)
Implementation Notes
An expression that contains no side effects and whose resulting value is
not used need not be evaluated. For example, the expression statements
6;, $i + 6;, and $i/$j; are well formed, but they contain no side
effects and their results are not used.
A side effect need not be executed if it can be defined that no other
program code relies on its having happened. (For example, in the cases
of return $a++; and return ++$a;, it is obvious what value must be
returned in each case, but if $a is a variable local to the enclosing
function, $a need not actually be incremented.
Restrictions on Arithmetic Operations
No arithmetic operation can be performed on the value null or on a value of
type bool, string (not even if the string is numeric), or any nullable type (including nullable arithmetic types).
Operations on Operands Having One or More Subtypes
None of the subclauses in this Expressions clause discuss the use of operands
of supertypes such as num, arraykey, or ?int. Refer to §§ for a discussion of type side effects.
Primary Expressions
General
Syntax
primary-expression:
variable-name
qualified-name
literal
const-expression
intrinsic
collection-literal
tuple-literal
shape-literal
anonymous-function-creation-expression
awaitable-creation-expression
( expression )
$this
$$
Defined elsewhere
- anonymous-function-creation-expression
- awaitable-creation-expression
- collection-literal
- const-expression
- expression
- intrinsic
- literal
- qualified-name
- shape-literal
- tuple-literal
- variable-name
Semantics
The type and value of a parenthesized expression are identical to those of the un-parenthesized expression.
The variable $this is predefined inside any instance method,
constructor, or destructor when that method is called from within an object
context. $this is a handle that points to the calling
object or to the object being constructed. The type of $this is
this. $this is a non-modifiable lvalue.
The pipe variable $$ is predefined only within the
coalesce-expression of a
piped-expression. The type and value of
$$ is the type and value of that coalesce-expression. $$ is a
non-modifiable lvalue.
Intrinsics
General
Syntax
intrinsic:
array-intrinsic
echo-intrinsic
exit-intrinsic
invariant-intrinsic
list-intrinsic
Defined elsewhere
Semantics
The names in this series of subclauses are reserved and are called intrinsics. These names are not keywords; nor are they functions.
Note: The initial Hack execution environment was built on top of that for PHP,
which has an intrinsic called empty. And even though an intrinsic by that
name is not supported by Hack strict mode, the case-indistinct name empty is
reserved in Hack as well.
array
Syntax
array-intrinsic:
array ( array-initializeropt )
Defined elsewhere
Semantics
This intrinsic creates and initializes an array. It is equivalent to the
array-creation operator [].
echo
Syntax
echo-intrinsic:
echo expression
echo ( expression )
echo expression-list-two-or-more
expression-list-two-or-more:
expression , expression
expression-list-two-or-more , expression
Defined elsewhere
Constraints
expression must not designate an array nor an instance of a type not
having a __toString method.
Semantics
After converting each of its expressions' values to strings, if
necessary, echo concatenates them in lexical order, and writes the
resulting string to STDOUT.
For value substitution in string literals, see §§ and §§. For conversion to string, see §§.
Examples
$v1 = true;
$v2 = 123;
echo '>>' . $v1 . '|' . $v2 . "<<\n"; // outputs ">>1|123<<"
echo '>>' , $v1 , '|' , $v2 , "<<\n"; // outputs ">>1|123<<"
echo ('>>' . $v1 . '|' . $v2 . "<<\n"); // outputs ">>1|123<<"
$v3 = "qqq{$v2}zzz";
echo "$v3\n";
exit
Syntax
exit-intrinsic:
exit expressionopt
exit ( expressionopt )
Defined elsewhere
Constraints
When expression designates an integer, its value must be in the range 0–254.
Semantics
This intrinsic terminates the current script. If expression designates
a string, that string is written to STDOUT. If expression
designates an integer, that represents the script's exit status code.
Code 255 is reserved by Hack. Code 0 represents "success". The exit
status code is made available to the execution environment. If
expression is omitted or is a string, the exit status code is zero.
exit does not have a resulting value.
exit performs the following operations, in order:
- Writes the optional string to
STDOUT. - Calls any functions registered via the library function
register_shutdown_functionin their order of registration. - Invokes destructors for all remaining instances.
Examples
exit ("Closing down");
exit (1);
exit;
invariant
Syntax
invariant-intrinsic:
invariant ( condition , format )
invariant ( condition , format , values )
Constraints
condition can be any expression allowed as the operand of the ! operator. format is a string that can contain text and/or optional
formatting information as understood by the library function sprintf.
The optional comma-separated list of values designated by values must match
the set of types expected by the optional formatting information inside
format.
Semantics
If condition tests true, the program continues execution; otherwise, the
library function invariant_violation is called. That function does not
return; instead, it either throws an exception of type
\HH\InvariantException, or calls the handler previously registered by the
library function invariant_callback_register.
This intrinsic behaves like a function with a void return type. It is
intended to indicate a programmer error for a condition that should never
occur.
Examples
invariant($interf instanceof B, "Object must have type B");
// -----------------------------------------
invariant(!is_null($p), "Value can't be null");
// -----------------------------------------
$max = 100;
invariant(!is_null($p) && $p <= $max, "Value %d must be <= %d", $p, $max);
list
Syntax
list-intrinsic:
list ( list-expression-listopt )
list-expression-list:
expression
,
list-expression-list , expressionopt
Defined elsewhere
Constraints
list-intrinsic must be used as the left-hand operand in a
simple-assignment-expression of which the right-hand
operand must be an expression that designates a vector-like array or an instance of the class types Vector, ImmVector, or Pair (the
"source").
Each expression in list-expression-list must designate a variable (the "target variable").
There must not be fewer element candidates in the source than there are target variables.
Only the right-most list-or-variable can be omitted.
Semantics
This intrinsic assigns zero or more elements of the source to the target variables. On success, it returns a copy of the source.
When the source is a vector-like array, the element having an int key of 0 is assigned to the first target
variable, the element having an int key of 1 is assigned to the second
target variable, and so on, until all target variables have been
assigned. Any elements having an int key outside the range 0–(n-1),
where n is the number of target variables, are ignored.
If ($_ is used as a target variable, the value of the corresponding source element is ignored; no assignment takes place. Multiple target variables in the same list-expression-list may be $_.
When the source is an instance of the classes Vector, ImmVector, or Pair, the
elements in the source are assigned to the target variables in lexical order,
until all target variables have been assigned.
If the source elements and the target variables overlap in any way, the behavior is unspecified.
Examples
// $min, $max, and $avg must be defined at this point
list($min, $max, $avg) = array(0, 100, 67);
// $min is 0, $max is 100, $avg is 67
$a = array();
$v = list($a[0], $a[2], $a[4]) = array(50, 5100, 567);
// $a[0] is 50, $a[2] is 5100, $a[4] is 567
list($min, $max, ) = array(10, 1100, 167);
// $min is 10, $max is 1100
$v = Vector {1, 2, 3};
list($_, $b, $_) = $v; // $b is assigned 2; 1 and 3 are ignored
Collection Literals
Note: The term literal as used here is a misnomer; cl-element-keys and cl-element-values need not be compile-time constants.
Syntax
collection-literal:
non-key-collection-class-type { cl-initializer-list-without-keysopt }
key-collection-class-type { cl-initializer-list-with-keysopt }
pair-type { cl-element-value , cl-element-value }
non-key-collection-class-type:
qualified-name
key-collection-class-type:
qualified-name
pair-type:
qualified-name
cl-initializer-list-without-keys:
cl-element-value
cl-initializer-list-without-keys , cl-element-value
cl-initializer-list-with-keys:
cl-element-key => cl-element-value
cl-initializer-list-with-keys , cl-element-key => cl-element-value
cl-element-key:
expression
cl-element-value:
expression
Defined elsewhere
Constraints
For key-collection-class-type, qualified-name must designate the library
type Map or ImmMap, and in both cases, each cl-element-key must have
type int or string.
For key-collection-class-type, qualified-name must designate the library
type Vector, ImmVector, Set, or ImmSet, and in all such cases, each
cl-element-value must have type int or string.
For pair-type, qualified-name must designate the library type Pair.
Semantics
For non-key-collection-class-types Vector and ImmVector, an instance of
the corresponding class is created with elements having values as specified by
cl-initializer-list-without-keys, inserted in that order, and assigned
consecutive keys starting at zero. If cl-initializer-list-without-keys is
omitted, the resulting vector is empty.
For non-key-collection-class-types Map and ImmMap, an instance of the
corresponding class is created with elements having keys and values as
specified by cl-initializer-list-with-keys, inserted in that order. If
cl-initializer-list-with-keys is omitted, the resulting map is empty. If two
or more cl-element-keys in a cl-initializer-list-with-keys contain the
same key, the lexically right-most one is the one whose cl-element-value is
used to initialize the element designated by that key.
For non-key-collection-class-types Set and ImmSet, an instance of the
corresponding class is created with elements having values as specified by
cl-initializer-list-without-keys, inserted in that order. If
cl-initializer-list-without-keys is omitted, the resulting set is empty.
Duplicate cl-element-values are ignored.
For type Pair, an instance of that class is created with element 0 having
the value of the left-hand cl-element-value, and element 1 having the value
of the right-hand cl-element-value.
Examples
Vector {22, 33} // size 2; 22, 33
(Vector {})->addAll(array(3, 6, 9)) // size 0 then size 3; 3, 6, 9
ImmVector {5, $x, 15} // size 3; 5, ?, 15
Map {'x' => -1, 'a' => -4, 'x' => 5, 'a' => 12} // size 2; 'x'/5, 'a'/12
ImmSet {1, 1, 1, 5, 10, 1, 'red', 1} // size 4; 1, 5, 10, 'red'
Pair {55, new C()}
Tuple Literals
Note: The term literal as used here is a misnomer; the expressions in expression-list need not be compile-time constants.
Syntax
tuple-literal: tuple ( expression-list-one-or-more ) expression-list-one-or-more: expression expression-list-one-or-more , expression
Defined elsewhere
Semantics
A tuple-literal creates a tuple with elements having values as specified by expression-list-one-or-more, inserted in that order.
The type of a tuple-literal is "tuple of type <element type list in lexical order>".
Note: Although a tuple of only one element can be created using a tuple literal, a tuple-type-specifier must contain at least two elements.
Examples
return tuple(true, array(99, 88, 77), 10.5);
$t1 = tuple(10, true, 2.3, 'abc', null, $p1, Vector {$p2 + 3, 12}, new C());
$t2 = tuple(100, tuple('abc', false));
Shape Literals
Note: The term literal as used here is a misnomer; the expressions in field-initializer need not be compile-time constants.
Syntax
shape-literal:
shape ( field-initializer-listopt )
field-initializer-list:
field-initializers ,opt
field-initializers:
field-initializer
field-initializers , field-initializer
field-initializer:
single-quoted-string-literal => expression
integer-literal => expression
qualified-name => expression
scope-resolution-expression => expression
Defined elsewhere
Constraints
Each string in the set of strings designated by all the single-quoted-string-literals, qualified-names and scope-resolution-expressions in a field-initializer-list must have a distinct value, and each string must match exactly a field name in the shape type's shape-specifier.
Each integer in the set of integer-literals, qualified-names and scope-resolution-expressions in a field-initializer-list must have a distinct value, and each integer must match exactly a field name in the shape type's shape-specifier.
The number of field-initializers must match exactly the number of field-specifiers in the shape type's shape-specifier.
The type of expression in a field-initializer must be a subtype of the corresponding field type in the shape type's shape-specifier.
Semantics
A shape-literal creates a shape with fields having values as specified by field-initializer-list. The order of the field-initializers need not be the same as the order of the field-specifiers in the shape type's shape-specifier.
Examples
shape()
shape('x' => $prevX, 'y' => getY())
shape('id' => null, 'url' => null, 'count' => 0)
Anonymous Function-Creation
Syntax
anonymous-function-creation-expression: asyncopt function ( anonymous-function-parameter-listopt ) anonymous-function-returnopt anonymous-function-use-clauseopt compound-statement anonymous-function-parameter-list: ... anonymous-function-parameter-declaration-list anonymous-function-parameter-declaration-list , anonymous-function-parameter-declaration-list , ... anonymous-function-parameter-declaration-list: anonymous-function-parameter-declaration anonymous-function-parameter-declaration-list , anonymous-function-parameter-declaration anonymous-function-parameter-declaration: attribute-specificationopt type-specifieropt variable-name default-argument-specifieropt anonymous-function-return: : return-type anonymous-function-use-clause: use ( use-variable-name-list ,opt ) use-variable-name-list: variable-name use-variable-name-list , variable-name
Defined elsewhere
- attribute-specification
- compound-statement
- default-argument-specifier
- return-type
- type-specifier
- variable-name
Constraints
Each variable-name in an anonymous-function-parameter-declaration-list must be distinct.
If any anonymous-function-parameter-declaration has a default-argument-specifier, then all subsequent anonymous-function-parameter-declarations in the same anonymous-function-parameter-declaration-list must also have a default-argument-specifier.
If the type-specifier in anonymous-function-return is void, the
compound-statement must not contain any return statements
having an expression. Otherwise, all return statements must contain an
expression whose type is a subtype of the type indicated by type-specifier.
If async is present, return-type must be a type that implements
Awaitable<T>.
Semantics
This operator returns an object that encapsulates the anonymous function defined within. An anonymous function is defined like, and behaves like, a named function except that the former has no name and has an optional anonymous-function-use-clause.
The use-variable-name-list is a list of variables from the enclosing scope, which are to be made available by name to the body of the anonymous function. The values used for these variables are those at the time the closure object is created, not when it is used to call the function it encapsulates.
An anonymous function defined inside an instance method has access to
the variable $this.
If the type-specifier for a parameter is omitted, that type is inferred.
If anonymous-function-return is omitted, the return type is inferred.
An anonymous function can be asynchronous.
The function-return types this and noreturn are described in (§§).
Examples
function doit(int $value, (function (int): int) $process): int {
return $process($value);
}
$result = doit(5, function (int $p): int { return $p * 2; }); // doubles
$result = doit(5, function (int $p): int { return $p * $p; }); // squares
// -----------------------------------------
function compute(array<int> $values): void {
$count = 5;
$callback = function () use ($count)
{
…
};
$callback();
…
}
Async Blocks
Syntax
awaitable-creation-expression:
async { async-statement-listopt }
async-statement-list:
statement
async-statement-list statement
Defined elsewhere
Constraints
awaitable-creation-expression must not be used as the lambda-body in a lambda-expression.
Semantics
The (possibly) asynchronous operations designated by async-statement-list are executed, in order.
An awaitable-creation-expression produces a result of type Awaitable<T>, where T is the return type of the final statement in async-statement-list. If async-statement-list is omitted, or its final statement is return;, or its final statement is not a return statement, the final statement is treated as being return;, T is void, and no value is wrapped into the Awaitable object. Otherwise, the final statement has the form return expression;, T is the type of expression, and the value of expression is wrapped into the Awaitable object.
Examples
$x = await async {
$y = await task1();
$z = await task2();
return $y + $z;
};
Postfix Operators
General
Syntax
postfix-expression:
primary-expression
clone-expression
object-creation-expression
array-creation-expression
subscript-expression
function-call-expression
member-selection-expression
null-safe-member-selection-expression
postfix-increment-expression
postfix-decrement-expression
scope-resolution-expression
exponentiation-expression
Defined elsewhere
- array-creation-expression
- clone-expression
- exponentiation-expression
- function-call-expression
- member-selection-expression
- null-safe-member-selection-expression
- object-creation-expression
- postfix-decrement-expression
- postfix-increment-expression
- primary-expression
- scope-resolution-expression
- subscript-expression
Semantics
These operators associate left-to-right.
The clone Operator
Syntax
clone-expression:
clone expression
Defined elsewhere
Constraints
expression must designate an object.
Semantics
The clone operator creates a new object that is a shallow copy of the object designated by expression. Then, if the class type of expression has a method called __clone, that is called to perform a deep copy. The result is a handle that points to the new object.
Examples
Consider a class Employee, from which is derived a class Manager. Let us
assume that both classes contain properties that are objects. clone is
used to make a copy of a Manager object, and behind the scenes, the
Manager object uses clone to copy the properties for the base class,
Employee.
class Employee
{
...
public function __clone(): void {
// make a deep copy of Employee object
}
}
class Manager extends Employee
{
...
public function __clone(): void
{
$v = parent::__clone();
// make a deep copy of Manager object
}
}
$obj1 = new Manager("Smith", 23);
$obj2 = clone $obj1; // creates a new Manager that is a deep copy
The new Operator
Syntax
object-creation-expression:
new class-type-designator ( argument-expression-listopt )
class-type-designator:
parent
self
static
member-selection-expression
null-safe-member-selection-expression
qualified-name
scope-resolution-expression
subscript-expression
variable-name
Defined elsewhere
- argument-expression-list
- member-selection-expression
- null-safe-member-selection-expression
- qualified-name
- scope-resolution-expression
- subscript-expression
- variable-name
Constraints
If the class-type-designator is a scope-resolution-expression then it must not have class as the right hand side of the :: operator.
Otherwise, if the class-type-designator is a qualified-name or scope-resolution-expression which resolves a qualified name, or self, or parent, then it must designate a class.
Otherwise,the class-type-designator must be an expression evaluating to a value having the classname type. Furthermore, it must designate a class that has the attribute __ConsistentConstruct, or that has an abstract constructor or a final constructor.
The class-type-designator must not designate an abstract class.
The class-type-designator must not be a generic type parameter.
The object-creation-expression will invoke the constructor of the class designated by the class-type-designator.
argument-expression-list must contain an argument for each parameter in the constructor's definition not having a default value, and each argument's type must be a subtype of the corresponding parameter's type.
If the constructor is not variadic, the call must not contain more arguments than there are corresponding parameters.
Semantics
The new operator allocates memory for an object that is an instance of
the class specified by the class-type-designator.
The object is initialized by calling the class's constructor (16.8)
passing it the optional argument-expression-list. If the class has no
constructor, the constructor that class inherits (if any) is used.
Otherwise, each instance property having any nullable type takes on the value
null.
The result of an object-creation-expression is a handle to an object of the type specified by the class-type-designator.
From within a method, the use of static corresponds to the class in the
inheritance context in which the method is called. The type of the object created by an expression of the form new static is this.
Because a constructor call is a function call, the relevant parts of 10.5.6 also apply.
Examples
class Point
{
public function __construct(float $x = 0, float $y = 0)
{
...
}
...
}
$p1 = new Point(); // create Point(0, 0)
$p1 = new Point(12); // create Point(12, 0)
// -----------------------------------------
class C { ... }
function f(classname<C> $clsname): void {
$w = new $clsname();
…
}
Array Creation Operator
An array is created and initialized by one of two equivalent ways: via
the array-creation operator [], as described below, or the intrinsic
array.
Syntax
array-creation-expression:
array ( array-initializeropt )
[ array-initializeropt ]
array-initializer:
array-initializer-list ,opt
array-initializer-list:
array-element-initializer
array-element-initializer , array-initializer-list
array-element-initializer:
element-value
element-key => element-value
element-key:
expression
element-value
expression
Defined elsewhere
Constraints
If any array-element-initializer in an array-initializer-list contains an element-key, then all array-element-initializers in that array-initializer-list must contain an element-key.
Semantics
This operator creates an array. If array-initializer contains any element-keys, the resulting array is a map-like array; otherwise, it is a vector-like array. If array-initializer is omitted, the array has zero elements, and the resulting array is neither a vector-like nor a map-like array, although it is a subtype of both types.. For convenience, an array-initializer may have a trailing comma; however, this comma has no purpose. An array-initializer-list consists of a vector-like array. If array-initializer is omitted, the array has zero elements. For convenience, an array-initializer may have a trailing comma; however, this comma has no purpose. An array-initializer-list consists of a comma-separated list of one or more array-element-initializers, each of which is used to provide an element-value and an optional element-key.
If the value of element-key is neither int nor string, keys with float
or bool values, or strings whose contents match exactly the pattern of
decimal-literal, are converted to int, and values
of all other key types are converted to string.
If element-key is omitted from an array-element-initializer, an
element key of type int is associated with the corresponding
element-value. The key associated is one more than the previously
assigned int key for this array. However, if this is the first element
with an int key, key zero is associated.
Once the element keys have been converted to int or string, if two or more
array-element-initializers in an array-initializer contain the same
key, the lexically right-most one is the one whose element-value is used
to initialize that element.
The result of this operator is a handle to the set of array elements.
Examples
$v = []; // array has 0 elements
$v = array(true); // vector-like array has 1 element, true
$v = [123, -56]; // vector-like array of two ints, with keys 0 and 1
$v = [0 => 123, 1 => -56]; // map-like array of two ints, with keys 0 and 1
$i = 10;
$v = [$i - 10 => 123, $i - 9 => -56]; // key can be a runtime expression
$i = 6; $j = 12;
$v = [7 => 123, 3 => $i, 6 => ++$j]; // keys are in arbitrary order
$v[4] = 99; // extends array with a new element
$v = [2 => 23, 1 => 10, 2 => 46, 1.9 => 6];
// map-like array has 2 elements, with keys 2 and 1, values 46 and 6
$v = ["red" => 10, "4" => 3, 9.2 => 5, "12.8" => 111, null => 1];
// map-like array has 5 elements, with keys "red", 4, 9, "12.8", and "".
$c = array("red", "white", "blue");
$v = array(10, $c, null, array(false, null, $c));
$v = array(2 => true, 0 => 123, 1 => 34.5, -1 => "red");
foreach($v as $e) { … } // iterates over keys 2, 0, 1, -1
for ($i = -1; $i <= 2; ++$i) { … $v[$i] } // retrieves via keys -1, 0, 1, 2
Subscript Operator
Syntax
subscript-expression:
postfix-expression [ expressionopt ]
postfix-expression { expressionopt } [Deprecated form]
Defined elsewhere
Constraints
If postfix-expression designates a string, expression must not designate a string.
expression can be omitted only if subscript-expression is used in a modifiable-lvalue context and postfix-expression does not designate a string.
If subscript-expression is used in a non-lvalue context, the element being designated must exist.
When postfix-expression designates a vector-like array, expression must
have type int.
When postfix-expression designates a map-like array, elements cannot be appended using empty [].
When postfix-expression designates a tuple, expression must be a constant.
When postfix-expression designates a shape, expression must be a single-quoted-string-literal that specifies a key in that shape's shape-specifier.
When postfix-expression designates an instance of a collection class:
- The deprecated form,
{ … }, is not supported. VectororImmVector, expression must have typeint.Vector, if expression is omitted, subscript-expression must be the left-hand side of a simple-assignment-expression.MaporImmMap, expression must have typeintorstring.Map, if expression is omitted, subscript-expression must be the left-hand side of a simple-assignment-expression whose right-hand operand has typePair.SetorImmSet, subscripting is not permitted.Pair, expression must be either the literal 0 or 1.
Semantics
A subscript-expression designates a (possibly non-existent) element of
an array, a string, a vector, a map, or a Pair. When subscript-expression designates an object of
a type that implements ArrayAccess, the minimal semantics are
defined below; however, they can be augmented by that object's methods
offsetGet and offsetSet.
The element key is designated by expression. If the value of
element-key is neither int nor string, keys with float or bool values,
or strings whose contents match exactly the pattern of decimal-literal, are converted to int, and values of all other key
types are converted to string.
If both postfix-expression and expression designate strings,
expression is treated as if it specified the int key zero instead.
A subscript-expression designates a modifiable lvalue if and only if postfix-expression designates a modifiable lvalue.
postfix-expression designates an array
If expression is present, if the designated element exists, the type
and value of the result is the type and value of that element;
otherwise, the result is null.
If expression is omitted, a new element is inserted. Its key has type
int and is one more than the highest, previously assigned, non-negative
int key for this array. If this is the first element with a non-negative
int key, key zero is used. However, if the highest, previously assigned
int key for this array is PHP_INT_MAX, no new element is
inserted. The type and value of the result is the type and value of
the new element.
- If the usage context is as the left-hand side of a simple-assignment-expression: The value of the new element is the value of the right-hand side of that simple-assignment-expression.
- If the usage context is as the left-hand side of a compound-assignment-expression: The expression
e1 op= e2is evaluated ase1 = null op (e2). - If the usage context is as the operand of a postfix- or prefix-increment or decrement operator: The value of the new element is
null.
postfix-expression designates a string
If the designated element exists, the type and value of the result is the type and value of that element; otherwise, the result is an empty string.
postfix-expression designates a vector
For a Vector or ImmVector, if expression is present, if the designated
element exists, the type and value of the result is the type and value of that
element; otherwise, an exception of type \OutOfBoundsException is thrown.
For a Vector, if expression is omitted, a new element is inserted whose
value is that of the right-hand side of the simple-assignment-expression.
Its key has type int and is one more than the highest, previously assigned,
int key for this Vector. If this is the first element, key zero is used.
The type and value of the result is the type and value of the new element.
postfix-expression designates a map
For a Map or ImmMap, if expression is present, if the designated element
exists, the type and value of the result is the type and value of that
element; otherwise, an exception of type \OutOfBoundsException is thrown.
For a Map, if expression is omitted, the contents of the Pair right-hand
operand of the simple-assignment-expression for which this
subscript-expression is the left operand, is examined. Element 0 of that
Pair represents the key while element 1 represents the value. If the Map
already contains an element having that key, that element's value is changed
to the value in the Pair; otherwise, a new element is inserted in the Map with
the key and value from the Pair. The type and value of the result is the type
and value of the modified or new element.
postfix-expression designates a Pair
If expression is the literal 0, the type and value of the result is the type
and value of the first element in that Pair.
If expression is the literal 1, the type and value of the result is the type
and value of the second element in that Pair.
postfix-expression designates an object of a type that implements
ArrayAccess
If expression is present,
- If subscript-expression is used in a non-lvalue context, the object's method
offsetGetis called with an argument of expression. The type and value of the result is the type and value returned byoffsetGet. - If the usage context is as the left-hand side of a simple-assignment-expression: The object's method
offsetSetis called with a first argument of expression and a second argument that is the value of the right-hand side of that simple-assignment-expression. The type and value of the result is the type and value of the right-hand side of that simple-assignment-expression. - If the usage context is as the left-hand side of a compound-assignment-expression: The expression
e1 op= e2is evaluated ase1 = offsetGet(expression) op (e2), which is then processed according to the rules for simple assignment immediately above. - If the usage context is as the operand of a postfix- or prefix-increment or decrement operator: The object's method
offsetGetis called with an argument of expression. However, this method has no way of knowing if an increment or decrement operator was used, or whether it was a prefix or postfix operator. The type and value of the result is the type and value returned byoffsetGet.
If expression is omitted,
- If the usage context is as the left-hand side of a simple-assignment-expression: The object's method
offsetSetis called with a first argument ofnulland a second argument that is the value of the right-hand side of that simple-assignment-expression. The type and value of the result is the type and value of the right-hand side of that simple-assignment-expression. - If the usage context is as the left-hand side of a compound-assignment-expression: The expression
e1 op= e2is evaluated ase1 = offsetGet(null) op (e2), which is then processed according to the rules for simple assignment immediately above. - If the usage context is as the operand of a postfix- or prefix-increment or decrement operator: The object's method
offsetGetis called with an argument ofnull. However, this method has no way of knowing if an increment or decrement operator was used, or whether it was a prefix or postfix operator. The type and value of the result is the type and value returned byoffsetGet.
Note: The brace ({...}) form of this operator has been deprecated.
Examples
$v = array(10, 20, 30);
$v[1] = 1.234; // change the value (and type) of element [1]
$v[-10] = 19; // insert a new element with int key -10
$v["red"] = true; // insert a new element with string key "red"
[[2,4,6,8], [5,10], [100,200,300]][0][2] // designates element with value 6
["black", "white", "yellow"][1][2] // designates substring "i" in "white"
function f(): array<int> { return [1000, 2000, 3000]; }
f()[2] // designates element with value 3000
"red"[1.9] // designates [1]
"red"[0][0][0] // designates [0]
// -----------------------------------------
class MyVector implements ArrayAccess { … }
$vect1 = new MyVector(array(10, 'A' => 2.3, "up"));
$vect1[10] = 987; // calls MyVector::offsetSet(10, 987)
$vect1[] = "xxx"; // calls MyVector::offsetSet(null, "xxx")
$x = $vect1[1]; // calls MyVector::offsetGet(1)
// -----------------------------------------
$v1 = Vector {5, 10, 15};
$v1[] = 20; // add a new element with value 20 to the end
$v1[0] = -5; // change the value of existing element 0 to -5
// -----------------------------------------
$m1 = Map {'red' => 5, 'green' => 12};
$m1['blue'] = 35; // add an element with a new key
$m1['red'] = 6; // change the value of an existing element
$m1[] = Pair {'black', 43}; // append value 43 with key 'black'
$m1[] = Pair {'red', 123}; // replaces existing element's value with 123
// -----------------------------------------
$p1 = Pair {55, 'auto'};
echo "\$p1[0] = " . $p1[0] . "\n"; // outputs '$p1[0] = 55'
Function Call Operator
Syntax
function-call-expression:
postfix-expression ( argument-expression-listopt )
argument-expression-list:
argument-expressions ,opt
argument-expressions:
expression
argument-expressions , expression
Defined elsewhere
Constraints
postfix-expression must designate a function, by name, be a variable of closure type.
The function call must contain an argument for each parameter in the called function's definition not having a default value, and the argument type must be a subtype of the parameter type.
If the called function is not variadic, the function call must not contain more arguments than there are corresponding parameters.
Semantics
If postfix-expression is a null-safe-member-selection-expression, special handling occurs; see later below.
An expression of the form function-call-expression is a function call. The postfix expression designates the called function, and argument-expression-list specifies the arguments to be passed to that function. Each argument corresponds to a parameter or the optional ellipsis in the called function's definition. An argument can have any type. In a function call, postfix-expression is evaluated first, followed by each assignment-expression in the order left-to-right. There is a sequence point right before the function is called. For details of the type and value of a function call see §§. The value of a function call, if any, is a non-modifiable lvalue.
If the called function is variadic, the function call can have any number of arguments, provided the function call has at least an argument for each parameter not having a default value.
When an argument corresponds to the ellipsis in the called function's definition, the argument can have any type.
When postfix-expression designates an instance method or constructor,
the instance used in that designation is used as the value of $this in
the invoked method or constructor. However, if no instance was used in
that designation (for example, in the call C::instance_method()) the
invoked instance has no $this defined.
When a function is called, the value of each argument passed to it is assigned to the corresponding parameter in that function's definition, if such a parameter exists. The assignment of argument values to parameters is defined in terms of simple assignment. Any parameters having a default value but no corresponding argument, takes on that default value.
If an undefined variable is passed using byRef, that variable becomes
defined, with a default value of null.
Direct and indirect recursive function calls are permitted.
The following discussion applies when postfix-expression is a
null-safe-member-selection-expression: If postfix-expression is not
null, the behavior is the same as if a member-selection-expression were used instead of a null-safe-member-selection-expression.
Otherwise, no function is called, and the function-call-expression
evaluates to null. The expressions in
argument-expression-list are evaluated.
Examples
function f3(int $p1 = -1, float $p2 = 99.99, string $p3 = '??'): void { … }
f3(); // $p1 is -1, $p2 is 99.99, $p3 is ??
f3(123); // $p1 is 123, $p2 is 99.99, $p3 is ??
f3(123, 3.14); // $p1 is 123, $p2 is 3.14, $p3 is ??
f3(123, 3.14, 'Hello'); // $p1 is 123, $p2 is 3.14, $p3 is Hello
// -----------------------------------------
function fx(int $p1, int $p2, int $p3, int $p4, int $p5): void { … }
function fy(int $p1, int $p2, int $p3, int $p4, int $p5): void { … }
function fz(int $p1, int $p2, int $p3, int $p4, int $p5): void { … }
$funcTable = array(fun('fx'), fun('fy'), fun('fz')); // use lib function fun
$i = 1;
$funcTable[$i++]($i, ++$i, $i, $i = 12, --$i); // calls fy(2,3,3,12,11)
// -----------------------------------------
$anon = function (): void { … }; // store a closure in $anon
$anon(); // call the anonymous function
Member-Selection Operator
Syntax
member-selection-expression:
postfix-expression -> name
postfix-expression -> variable-name
Defined elsewhere
Constraints
postfix-expression must designate an object.
name must designate an instance property, or an instance method of the class designated by postfix-expression.
variable-name must name a variable which when evaluated produces a string containing an instance property or an instance method of the class designated by postfix-expression.
Semantics
A member-selection-expression designates an instance property or an instance method of the object designated by postfix-expression. For a property, the value is that of the property, and is a modifiable lvalue if postfix-expression is a modifiable lvalue.
Examples
class Point {
private float $x;
private float $y;
public function move(float $x, float $y): void {
$this->x = $x; // sets private property $x
$this->y = $y; // sets private property $x
}
public function __toString(): string {
return '(' . $this->x . ',' . $this->y . ')';
} // get private properties $x and $y
}
$p1 = new Point();
$p1->move(3, 9); // calls public instance method move by name
Null-Safe Member-Selection Operator
Syntax
null-safe-member-selection-expression: postfix-expression ?-> name postfix-expression ?-> variable-name
Defined elsewhere
Constraints
postfix-expression must designate a nullable-typed object.
name must designate an instance property or an instance method of the class designated by postfix-expression.
variable-name must name a variable which when evaluated produces a string containing an instance property or an instance method of the class designated by postfix-expression.
Semantics
If postfix-expression is null, no property or method is selected and the
resulting value is null. Otherwise, the behavior is like that of the
member-selection operator ->,
except that the resulting value is not an lvalue.
Postfix Increment and Decrement Operators
Syntax
postfix-increment-expression:
unary-expression ++
postfix-decrement-expression:
unary-expression --
Defined elsewhere
Constraints
The operand of the postfix ++ and -- operators must be a modifiable lvalue that has arithmetic type.
Semantics
These operators behave like their prefix counterparts except that the value of a postfix ++ or -- expression is the value before any increment or decrement takes place.
Examples
$i = 10; $j = $i-- + 100; // old value of $i (10) is added to 100
$a = array(100, 200); $v = $a[1]++; // old value of $ia[1] (200) is assigned
Scope-Resolution Operator
Syntax
scope-resolution-expression:
scope-resolution-qualifier :: name
scope-resolution-qualifier :: variable-name
scope-resolution-qualifier :: class
scope-resolution-qualifier:
qualified-name
variable-name
self
parent
static
Defined elsewhere
Constraints
Scope resolution expressions of the form qualified-name::name must have the name of an enum, class or interface type on the left of the ::, and an enumeration constant or type member on the right.
Scope resolution expressions of the form qualified-name::variable-name must have the name of a class or interface type on the left of the ::, and a static property of that type on the right.
Scope resolution expressions of the form qualified-name::class must have the name of a class or interface type on the left of the ::.
Scope resolution expressions with a variable-name to the left of the :: must name a variable having the classname type.
variable-name :: class is not permitted.
Semantics
When qualified-name is the name of an enumerated type, scope-resolution-expression designates an enumeration constant within that type.
From inside or outside a class or interface, operator :: allows the
selection of a constant. From inside or outside a class, this operator
allows the selection of a static property, static method, or instance
method. From within a class, it also allows the selection of an
overridden property or method. For a property, the value is that of the
property, and is a modifiable lvalue if name is
a modifiable lvalue.
From within a class, self::m refers to the member m in that class,
whereas parent::m refers to the closest member m in the base-class
hierarchy, not including the current class. From within a method,
static::m refers to the member m in the class that corresponds to the
class inheritance context in which the method is called. This allows
late static binding. Consider the following scenario:
class Base
{
public function b(): void
{
static::f(); // calls the most appropriate f()
}
public function f(): void { ... }
}
class Derived extends Base
{
public function f(): void { ... }
}
$b1 = new Base();
$b1->b(); // as $b1 is an instance of Base, Base::b() calls Base::f()
$d1 = new Derived();
$d1->b(); // as $d1 is an instance of Derived, Base::b() calls Derived::f()
The value of the form of scope-resolution-expression ending in ::class
is a string containing the fully qualified name of the current class,
which for a static qualifier, means the current class context.
variable-name :: name results in a constant whose value has the classname type for the type designated by variable-name.
Examples
final class MathLibrary
enum ControlStatus: int {
Stopped = 0;
Stopping = 1;
Starting = 2;
Started = 3;
}
function main(ControlStatus $p1): void {
switch ($p1)
{
case ControlStatus::Stopped:
…
break;
case ControlStatus::Stopping:
…
break;
…
}
…
}
// -----------------------------------------
final class MathLibrary {
public static function sin(): float { … }
…
}
$v = MathLibrary::sin(2.34); // call directly by class name
// -----------------------------------------
class MyRangeException extends Exception {
public function __construct(string $message, …)
{
parent::__construct($message);
…
}
…
}
// -----------------------------------------
class Point {
private static int $pointCount = 0;
public static function getPointCount(): int {
return self::$pointCount;
}
…
}
Exponentiation Operator
Syntax
exponentiation-expression:
expression ** expression
Defined elsewhere
Constraints
Both expressions must have an arithmetic type.
Semantics
The ** operator produces the result of raising the value of the
left-hand operand to the power of the right-hand one. If both operands have non-negative integer
values and the result can be represented as an int, the result has type
int; otherwise, the result has type float.
Examples
2**3; // int with value 8
2**3.0; // float with value 8.0
"2.0"**"3"; // float with value 8.0
Unary Operators
General
Syntax
unary-expression:
postfix-expression
prefix-increment-expression
prefix-decrement-expression
unary-op-expression
error-control-expression
cast-expression
await-expression
Defined elsewhere
- await-expression
- cast-expression
- error-control-expression
- postfix-expression
- prefix-decrement-expression
- prefix-increment-expression
- unary-op-expression
Semantics
These operators associate right-to-left.
Prefix Increment and Decrement Operators
Syntax
prefix-increment-expression:
++ unary-expression
prefix-decrement-expression:
-- unary-expression
Defined elsewhere
Constraints
The operand of the prefix ++ or -- operator must be a modifiable lvalue
that has arithmetic type.
Semantics
Arithmetic Operands
For a prefix ++ operator, the side effect of the operator is to increment by 1, as appropriate, the
value of the operand. The result is the value of the operand after it
has been incremented. If an int operand's value is the largest
representable for that type, the type and value of the result is implementation-defined.
For a prefix -- operator, the side
effect of the operator is to decrement by 1, as appropriate, the value
of the operand. The result is the value of the operand after it has been
decremented. If an int operand's value is the smallest representable for
that type, the type and value of the result is implementation-defined.
For a prefix ++ or -- operator used with an operand having the value
INF, -INF, or NAN, there is no side effect, and the result is the
operand's value.
Examples
$i = 10; $j = --$i + 100; // new value of $i (9) is added to 100
$a = array(100, 200); $v = ++$a[1]; // new value of $ia[1] (201) is assigned
Unary Arithmetic Operators
Syntax
unary-op-expression:
unary-operator cast-expression
unary-operator: one of
+ - ! ~
Defined elsewhere
Constraints
The operand of the unary + and unary - operators must have
arithmetic type.
The operand of the unary ! operator must have arithmetic or enumerated type. (The validity of allowing this operator to have an enumerated type operand is questionable; avoid such usage lest support for it disappears.)
The operand of the unary ~ operator must have integer type.
Semantics
For a unary + operator, the type and
value of the result is the type and value of the operand.
For a unary - operator, the value of the
result is the negated value of the operand. However, if an int operand's
original value is the smallest representable for that type, the type and
value of the result is implementation-defined.
For a unary ! operator, the type of the
result is bool. The value of the result is true if the value of the
operand is non-zero (or for a string-based enumeration, a non-empty string); otherwise, the value of the result is false. For
the purposes of this operator, NAN is considered a non-zero value. The
expression !E is equivalent to (E == 0).
For a unary ~ operator, the type of the result
is int. The value of the result is the bitwise complement of the value
of the operand (that is, each bit in the result is set if and only if
the corresponding bit in the operand is clear).
Examples
$v = +10;
if ($v1 > -5) ...
$t = true;
if (!$t) ...
$v = ~0b1010101;
Error Control Operator
Syntax
error-control-expression:
@ expression
Defined elsewhere
Semantics
Operator @ suppresses any error messages generated by the evaluation of
expression.
If a custom error-handler has been established using the library
function set_error_handler that handler is
still called.
Examples
$infile = @fopen("NoSuchFile.txt", 'r');
On open failure, the value returned by fopen is false, which is
sufficient to know to handle the error. There is no need to have any
error message displayed.
Cast Operator
Syntax
cast-expression:
( cast-type ) unary-expression
cast-type: one of
bool int float string
Defined elsewhere
Semantics
The value of the operand cast-expression is converted to the type specified by cast-type, and that is the type and value of the result. This construct is referred to a cast, and is used as the verb, "to cast". If no conversion is involved, the type and value of the result are the same as those of cast-expression.
A cast can result in a loss of information.
A cast-type of bool results in a conversion to type bool.
See §§ for details.
A cast-type of int results in a conversion to type int. See §§ for details.
A cast-type of float results in a conversion to type float. See §§ for details.
A cast-type of string results in a conversion to type string. See §§
for details.
Note that cast-type cannot be a generic type parameter.
Examples
(int)(10/3) // results in the int 3 rather than the float 3.333...
Await Operator
Syntax
await-expression:
await expression
Defined elsewhere
Constraints
This operator must be used within an asynchronous function.
expression must have a type that implements [Awaitable<T>(17-interfaces.md#interface-awaitable).
The return type of the function containing a use of this operator must be a type that implements Awaitable<T>.
await-expression can only be used in the following contexts:
- As an expression-statement
- As the assignment-expression in a simple-assignment-expression
- As expression in a return-statement
Semantics
await suspends the execution of an async function until the result of the asynchronous operation represented by expression is available. See §§ for more information.
The resulting value is the value of type T that was wrapped in the object of type `Awaitable
async function f(): Awaitable<int> {…}
$x = await f(); // $x is an int
$x = f(); // $x is an Awaitable<int>
Examples
async function f(): Awaitable<int> {
…
$r1 = await g();
…
return $r1;
}
async function g(): Awaitable<int> {
…
return $r2;
}
function main (): void {
…
$v = f();
…
}
Function main calls async function f, which in turn awaits on async function g. When g terminates normally, the int value returned is automatically wrapped in an object of type Awaitable<int>. Back in function f, that object is unwrapped, and the int it contained is extracted and assigned to local variable $r1. When f terminates normally, the int value returned is automatically wrapped in an object of type Awaitable<int>. Back in function main, that object is assigned to local variable $v.
instanceof Operator
Syntax
instanceof-expression: unary-expression instanceof-subject instanceof instanceof-type-designator instanceof-subject: expression instanceof-type-designator: qualified-name variable-name
Defined elsewhere
Constraints
The expression in instanceof-subject must designate a variable.
qualified-name must be the name of a class or interface type.
variable-name must name a value having the classname type.
Semantics
Operator instanceof returns true if the variable designated by
expression in instanceof-subject is an object having type
qualified-name or variable-name, is an object whose type is derived from type
qualified-name or variable-name, or is an object whose type implements interface
qualified-name or variable-name. Otherwise, it returns false.
If either expression is not an instance, false is returned.
Note: This operator supersedes the library function is_a, which
has been deprecated.
Examples
class C1 { … } $c1 = new C1();
class C2 { … } $c2 = new C2();
class D extends C1 { … } $d = new D();
$d instanceof C1 // true
$d instanceof C2 // false
$d instanceof D // true
// -----------------------------------------
interface I1 { … }
interface I2 { … }
class E1 implements I1, I2 { … }
$e1 = new E1();
$e1 instanceof I1 // true
Multiplicative Operators
Syntax
multiplicative-expression:
instanceof-expression
multiplicative-expression * instanceof-expression
multiplicative-expression / instanceof-expression
multiplicative-expression % instanceof-expression
Defined elsewhere
Constraints
The operands of the * and / operators must have arithmetic type.
The operands of the % operator must have integer type.
The right-hand operand of operator / and operator % must not be zero.
Semantics
The binary * operator produces the product of its operands. If either operand has type
float, the other is converted to that type, and the result has type
float. Otherwise, both operands have type int, in which case, if the
resulting value can be represented in type int that is the result type.
Otherwise, the type and value of the result is implementation-defined.
Division by zero results in a diagnostic followed by a bool result
having value false. (The values +/- infinity and NaN cannot be generated
via this operator; instead, use the predefined constants INF and NAN.)
The binary / operator produces the quotient from dividing the left-hand
operand by the right-hand one. If either operand has type float, the other is
converted to that type, and the result has type float. Otherwise, both
operands have type int, in which case, if the mathematical value of the
computation can be preserved using type int, that is the result type;
otherwise, the type of the result is float.
The binary % operator produces the remainder from dividing the left-hand
operand by the right-hand one. The result has type int.
These operators associate left-to-right.
Examples
-10 * 100 → int with value -1000
100 * -3.4e10 → float with value -3400000000000
"123" * "2e+5" → float with value 24600000
100 / 100 → int with value 1
100 / 123 → float with value 0.8130081300813
123 % 100 → int with value 23
Additive Operators
Syntax
additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - multiplicative-expression
additive-expression . multiplicative-expression
Defined elsewhere
Constraints
If either operand has array type, the other operand must also have array type, and the two types must have a subtype relationship.
If the operands of the * and / operators do not both have array type, they must both have arithmetic type.
Semantics
For non-array operands, the binary + operator produces the sum of those
operands, while the binary - operator produces the difference of its
operands when subtracting the right-hand operand from the left-hand one.
If either operand has type float, the other is converted to that type, and
the result has type float. Otherwise, both operands have type int, in
which case, if the resulting value can be represented in type int that
is the result type. Otherwise, the type and value of the result is implementation-defined.
If both operands have array type, the binary + operator produces a new
array that is the union of the two operands. The result is a copy of the
left-hand array with elements inserted at its end, in order, for each
element in the right-hand array whose key does not already exist in the
left-hand array. Any element in the right-hand array whose key exists in
the left-hand array is ignored. In this context, this operator is not commutative.
The binary . operator creates a string that is the concatenation of the
left-hand operand and the right-hand operand, in that order. If either
or both operands have types other than string, their values are
converted to type string. The result has type string.
These operators associate left-to-right.
Examples
-10 + 100 → int with value 90
100 + -3.4e10 → float with value -33999999900
100 - 123 → int with value 23
-3.4e10 - abc → float with value -34000000000
// -----------------------------------------
array(66) + array(100, 200) → array(66, 200)
array(2 => 'aa') + array(-4 => 'bb', 6 => 'cc') → array(2 => 'aa', -4 => 'bb', 6 => 'cc')
array('red' => 12, 'green' => 7) + array('blue' => 3) → array('red' => 12, 'green' => 7, 'blue' => 3)
// -----------------------------------------
-10 . NAN → string with value "-10NAN"
INF . "2e+5" → string with value "INF2e+5"
true . null → string with value "1"
10 + 5 . 12 . 100 - 50 → int with value 1512050; ((((10 + 5).12).100)-50)
Bitwise Shift Operators
Syntax
shift-expression:
additive-expression
shift-expression << additive-expression
shift-expression >> additive-expression
Defined elsewhere
Constraints
Each of the operands must have int type.
Semantics
Given the expression e1 << e2, the bits in the value of e1 are shifted
left by e2 positions. Bits shifted off the left end are discarded, and
zero bits are shifted on from the right end. Given the expression
e1 >> e2, the bits in the value of e1 are shifted right by
e2 positions. Bits shifted off the right end are discarded, and the sign
bit is propagated from the left end.
The type of the result is int, and the value of the result is that after
the shifting is complete. The values of e1 and e2 are unchanged.
If the shift count is negative, the actual shift applied is n - (-shift count % n), where n is the number of bits per int. If the
shift count is greater than the number of bits in an int, the actual
shift applied is shift count % n.
These operators associate left-to-right.
Examples
1000 >> 2 // 3E8 is shifted right 2 places
-1000 << 2 // FFFFFC18 is shifted left 5 places
123 >> 128 // adjusted shift count = 0
123 << 33 // For a 32-bit int, adjusted shift count = 1; otherwise, 33
Relational Operators
Syntax
relational-expression:
shift-expression
relational-expression < shift-expression
relational-expression > shift-expression
relational-expression <= shift-expression
relational-expression >= shift-expression
Defined elsewhere
Constraints
If either operand has an enumerated type, the other operand must have the exact same type.
Semantics
Operator < represents less-than, operator > represents
greater-than, operator <= represents less-than-or-equal-to, and
operator >= represents greater-than-or-equal-to.
The type of the result is bool.
The operands are processed using the following steps, in order:
- If the operands both have arithmetic type, the result is the numerical comparison of the two operands after conversion.
- If both operands are non-numeric strings, the result is the lexical comparison of the two operands. Specifically, the strings are compared byte-by-byte starting with their first byte. If the two bytes compare equal and there are no more bytes in either string, the strings are equal and the comparison ends; otherwise, if this is the final byte in one string, the shorter string compares less-than the longer string and the comparison ends. If the two bytes compare unequal, the string having the lower-valued byte compares less-than the other string, and the comparison ends. If there are more bytes in the strings, the process is repeated for the next pair of bytes.
- If both operands are numeric strings, the result is the numeric comparison of the two operands after conversion.
- If both operands have vector-like or map-like array type, if the arrays have different numbers of elements, the one with the fewer is considered less-than the other one—regardless of the keys and values in each—, and the comparison ends. For arrays having the same numbers of elements, if the next key in the left-hand operand exists in the right-hand operand, the corresponding values are compared. If they are unequal, the array containing the lesser value is considered less-than the other one, and the comparison ends; otherwise, the process is repeated with the next element. If the next key in the left-hand operand does not exist in the right-hand operand, the arrays cannot be compared and
falseis returned. For array comparison, the order of insertion of the elements into those arrays is irrelevant. - If the operands have the same object type, the result is decided by comparing the lexically first-declared instance property in each object. If those properties have object type, the comparison is applied recursively.
Regarding operands having the same enumeration type, for an int-based enumeration the enumeration is compared using its value directly as an int. For a string-based enumeration, the enumeration is compared using its value directly as a string. (The validity of allowing these operators to have an enumerated type as an operand is questionable; avoid such usage lest support for it disappears.)
These operators associate left-to-right.
Examples
"" < "ab" → result has value true
"a" > "A" → result has value true
"a0" < "ab" → result has value true
"aA <= "abc" → result has value true
// -----------------------------------------
10 <= 0 → result has value false
'123' <= '4') → false; is doing a numeric comparison
'X123' <= 'X4' → true; is doing a string comparison
// -----------------------------------------
[100] < [10,20,30] → result has value true (LHS array is shorter)
Notes
Ideally, one might expect some constraints on the combination of operand types. However, for historical reasons, other behaviors from PHP have been retained, as documented.
Equality Operators
Syntax
equality-expression:
relational-expression
equality-expression == relational-expression
equality-expression != relational-expression
equality-expression === relational-expression
equality-expression !== relational-expression
Defined elsewhere
Semantics
Operator == represents value-equality, operator != represents value-inequality, operator === represents
same-type-and-value-equality, and operator !== represents
not-same-type-and-value-equality. However, when comparing two objects,
operator === represents identity and operator !== represents
non-identity. Specifically, in this context, these operators check to
see if the two operands are the exact same object, not two different
objects of the same type and value.
The type of the result is bool.
The operands are processed using the following steps, in order:
- For operators
==,!=, and<>, if either operand has the valuenull, then if the other operand has type string, thenullis converted to the empty string (""); otherwise, thenullis converted to type bool. - If both operands are non-numeric strings or one is a numeric string and the other a leading-numeric string, the result is the lexical comparison of the two operands. Specifically, the strings are compared byte-by-byte starting with their first byte. If the two bytes compare equal and there are no more bytes in either string, the strings are equal and the comparison ends; otherwise, if this is the final byte in one string, the shorter string compares less-than the longer string and the comparison ends. If the two bytes compare unequal, the string having the lower-valued byte compares less-than the other string, and the comparison ends. If there are more bytes in the strings, the process is repeated for the next pair of bytes.
- If either operand has type bool, for operators
==and!=, the other operand is converted to that type. The result is the logical comparison of the two operands after any conversion, wherefalseis defined to be less thantrue. - If the operands both have arithmetic type, string type, or are
resources, for operators
==and!=, they are converted to the corresponding arithmetic type §§ and §§). The result is the numerical comparison of the two operands after any conversion. - If both operands have array type, for operators
==and!=, the arrays are equal if they have the same set of key/value pairs and the corresponding values have the same type, after element type conversion, without regard to the order of insertion of their elements. For operators===and!==the arrays are equal if they have the same set of key/value pairs, the corresponding values have the same type, and the order of insertion of their elements are the same. - If only one operand has object type, the two operands are never equal.
- If only one operand has array type, the two operands are never equal.
- If the operands have different object types, the two operands are
never equal except that a
Vectorand anImmVectorhaving the same member type and value set can be equal, as can aMapand anImmMap, and aSetand anImmSet. - If the operands have the same object type, the two operands are equal if the instance properties in each object have the same values. Otherwise, the objects are unequal. The instance properties are compared, one at a time, in the lexical order of their declaration. For properties that have object type, the comparison is applied recursively.
These operators associate left-to-right.
Examples
"a" <> "aa" // result has value true
// -----------------------------------------
null == 0 // result has value true
null === 0 // result has value false
true != 100 // result has value false
true !== 100 // result has value true
// -----------------------------------------
"10" != 10 // result has value false
"10" !== 10 // result has value true
// -----------------------------------------
[10,20] == [10,20.0] // result has value true
[10,20] === [10,20.0] // result has value false
["red"=>0,"green"=>0] === ["red"=>0,"green"=>0] // result has value true
["red"=>0,"green"=>0] === ["green"=>0,"red"=>0] // result has value false
Notes
Ideally, one might expect some constraints on the combination of operand types. However, for historical reasons, other behaviors from PHP have been retained, as documented.
# Bitwise AND Operator
Syntax
bitwise-AND-expression:
equality-expression
bit-wise-AND-expression & equality-expression
Defined elsewhere
Constraints
Each of the operands must have int type.
Semantics
The result of this operator is the bitwise-AND of the two operands, and
the type of that result is int.
This operator associates left-to-right.
Examples
0b101111 & 0b101 // 0b101
$lLetter = 0x73; // letter 's'
$uLetter = $lLetter & ~0x20; // clear the 6th bit to make letter 'S'
Bitwise Exclusive OR Operator
Syntax
bitwise-exc-OR-expression:
bitwise-AND-expression
bitwise-exc-OR-expression ^ bitwise-AND-expression
Defined elsewhere
Constraints
Each of the operands must have int type.
Semantics
The result of this operator is the bitwise exclusive-OR of the two
operands, and the type of that result is int.
This operator associates left-to-right.
Examples
0b101111 | 0b101 // 0b101010
$v1 = 1234; $v2 = -987; // swap two integers having different values
$v1 = $v1 ^ $v2;
$v2 = $v1 ^ $v2;
$v1 = $v1 ^ $v2; // $v1 is now -987, and $v2 is now 1234
Bitwise Inclusive OR Operator
Syntax
bitwise-inc-OR-expression:
bitwise-exc-OR-expression
bitwise-inc-OR-expression | bitwise-exc-OR-expression
Defined elsewhere
Constraints
Each of the operands must have int type.
Semantics
The result of this operator is the bitwise inclusive-OR of the two
operands, and the type of that result is int.
This operator associates left-to-right.
Examples
0b101111 | 0b101 // 0b101111
$uLetter = 0x41; // letter 'A'
$lLetter = $upCaseLetter | 0x20; // set the 6th bit to make letter 'a'
Logical AND Operator
Syntax
logical-AND-expression:
bitwise-inc-OR-expression
logical-AND-expression && bitwise-inc-OR-expression
Defined elsewhere
Constraints
Each of the operands must have scalar type.
Semantics
If either operand does not have type bool, its value is first converted
to that type. An int-based enumeration with value zero is converted to false, while a non-zero value is converted to true. Likewise, a string-based enumeration with value empty string is converted to false, while a non-empty value is converted to true. (The validity of allowing this operator to have an enumerated type operand is questionable; avoid such usage lest support for it disappears.)
Given the expression e1 && e2, e1 is evaluated first. If e1 is false, e2 is not evaluated, and the result has type bool, value false. Otherwise, e2 is evaluated. If e2 is false, the result has type bool, value false; otherwise, it has type bool, value true. There is a sequence point after the evaluation of e1.
This operator associates left-to-right.
Examples
if ($month > 1 && $month <= 12) ...
Logical Inclusive OR Operator
Syntax
logical-inc-OR-expression:
logical-AND-expression
logical-inc-OR-expression || logical-AND-expression
Defined elsewhere
Constraints
Each of the operands must have scalar type.
Semantics
If either operand does not have type bool, its value is first converted
to that type. An int-based enumeration with value zero is converted to false, while a non-zero value is converted to true. Likewise, a string-based enumeration with value empty string is converted to false, while a non-empty value is converted to true. (The validity of allowing this operator to have an enumerated type operand is questionable; avoid such usage lest support for it disappears.)
Given the expression e1 || e2, e1 is evaluated first. If e1 is true, e2 is not evaluated, and the result has type bool, value true. Otherwise, e2 is evaluated. If e2 is true, the result has type bool, value true; otherwise, it has type bool, value false. There is a sequence point after the evaluation of e1.
This operator associates left-to-right.
Examples
if ($month < 1 || $month > 12) ...
Conditional Operator
Syntax
conditional-expression:
logical-inc-OR-expression
logical-inc-OR-expression ? expressionopt : conditional-expression
Defined elsewhere
Semantics
Given the expression e1 ? e2 : e3, if e1 is true, then and only then is e2 evaluated, and the result and its type become the result and type of
the whole expression. Otherwise, then and only then is e3 evaluated, and
the result and its type become the result and type of the whole
expression. There is a sequence point after the evaluation of e1. If e2
is omitted, the result and type of the whole expression is the value and
type of e1 when it was tested.
Regarding a left-most operand that is an enumeration, an int-based enumeration with value zero is converted to false, while a non-zero value is converted to true. Likewise, a string-based enumeration with value empty string is converted to false, while a non-empty value is converted to true. (The validity of allowing this operator to have an enumerated type as its first operand is questionable; avoid such usage lest support for it disappears.)
This operator associates left-to-right.
Examples
for ($i = -5; $i <= 5; ++$i)
echo "$i is ".(($i & 1 == true) ? "odd\n" : "even\n");
// -----------------------------------------
$a = 10 ? : "Hello"; // result is int with value 10
$a = 0 ? : "Hello"; // result is string with value "Hello"
$i = PHP_INT_MAX;
$a = $i++ ? : "red"; // result is int with value 2147483647 (on a 32-bit
// system) even though $i is now the float 2147483648.0
// -----------------------------------------
$i++ ? f($i) : f(++$i); // the sequence point makes this well-defined
// -----------------------------------------
function factorial(int $int): int
{
return ($int > 1) ? $int * factorial($int - 1) : $int;
}
Coalesce Operator
Syntax
coalesce-expression:
logical-inc-OR-expression ?? expression
Defined elsewhere
Semantics
Given the expression e1 ?? e2, if e1 is set and not null, then the result is e1. Otherwise, then and only then is e2
evaluated, and the result becomes the result of the whole expression. There is a sequence point after the evaluation of e1.
This operator associates right-to-left.
Examples
function foo(): void {
echo "executed!", PHP_EOL;
}
function main(): void {
$arr = ["foo" => "bar", "qux" => null];
$obj = (object)$arr;
$a = $arr["foo"] ?? "bang"; // "bar" as $arr["foo"] is set and not null
$a = $arr["qux"] ?? "bang"; // "bang" as $arr["qux"] is null
$a = $arr["bing"] ?? "bang"; // "bang" as $arr["bing"] is not set
$a = $obj->foo ?? "bang"; // "bar" as $obj->foo is set and not null
$a = $obj->qux ?? "bang"; // "bang" as $obj->qux is null
$a = $obj->bing ?? "bang"; // "bang" as $obj->bing is not set
$a = null ?? $arr["bing"] ?? 2; // 2 as null is null, and $arr["bing"] is not set
var_dump(true ?? foo()); // outputs bool(true), "executed!" does not appear as it short-circuits
}
Pipe Operator
Syntax
piped-expression: coalesce-expression piped-expression |> coalesce-expression
coalesce-expression is defined in §§.
Constraints
piped-expression cannot be used as the right-hand operand of an assignment operator.
coalesce-expression must contain at least one occurrence of the pipe variable $$.
Semantics
piped-expression is evaluated with the result being stored in the pipe variable $$. There is a sequence point after the evaluation of piped-expression. Then coalesce-expression is evaluated, and its type and value become the type and value of the result.
This operator associates left-to-right.
Examples
class Widget { … }
function pipe_operator_example(array<Widget> $arr): int {
return $arr
|> array_map($x ==> $x->getNumber(), $$)
|> array_filter($$, $x ==> $x % 2 == 0)
|> count($$);
}
Lambda Expressions
Syntax
lambda-expression: piped-expression asyncopt lambda-function-signature ==> lambda-body lambda-function-signature: variable-name ( anonymous-function-parameter-listopt ) anonymous-function-returnopt lambda-body: expression compound-statement
Defined elsewhere
- anonymous-function-parameter-list
- anonymous-function-return
- coalesce-expression
- compound-statement
- conditional-expression
- expression
- piped-expression
- variable-name
Constraints
Each variable-name in an anonymous-function-parameter-list must be distinct.
If any anonymous-function-parameter-declaration has a default-argument-specifier, then all subsequent anonymous-function-parameter-declarations in the same anonymous-function-parameter-declaration-list must also have a default-argument-specifier.
If the type-specifier in anonymous-function-return is void, the compound-statement must not contain any return statements having an expression. Otherwise, if that type-specifier is not omitted, the expression in lambda-body, or all return statements in compound-statement must contain an expression whose type is a subtype of the type indicated by the return type's type-specifier.
If async is present, return-type must be a type that implements Awaitable<T>.
Semantics
A lambda expression is an anonymous function implemented using an operator. In many cases, the lambda-expression version is simpler to writer and easier to read, as is shown in the following:
$doublerl = ($p) ==> $p * 2;
$doubler2 = function ($p) { return $p * 2; };
Lambda expressions automatically capture any variables appearing in their body that also appear in the enclosing lexical function scopes transitively (i.e., nested lambda expressions can refer to variables from several levels out, with intermediate lambda expressions capturing that variable so it can be forwarded to the inner lambda expression). Variables are only captured when they are statically visible as names in the enclosing scope; i.e., the capture list is computed statically, not based on dynamically defined names in the scope. A lambda expression's captured variables are captured with the same by-value semantics that are used for variables in an anonymous-function-use-clause of an anonymous-function-creation-expression.
This operator is right-associative and lambda-expressions can be chained together, allowing expressions of the form $f = $x ==> $y ==> $x + $y.
When a lambda expression is executed, it creates an object of some, unspecified closure type.
If the type-specifier for a parameter is omitted, that type is inferred.
If anonymous-function-return is omitted, the return type is inferred.
The anonymous function in a lambda-expression can be asynchronous.
Examples
// returns 73
$fn = $x ==> $x + 1; $fn(12); // returns 13$fn = () ==> 73; $n();
$fn = ($a = -1, ...): int ==> $a * 2;
// -----------------------------------------
$dump_map = ($name, $x) ==> {
echo "Map $name has:\n";
foreach ($x as $k => $v) {
echo " $k => $v\n";
}
};
// -----------------------------------------
$fn1 = $x ==> $y ==> $x + $y;
$fn2 = $fn1(10); $res = $fn2(7); // result is 17
Assignment Operators
General
Syntax
assignment-expression:
lambda-expression
simple-assignment-expression
compound-assignment-expression
Defined elsewhere
Constraints
The left-hand operand of an assignment operator must be a modifiable lvalue.
Semantics
These operators associate right-to-left.
Simple Assignment
Syntax
simple-assignment-expression:
unary-expression = assignment-expression
Defined elsewhere
Constraints
If the location designated by the left-hand operand is a string element,
the key must not be a negative-valued int, and the right-hand operand
must have type string.
If unary-expression is a subscript-expression whose postfix-expression designates a vector-like array and whose expression is present, if expression designates a non-existent element, the behavior is unspecified.
If unary-expression is a subscript-expression whose postfix-expression designates a Map and whose expression is omitted, assignment-expression must designate a Pair.
Semantics
If assignment-expression designates an expression having value type, see §§. If assignment-expression designates an expression having a handle, see §§. If assignment-expression designates an expression having array type, see §§. If assignment-expression designates an expression having a shape type, it is treated as if it had array type.
The type and value of the result is the type and value of the left-hand operand after the store (if any [see below]) has taken place. The result is not an lvalue.
If the location designated by the left-hand operand is a non-existent array element, a new element is inserted with the designated key and with a value being that of the right-hand operand.
If the location designated by the left-hand operand is a string element,
then if the key is a negative-valued int, there is no side effect.
Otherwise, if the key is a non-negative-valued int, the left-most single
character from the right-hand operand is stored at the designated
location; all other characters in the right-hand operand string are
ignored. If the designated location is beyond the end of the
destination string, that string is extended to the new length with
spaces (U+0020) added as padding beyond the old end and before the newly
added character. If the right-hand operand is an empty string, the null
character \0 (U+0000) is stored.
Examples
$a = $b = 10 // equivalent to $a = ($b = 10)
$v = array(10, 20, 30);
$v[-10] = 19; // insert a new element with int key -10
$s = "red";
$s[1] = "X"; // OK; "e" -> "X"
$s[-5] = "Y"; // warning; string unchanged
$s[5] = "Z"; // extends string with "Z", padding with spaces in [3]-[5]
$s = "red";
$s[0] = "DEF"; // "r" -> "D"; only 1 char changed; "EF" ignored
$s[0] = ""; // "D" -> "\0"
$s["zz"] = "Q"; // warning; defaults to [0], and "Q" is stored there
// -----------------------------------------
class C { … }
$a = new C(); // make $a point to the allocated object
Compound Assignment
Syntax
compound-assignment-expression:
unary-expression compound-assignment-operator assignment-expression
compound-assignment-operator: one of
**= *= /= %= += -= .= <<= >>= &= ^= |=
Defined elsewhere
Constraints
Any constraints that apply to the corresponding postfix or binary operator apply to the compound-assignment form as well.
Semantics
The expression e1 op= e2 is equivalent to e1 = e1 op (e2), except
that e1 is evaluated once only.
Examples
$v = 10;
$v += 20; // $v = 30
$v -= 5; // $v = 25
$v .= 123.45 // $v = "25123.45"
$a = [100, 200, 300];
$i = 1;
$a[$i++] += 50; // $a[1] = 250, $i → 2
yield Operator
Syntax
expression:
assignment-expression
yield array-element-initializer
Defined elsewhere
Constraints
yield must not be used inside an async function, method, or closure.
Semantics
Any function containing a yield operator is a generator function.
A generator function generates a collection of zero or more key/value
pairs where each pair represents the next in some series. For example, a
generator might yield random numbers or the series of Fibonacci
numbers. When a generator function is called explicitly, it returns an
object of type Generator (see below and §§), which implements the interface
Iterator. As such, this allows that object to be iterated over
using the foreach statement. During each iteration, the Engine
calls the generator function implicitly to get the next key/value pair.
Then the Engine saves the state of the generator for subsequent
key/value pair requests.
This operator produces the result null unless the method
Generator->send was called to provide a result value. This
operator has the side effect of generating the next value in the
collection.
Before being used, an element-key must have, or be converted to, type
int or string. Keys with float or bool values, or strings whose contents
match exactly the pattern of decimal-literal, are
converted to int. Values of all other key types are converted to string.
If element-key is omitted from an array-element-initializer, an
element key of type int is associated with the corresponding
element-value. The key associated is one more than the previously
assigned int key for this collection. However, if this is the first
element in this collection with an int key, key zero is used. If
element-key is provided, it is associated with the corresponding
element-value. The resulting key/value pair is made available by
yield.
If array-element-initializer is omitted, default int-key assignment is
used and each value is null.
A generator function's return type is Generator<Tk, Tv, Ts>, where Tk is the type of "key" (must be int if there is no key), Tv is the type of "value", and "?Ts" is the result of the entire yield expression. Continuation<T> is an alias for Generator<int, T, void>; the two are interchangeable. For a generator function that always returns a single value of the same type T, declare the return type as Continuation<T>.
Examples
function getTextFileLines(string $filename): Continuation<string> {
$infile = fopen($filename, 'r');
if ($infile == false) { /* deal with the file-open failure */ }
try {
while ($textLine = fgets($infile)) // while not EOF {
$textLine = rtrim($textLine, "\r\n"); // strip off terminator
yield $textLine;
}
} finally {
fclose($infile);
}
}
foreach (getTextFileLines("Testfile.txt") as $line) { /* process each line */ }
// -----------------------------------------
function series(int $start, int $end, string $keyPrefix = ""):
Generator<string, int, void> {
for ($i = $start; $i <= $end; ++$i) {
yield $keyPrefix . $i => $i; // generate a key/value pair
}
}
foreach (series(1, 5, "X") as $key => $val) { /* process each key/val pair */ }
Constant Expressions
Syntax
constant-expression:
array-creation-expression
collection-literal
tuple-literal
shape-literal
const-expression
const-expression:
expression
Defined elsewhere
Constraints:
All of the element-key and element-value expressions in array-creation-expression must be literals, or tuple-literals and/or shape-literals containing only literals.
All of the expressions in a collection-literal must be literals.
All of the expressions in tuple-literal must be literals.
All of the expressions in shape-literal must be literals.
expression must have a scalar type, and be a literal or the name of an existing c-constant that is currently in scope.
Semantics:
A const-expression is the value of a c-constant. A const-expression is required in several contexts, such as in initializer values in a const-declaration and default initial values in a function definition.