9

Check out this code:

$last = end($p = explode('/', $someString));  

Getting this notice:

Only variables should be passed by reference

I'm really confused because $p is a variable.

Amal Murali
  • 75,622
  • 18
  • 128
  • 150
thelolcat
  • 10,995
  • 21
  • 60
  • 102

4 Answers4

12

end() expects a variable, not a reference. In your exemple $p = explode('/', $someString) is not a variable, it's an assignment. As the documentation said:

This array is passed by reference because it is modified by the function. This means you must pass it a real variable and not a function returning an array because only actual variables may be passed by reference.

You should do this instead:

$p = explode('/', $someString);
$last = end($p); 
BlitZ
  • 12,038
  • 3
  • 49
  • 68
Tchoupi
  • 14,560
  • 5
  • 37
  • 71
  • I think it's pretty obvious that I'm not passing a return value, but `$p` which is a variable... – thelolcat Dec 25 '13 at 19:24
  • @thelolcat It is not so obvious. If I am not mistaken, the returned value is used, then the assignment is made. Check out my edit. – Tchoupi Dec 25 '13 at 19:27
  • Your edit is misguided. `echo $a = $b` would print `2` either way: the OP expects it to mean the same as `$a = $b; echo $a`, whereas you're saying it should mean `echo $b; $a = $b`, but both of those are equivalent. (And in fact, I think it actually means `$a = $b; echo $b` -- but again, equivalent.) – ruakh Dec 25 '13 at 19:30
  • @ruakh You are right, it doesn't make sense... I will remove it. Thanks. – Tchoupi Dec 25 '13 at 19:35
  • Well I still don't get it. I specifically assigned the return value of explode to a variable so I don't see that notice, and I'm still seeing it – thelolcat Dec 25 '13 at 19:50
  • 1
    @thelolcat Actually I don't understand the underlying reason either. If you follow the documentation precisely, `$p = explode('/', $someString)` is not a variable. I am currently researching why the reference is used as a parameter instead of the variable itself. – Tchoupi Dec 25 '13 at 20:05
  • @MathieuImbert You are right. It is not using variable, because `$p = explode('/', $someString)` is a STATEMENT, which returns not the reference, but the VALUE itself. I.e. `$a = $b = 'x'`, means to return to `$a` not a reference of `$b`, but it's VALUE. In anoter case, that OP is asking, `$a` will depend on `$b`s value (changes, applied to `$b`s value will affect the value of `$a`), which is not obvious. By default, they should be COMPLETELY INDEPENDENT, unless the value is an object, because objects are implicitly passed by reference. – BlitZ Dec 29 '13 at 09:47
  • 2
    @MathieuImbert in any case, multiple assignment returns value, not a variable, so there is no way (yet) for PHP to create a reference by approaches like OP suggests. – BlitZ Dec 29 '13 at 10:10
11

The function end() expects a real variable and not a function returning an array, but if you put the function return inside double parentheses PHP does not report a strict standards notice:

$last = end( ( explode( '/', $someString ) ) );
Danijel
  • 12,408
  • 5
  • 38
  • 54
  • that's better :) I hope it's not a bug – thelolcat Dec 30 '13 at 23:32
  • Although I'd not recommend suppressing warnings and notices, this is pretty interesting. It apparently works exactly as you describe, Danijel. Is this behavior documented somewhere in the php manual? I can find no reference whatsoever. – Jiri Dec 31 '13 at 09:35
  • [Here](http://stackoverflow.com/questions/6726589/parentheses-altering-semantics-of-function-call-result) is an interesting discussion about this topic, but it seems that nobody knows for sure whether this behavior is a feature or a bug. – Danijel Dec 31 '13 at 16:41
  • 2
    @Jirka It's documented, when you're using `$b = ($a = 5)`, at the second side is not an expression anymore, you made it a variable. – revo Jan 02 '14 at 15:41
5

The problem is that you're doing assignment to a function, and the value being passed to $last is actually the result of the function, not $p. And, explode returns a reference to a variable.

Do it in two lines:

$p = explode('/', $someString);
$last = end($p);  

There's no reason to be assigning to $p inside the function call of end(), unless you're using $p later. And for stylistic clarity I'd suggest doing it in two steps anyhow.

JAL
  • 21,295
  • 1
  • 48
  • 66
3

Functions like end() will affect the variable and change their values if they're supposed to, and as all of us know, it means Pass by Reference! But this kind of passing needs tendency, we can't just push things to them without any care!

On giving Only variables should be passed by reference actually the problem is not that variable, however you did it when tried end($p = explode('/', $someString)); but the kind of passing it to the function, because you're not allowed to make a reference on that on calling end(), you made an Expression as an input not a variable.

Expressions:

PHP takes expressions much further, in the same way many other languages do. PHP is an expression-oriented language, in the sense that almost everything is an expression. Consider the example we've already dealt with, $a = 5. It's easy to see that there are two values involved here, the value of the integer constant 5, and the value of $a which is being updated to 5 as well. But the truth is that there's one additional value involved here, and that's the value of the assignment itself. The assignment itself evaluates to the assigned value, in this case 5. In practice, it means that $a = 5, regardless of what it does, is an expression with the value 5. Thus, writing something like $b = ($a = 5) is like writing $a = 5; $b = 5; (a semicolon marks the end of a statement). Since assignments are parsed in a right to left order, you can also write $b = $a = 5.

That error is just a kind of strict error at your case and not a fatal one! well you know that PHP is a strict programming language. But you can disable them manually. That doesn't change the result.

Some examples on this issue:

function foo($a)
{
    return ++$a;
};
$b = 1;
echo foo(&$b);

Error:

Fatal error: Call-time pass-by-reference has been removed; If you would like to pass argument by reference, modify the declaration of foo() since PHP 5.3.0

Or

function foo(&$a)
{
    return ++$a;
};
echo foo($b = 1); // output: 2

We've seen it before Strict Standards: Only variables should be passed by reference

revo
  • 47,783
  • 14
  • 74
  • 117