82
$el = array_shift($instance->find(..))

The above code somehow reports the strict standards warning, but this will not:

function get_arr(){
    return array(1, 2);
}
$el = array_shift(get_arr());

So when will it report the warning anyway?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user198729
  • 61,774
  • 108
  • 250
  • 348
  • 1
    What does $instance->find(..) return? – Silver Light Mar 01 '10 at 08:44
  • 2
    Here is the solution: http://stackoverflow.com/questions/9848295/strict-standards-only-variables-should-be-passed-by-reference-error – ajaristi Mar 07 '13 at 22:09
  • I think the examples (or logic) might be the wrong way round in the question, since the 2nd example (`get_arr()` function) **does** produce the strict standards notice (tested PHP 5.2 and PHP 5.5). – MrWhite May 01 '14 at 00:03

6 Answers6

93

Consider the following code:

error_reporting(E_STRICT);
class test {
    function test_arr(&$a) {
        var_dump($a);
    }
    function get_arr() {
        return array(1, 2);
    }
}

$t = new test;
$t->test_arr($t->get_arr());

This will generate the following output:

Strict Standards: Only variables should be passed by reference in `test.php` on line 14
array(2) {
  [0]=>
  int(1)
  [1]=>
  int(2)
}

The reason? The test::get_arr() method is not a variable and under strict mode this will generate a warning. This behavior is extremely non-intuitive as the get_arr() method returns an array value.

To get around this error in strict mode, either change the signature of the method so it doesn't use a reference:

function test_arr($a) {
    var_dump($a);
}

Since you can't change the signature of array_shift you can also use an intermediate variable:

$inter = get_arr();
$el = array_shift($inter);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
leepowers
  • 37,828
  • 23
  • 98
  • 129
  • 7
    @user198729: I was looking for an explanation or fix too, and found you can use current() for the first item. Alas end() doesn't work for the last since it "advances the internal pointer to the last element". current(array_reverse(somefunction())) works (yes, it's silly) – MSpreij Oct 11 '11 at 09:27
  • I like the `current()` workaround. In fact, a lot of times I find myself using `array_shift()` what I really want is `current()`, but that could be just me. – Dominic P Jul 26 '13 at 03:06
  • 1
    Using `current` makes the assumption that the array pointer is at the first element. It may be a valid assumption in most cases, but one to watch out for. – cmbuckley Aug 03 '13 at 12:58
  • @cbuckley - very true - which is why one should use `reset()` instead of `current()` to get the first element. `reset()` will reset the internal array pointer *and* return the first element of the array. – leepowers Aug 04 '13 at 02:43
  • 1
    @leepowers Of course, then there'd be the same issue as `array_shift()` in that it expects a reference to modify :-) – cmbuckley Aug 05 '13 at 08:45
  • 1
    @user198729 You can avoid the `$intermediate` value by using an extra pair of parenthesis. `$el = array_shift( ( get_arr() ) );`. See http://stackoverflow.com/questions/9848295/strict-standards-only-variables-should-be-passed-by-reference-error#comment51192476_9848317 – Chloe Oct 22 '15 at 20:44
  • 1
    @Chloe This is the most brilliant solution I have seen for keeping the code simple!! Thank you! – hargobind Feb 06 '16 at 05:20
7

$instance->find() returns a reference to a variable.

You get the report when you are trying to use this reference as an argument to a function, without storing it in a variable first.

This helps preventing memory leaks and will probably become an error in the next PHP versions.

Your second code block would throw an error if it wrote like (note the & in the function signature):

function &get_arr(){
    return array(1, 2);
}
$el = array_shift(get_arr());

So a quick (and not so nice) fix would be:

$el = array_shift($tmp = $instance->find(..));

Basically, you do an assignment to a temporary variable first and send the variable as an argument.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sagi
  • 8,009
  • 3
  • 26
  • 25
  • It should work now (checked it). In order to return reference you have to declare it at method signature, not return statement (my fault). – Sagi Mar 01 '10 at 09:10
  • No,I can't change the signature.@pygorex1's intermediate variable can solve this,but it looks redundant,doesn't it? – user198729 Mar 01 '10 at 09:29
  • I know you can't change the signature, just explained how it happens. You **have** to use temporary (=intermediate) variable, but you can do it in the same line. Look at my second code snippet. – Sagi Mar 01 '10 at 10:13
  • 4
    I tried your second snippet,not working.It only works in a separate line – user198729 Mar 01 '10 at 11:27
  • 3
    Indeed. An assignment *returns the assigned value*. `array_shift($tmp = $instance->find(..))` assigns the value of `$instance->find(..)` to `$tmp` and then passes the *value of the assignment* to `array_shift()` -- which is not the same thing as passing `$tmp` itself, so is no better than the original situation without the assignment. – phils Feb 12 '14 at 01:28
6

The cause of the error is the use of the internal PHP programming data structures function, array_shift() [php.net/end].

The function takes an array as a parameter. Although an ampersand is indicated in the prototype of array_shift() in the manual", there isn't any cautionary documentation following in the extended definition of that function, nor is there any apparent explanation that the parameter is in fact passed by reference.

Perhaps this is /understood/. I did not understand, however, so it was difficult for me to detect the cause of the error.

Reproduce code:

function get_arr()
{
    return array(1, 2);
}
$array = get_arr();
$el = array_shift($array);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Biju B Adoor
  • 528
  • 4
  • 6
3

The second snippet doesn't work either and that's why.

array_shift is a modifier function, that changes its argument. Therefore it expects its parameter to be a reference, and you cannot reference something that is not a variable. See Rasmus' explanations here: Strict standards: Only variables should be passed by reference

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user187291
  • 53,363
  • 19
  • 95
  • 127
3

This code:

$monthly_index = array_shift(unpack('H*', date('m/Y')));

Need to be changed into:

$date_time = date('m/Y');
$unpack = unpack('H*', $date_time);
array_shift($unpack);
Alive to die - Anant
  • 70,531
  • 10
  • 51
  • 98
0

Well, in obvious cases like that, you can always tell PHP to suppress messages by using "@" in front of the function.

$monthly_index = @array_shift(unpack('H*', date('m/Y')));

It may not be one of the best programming practices to suppress all errors this way, but in certain cases (like this one) it comes handy and is acceptable.

As result, I am sure your friend 'system administrator' will be pleased with a less polluted error.log.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Julio Marchi
  • 730
  • 6
  • 15
  • I don't know who downvoted this answer, but the presented solution DOES work and it IS a PHP standard technique. Really disappointing... Next time I might no answer a question anymore... :( – Julio Marchi Dec 17 '16 at 01:36
  • 5
    I would assume it was because suppressing the error message does not fix the problem with the code. What will you do when this type of error changes from E_STRICT to E_ERROR in a future PHP release and your code now does not run, and also does not produce any errors/output? – Luke Mar 06 '17 at 22:42
  • @TinoDidriksen, I understand and agree with the reasons to advise against some "bad habits", especially for the new generations. However, a resource exists to be used when (and if) it is safe to use and applicable to the proposed context. If the error suppressor "@" was to be abolished, it would have been removed from the language itself. Same as "eval" (it may be evil, but it has its purposes). What I am against is not about the usage of some resources but the generalization of an advice. In specific for the proposed case, it wouldn't be of any harm to use it, not even for debugging purposes. – Julio Marchi Aug 29 '17 at 03:55