On the http://php.net/manual/en/language.operators.precedence.php webpage, the second highest precedence level contains a left-associative operator called [
.
This is a very valid question.
First there is never an ambiguity to what PHP should evaluate first when looking at the
right side of the [
, since the bracket requires a closing one to go with it, and so
every operator in between has precedence over the opening bracket.
Example:
$a[1+2]
The +
has precedence, i.e. first 1+2 has to be evaluated before PHP can determine which
element to retrieve from $a.
But the operator precedence list is not about this.
Secondly there is an order of evaluating consecutive pairs of []
, like here:
$b[1][2]
PHP will first evaluate $b[1]
and then apply [2]
to that. This is left-to-right
evaluation and is what is intended with left associativity.
But the question at hand is not so much about associativity, but about precedence with regards to other operators.
The list states that clone
and new
operators have precedence over [
, and this is not easy to test.
First of all, most of the constructs where you would combine new
with square brackets are considered invalid
syntax. For example, both of these statements:
$a = new myClass()[0];
$a = new myClass[0];
will give a parsing error:
syntax error, unexpected '['
PHP requires you to add parentheses to make the syntax valid. So there is no way we can test the precedence rules like this.
But there is another way, by using a variable containing a class name:
$a = new $test[0];
This is valid syntax, but now the challenge is to make a class that creates something that acts like an array.
This is not trivial to do, as an object property is referenced like this: obj->prop
, not
like obj["prop"]
. One can however use the ArrayObject class which can deal with square brackets. The idea is to extend this class and redefine the offsetGet method to make sure a freshly made object of that class has array elements to return.
To make objects printable, I ended up using the magical method __toString, which is executed when an object needs to be cast to a string.
So I came up with this set-up, defining two similar classes:
class T extends ArrayObject {
public function __toString() {
return "I am a T object";
}
public function offsetGet ($offset) {
return "I am a T object's array element";
}
}
class TestClass extends ArrayObject {
public function __toString() {
return "I am a TestClass object";
}
public function offsetGet ($offset) {
return "I am a TestClass object's array element";
}
}
$test = "TestClass";
With this set-up we can test a few things.
echo new $test;
This statement creates a new TestClass instance, which then needs to be converted to string, so the __toString method is called on that new instance, which returns:
I am a TestClass object
This is as expected.
echo (new $test)[0];
Here we start with the same actions, as the parentheses force the new
operation to be executed first. This time PHP does not convert the created object to string, but requests array element 0 from it. This request is answered by the offsetGet method, and so the above statement outputs:
I am a TestClass object's array element
echo new ($test[0]);
The idea is to force the opposite order of execution. Sadly enough, PHP does not allow this syntax, so will have to break the statement into two in order to get the intended evaluation order:
$name = $test[0];
echo new $name;
So now the [
is executed first, taking the first character of the value of
$test, i.e. "T", and then new
is applied to that. That's why I
defined also a T class. The echo
calls __toString on that instance, which yields:
I am a T object
Now comes the final test to see which is the order when no parentheses are present:
echo new $test[0];
This is valid syntax, and...
The output is:
I am a T object
So in fact, PHP applied the [
before the new
operator, despite what is stated in the
operator precedence table!
clone
with new
The clone
operator has similar behaviour in combination with [
. Strangely enough, clone
and new
are not completely equal in terms of syntax rules. Repeating test 2 with clone
:
echo (clone $test)[0];
yields a parsing error:
syntax error, unexpected '['
But test 4 repeated with clone
shows that [
has precedence over it.
@bishop informed that this reproduces the long standing documentation bug #61513: "clone operator precedence is wrong".