php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71030 PHP 7.0 list() behavior
Submitted: 2015-12-04 16:41 UTC Modified: 2019-05-09 11:13 UTC
Votes:4
Avg. Score:3.2 ± 0.4
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:1 (100.0%)
From: filippo dot desantis at gmail dot com Assigned: nikic (profile)
Status: Closed Package: *General Issues
PHP Version: 7.0.0 OS: OSX
Private report: No CVE-ID: None
 [2015-12-04 16:41 UTC] filippo dot desantis at gmail dot com
Description:
------------
I'm using

PHP 7.0.0 (cli) (built: Dec  2 2015 13:35:31)
Zend Engine v3.0.0, Copyright (c) 1998-2015 Zend Technologies

Running the example in the RFC https://wiki.php.net/rfc/abstract_syntax_tree for the list() function 

$a = [1, 2];
list($a, $b) = $a;
 
// OLD: $a = 1, $b = 2
// NEW: $a = 1, $b = null + "Undefined index 1"


I don't get the "NEW" result but the "OLD" one.

Looking for some more information I found in the documentation (http://php.net/list) the warning "Modification of the array during list() execution (e.g. using list($a, $b) = $b) results in undefined behavior.".

I was wondering which of the two behaviour should be the good one :)






Test script:
---------------
<?php

$a = [1, 2];
list($a, $b) = $a;

var_dump($a, $b);


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-12-05 01:36 UTC] requinix@php.net
-Status: Open +Status: Verified
 [2015-12-05 01:36 UTC] requinix@php.net
https://3v4l.org/vBGTH

It looks like the RHS variable does not get reevaluated. This is the behavior that would make the most sense to me, but it does contradict what the AST RFC says. I haven't found any talk about changing how list() works post-RFC.

So either
a) list() is not behaving correctly, or
b) The warning can be removed and the migration guide needs to mention the change
 [2015-12-06 15:48 UTC] bwoebi@php.net
-Status: Verified +Status: Assigned -Type: Documentation Problem +Type: Bug -Assigned To: +Assigned To: nikic
 [2015-12-06 15:48 UTC] bwoebi@php.net
The implementation is definitely not matching the RFC here.
In current code the case of list($a, $b) = $a; is special cased (basically when the RHS variable occurs on the LHS).
In that case we do a quick (COW) copy first.

Though, try:
$a = [1, 2];
$_a = "a";
list($$_a, $b) = $a; // variable variable
# or
$c = &$a;
list($c, $b) = $a; // with reference

then you'll get what's expected as per the RFC.

I wonder though whether we really should have a special case here … either for all or for none?

Assigning to Nikita (RFC author) so that he can clarify...
 [2015-12-06 16:21 UTC] nikic@php.net
Yes, the text in the RFC is no longer correct (many parts of that RFC are outdated). We've special case list($a, $b) = $a to provide the behavior that you would intuitively expect and that complies with our normal list() evaluation semantics in PHP 7.

However the documentation is still technically correct in that we don't *guarantee* this for all possible cases, as is illustrated by bwoebi's examples.

We could guarantee it by always emitting a QM_ASSIGN for a CV RHS. It should not be overly expensive (maybe even cheaper with many list assignments). But then again, I'm not sure I see the necessity for this sort of edge case. We don't provide these kind of guarantees regarding CV evaluation order in other places either.
 [2019-05-09 11:13 UTC] nikic@php.net
While I don't really care about how this interacts with variable variables, I do think that @bwoebi's reference example should work.

I tried to fix this by always generating a QM_ASSIGN instead of only for known self-assignment. Unfortunately this broke some existing tests, because apparently we allow passing the result of a list() assignment by reference if the RHS is a CV. This of course breaks if there's a QM_ASSIGN in between. This is already a problem now for the self assign case:

<?php
function change(&$ref) {
    $ref = [1, 2, 3];
}

// Works
$array = [1];
change(list($val) = $array);
var_dump($array);

// Notice: Only variables should be passed by reference
$array = [[1]];
change(list($array) = $array);
var_dump($array);

I think I'm going to go ahead with this change anyway, because it clearly only works by accident. E.g. if the RHS happens to be anything but a CV, you'll get a fatal error:

// Fatal error: Only variables can be passed by reference
$array = [[1]];
change(list($val) = $array[1]);
var_dump($array);

Since PHP 7.3, it is possible to pass the result directly by-ref if a by-ref list() assignment is used, in which case this will always be well-defined.
 [2019-05-09 12:35 UTC] nikic@php.net
Automatic comment on behalf of nikita.ppv@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=d0a56f707f9b043d9af512524e9c486044cbf510
Log: Fixed bug #71030
 [2019-05-09 12:35 UTC] nikic@php.net
-Status: Assigned +Status: Closed
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 15:01:29 2024 UTC