19

I had the bright idea of using a custom error handler which led me down a rabbit hole.

Following code gives (with and without custom error handler): Fatal error: Only variables can be passed by reference

function foo(){
    $b=array_pop(array("a","b","c"));
    return $b;
}
print_r(foo());

Following code gives (only with a custom error handler): (2048) Only variables should be passed by reference

function foo(){
    $a=explode( '/' , 'a/b/c');
    $c=array_pop(array_slice($a,-2,1));
    return $c;
}
print_r(foo());

The second one worries me since I have a lot of 'compact' code. So, I either ditch the bright idea of using a custom error handler (to improve my logging module) or expand all my code.

Anyone with better ideas? Also, WTF?

UPDATE:

Thanks to the answers I've learnt something about how php does error handling. The confusion of E_ALL not including E_STRICT (php 5) is not cool.

On top of all this, creating your own custom error handler enables E_STRICT by default and thats where problems start.

The moral of the story is to use your own error handler to catch them ALL and use the error constants (E_STRICT, E_USER_WARNING, E_USER_ERROR, etc.) to do your filtering.

As for the 'memory corruption issue' with variable references and certain functions, what can I say? Doubly uncool. I'll (which doesn't mean you should) ignore E_STRICT in my error handler and life goes on.

zaf
  • 22,776
  • 12
  • 65
  • 95

8 Answers8

20

array_pop() tries to change that value which is passed as parameter. Now in your second example this is the return value from array_slice(). In engine terms this is a "temporary value" and such a value can't be passed by references. what you need is a temporary variable:

function foo(){
    $a=explode( '/' , 'a/b/c');
    $b=array_slice($a,-2,1);
    $c=array_pop($b);
    return $c;
}
print_r(foo());

Then a reference to $b can be passed to array_pop(). See http://php.net/references for more details on references.

johannes
  • 15,807
  • 3
  • 44
  • 57
10

Here is what I get when trying your second php code snippet in php-cli after setting error_reporting to E_ALL | E_STRICT

    gparis@techosaure:~/workspace/universcine.com$ php -a
Interactive shell

php > function foo(){
php {     $a=explode( '/' , 'a/b/c');
php {     $c=array_pop(array_slice($a,-2,1));
php {     return $c;
php { }
php > print_r(foo());
PHP Strict standards:  Only variables should be passed by reference in php shell code on line 3
PHP Stack trace:
PHP   1. {main}() php shell code:0
PHP   2. foo() php shell code:1

As you can see, it's only strict standards here. And you can easily let your custom error handler ignore them (based on the value you get : 2048 for instance, here).

As of php 5.3, E_ALL does not include E_STRICT, look at this :

php > foreach(array("E_ALL", "E_DEPRECATED", "E_STRICT", "E_NOTICE", "E_PARSE", "E_WARNING") as $const) echo $const . "  :\t" . constant($const) ."\t". decbin(constant($const)). "\n";
E_ALL  :        30719   111011111111111
E_DEPRECATED  : 8192     10000000000000
E_STRICT  :     2048       100000000000
E_NOTICE  :     8                  1000
E_PARSE  :      4                   100
E_WARNING  :    2                    10

As of php 5.4, E_ALL does include E_STRICT :

E_ALL  :            32767   111111111111111
E_DEPRECATED  :     8192     10000000000000
E_STRICT  :         2048       100000000000
E_NOTICE  :         8                  1000
E_PARSE  :          4                   100
E_WARNING  :        2                    10
greg0ire
  • 22,714
  • 16
  • 72
  • 101
  • No it does not, I edited my post to add an explainatory code snippet. See the missing 1 in E_ALL? If you add E_STRICT, you get 111111111111111! Means jackpot! – greg0ire Jun 03 '10 at 16:23
  • in PHP 5.3 E_ALL contains E_STRICT – johannes Jun 03 '10 at 18:44
  • No, but it will in php 6. See this post : http://stackoverflow.com/questions/1638238/why-is-the-result-of-e-all-e-strict-the-same-as-only-e-all – greg0ire Jun 03 '10 at 21:12
  • Actually, E_STRICT is included in E_ALL from PHP 5.4 (as is now stated in that linked question) - see the PHP Manual: http://www.php.net/manual/en/errorfunc.constants.php - However, [w3schools.com](http://www.w3schools.com/php/func_error_reporting.asp) still incorrectly reports that it is only included from PHP 6. – MrWhite Sep 22 '13 at 13:58
  • @w3d : you're right, I updated my answer, feel free to do so yourself if you see mistakes. – greg0ire Sep 22 '13 at 18:33
  • It may be a little easier on the brain if the binary numbers had their units column aligned, i.e. right aligned. – Jason Oct 21 '16 at 15:34
  • nice catch @Jason , fixed it – greg0ire Oct 21 '16 at 17:42
5

It's a memory corruption issue (according to PHP dev team). Just throw in an assignment:

function foo(){
    $b = array_pop($arr = array("a","b","c"));
    return $b;
}
print_r(foo());

:

function foo(){
    $a = explode( '/' , 'a/b/c');
    $c = array_pop($arr = array_slice($a,-2,1));
    return $c;
}
print_r(foo());

The second produces an E_STRICT. You can handle that differently in your error handler if you wish (if you don't want to change those functions).

webbiedave
  • 48,414
  • 8
  • 88
  • 101
  • I saw that trick somewhere. Does this apply to all or some functions? – zaf Jun 03 '10 at 16:04
  • You can throw assignment in any *function*. But not in *language constructs*, such as `empty`. – webbiedave Jun 03 '10 at 16:09
  • This seriously hinders my kung-fu-ness. – zaf Jun 03 '10 at 16:31
  • The assignment in the parameter list is undefined behavior. with array_pop($arr = array("a","b","c")); it is not clear what will happen first - $arr = array("a","b","c") or array_pop($arr) or array_pop(array("a","b","c")); and can change between all releases. – johannes Jun 03 '10 at 18:43
  • @johannes: Why do you say that? – webbiedave Jun 03 '10 at 19:06
  • @johannes: No, not really. [The value of an assignment expression is the value assigned.](http://php.net/manual/en/language.operators.assignment.php). Hence, array_pop operates on the returned value of the assignment, which is necessarily evaluated first before the function can be applied. – voetsjoeba Oct 29 '11 at 21:28
2

array_pop() changes that value passed to it which is where the error is coming from. A function cannot be changed. In other words, you need to assign the array to a variable first (ref: manual), and then run array_pop().

The code you need is this:

function foo(){
    $a = array("a","b","c");
    $b = array_pop($a);
    return $b;
}

Edit: Both functions you mentioned have the same problem. Assign the array to a variable and pass the variable to array_pop().

Joseph
  • 1,988
  • 14
  • 21
2

I think that now (since php 5) it should be:

function &foo(){ //NOTICE THE &
    $b=array_pop(array("a","b","c"));
    return $b;
}
print_r(foo());

and

function &foo(){ //NOTICE THE &
    $a=explode( '/' , 'a/b/c');
    $c=array_pop(array_slice($a, $b = -2, $c = 1)); //NOW NO DIRECT VALUES ARE PASSED IT MUST BE VARIABLES
    return $c;
}
print_r(foo());

but i'm just a begginer :)

popeyems
  • 37
  • 1
  • Thanks for the answer. I don't have time to try it but are you saying that adding a '&' makes it work? And what does it do? I'll check it tomorrow. – zaf Dec 26 '11 at 18:53
1

Try this:

function foo(){
    $a = array("a","b","c");
    $b = array_pop($a);
    return $b;
}
baloo
  • 7,635
  • 4
  • 27
  • 35
1

I just got this error chaining methods.

doSomething()->andThis()

I had:

doSomething()-andThis() // missing `>` character

My scenario was a little more complex, but it stemmed from the accidental subtraction operation.

agm1984
  • 15,500
  • 6
  • 89
  • 113
0

Same question Strict Standards: Only variables should be passed by reference, also https://bugs.php.net/bug.php?id=48937.

Community
  • 1
  • 1
broadband
  • 3,266
  • 6
  • 43
  • 73