use & Referance in PHP Closure
缘由
《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"