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);
Related
I'm trying to store anonymous functions in a static array property of my class. These functions should be invoked later by their index, but calling
self::$arr['index']()
just doesn't work, while
$a = self::$arr['index'];
$a();
does!
This doesn't work:
class A {
private static $func = array('a' => '');
public function __construct() {
self::$func['a'] = create_function('$str', 'echo "$str";');
}
public function go($str) {
self::$func['a']($str); // Call the function directly
}
}
$a = new A();
$a->go("hooray"); // Outputs "Undefined variable: func"
But this does:
class A {
private static $func = array('a' => '');
public function __construct() {
self::$func['a'] = create_function('$str', 'echo "$str";');
}
public function go($str) {
$a = self::$func['a']; // Pass the function name to a variable
$a($str); // Call the function via the variable
}
}
$a = new A();
$a->go("hooray"); // Outputs "hooray"
Why?
I'm using PHP Version 5.4.3
this is the behavior of php's parser
$functionName['a'] = "hello";
self::$functionName['a']();
calls
self::hello();
... the very sad thing is that in php you can't do this:
(self::$functionName['a'])(); // doesn't work in php :(
as you can do in javascript, for example
what you can do is... use a temporary variable like you said
$a = self::$func['a'];
$a($parameter);
or
call_user_func(self::$func['a'], $parameter);
hope this helps...
in latest phps these features were added
$a['sss'] = function(){ echo 'bla'; };
$a['sss']();
class Bla
{
private $a;
function test()
{
$this->a['sss'] = function(){ echo 'bla2'; };
$this->a['sss']();
}
}
$c = new Bla();
$c->test();
and they work properly... so for some reason, this syntax doesn't work only when using the scope resolution operator ( ClassName:: self:: etc)
Well, in php you simply can not do that, it is a php feature. But you can use call_user_func or its relatives:
return call_user_func(self::$func['$a'], $str);
This is a consequence of how the PHP parser currently works. Since the function call () is evaluated before the static operator ::, you end up with the parser attempting to reference the local variable $func instead, and then giving you the error about $func being undefined (which it is, since there is no variable named $func in the method).
As you've discovered, you can solve this by doing two separate statements.
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.
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
}
In PHP, I am able to use a normal function as a variable without problem, but I haven't figured out how to use a static method. Am I just missing the right syntax, or is this not possible?
(EDIT: the first suggested answer does not seem to work. I've extended my example to show the errors returned.)
function foo1($a,$b) { return $a/$b; }
class Bar
{
static function foo2($a,$b) { return $a/$b; }
public function UseReferences()
{
// WORKS FINE:
$fn = foo1;
print $fn(1,1);
// WORKS FINE:
print self::foo2(2,1);
print Bar::foo2(3,1);
// DOES NOT WORK ... error: Undefined class constant 'foo2'
//$fn = self::foo2;
//print $fn(4,1);
// DOES NOT WORK ... error: Call to undefined function self::foo2()
//$fn = 'self::foo2';
//print $fn(5,1);
// DOES NOT WORK ... error: Call to undefined function Bar::foo2()
//$fn = 'Bar::foo2';
//print $fn(5,1);
}
}
$x = new Bar();
$x->UseReferences();
(I am using PHP v5.2.6 -- does the answer change depending on version too?)
PHP handles callbacks as strings, not function pointers. The reason your first test works is because the PHP interpreter assumes foo1 as a string. If you have E_NOTICE level error enabled, you should see proof of that.
"Use of undefined constant foo1 - assumed 'foo1'"
You can't call static methods this way, unfortunately. The scope (class) is relevant so you need to use call_user_func instead.
<?php
function foo1($a,$b) { return $a/$b; }
class Bar
{
public static function foo2($a,$b) { return $a/$b; }
public function UseReferences()
{
$fn = 'foo1';
echo $fn(6,3);
$fn = array( 'self', 'foo2' );
print call_user_func( $fn, 6, 2 );
}
}
$b = new Bar;
$b->UseReferences();
In php 5.2, you can use a variable as the method name in a static call, but to use a variable as the class name, you'll have to use callbacks as described by BaileyP.
However, from php 5.3, you can use a variable as the class name in a static call. So:
class Bar
{
public static function foo2($a,$b) { return $a/$b; }
public function UseReferences()
{
$method = 'foo2';
print Bar::$method(6,2); // works in php 5.2.6
$class = 'Bar';
print $class::$method(6,2); // works in php 5.3
}
}
$b = new Bar;
$b->UseReferences();
?>
You could use the full name of static method, including the namespace.
<?php
function foo($method)
{
return $method('argument');
}
foo('YourClass::staticMethod');
foo('Namespace\YourClass::staticMethod');
The name array array('YourClass', 'staticMethod') is equal to it. But I think the string may be more clear for reading.
In PHP 5.3.0, you could also do the following:
<?php
class Foo {
static function Bar($a, $b) {
if ($a == $b)
return 0;
return ($a < $b) ? -1 : 1;
}
function RBar($a, $b) {
if ($a == $b)
return 0;
return ($a < $b) ? 1 : -1;
}
}
$vals = array(3,2,6,4,1);
$cmpFunc = array('Foo', 'Bar');
usort($vals, $cmpFunc);
// This would also work:
$fooInstance = new Foo();
$cmpFunc = array('fooInstance', 'RBar');
// Or
// $cmpFunc = array('fooInstance', 'Bar');
usort($vals, $cmpFunc);
?>
Coming from a javascript background and being spoiled by it, I just coded this:
function staticFunctionReference($name)
{
return function() use ($name)
{
$className = strstr($name, '::', true);
if (class_exists(__NAMESPACE__."\\$className")) $name = __NAMESPACE__."\\$name";
return call_user_func_array($name, func_get_args());
};
}
To use it:
$foo = staticFunctionReference('Foo::bar');
$foo('some', 'parameters');
It's a function that returns a function that calls the function you wanted to call. Sounds fancy but as you can see in practice it's piece of cake.
Works with namespaces and the returned function should work just like the static method - parameters work the same.
This seems to work for me:
<?php
class Foo{
static function Calc($x,$y){
return $x + $y;
}
public function Test(){
$z = self::Calc(3,4);
echo("z = ".$z);
}
}
$foo = new Foo();
$foo->Test();
?>
In addition to what was said you can also use PHP's reflection capabilities:
class Bar {
public static function foo($foo, $bar) {
return $foo . ' ' . $bar;
}
public function useReferences () {
$method = new ReflectionMethod($this, 'foo');
// Note NULL as the first argument for a static call
$result = $method->invoke(NULL, '123', 'xyz');
}
}
"A member or method declared with static can not be accessed with a variable that is an instance of the object and cannot be re-defined in an extending class"
(http://theserverpages.com/php/manual/en/language.oop5.static.php)