缘由

《PHP Reactive Programming》中有一个例子:

<?php
use Rx\Observable; 
use Rx\Scheduler\EventLoopScheduler; 
use React\EventLoop\StreamSelectLoop; 

$loop = new StreamSelectLoop(); 
$scheduler = new EventLoopScheduler($loop); 

$disposable = Observable::range(1, 5) 
    ->subscribeCallback(function($val) use (&$disposable) { 
        echo "$val\n"; 
        if ($val == 3) { 
            $disposable->dispose(); 
        } 
    }, null, null, $scheduler); 

$scheduler->start();

其中对$disposable对象中传递了一个closure,use的对象是带引用符号的$disposable,我尝试把引用符号去掉,然而程序报错PHP Notice: Undefined variable: disposable

突然想起来之前做的一个形成cms菜单栏的方法,要求实现一个closure自身的递归,当时查到的解决方案就是使用&符号进行调用。

<?php
            $retChild = function($menu) use (&$retChild, $userMenu) {
                switch ($menu->depth) {
                    case 0:
                        $ret = "";
                        foreach ($menu->children as $child) {
                            $ret .= $retChild($child);
                        }
                        return $ret;
                    case 1:
                        $ret = <<<STR
<li class="header">{$menu->title}</li>
<li class="treeview">
STR;
                        $childRet = false;
                        foreach ($menu->children as $child) {
                            $childStr = $retChild($child);
                            if (!empty($childStr)) {
                                $ret .= $childStr . '</li><li class="treeview">' . "\n";
                                $childRet = true;
                            }
                        }
                        if (true === $childRet) {
                            $ret .= <<<STR
</li>
STR;
                            return $ret;
                        }
                        return "";
                    default:
                        if (empty($menu->icon)) {
                            $icon = "fa fa-link";
                        } else {
                            $icon = $menu->icon;
                        }
                        $ret = <<<STR
<a href="#"><i class='{$icon}'></i> <span>{$menu->title}</span> <i class="fa fa-angle-left pull-right"></i></a>
<ul class="treeview-menu">
STR;
                        $retChildStr = "";
                        if (!in_array($menu->id, array_keys($userMenu))) {
                            continue;
                        }
                        foreach ($menu->children as $child) {
                            if (!in_array($menu->id, array_keys($userMenu))) {
                                continue;
                            }
                            $childStr = $retChild($child);
                            if (!empty($childStr)) {
                                $retChildStr .= '<li class="treeview">' . $childStr . '</li>';
                            }
                        }
                        if (!empty($retChildStr)) {
                            $ret .= $retChildStr . "\n";
                        }

                        $retRowStr = "";
                        $routes = $menu->routesColl;
                        if (empty($routes) || $routes->isEmpty()) {
                            $retRowStr = "";
                        } else {
                            foreach ($routes as $route) {
                                $uri = $route->uri;
                                $descLong = $route->descLong;
                                $descShort = $route->descShort;
                                if ($route->monitorShow) {
                                    $retRowStr .= <<<STR
    <li><a href="/{$uri}" desc="{$descLong}">{$descShort}</a></li>
STR;
                                }
                            }
                        }
                        if (!empty($retRowStr)) {
                            $ret .= $retRowStr;
                        }

                        if (empty($retRowStr) && empty($retChildStr)) {
                            return "";
                        }

                        return $ret . '</ul>';
                }
            };

解释

凡是关于PHP的东西,一般都可以从文档上找到,即使文档中没有,那么下面的user notes里面也会存在。

匿名函数下面的Hayley Watson评论中就是针对这种情况举的一个例子,例子用Fibonacci数列进行示意。

<?php
$fib = function($n)use(&$fib)
{
    if($n == 0 || $n == 1) return 1;
    return $fib($n - 1) + $fib($n - 2);
};

echo $fib(10);

php的匿名函数中,如果use一个基本数据类型(非对象)的时候,传递的是当时此数据的快照;而use一个对象的时候,与函数参数中传递该对象相同,都是引用。

这一点可以参照mail at mkharitonov dot net里面的注释。

<?php
$aaa = 111;
$func = function() use($aaa){ print $aaa; };
$aaa = 222;
$func(); // Outputs "111"
<?php
$aaa = 111;
$func = function() use(&$aaa){ print $aaa; };
$aaa = 222;
$func(); // Outputs "222"
<?php
$aaa = 111;
$func = function() use(&$aaa){ print $aaa; };
$aaa = 222;
$func(); // Outputs "222"
<?php
class Foo
{

    public $foo = 'default';

}

$foo = new Foo;

$func = function() use ($foo) {
    echo $foo->foo . "\n";
};

$foo->foo = 'changable';
$func();// 输出 "changable"