php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #78970 Compact can't resolve outer scoped variables using short closures
Submitted: 2019-12-16 13:37 UTC Modified: 2019-12-16 13:52 UTC
Votes:9
Avg. Score:3.7 ± 0.9
Reproduced:8 of 9 (88.9%)
Same Version:3 (37.5%)
Same OS:4 (50.0%)
From: stanislav dot goldmann at gmail dot com Assigned:
Status: Verified Package: Arrays related
PHP Version: 7.4.0 OS: Linux/Any
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: stanislav dot goldmann at gmail dot com
New email:
PHP Version: OS:

 

 [2019-12-16 13:37 UTC] stanislav dot goldmann at gmail dot com
Description:
------------
In the proposal of short closures (https://wiki.php.net/rfc/arrow_functions_v2) the following is stated:

"When a variable used in the expression is defined in the parent scope it will be implicitly captured by-value."

With this example:


$y = 1;

$fn1 = fn($x) => $x + $y;

$fn2 = function ($x) use ($y) {
    return $x + $y;
};

These two behave exactly the same, as expected.
However compact doesn't behave that way, as it doesn't recognize a variable from outer scope until it is explicitly called.

Test script:
---------------
$name = 'foo';
$array = ['bar'];

$failing = fn($value) => compact('name', 'value');
$working = fn($value) => compact('name', 'value') + [$name];

var_dump(array_map($failing, $array));
var_dump(array_map($working, $array));

Expected result:
----------------
Short closures using compact should resolve outer scoped variables and behave like "long" closures using an use statement.

fn($value) => compact('name', 'value')

should work just like

function($value) use ($name) {
    return compact('name', 'value');
}

And return the following

array(1) {
  [0]=>
  array(3) {
    ["name"]=>
    string(3) "foo"
    ["value"]=>
    string(3) "bar"
  }
}

Actual result:
--------------
// Notice: compact(): Undefined variable: name

array(1) {
  [0]=>
  array(1) {
    ["value"]=>
    string(3) "bar"
  }
}


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-12-16 13:52 UTC] nikic@php.net
-Status: Open +Status: Verified
 [2019-12-16 13:52 UTC] nikic@php.net
Due to lack of varvar support in arrow functions. Not going to be entirely simple.
 [2023-11-17 18:33 UTC] piurafunk at gmail dot com
I believe this also applies to `require`-ing a file.

Context: Working in the Laravel task scheduler. I have 2 relevant files:

`tasks/sales.php`
---
<?php

use App\Console\Commands\Command;

/** @var \Illuminate\Console\Scheduling\Schedule $schedule */

return [
    $schedule->command(Command::class)->cron('* * * * *'),
];
---

`app/Console/Kernel.php`
---
...
->map(fn(SplFileInfo $file) => require $file->getPathname())
...
---

Results in an exception:
---
   ErrorException 

  Undefined variable $schedule

  at tasks/sales.php:8
      4▕ 
      5▕ use App\Console\Commands\Command;
      6▕ 
      7▕ return [
  ➜   8▕     $schedule->command(Command::class)->cron('* * * * *'),
      9▕ ];
     10▕ 
---

Whereas a traditional closure with `use()` works fine:
---
->map(function (SplFileInfo $file) use ($schedule) {
    return require $file->getPathname();
})
---
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 17:01:58 2024 UTC