From:             nate at frickenate dot com
Operating system: Linux
PHP version:      5.3.1
PHP Bug Type:     Unknown/Other Function
Bug description:  lambda/anonymous functions do not have proper scope

Description:
------------
The way in which variables are passed to the inside of a lambda function
via the 'use (...)' syntax is broken. The entire concept of "scope" or
"closures" does not seem to apply in php whatsoever.

Variables within a lambda are supposed to be resolved/looked up in the
parent scope at runtime when the variable is actually accessed within the
lambda function's code. It seems that php just dumbly creates a copy of the
variable when it sees the definition of the lambda function with the 'use
(...)' clause. Changes to the variable in the parent scope between the
moment where the lambda is defined and the lambda function is called are
not propagated.

The worst part happens with objects. When passing in an object instance to
a lambda, any changes to the original object in the parent scope after the
lambda definition do affect the same object (problem 4 in reproduce code),
but replacing the object altogether does not (problem 3 in reproduce code).
I imagine this has to do with the php5 "fake references" that are used to
pass objects around.

I get the feeling this is just a horrible limitation of the devs having
hacked a "best-possible" implementation of "lambdas" into a Zend codebase
that can't handle proper scope lookup. If this is the case, it would be
nice if the documentation could be updated to really nail home the fact
that there is no scoping going on at all, and that quite literally all you
get is a copy of the variable as it exists at the moment the lambda is
defined (except for the strangeness going on with objects).

It's unfortunate that the "solution" for the time being is to *always*
pass in *every* variable as a reference, which results in expected output.
But this requires creating a local copy within the lambda for every
variable that one wants to modify without affecting the parent scope. :'(

Reproduce code:
---------------
<?php

// problem 1: this should echo "Canada", not a php notice
$fn = function () use ($country) { echo $country . "\n"; };
$country = 'Canada';
$fn();


// problem 2: this should echo "Canada", not "UnitedStates"
$country = 'UnitedStates';
$fn = function () use ($country) { echo $country . "\n"; };
$country = 'Canada';
$fn();


// problem 3: this should echo "Canada", not "UnitedStates"
$country = (object)array('name' => 'UnitedStates');
$fn = function () use ($country) { echo $country->name . "\n"; };
$country = (object)array('name' => 'Canada');
$fn();


// problem 4: this outputs "Canada". if this outputs "Canada",
// then so should problem 2 above. otherwise this should be
// just as broken as problem 2 and be outputting "UnitedStates"
$country = (object)array('name' => 'UnitedStates');
$fn = function () use ($country) { echo $country->name . "\n"; };
$country->name = 'Canada';
$fn();

?>

Expected result:
----------------
If scope was actually handled properly, then the lookup of the "use
(...)"'d variable would occur at the moment the variable is used within the
lambda, resulting in the following output:

Canada
Canada
Canada
Canada

Actual result:
--------------
PHP Notice:  Undefined variable: country in ... on line 5

UnitedStates
UnitedStates
Canada

-- 
Edit bug report at http://bugs.php.net/?id=50980&edit=1
-- 
Try a snapshot (PHP 5.2):            
http://bugs.php.net/fix.php?id=50980&r=trysnapshot52
Try a snapshot (PHP 5.3):            
http://bugs.php.net/fix.php?id=50980&r=trysnapshot53
Try a snapshot (PHP 6.0):            
http://bugs.php.net/fix.php?id=50980&r=trysnapshot60
Fixed in SVN:                        
http://bugs.php.net/fix.php?id=50980&r=fixed
Fixed in SVN and need be documented: 
http://bugs.php.net/fix.php?id=50980&r=needdocs
Fixed in release:                    
http://bugs.php.net/fix.php?id=50980&r=alreadyfixed
Need backtrace:                      
http://bugs.php.net/fix.php?id=50980&r=needtrace
Need Reproduce Script:               
http://bugs.php.net/fix.php?id=50980&r=needscript
Try newer version:                   
http://bugs.php.net/fix.php?id=50980&r=oldversion
Not developer issue:                 
http://bugs.php.net/fix.php?id=50980&r=support
Expected behavior:                   
http://bugs.php.net/fix.php?id=50980&r=notwrong
Not enough info:                     
http://bugs.php.net/fix.php?id=50980&r=notenoughinfo
Submitted twice:                     
http://bugs.php.net/fix.php?id=50980&r=submittedtwice
register_globals:                    
http://bugs.php.net/fix.php?id=50980&r=globals
PHP 4 support discontinued:          http://bugs.php.net/fix.php?id=50980&r=php4
Daylight Savings:                    http://bugs.php.net/fix.php?id=50980&r=dst
IIS Stability:                       
http://bugs.php.net/fix.php?id=50980&r=isapi
Install GNU Sed:                     
http://bugs.php.net/fix.php?id=50980&r=gnused
Floating point limitations:          
http://bugs.php.net/fix.php?id=50980&r=float
No Zend Extensions:                  
http://bugs.php.net/fix.php?id=50980&r=nozend
MySQL Configuration Error:           
http://bugs.php.net/fix.php?id=50980&r=mysqlcfg

Reply via email to