©
Ce document utilise Manuel du site Web PHP chinois Libérer
匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数。最经常用作回调函数(callback)参数的值。当然,也有其它应用的情况。
Example #1 匿名函数示例
<?php
echo preg_replace_callback ( '~-([a-z])~' , function ( $match ) {
return strtoupper ( $match [ 1 ]);
}, 'hello-world' );
// 输出 helloWorld
?>
闭包函数也可以作为变量的值来使用。PHP 会自动把此种表达式转换成内置类 Closure 的对象实例。把一个 closure 对象赋值给一个变量的方式与普通变量赋值的语法是一样的,最后也要加上分号:
Example #2 匿名函数变量赋值示例
<?php
$greet = function( $name )
{
printf ( "Hello %s\r\n" , $name );
};
$greet ( 'World' );
$greet ( 'PHP' );
?>
闭包可以从父作用域中继承变量。 任何此类变量都应该用 use 语言结构传递进去。
Example #3 从父作用域继承变量
<?php
$message = 'hello' ;
// 没有 "use"
$example = function () {
var_dump ( $message );
};
echo $example ();
// 继承 $message
$example = function () use ( $message ) {
var_dump ( $message );
};
echo $example ();
// Inherited variable's value is from when the function
// is defined, not when called
$message = 'world' ;
echo $example ();
// Reset message
$message = 'hello' ;
// Inherit by-reference
$example = function () use (& $message ) {
var_dump ( $message );
};
echo $example ();
// The changed value in the parent scope
// is reflected inside the function call
$message = 'world' ;
echo $example ();
// Closures can also accept regular arguments
$example = function ( $arg ) use ( $message ) {
var_dump ( $arg . ' ' . $message );
};
$example ( "hello" );
?>
以上例程的输出类似于:
Notice: Undefined variable: message in /example.php on line 6 NULL string(5) "hello" string(5) "hello" string(5) "hello" string(5) "world" string(11) "hello world"
这些变量都必须在函数或类的头部声明。 从父作用域中继承变量与使用全局变量是不同的。全局变量存在于一个全局的范围,无论当前在执行的是哪个函数。而 闭包的父作用域是定义该闭包的函数(不一定是调用它的函数)。示例如下:
Example #4 Closures 和作用域
<?php
// 一个基本的购物车,包括一些已经添加的商品和每种商品的数量。
// 其中有一个方法用来计算购物车中所有商品的总价格,该方法使
// 用了一个 closure 作为回调函数。
class Cart
{
const PRICE_BUTTER = 1.00 ;
const PRICE_MILK = 3.00 ;
const PRICE_EGGS = 6.95 ;
protected $products = array();
public function add ( $product , $quantity )
{
$this -> products [ $product ] = $quantity ;
}
public function getQuantity ( $product )
{
return isset( $this -> products [ $product ]) ? $this -> products [ $product ] :
FALSE ;
}
public function getTotal ( $tax )
{
$total = 0.00 ;
$callback =
function ( $quantity , $product ) use ( $tax , & $total )
{
$pricePerItem = constant ( __CLASS__ . "::PRICE_" .
strtoupper ( $product ));
$total += ( $pricePerItem * $quantity ) * ( $tax + 1.0 );
};
array_walk ( $this -> products , $callback );
return round ( $total , 2 );;
}
}
$my_cart = new Cart ;
// 往购物车里添加条目
$my_cart -> add ( 'butter' , 1 );
$my_cart -> add ( 'milk' , 3 );
$my_cart -> add ( 'eggs' , 6 );
// 打出出总价格,其中有 5% 的销售税.
print $my_cart -> getTotal ( 0.05 ) . "\n" ;
// 最后结果是 54.29
?>
匿名函数目前是通过 Closure 类来实现的。
版本 | 说明 |
---|---|
5.4.0 | $this 可用于匿名函数。 |
5.3.0 | 可以使用匿名函数。 |
Note: 可以在闭包中使用 func_num_args() , func_get_arg() 和 func_get_args() 。
[#1] Chakib Dohsset [2015-10-21 10:27:07]
Notice : in entire of Example #3 Inheriting variables from the parent scope
You don't need to add echo before $exemple() it contain var_dump and it is enough for printing the result
<?php
$message = 'hello';
// Inherit $message
$example = function () use($message) {
var_dump($message);
};
echo $example(); // hello
?>
no need for echo
<?php
$message = 'hello';
// Inherit $message
$example = function () use($message) {
var_dump($message);
};
$example();// hello
?>
[#2] erolmon dot kskn at gmail dot com [2015-06-19 21:48:04]
<?php
class Foo
{
public function add($func,$name)
{
$this->{$name} = $func;
}
public function __call($func,$arguments){
call_user_func_array($this->{$func}, $arguments);
}
}
$Foo = new Foo();
$Foo->add(function(){
echo "Hello World";
},"helloWorldFunction");
$Foo->add(function($parameterone){
echo $parameterone;
},"exampleFunction");
$Foo->helloWorldFunction();
$Foo->exampleFunction("Hello PHP");
?>
[#3] inaitana [2014-10-10 16:12:10]
Doesn't anyone else think Closure is used here with an inappropriate meaning?
Isn't closure just the concept of being able to reference the outer function environment from the inner function, as in example #3?
It's commonly used with anonymous functions, but from what I know it's not the same thing as an anonymous function.
"Lambda functions" might be another name for anonymous functions, not "Closures".
I mean, a named function could employ closure too.
And anonymous functions don't necessarily support closure in all languages.
[#4] andris at codeaid dot net [2014-03-05 12:33:59]
Haven't seen it documented anywhere but PHP 5.4 now allows accessing private and protected members of an object if it's passed into a lambda function:
<?php
class Scope
{
protected $property = 'default';
// or even
// private $property = 'default';
public function run()
{
$self = $this;
$func = function() use ($self) {
$self->property = 'changed';
};
$func();
var_dump($this->property);
}
}
$scope = new Scope();
$scope->run();
?>
When running the file under PHP 5.3 you get the following error message:
* Fatal error: Cannot access protected property Scope::$property in ./file.php on line 11
PHP 5.4, however, does not complain and outputs "changed" as expected.
Not sure why it's like that but I suspect it has something to do with 5.4 supporting passing $this into lambda functions.
Speaking of which, something to remember is that the following won't work:
<?php
$func = function() use ($this) {
$this->property = 'changed';
};
?>
The error you'll get is:
* PHP Fatal error: Cannot use $this as lexical variable in ./file.php on line 9
You have to totally omit the use statement for it to work:
<?php
$func = function() {
$this->property = 'changed';
};
?>
[#5] Victor [2014-02-25 08:26:35]
Please note that while You can assign a closure to an array key with the method
$array['key'] = function() { return $whatever; };
You cannot do the same with the array assignment operator.
So
$array = Array(
'key' => function() { return $whatever; }
);
will NOT work.
(This is indeed awkward.)
[#6] mail at mkharitonov dot net [2014-02-20 11:20:18]
Some comparisons of PHP and JavaScript closures.
=== Example 1 (passing by value) ===
PHP code:
<?php
$aaa = 111;
$func = function() use($aaa){ print $aaa; };
$aaa = 222;
$func(); // Outputs "111"
?>
Similar JavaScript code:
<script type="text/javascript">
var aaa = 111;
var func = (function(aaa){ return function(){ alert(aaa); } })(aaa);
aaa = 222;
func(); // Outputs "111"
</script>
Be careful, following code is not similar to previous code:
<script type="text/javascript">
var aaa = 111;
var bbb = aaa;
var func = function(){ alert(bbb); };
aaa = 222;
func(); // Outputs "111", but only while "bbb" is not changed after function declaration
// And this technique is not working in loops:
var functions = [];
for (var i = 0; i < 2; i++)
{
var i2 = i;
functions.push(function(){ alert(i2); });
}
functions[0](); // Outputs "1", wrong!
functions[1](); // Outputs "1", ok
</script>
=== Example 2 (passing by reference) ===
PHP code:
<?php
$aaa = 111;
$func = function() use(&$aaa){ print $aaa; };
$aaa = 222;
$func(); // Outputs "222"
?>
Similar JavaScript code:
<script type="text/javascript">
var aaa = 111;
var func = function(){ alert(aaa); };
aaa = 222; // Outputs "222"
func();
</script>
[#7] jc at jmccc dot com [2014-01-21 05:48:43]
In 5.3, $this cannot be used in an anonymous function, though you can simply assign $this to another variable and pass that into the function in the use section, This quick and dirty hack works for at least calling public functions and accessing public variables of the parent class.
<?php
class Example {
public $blah;
function foo() {
$that = $this;
$tmp = function() use ($that) {
return $that->blah;
}
return $tmp;
}
}
$test = new Example();
$example->blah = "whatever";
echo $example->foo(); // prints "whatever"
[#8] derkontrollfreak+9hy5l at gmail dot com [2014-01-09 16:41:01]
Beware that since PHP 5.4 registering a Closure as an object property that has been instantiated in the same object scope will create a circular reference which prevents immediate object destruction:
<?php
class Test
{
private $closure;
public function __construct()
{
$this->closure = function () {
};
}
public function __destruct()
{
echo "destructed\n";
}
}
new Test;
echo "finished\n";
?>
To circumvent this, you can instantiate the Closure in a static method:
<?php
public function __construct()
{
$this->closure = self::createClosure();
}
public static function createClosure()
{
return function () {
};
}
?>
[#9] cHao [2013-12-13 23:42:26]
In case you were wondering (cause i was), anonymous functions can return references just like named functions can. Simply use the & the same way you would for a named function...right after the `function` keyword (and right before the nonexistent name).
<?php
$value = 0;
$fn = function &() use (&$value) { return $value; };
$x =& $fn();
var_dump($x, $value); // 'int(0)', 'int(0)'
++$x;
var_dump($x, $value); // 'int(1)', 'int(1)'
[#10] alexander at cheprasov dot com [2013-11-26 13:06:17]
Example:
<?php
$a = 213;
$f = function() use ($a){
echo $a;
};
$a = 144;
echo $a;
$f();
?>
Result:
144
213
[#11] assarte dot draven at gmail dot com [2013-11-08 08:51:47]
You cannot use closures as a class variable member in its declaration like this:
<?php
class foo
{
public $bar = function() {
echo "baz";
};
}
?>
I don't know why but this will raise a
Parse error: syntax error, unexpected 'function' (T_FUNCTION)
(used PHP 5.4)
[#12] rmckay at webaware dot com dot au [2013-10-08 23:10:10]
Some hosts are installing the eAccelerator opcode cache with PHP 5.4, and current production versions of this opcode cache break closures. If you find that your nice, working closures break when you load your code to a hosted website, check for eAccelerator (e.g. by calling phpinfo() and checking the output).
Simple fixes include:
* disabling eAccelerator and opcode caching
* replacing eAccelerator with Zend's opcache
* reverting to PHP 5.3
Apparently, the eAccelerator project has solved this issue, but hosts move slowly so I recommend removing eAccelerator from the equation for now.
[#13] mrdaniel619 at gmail dot com [2013-06-03 06:54:08]
just in case you ever want to change a member function run time
<?php
class t
{
var $num;
var $dynamic_function;
public function dynamic_function()
{
$func = $this->dynamic_function;
$func($this);
}
}
$p = new t();
$p->num = 5;
$p->dynamic_function = function($this_ref) // param cannot be named $this
{
echo $this_ref->num++.'<br />';
};
$p->dynamic_function(); // CALL YOUR DYNAMIC FUNCTION
$p->dynamic_function = function($this_ref) // NEW DYNAMIC fUNCRION
{
echo $this_ref->num.'<br />';
$this_ref->num *= 3;
};
$p->dynamic_function(); // CALL DYNAMIC FUNCTION
echo $p->num; // display number
exit;
?>
output:
5
6
18
[#14] nbjayme at yahoo dot com [2013-06-01 08:57:23]
as of PHP version 5.3
self::mycollection is an anonymous function array
self::mycollection["funcId"](); // error unknown
To fix:
$theFunc = & self::mycollection["funcId"];
$theFunc();
[#15] nathan at crause dot name [2013-05-15 22:35:51]
One item not mentioned anywhere is how to invoke an anonymous function "in-line".
To explain what I mean, let me use an actual example. Instead of creating code like this:
<?php
$wording = ($from == 0 && $to == 0
? '< 1 Year'
: ($to == 9999
? ($from - 1) . '+ Years'
: "$from - $to Years"));
?>
You might, instead, want to use an anonymous function right in the assignment line. However, doing:
<?php
$wording = function() use ($from, $to) { ... }
?>
would actually assign $wording to the Closure instance, not actually execute the function.
The best way to do this that I've found is:
<?php
$wording = call_user_func(function() use ($from, $to) {
if ($from == 0 && $to == 0) return '< 1 Year';
if ($to == 9999) return ($from - 1) . '+ Years';
return "$from - $to Years";
});
?>
It's a little longer than the first code sample, but I think it's more readable, and using this methodology opens up the ability to use more complex means of calculating a value without filling the variable scope with a bunch of temporary variables.
[#16] benkuhl at gmail dot com [2013-01-31 16:34:35]
Beware of scope! $this refers to the class that the function was defined within.
----------- Example #1 - Doesn't work (Notice: Undefined variable: this) -----------
<?php
$callableFunc = function () {
var_dump($this);
};
call_user_func($callableFunc);
?>
----------- Example #2 - Doesn't work (Notice: Undefined variable: this) -----------
<?php
class myClass {
public function execute($func) {
if (is_callable($func)) {
call_user_func($func);
}
}
}
$class = new myClass();
$class->execute(function () {
var_dump($this);
});
?>
----------- Example #3 - Works! -----------
<?php
class myClass {
public function getMyExecutableFunction() {
return function () {
var_dump($this);
};
}
public function execute($func) {
if (is_callable($func)) {
call_user_func($func);
}
}
}
$class = new myClass();
$class->execute($class->getMyExecutableFunction()); //object(myClass)[1]
?>
$this is not available in a static method!
[#17] marcellorvalle at gmail dot com [2012-12-29 22:05:29]
I fell uncomfortable with php's way to pass callbacks ahead. It's just a personal opinion but i think the structure array($object, 'method') a little bit ugly. In some special cases i would like to do something more verbose, like that:
<?php
$sortedArray = ArrayServices::sort($arrayOfPerson, $sortPerson->byName);
?>
Here, "$sortPerson->byName" is a pointer to a function that receives 2 instances of Person and return true if the name of the first is "bigger" than the second. "ArrayServices::sort" is a function that can sort any kind of array using different criterias, it uses the function passed on the second parameter to compare two items on the array
I am able do that defining a simple abstract class which gives the ability to it's children to expose their methods as closures using __get() magic funtion.
<?php
abstract class ClosureExposerObject
{
public function __get($methodName)
{
if (is_callable(
array($this, $methodName)))
{
return function() use ($methodName)
{
$args = func_get_args();
return call_user_func_array(array($this, $methodName), $args);
};
}
else
{
$className = get_class($this);
throw new \BadMethodCallException("$method is not a callable at $className class.");
}
}
}
?>
Now it is possible to define a class SortPerson so that i could use it like in the first code snippet.
<?php
class SortPerson extends ClosureExposerObject
{
public function byName(Person p1, Person p2)
{
return p1->getName() > p2->getName();
}
public function byAge(Person p1, Person p2)
{
return p1->getAge() > p2->getAge();
}
public function bySurName(Person p1, Person p2)
(...)
}
?>
I just needed to extend ClosureExposerObject and do nothing more.
Some observations: SortPerson is a service class but i decided not to implement it with static methods. It is a personal matter but i really dont like to implement such small classes with statics. Also i have inverted some naming conventions (class names as substantives and methods as verbs) but in some very special cases i thing that it actually helps readability.
[#18] orwellophile at phpblue dot net [2012-03-16 04:42:45]
You can create a dynamic method with a class, with access to member variables, with a little bit of trickery:
<?php
class DynamicFunction {
var $functionPointer;
var $mv = "The Member Variable";
function __construct() {
$this->functionPointer = function($arg) {
return sprintf("I am the default closure, argument is %s\n", $arg);
};
}
function changeFunction($functionSource) {
$functionSource = str_replace('$this', '$_this', $functionSource);
$_this = clone $this;
$f = '$this->functionPointer = function($arg) use ($_this) {' . PHP_EOL;
$f.= $functionSource . PHP_EOL . "};";
eval($f);
}
function __call($method, $args) {
if ( $this->{$method} instanceof Closure ) {
return call_user_func_array($this->{$method},$args);
} else {
throw new Exception("Invalid Function");
}
}
}
if (!empty($argc) && !strcmp(basename($argv[0]), basename(__FILE__))) {
$dfstring1 = 'return sprintf("I am dynamic function 1, argument is %s, member variables is %s\n", $arg, $this->mv);';
$dfstring2 = 'return sprintf("I am dynamic function 2, argument is %s, member variables is %s\n", $arg, $this->mv);';
$df = new DynamicFunction();
$df->changeFunction($dfstring1);
echo $df->functionPointer("Rabbit");
$df->changeFunction($dfstring2);
$df->mv = "A different var";
echo $df->functionPointer("Cow");
};
?>
[#19] mike at borft dot student dot utwente dot nl [2012-01-24 14:46:19]
Since it is possible to assign closures to class variables, it is a shame it is not possible to call them directly. ie. the following does not work:
<?php
class foo {
public test;
public function __construct(){
$this->test = function($a) {
print "$a\n";
};
}
}
$f = new foo();
$f->test();
?>
However, it is possible using the magic __call function:
<?php
class foo {
public test;
public function __construct(){
$this->test = function($a) {
print "$a\n";
};
}
public function __call($method, $args){
if ( $this->{$method} instanceof Closure ) {
return call_user_func_array($this->{$method},$args);
} else {
return parent::__call($method, $args);
}
}
}
$f = new foo();
$f->test();
?>
it
Hope it helps someone ;)
[#20] reinaldorock at yahoo dot com dot br [2011-08-27 20:15:39]
Using closure to encapsulate environment
<?php
$fib = function($n) use(&$fib) {
if($n == 0 || $n == 1) return 1;
return $fib($n - 1) + $fib($n - 2);
};
echo $fib(2) . "\n"; // 2
$lie = $fib;
$fib = function(){die('error');};//rewrite $fib variable
echo $lie(5); // error because $fib is referenced by closure
?>
Alternative Fibonacci implementation using a self called function like javascript to encapsulate references variables.
<?php
$fib = call_user_func(function(){
$fib = function($n) use(&$fib) {
if($n == 0 || $n == 1) return 1;
return $fib($n - 1) + $fib($n - 2);
};
return $fib;
});
echo $fib(2) . "\n";//2
$ok = $fib;
$fib = function(){die('error')};//rewrite $fib variable but don't referenced $fib used by closure
echo $ok(5);//result ok
?>
[#21] ldrut [2011-08-13 13:24:35]
A common way to avoid contaminating Javascript global space with unneeded variables is to move the code into an immediately called anonymous closure.
(function(){ ... })()
The equivalent way to do that in PHP 5.3+ is
call_user_func(function() use(closure-vars){ ... });
[#22] simon at generalflows dot com [2011-08-05 08:23:52]
<?php
session_start();
$authorise = function($func) {
return function() use ($func) {
if ($_SESSION['is_authorised'] == true) {
$args = func_get_args($func);
call_user_func_array($func, $args);
}
else {
echo "Access Denied";
}
};
};
$service = function($foo) {
echo "Service returns: $foo";
};
$service = $authorise($service);
$_SESSION['is_authorised'] = true;
$service('test 1');
$_SESSION['is_authorised'] = false;
$service('test 2');
?>
[#23] paul at somewhere dot com [2011-03-06 14:36:43]
I benched instantiating a function and lambda function.
Functions used:
<?php
function data() {
$var = 'hi';
}
$lambda = function() {
$var = 'hi';
}
?>
Bench for instantiating a function:
1.692800000000000082422957 ??s
Bench for instantiating Lambda Function:
0.906000000000000027533531 ??s
Bench for calling function:
0.7153000000000000468958206 ??s
Bench for calling lambda function:
0.6914000000000000145661261 ??s
Calling the lambda function and regular function fluctuates between .81 and .65 so they seem to be the same.
[#24] fabiolimasouto at gmail dot com [2011-02-24 07:51:10]
You may have been disapointed if you tried to call a closure stored in an instance variable as you would regularly do with methods:
<?php
$obj = new StdClass();
$obj->func = function(){
echo "hello";
};
//$obj->func(); // doesn't work! php tries to match an instance method called "func" that is not defined in the original class' signature
// you have to do this instead:
$func = $obj->func;
$func();
// or:
call_user_func($obj->func);
// however, you might wanna check this out:
$array['func'] = function(){
echo "hello";
};
$array['func'](); // it works! i discovered that just recently ;)
?>
Now, coming back to the problem of assigning functions/methods "on the fly" to an object and being able to call them as if they were regular methods, you could trick php with this lawbreaker-code:
<?php
class test{
private $functions = array();
private $vars = array();
function __set($name,$data)
{
if(is_callable($data))
$this->functions[$name] = $data;
else
$this->vars[$name] = $data;
}
function __get($name)
{
if(isset($this->vars[$name]))
return $this->vars[$name];
}
function __call($method,$args)
{
if(isset($this->functions[$method]))
{
call_user_func_array($this->functions[$method],$args);
} else {
// error out
}
}
}
// LET'S BREAK SOME LAW NOW!
$obj = new test;
$obj->sayHelloWithMyName = function($name){
echo "Hello $name!";
};
$obj->sayHelloWithMyName('Fabio'); // Hello Fabio!
// THE OLD WAY (NON-CLOSURE) ALSO WORKS:
function sayHello()
{
echo "Hello!";
}
$obj->justSayHello = 'sayHello';
$obj->justSayHello(); // Hello!
?>
NOTICE: of course this is very bad practice since you cannot refere to protected or private fields/methods inside these pseudo "methods" as they are not instance methods at all but rather ordinary functions/closures assigned to the object's instance variables "on the fly". But I hope you've enjoyed the jurney ;)
[#25] Suman Madavapeddi. [2011-02-14 17:31:04]
These Closures are really interesting to me .This would be helpful.
<?php
Class Operations{
public function ops($x,$y,$op){
switch ($op){
case 'ADD':return function() use($x,$y){
return $x+$y."<br>";
};
break;
case 'SUB': return function() use ($x,$y){
return $x-$y."<br>";
};
break;
default: return function(){
return "Operation is not supported by class"."<br>";
};
}
}
}
$op = new Operations();
$fn1 = $op->ops(6,7,'ADD');
echo $fn1();
$fn2 = $op->ops(6,2,'SUB');
echo $fn2();
$fn2 = $op->ops(6,7,'MUL');
echo $fn2();
?>
[#26] Anonymous [2010-10-28 00:54:33]
use() parameters are early binding - they use the variable's value at the point where the lambda function is declared, rather than the point where the lambda function is called (late binding).
If you want late binding put & before the variable inside use()
<?php
$fn = function () use (&$var) { echo $var; };
?>
Examples:
<?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();
?>
see http://bugs.php.net/bug.php?id=50980
(I've just quoted from there, but if you want you can read there the whole feature request)
[#27] Hayley Watson [2010-10-22 07:00:10]
As an alternative to gabriel's recursive construction, you may instead assign the recursive function to a variable, and use it by reference, thus:
<?php
$fib = function($n)use(&$fib)
{
if($n == 0 || $n == 1) return 1;
return $fib($n - 1) + $fib($n - 2);
};
echo $fib(10);
?>
Hardly a sensible implementation of the Fibonacci sequence, but that's not the point! The point is that the variable needs to be used by reference, not value.
Without the '&', the anonymous function gets the value of $fib at the time the function is being created. But until the function has been created, $fib can't have it as a value! It's not until AFTER the function has been assigned to $fib that $fib can be used to call the function - but by then it's too late to pass its value to the function being created!
Using a reference resolves the dilemma: when called, the anonymous function will use $fib's current value, which will be the anonymous function itself.
At least, it will be if you don't reassign $fib to anything else between creating the function and calling it:
<?php
$fib = function($n)use(&$fib)
{
if($n == 0 || $n == 1) return 1;
return $fib($n - 1) + $fib($n - 2);
};
$lie = $fib;
$fib = function($n)
{
return 100;
};
echo $lie(10); // 200, because $fib(10 - 1) and $fib(10 - 2) both return 100.
?>
Of course, that's true of any variable: if you don't want its value to change, don't change its value.
All the usual scoping rules for variables still apply: a local variable in a function is a different variable from another one with the same name in another function:
<?php
$fib = function($n)use(&$fib)
{
if($n == 0 || $n == 1) return 1;
return $fib($n - 1) + $fib($n - 2);
};
$bark = function($f)
{
$fib = 'cake'; // A totally different variable from the $fib above.
return 2 * $f(5);
};
echo $bark($fib); // 16, twice the fifth Fibonacci number
?>
[#28] anonymous [2010-08-19 03:21:05]
Base dao class illustrating the usefulness of closures.
* Handles opening and closing of connections.
* Adds slashes sql
* Type checking of sql parameters and casts as appropriate
* Provides hook for processing of result set and emitting one or more objects.
* Provides hook for accessing underlying link and result objects.
<?php
define("userName","root");
define("password","root");
define("dbName","ahcdb");
define("hostName","localhost");
class BaseDao {
function getConnection() {
$link = mysql_connect(hostName, userName, password);
if (!$link)
die("Could not connect: " . mysql_error());
if (!mysql_select_db(dbName))
die("Could not select database: " . mysql_error());
return $link;
}
function setParams(& $sql, $params) {
if($params != null)
$sql = vsprintf($sql, array_map(function($n) {
if(is_int($n))
return (int)$n;
if(is_float($n))
return (float)$n;
if(is_string($n))
return "'".mysql_real_escape_string($n)."'";
return mysql_real_escape_string($n);
}, $params));
}
function executeQuery($sql, $params, $callback = null) {
$link = $this->getConnection();
$this->setParams($sql, $params);
$return = null;
if(($result = mysql_query($sql, $link)) != null)
if($callback != null)
$return = $callback($result, $link);
if($link != null)
mysql_close($link);
if(!$result)
die("Fatal Error: Invalid query '$sql' : " . mysql_error());
return $return;
}
function getList($sql, $params, $callback) {
return $this->executeQuery($sql, $params, function($result, $link) use ($callback) {
$idx = 0;
$list = array();
while ($row = mysql_fetch_assoc($result))
if($callback != null)
$list[$idx] = $callback($idx++, $row);
return $list;
});
}
function getSingle($sql, $params, $callback) {
return $this->executeQuery($sql, $params, function($result, $link) use ($callback) {
if ($row = mysql_fetch_assoc($result))
$obj = $callback($row);
return $obj;
});
}
}
class Example {
var $id;
var $name;
function Example($id, $name){
$this->id = $id;
$this->name = $name;
}
function setId($id){
$this->id = $id;
}
}
class ExampleDao extends BaseDao {
function getAll(){
return parent::getList("select * from nodes", null, function($idx, $row) {
return new Example($row["id"], $row["name"]);
});
}
function load($id){
return parent::getSingle("select * from nodes where id = %1\$s", array($id), function($row) {
return new Example($row["id"], $row["name"]);
});
}
function update($example){
return parent::executeQuery("update nodes set name = '' where id = -1", null, function($result, $link){
return $result;
});
}
function insert(& $example){
return parent::executeQuery("insert into nodes", null, function($result, $link) use ($example){
$id = mysql_insert_id($link);
$example->setId($id);
return $result;
});
}
}
$exampleDao = new ExampleDao();
$list = $exampleDao->getAll());
$exampleObject = $exampleDao->load(1));
$exampleDao->update($exampleObject);
?>
[#29] orls [2010-08-08 18:53:08]
Watch out when 'importing' variables to a closure's scope -- it's easy to miss / forget that they are actually being *copied* into the closure's scope, rather than just being made available.
So you will need to explicitly pass them in by reference if your closure cares about their contents over time:
<?php
$result = 0;
$one = function()
{ var_dump($result); };
$two = function() use ($result)
{ var_dump($result); };
$three = function() use (&$result)
{ var_dump($result); };
$result++;
$one(); // outputs NULL: $result is not in scope
$two(); // outputs int(0): $result was copied
$three(); // outputs int(1)
?>
Another less trivial example with objects (what I actually tripped up on):
<?php
//set up variable in advance
$myInstance = null;
$broken = function() uses ($myInstance)
{
if(!empty($myInstance)) $myInstance->doSomething();
};
$working = function() uses (&$myInstance)
{
if(!empty($myInstance)) $myInstance->doSomething();
}
//$myInstance might be instantiated, might not be
if(SomeBusinessLogic::worked() == true)
{
$myInstance = new myClass();
}
$broken(); // will never do anything: $myInstance will ALWAYS be null inside this closure.
$working(); // will call doSomething if $myInstance is instantiated
?>
[#30] gabriel dot totoliciu at ddsec dot net [2010-07-19 22:56:51]
If you want to make a recursive closure, you will need to write this:
$some_var1="1";
$some_var2="2";
function($param1, $param2) use ($some_var1, $some_var2)
{
//some code here
call_user_func(__FUNCTION__, $other_param1, $other_param2);
//some code here
}
If you need to pass values by reference you should check out
http://www.php.net/manual/en/function.call-user-func.php
http://www.php.net/manual/en/function.call-user-func-array.php
If you're wondering if $some_var1 and $some_var2 are still visible by using the call_user_func, yes, they are available.
[#31] martin dot partel at gmail dot com [2010-06-11 10:50:36]
$this is currently (PHP 5.3.2) not usable directly with closures.
One can write:
<?php
$self = $this;
function () use ($self) { ... }
?>
but then the private/protected members of $this cannot be used inside the closure. This makes closures much less useful in OO code.
Until this is fixed, one can cheat using reflection:
<?php
class FullAccessWrapper
{
protected $_self;
protected $_refl;
public function __construct($self)
{
$this->_self = $self;
$this->_refl = new ReflectionObject($self);
}
public function __call($method, $args)
{
$mrefl = $this->_refl->getMethod($method);
$mrefl->setAccessible(true);
return $mrefl->invokeArgs($this->_self, $args);
}
public function __set($name, $value)
{
$prefl = $this->_refl->getProperty($name);
$prefl->setAccessible(true);
$prefl->setValue($this->_self, $value);
}
public function __get($name)
{
$prefl = $this->_refl->getProperty($name);
$prefl->setAccessible(true);
return $prefl->getValue($this->_self);
}
public function __isset($name)
{
$value = $this->__get($name);
return isset($value);
}
}
function giveAccess($obj)
{
return new FullAccessWrapper($obj);
}
// Example:
class Foo
{
private $x = 3;
private function f()
{
return 15;
}
public function getClosureUsingPrivates()
{
$self = giveAccess($this);
return function () use ($self) {
return $self->x * $self->f();
};
}
}
$foo = new Foo();
$closure = $foo->getClosureUsingPrivates();
echo $closure() . "\n"; // Prints 45 as expected
?>
[#32] kdelux at gmail dot com [2010-05-14 20:55:27]
Here is an example of one way to define, then use the variable ( $this ) in Closure functions. The code below explores all uses, and shows restrictions.
The most useful tool in this snippet is the requesting_class() function that will tell you which class is responsible for executing the current Closure().
Overview:
-----------------------
Successfully find calling object reference.
Successfully call $this(__invoke);
Successfully reference $$this->name;
Successfully call call_user_func(array($this, 'method'))
Failure: reference anything through $this->
Failure: $this->name = '';
Failure: $this->delfect();
<?php
function requesting_class()
{
foreach(debug_backtrace(true) as $stack){
if(isset($stack['object'])){
return $stack['object'];
}
}
}
class Person
{
public $name = '';
public $head = true;
public $feet = true;
public $deflected = false;
function __invoke($p){ return $this->$p; }
function __toString(){ return 'this'; } // test for reference
function __construct($name){ $this->name = $name; }
function deflect(){ $this->deflected = true; }
public function shoot()
{ // If customAttack is defined, use that as the shoot resut. Otherwise shoot feet
if(is_callable($this->customAttack)){
return call_user_func($this->customAttack);
}
$this->feet = false;
}
}
$p = new Person('Bob');
$p->customAttack =
function(){
echo $this; // Notice: Undefined variable: this
#$this = new Class() // FATAL ERROR
// Trick to assign the variable '$this'
extract(array('this' => requesting_class())); // Determine what class is responsible for making the call to Closure
var_dump( $this ); // Passive reference works
var_dump( $$this ); // Added to class: function __toString(){ return 'this'; }
$name = $this('name'); // Success
echo $name; // Outputs: Bob
echo '<br />';
echo $$this->name;
call_user_func_array(array($this, 'deflect'), array()); // SUCCESSFULLY CALLED
#$this->head = 0; /
?>
[#36] ljackson at jjcons dot com [2010-02-21 22:46:26]
appears kwilson at shuttlebox dot net that you may have just made unintended side effect. Note that adding the global $variable to your test function make the closure function echo second rather than first So the anonymous function works as expected with respect to globals.
<?php
$variable = "first";
$closure = function() {
global $variable;
echo $variable . "\n";
};
$closure();
function test($closure)
{
global $variable; //Note the scope added here
$variable = "second";
$closure();
}
test($closure);
?>
prints:
first
second
tested with php 5.3.1
[#37] kwilson at shuttlebox dot net [2010-01-21 07:24:38]
Using the global keyword apparently pulls variables from the scope where the function was created, not where it is executed.
Example:
<?php
$variable = "first";
$closure = function() {
global $variable;
echo $variable . "\n";
};
$closure();
function test($closure)
{
$variable = "second";
$closure();
}
test($closure);
?>
Will print:
first
first
[#38] puskulcu at gmail dot com [2009-12-14 12:46:49]
hello there!
here is a little code which shows use of the closures as event handlers:
<?php
class Button
{
public $OnBeforeClick;
public $OnAfterClick;
public $Name;
function Button()
{
$this->Name = 'MyButton';
}
public function Click()
{
$this->DoBeforeClick();
echo 'Click!';
$this->DoAfterClick();
}
private function DoBeforeClick()
{
if (isset($this->OnBeforeClick))
{
$Event = $this->OnBeforeClick;
$Event($this);
}
}
private function DoAfterClick()
{
if (isset($this->OnAfterClick))
{
$Event = $this->OnAfterClick;
$Event($this);
}
}
}
//eclipse may warn here about syntax error but no problem, it runs well.
$BeforeClickEventHandler = function($Sender) { echo $Sender->Name . ' (Before Click)'; };
$AfterClickEventHandler = function($Sender) { echo $Sender->Name . ' (After Click)'; };
$MyWidget = new Button();
$MyWidget->OnBeforeClick = $BeforeClickEventHandler;
$MyWidget->OnAfterClick = $AfterClickEventHandler;
$MyWidget->Click();
?>
output:
MyButton (Before Click)
Click!
MyButton (After Click)
i hope you find this useful.
regards.
emre
[#39] rob at ubrio dot us [2009-11-25 10:20:03]
You can always call protected members using the __call() method - similar to how you hack around this in Ruby using send.
<?php
class Fun
{
protected function debug($message)
{
echo "DEBUG: $message\n";
}
public function yield_something($callback)
{
return $callback("Soemthing!!");
}
public function having_fun()
{
$self =& $this;
return $this->yield_something(function($data) use (&$self)
{
$self->debug("Doing stuff to the data");
// do something with $data
$self->debug("Finished doing stuff with the data.");
});
}
// Ah-Ha!
public function __call($method, $args = array())
{
if(is_callable(array($this, $method)))
return call_user_func_array(array($this, $method), $args);
}
}
$fun = new Fun();
echo $fun->having_fun();
?>
[#40] mike at blueroot dot co dot uk [2009-10-28 08:40:35]
To recursively call a closure, use this code.
<?php
$recursive = function () use (&$recursive){
// The function is now available as $recursive
}
?>
This DOES NOT WORK
<?php
$recursive = function () use ($recursive){
// The function is now available as $recursive
}
?>
[#41] kukoman at pobox dot sk [2009-10-13 05:22:11]
be aware of Fatal error: Using $this when not in object context when using in closures
http://wiki.php.net/rfc/closures/removal-of-this
[#42] gerard at visei dot nl [2009-10-07 07:41:46]
The text above the third example tries to explain that anonymous functions can inherit variables from the parent scope, but fails to properly explain how this is done: namely using the "use" keyword in the function definition.
The following page has a much more detailed explanation of closures in PHP 5.3:
http://wiki.php.net/rfc/closures
[#43] dave at mausner dot us [2009-08-10 14:34:35]
Ulderico had it almost right. To avoid confusing the interpreter, when using a simple closure stored in a $variable, you must invoke the nameless function using the function syntax.
<?php
$helloworld = function(){
return "each hello world is different... ".date("His");
};
echo $helloworld( );
?>
Note the empty actual-parameter list in the "echo". NOW IT WORKS.
[#44] Anonymous [2009-08-03 02:50:52]
If you want to check whether you're dealing with a closure specifically and not a string or array callback you can do this:
<?php
$isAClosure = is_callable($thing) && is_object($thing);
?>
[#45] mcm dot matt at gmail dot com [2009-06-30 05:49:05]
Example using uasort.
<?php
// Usual method.
function cmp($a, $b) {
return($a > $b);
}
uasort($array, 'cmp');
// New
uasort($array, function($a, $b) {
return($a > $b);
});
?>
[#46] a dot schaffhirt at sedna-soft dot de [2009-06-19 02:55:16]
When using anonymous functions as properties in Classes, note that there are three name scopes: one for constants, one for properties and one for methods. That means, you can use the same name for a constant, for a property and for a method at a time.
Since a property can be also an anonymous function as of PHP 5.3.0, an oddity arises when they share the same name, not meaning that there would be any conflict.
Consider the following example:
<?php
class MyClass {
const member = 1;
public $member;
public function member () {
return "method 'member'";
}
public function __construct () {
$this->member = function () {
return "anonymous function 'member'";
};
}
}
header("Content-Type: text/plain");
$myObj = new MyClass();
var_dump(MyClass::member); // int(1)
var_dump($myObj->member); // object(Closure)#2 (0) {}
var_dump($myObj->member()); // string(15) "method 'member'"
$myMember = $myObj->member;
var_dump($myMember()); // string(27) "anonymous function 'member'"
?>
That means, regular method invocations work like expected and like before. The anonymous function instead, must be retrieved into a variable first (just like a property) and can only then be invoked.
Best regards,