What are the differences between closures in JS and closures in PHP? Do they pretty much work the same way? Are there any caveats to be aware of when writing closures in PHP?
One difference is how both cope with storing the context in which an anonymous function is executed:
// JavaScript:
var a = 1;
var f = function() {
console.log(a);
};
a = 2;
f();
// will echo 2;
// PHP
$a = 1;
$f = function() {
echo $a;
};
$a = 2;
$f();
// will result in a "PHP Notice: Undefined variable: a in Untitled.php on line 5"
To fix this notice you'll have to use the use syntax:
$a = 1;
$f = function() use ($a) {
echo $a;
};
$a = 2;
$f();
// but this will echo 1 instead of 2 (like JavaScript)
To have the anonymous function behave somehow like the JavaScript counterpart you'll have to use references:
$a = 1;
$f = function() use (&$a) {
echo $a;
};
$a = 2;
$f();
// will echo 2
I think this is the most striking difference between JavaScript and PHP closures.
Second difference is that every JavaScript closure has a this context available which means, that you can use this inside the closure itself (although it's often quite complicated to figure out what this actually refers to) - PHP's current stable version (PHP 5.3) does not yet support $this inside a closure, but PHP's upcoming version (PHP 5.4) will support $this binding and rebinding using $closure->bind($this) (See the Object Extension RFC for more info.)
Third difference is how both languages treat closures assigned to object properties:
// JavaScript
var a = {
b: function() {}
};
a.b(); // works
// PHP
$a = new stdClass();
$a->b = function() {};
$a->b(); // does not work "PHP Fatal error: Call to undefined method stdClass::b() in Untitled.php on line 4"
$f = $a->b;
$f(); // works though
The same is true if closures are assigned to properties in class definitions:
class A {
public $b;
public function __construct() {
$this->b = function() {};
}
public function c() {
$this->b();
}
}
$a = new A();
// neither
$a->b();
// nor
$a->c();
// do work
Fourth difference: JavaScript Closures are full fledged objects, wheres in PHP they are restricted objects. For instance, PHP Closures cannot have properties of their own:
$fn = function() {};
$fn->foo = 1;
// -> Catchable fatal error: Closure object cannot have properties
while in JavaScript you can do:
var fn = function() {};
fn.foo = 1;
fn.foo; // 1
Fifth difference: Returned closures can be immediately called upon in Javascript:
var fn = function() { return function() { alert('Hi');}}
fn()();
Not in PHP:
$fn = function() { return function() { echo('Hi');};};
$fn()(); // syntax error
The only thing I've found in PHP (that is totally cool and really handy!) is the ability to use them as getters and setters in classes which was always a nightmare to achieve before, JavaScript can be used in the same way but they do both act almost identically from what I've seen.
I'm not sure about the namespacing convention differences between the two but as #Rijk pointed out there is a section on the PHP website dedicated to them
<?php
class testing {
private $foo = 'Hello ';
public $bar = 'Bar';
#Act like a getter and setter!
public static $readout = function ($val = null) {
if (!empty($val)) {
testing::$readout = $val;
}
return testing::$readout;
}
}
They are also really great for...
Looping through items with a controller rather than a new for/each loop on the page
Great for supplying as arguments to functions/classes
Whats annoying about them is...
You can't typecast them, since they're just functions...
They do pretty much work the same way. Here's more information about the PHP implementation: http://php.net/manual/en/functions.anonymous.php
You can use a closure (in PHP called 'anonymous function') as a callback:
// return array of ids
return array_map( function( $a ) { return $a['item_id']; }, $items_arr );
and assign it to a variable:
$greet = function( $string ) { echo 'Hello ' . $string; }; // note the ; !
echo $greet('Rijk'); // "Hello Rijk"
Furthermore, anonymous function 'inherit' the scope in which they were defined - just as the JS implementation, with one gotcha: you have to list all variables you want to inherit in a use():
function normalFunction( $parameter ) {
$anonymous = function() use( $parameter ) { /* ... */ };
}
and as a reference if you want to modify the orignal variable.
function normalFunction( $parameter ) {
$anonymous = function() use( &$parameter ) { $parameter ++ };
$anonymous();
$parameter; // will be + 1
}
Related
I'm trying to run the $greeter function property of the $greeter instance of the Greeter class. I've read the answers from this related post but could not get them to work (the post also mentions _call, traits, stdClass, returning a function from a function (which doesn't make sense to me why this works without having to call twice), and the given solutions seem like overkill for the simple thing I'm trying to achieve). Perhaps my case is a little different. I don't understand why the parser messes up.
class Greeter {
private $greeter;
function __construct() {
$this->greeter = function() {
echo "Hello!\n";
};
}
public function greet() {
$this->greeter();
}
}
// THIS WORKS AS EXPECTED:
$hello = function() { echo "Hi!\n"; };
$hello();
$greeter = new Greeter();
// NEITHER OF THESE WORK:
call_user_func($greeter->greet);
$greeter->greet();
$greeter['greet']();
OUTPUT:
Hi!
<br />
<b>Warning</b>: call_user_func() expects parameter 1 to be a valid callback, no array or string given on line <b>30</b><br />
<br />
<b>Fatal error</b>: Call to undefined method Greeter::greeter() on line <b>15</b><br />
OK, so this works, but why do I need to use call_user_func at all? Is it a problem with the PHP grammar, that for some reason the parser is having problems? C++ used to have a problem parsing << used with nested std::maps, and at a time it was necessary to write < < to avoid the problem. Then a trick in the grammar was introduced to fix the problem. I don't see why the same thing can't happen in the PHP grammar to make call_user_func unnecessary.
class Greeter {
private $greeter;
function __construct() {
$this->greeter = function() {
echo "Hello!\n";
};
}
public function greet() {
call_user_func($this->greeter);
}
}
// THIS WORKS AS EXPECTED:
$hello = function() { echo "Hi!\n"; };
$hello();
$greeter = new Greeter();
// NOW THIS ALSO WORKS AS EXPECTED:
$greeter->greet();
Welcom to funny PHP.
<?php
class A {
public function f() {
echo 'hi';
}
}
$a = new A();
$a->f(); // yes
call_user_func($a->f); // no $a->f is not a func pointer in PHP
call_user_func([$a, 'f']); // yes [$obj, $method_string] is callable
$c = [$a, 'f'];
$c(); // yes it's a callable
[$a, 'f'](); // no PHP don't like that
$c = function() use($a) { $a->f(); };
$c(); // yes
function() use($a) { $a->f(); }(); // no
(function() use($a) { $a->f(); })(); // no
// edit: there is more fun I forgot
$m = 'f';
$a->$m(); // yes
$a->{'greet'}(); // yes
Well, it's not easy to understand what PHP is doing sometimes, but there are many cases you can't write in one expression.
Same for empty($this->getNumber()) or in the old version with array dereference $this->getArray()[4].
By the way you meant the closing >> vs. > > in C++ templates which were parsed as bitshift operator, but now is fine with C++11.
i have this class (simple one just for example):
<?
class Test {
public function test1($a) {
$gen = function() {
$gen = function() {
global $a; // no effect
echo 'a='. $a; // how could i access $a from test1 parameter without passing?
};
$gen();
};
$gen();
}
};
$x = new Test();
$x->test1(123);
is there a way to access $a from test1 paramter inside last $gen function without passing it to the $gen() function?
Anonymous functions in PHP don't have an implicit variable scope like JavaScript does, so you need to specify which variables from the parent scope are needed. You do this with the use syntax:
$var = 123;
$fn = function() use ($var) {
// you can use $var here
}
$fn();
See also: Closures and scoping
You are missing the use statement. Refere to the 3rd example on PHP's documentation on closures.
This will work:
<?php
class Test {
public function test1($a) {
$gen = function() use ($a) {
$gen = function() use($a) {
echo 'a='. $a; // how could i access $a from test1 parameter without passing?
};
$gen();
};
$gen();
}
};
$x = new Test();
$x->test1(123);
For example, if I do this:
function bar(&$var)
{
$foo = function() use ($var)
{
$var++;
};
$foo();
}
$my_var = 0;
bar($my_var);
Will $my_var be modified? If not, how do I get this to work without adding a parameter to $foo?
No, they are not passed by reference - the use follows a similar notation like the function's parameters.
As written you achieve that by defining the use as pass-by-reference:
$foo = function() use (&$var)
It's also possible to create recursion this way:
$func = NULL;
$func = function () use (&$func) {
$func();
}
NOTE: The following old excerpt of the answer (Jun 2012) was written for PHP < 7.0. As since 7.0 (Dec 2015) the semantics of debug_zval_dump() changed (different zval handling) the refcount(?) output of it differs nowadays and are not that much saying any longer (integers don't have a refcount any longer).
Validation via the output by not displaying $my_var changed (from 0) still works though (behaviour).
You can validate that on your own with the help of the debug_zval_dump function (Demo):
function bar(&$var)
{
$foo = function() use ($var)
{
debug_zval_dump($var);
$var++;
};
$foo();
};
$my_var = 0;
bar($my_var);
echo $my_var;
Output:
long(0) refcount(3)
0
A full-through-all-scopes-working reference would have a refcount of 1.
Closures are, almost by definition, closed by value, not by reference. You may "use by reference" by adding an & in the argument list:
function() use (&$var)
This can be seen in example 3 in the anonymous functions manual page.
No, they are not passed by reference.
function foo(&$var)
{
$foo = function() use ($var)
{
$var++;
};
$foo();
}
$my_var = 0;
foo($my_var);
echo $my_var; // displays 0
function bar(&$var)
{
$foo = function() use (&$var)
{
$var++;
};
$foo();
}
$my_var = 0;
bar($my_var);
echo $my_var; // displays 1
I have a list of functions a(), b(), c()
I have a main() function.
Depending on case, I need to pass a different function to main() to use.
In javascript it would be:
var a = function(){}
var b = function(){}
var c = function(){}
var func = (some logic) a, b or c;
main(func);
How do I do that in php5.3?
I am trying to avoid using
$func_name = "a";
main($func_name){
$func_name();
}
Or may be it is the best way, and I should not use closures of any type?
Same idea in PHP 5.3, as you can create anonymous functions:
$sayHello = function($var)
{
echo "Hello ", $var;
};
// Logic here to determine what $func is set to
$func = $sayHello;
function callCustom($function)
{
if(!is_callback($function))
{
// throw exception
}
$function("World");
}
callCustom($func); // Hello World
Try this:
$a = function(){};
$b = function(){};
$c = function(){};
switch (rand(0, 2)) {
case 0: $func = $a; break;
case 1: $func = $b; break;
case 2: $func = $c; break;
}
var_dump($func);
You can see a working example here http://codepad.viper-7.com/Ut5yGQ
Note: I used var_dump instead of main as main is undefined
In PHP, anything that is "callable" (is_callableDocs) can be called (invoked). The manual often names these parameters as Callback, a pseudo-type in PHPDocs.
You can use any of the different callbacks and pass them as a function parameter:
function main($callback)
{
call_user_func($callback);
}
Then your function will work with any valid PHP callback.
Next to that, functions can be called with variables, some examples:
Variable Function Call:
function name() {};
$function = 'name'; # function name as string
$function(); # invoke
Anonymous Function as Variable Function Call:
$function = function() {}; # define function
$function(); # invoke
See as well Variable FunctionsDocs.
I have an anonymous function which is supposed to call itself. However, I have no variable or function name at hand, so I was hoping to find a function that could do return "this" in context of functions. Is there such a thing?
Here's an example:
$f = function() use($bar, $foo) {
// call this function again.
};
Calling like this:
call_user_func(__FUNCTION__);
Leads to this:
Warning: call_user_func() expects parameter 1 to be a valid callback,
function '{closure}' not found or invalid function name
If I try to put $f in the use-list, then it says the variable is not defined (because it is not yet).
__FUNCTION__ cannot be used in anonymous functions
Pass the variable holding the anonymous function as a reference in the 'use' clause....
$f = function() use($bar, $foo, &$f) {
$f();
};
Tip of the hat to this answer.
Okay, I found out the way to do this:
$f = function() use(&$f) {
$f();
};
$f();
The key thing is to pass $f as a reference. Thus PHP does not try to pass a value but a reference to a memory slot.
I have an anonymous function which is supposed to call itself.
I prefer to use call_user_func_array(__FUNCTION__, $params); when calling a recursive function.
As your example doesn't have any arguments then i guess call_user_func(__FUNCTION__); would be better suited.
You would expect and hope the following code would work but that would be too easy.
$bar = 10;
$foo = 0;
$f = function() use (&$bar,$foo) {
if($bar){ // condition needed to prevent infinite loop
echo $bar-- . PHP_EOL;
call_user_func(__FUNCTION__); // wont work
}
};
$f();
The __FUNCTION__ "Magic constant" is unavailable to closures so the code needs to be adapted to allow the passing of the function variable. we can make the function available by passing it as a regular argument or via the use statement.
Function passed as argument
$bar = 10;
$foo = 0;
$f = function( $__FUNCTION__ = null ) use (&$bar, $foo) {
if($__FUNCTION__ && $bar){
echo $bar-- . PHP_EOL;
call_user_func( $__FUNCTION__, $__FUNCTION__);
}
};
$f ( $f );
Function passed via use statement
$bar = 10;
$foo = 0;
$__FUNCTION__ = function() use (&$bar, $foo, &$__FUNCTION__) {
if($bar){
echo $bar-- . PHP_EOL;
call_user_func( $__FUNCTION__ );
}
};
$__FUNCTION__();
Working example, click edit-> ideone it! to re-run code.
http://www.php.net/manual/en/language.constants.predefined.php
Edit: Posted before code was given. Of course it doesn't work on anonymous functions.
call_user_func(__FUNCTION__, $param1, $param2);
call_user_func_array(__FUNCTION__, $params);
function i_dont_know() {
call_user_func(__FUNCTION__,$params);
//or
$funcname = __FUNCTION__;
$funcname($params);
}