是否可以在不使用全局变量的 PHP 5.2.x 中模拟闭包?
Posted
技术标签:
【中文标题】是否可以在不使用全局变量的 PHP 5.2.x 中模拟闭包?【英文标题】:Is it possible to simulate closures in PHP 5.2.x not using globals? 【发布时间】:2011-01-13 15:19:36 【问题描述】:我可以想到一种将所需变量作为额外参数传递给闭包的方法,但这并不是最佳实践。
有什么想法吗?
【问题讨论】:
很遗憾 php 不支持这么棒的结构。 @ChaosPandion 但它从 PHP5.3 开始 嗯,是的,但是您必须明确列出封闭变量。可以说是“拄着拐杖”。不过总比没有好。 【参考方案1】:有趣的问题。我会说根本不可能,但让我们看看
引用IBM - What's new in PHP5.3, Part 2
闭包是在自己的环境中计算的函数,它有一个或多个绑定变量,在调用函数时可以访问这些变量。
以及进一步(强调我的)
要从外部环境导入的变量在闭包函数定义的 use 子句中指定。默认情况下,它们按值传递,这意味着如果我们更新闭包函数定义中传递的值,它不会更新外部值。
使用global
将通过引用传递,尽管可以通过在use
子句中使用&
将变量通过引用与闭包绑定,但它已经偏离了5.3 的默认行为。
$var = 'yes';
$fn = create_function('', 'global $var; $var = "no";');
$fn();
echo $var; // outputs no
您可以复制全局变量以便按值使用它,例如
$var = 'yes';
$fn = create_function('', 'global $var; $tmp = $var; $tmp = "no";');
$fn();
echo $var; // outputs yes
另外,全局变量的值(使用create_function
时)不会在函数创建时而是在函数运行时计算(绑定)
$var = 'yes';
$fn = create_function('', 'global $var; $tmp = $var; return $tmp;');
$var = 'maybe';
echo $fn(); // outputs maybe
$var = 'yes';
$fn = function() use ($var) return $var; ;
$var = 'maybe';
echo $fn(); // outputs yes
同样重要的是
当在对象中定义时,一件方便的事情是闭包可以通过 $this 变量完全访问对象,而无需显式导入它。 *虽然我认为这在最终的 PHP5.3 中被删除了
使用global
关键字是不可能的,你也不能只使用$this
。使用create_function
定义函数体时,无法引用类中的属性。
class A
protected $prop = 'it works';
public function test()
$fn = create_function('', 'echo $this->prop;');
return $fn;
$a = new A;
$fn = $a->test();
$fn();
会导致
Fatal error: Using $this when not in object context
总结一下
虽然您可以创建一个从全局范围导入变量的函数,但您不能使用来自另一个范围的变量来创建一个函数。而且由于在使用 create_function
时您在技术上没有绑定,而是在执行创建的函数时导入,我想争辩说这种限制使闭包成为 lambda。
编辑:下面 Onno Marsman 提供的解决方案相当不错。它没有完全模拟闭包,但实现非常接近。
【讨论】:
没有言语可以形容这有多可怕。 (不是你的答案。) 非常好的答案!通过在 php 5.2.17 上维护一个遗留项目,全局变量绑定的细节使我从很多头痛中解脱出来。赞一个!【参考方案2】:我的解决方案:http://techblog.triptic.nl/simulating-closures-in-php-versions-prior-to-php-5-3/
但是,它确实将对象中的变量作为第一个参数传递给闭包。
【讨论】:
非常好的努力,尽管在我看来,必须调用 call() 从技术上讲它不符合闭包的资格(嗯,至少在某种程度上)。此外,我发现必须传入 Closure 实例才能使封闭变量有点尴尬,并且 call_user_func_array 应该返回。但是,还是很不错的。你是在 5.3 的 Closure 类之后建模的吗? 我首先想到了这个解决方案,然后才意识到 php 5.3 中甚至存在一个内部闭包类。我不喜欢自己调用 call() 并且会使用 __invoke 魔术方法,除非它在 php 5.3 之前不可用... 戈登:“call_user_func_array 应该返回”是什么意思?【参考方案3】:你是说像http://en.wikipedia.org/wiki/Currying这样的Currying
然后http://zaemis.blogspot.com/2009/06/currying-in-php.html
如果没有,没关系。 :-)
【讨论】:
不完全是我的意思,但还是不错的链接:D【参考方案4】:在一些特殊情况下你可以做到。
如果您需要按值(而不是按引用)捕获变量,并且该值是简单的值类型,如上述数字、字符串或数组(不是引用类型,如对象、资源和函数),那么您可以使用 var_export()
将其插入到函数定义中:
$var = array(1, 3);
$f = create_function('',
'$var=' . var_export($var,true) . '; return $var;');
如果您需要通过引用捕获变量以在函数调用之间保持可变状态,但您不需要将更改反映在创建它的原始范围中(例如创建累加器,但是对 sum 的更改不需要更改创建范围内的 sum 变量),然后您可以类似地插入它,但作为静态变量:
function make_accumulator($sum)
$f = create_function('$x',
'static $var=' . var_export($var,true) . '; return $var += $x;');
return $f;
【讨论】:
以上是关于是否可以在不使用全局变量的 PHP 5.2.x 中模拟闭包?的主要内容,如果未能解决你的问题,请参考以下文章
Ruby黑魔法, 在不使用全局变量的情况下, 在方法作用域里修改局部变量的值