PHP variables in anonymous functions - php

I was playing around with anonymous functions in PHP and realized that they don't seem to reach variables outside of them.
Is there any way to get around this problem?
Example:
$variable = "nothing";
functionName($someArgument, function() {
$variable = "something";
});
echo $variable; //output: "nothing"
This will output "nothing". Is there any way that the anonymous function can access the $variable?

Yes, use a closure:
functionName($someArgument, function() use(&$variable) {
$variable = "something";
});
Note that in order for you to be able to modify $variable and retrieve the modified value outside of the scope of the anonymous function, it must be referenced in the closure using &.

If your function is short and one-linear, you can use arrow functions, as of PHP 7.4:
$variable = "nothing";
functionName($someArgument, fn() => $variable = "something");

Related

php: return compact() value from inside a function [duplicate]

This question already has answers here:
Reference: What is variable scope, which variables are accessible from where and what are "undefined variable" errors?
(3 answers)
Closed 4 years ago.
I want to know why php global function compact() only return empty array when used inside a function.
Here is the code as an example:
$hello = "world";
function myFunc($str) {
return compact($str);
}
$arr = myFunc('hello');
print_r($arr);
Any help would be appreciated.
Thank you!
It's because myFunc isn't in the global scope, and can't access the $hello variable. Think about this code:
$hello = "world";
function myFunc () {
echo $hello;
}
myFunc(); // => PHP: Undefined variable: hello
myFunc simply can't access the global variable $hello. However, if you add a global statement in the function, you can access the global scope:
$hello = "world";
function myFunc () {
global $hello;
echo $hello;
}
myFunc(); // => world
So, in summary, if you add a global to the top of your function, your function can work as expected:
$hello = "world";
function myFunc ($str) {
global $hello;
return compact($str);
}
$arr = myFunc('hello');
print_r($arr); // => Array ( [hello] => world )
Here's some further information on variable scope: Reference: What is variable scope, which variables are accessible from where and what are "undefined variable" errors?
variable $hello scope issue
try this:
$hello = "world";
$myFunc = function($str) use($hello) {
return compact($str);
};
print_r($myFunc('hello'));
compact() function is an inbuilt function in PHP and it is used to create an array using variables. This function is opposite of extract() function. It creates an associative array whose keys are variable names and their corresponding values are array values.
you are passing variable like this compact($str), but in compact we used pass parameter like this (in single quote without $ sign)
compact('str');
know your code look like this
$hello = "world";
function myFunc($str) {
return compact('str');//compact('str'): means passing $str variable
}
$arr = myFunc('hello');//or myFunc($hello); (myFunc('hello'): means passing hello string and myFunc($hello): means passing $hello variable)
print_r($arr);
check output
If you have multiple variables then used , like this
$f_name='john';
$l_name='smith';
$address='xyz';
$result = compact("f_name", "l_name", "address");
print_r($result);
Parameters: This function accepts a variable number of arguments separated by comma operator (','). These arguments are of string data type and specify the name of variables which we want to use to create the array. We can also pass an array as an argument to this function, in that case, all of the elements in the array passed as a parameter will be added to the output array.
Reference

How to assign a function reference to a variable?

I would like to handle functions references and lambda in consistent way in PHP, but I have trouble when assigning a function reference to a variable.
function foo()
{
echo "hi\n";
}
$here = function() { echo "hello\n"; };
$here = foo;
The last line gives me a warning. I could use string literal, but I am afraid of two things -- using it later as string by mistake, and problems with name resolution when passing such string-reference over namespace boundaries.
Is there any way to grab that reference without using strings?
One way is to create it as an anonymous function:
$foo = function()
{
echo "hi\n";
};
$foo();

PHP 5.4 Anonymous Function in Array undefine

This is PHP 5.4 code...
<?php
function abc($YesNo){return $YesNo["value"];}
$YesNo = array("value"=>"No","text"=>"No");
$x = array("active"=>function(){return abc($YesNo);});
echo $x['active']();
?>
Notice: Undefined variable: YesNo on line 7
Output Should be : Yes
if i directly put array in code by replace $YesNo like
<?php
function abc($YesNo){return $YesNo["value"];}
$x = array("active"=>function(){return abc(array("value"=>"Yes","text"=>"Yes"));});
echo $x['active']();
?>
output : Yes
which is correct output. Now what's the problem in first code. I need that for re-usability
Try this,
You can use use for passing data to a closure.
<?php
function abc($YesNo){return $YesNo["value"];}
$YesNo = array("value"=>"No","text"=>"No");
$x = array("active"=>function() use ($YesNo) {return abc($YesNo);});
echo $x['active']();
?>
You provide your anonymous function with a parameter:
$x = array("active"=>function($param){return abc($param);});
then you call it:
echo $x['active']($YesNo);
You may use the use keyword to make your function aware of an external variable:
$x = array("active"=>function() use ($YesNo) {return abc($YesNo);});
but it would be quite against the idea of reusability, in this case.
The problem is that your variable is not accessible within the function due to Variable Scope.
Because the array is defined outside of the function, it is not by default available inside the function.
There's a couple of solutions
Disclaimer: These are intended to fit within the scope of the question. I understand that they are not necessarily best practice, which would require a larger discussion
First Option:
You can declare the array within the function, like below. This is useful if you don't need access to it outside of the function.
function abc($YesNo){
$YesNo = array("value"=>"No","text"=>"No");
return $YesNo["value"];
}
Second Option:
Within your abc function, you can add the line global $YesNo. This is useful if you do need access to the array outside of the function:
function abc($YesNo){
global $YesNo;
return $YesNo["value"];
}
Other options exist (such as moonwave99's answer).
Finally:
Why are you putting an anonymous function within the array of $x? Seems like a path that will lead to problems down the road....
Your variable $YesNo needs to be visible in the scope of your anonymous function. You need to add global $YesNo as the first statement in that function:
So
$x = array("active"=>function(){return abc($YesNo);});
becomes
$x = array("active"=>function(){global $YesNo; return abc($YesNo);});
... also "value"=>"No" should be "value"=>"Yes" if you want it to return "Yes"

How to access variable from scope of parent function?

I want my function to access an outside variable—from its parent function specifically. However, using the global keyword sets too broad a scope; I need to limit it. How do I get this code to spit out 'Level 2' instead of 'Level 1'? Do I have to make a class?
<?php
$a = "Level 1";
function first() {
$a = "Level 2";
function second() {
global $a;
echo $a.'<br />';
}
second();
}
first();
//outputs 'Level 1'
?>
Just for the sake of example, if I understand what you're trying to do, you could use a closure (PHP 5.3+), as "Closures may also inherit variables from the parent scope" with the use keyword.
$a = "Level 1";
function first() {
$a = "Level 2";
$func = function () use ($a) {
echo $a.'<br />';
};
$func();
}
first();
// prints 'Level 2<br />'
Closures are most commonly used for callback functions. This may not be the best scenario to use one, however. As others have suggested, just because you can do something doesn't mean it's the best idea.
PHP has no concept of nested functions or scopes and it's terrible practice to nest functions. What happens is that PHP simply encounters a function declaration and creates a normal function second. If you try to call first again, PHP will again encounter a function declaration for second and crash, since the function second is already declared. Therefore, don't declare functions within functions.
As for passing values, either explicitly pass them as function parameters or, as you say, make a class if that makes sense.

Pass a function by reference in PHP

Is it possible to pass functions by reference?
Something like this:
function call($func){
$func();
}
function test(){
echo "hello world!";
}
call(test);
I know that you could do 'test', but I don't really want that, as I need to pass the function by reference.
Is the only way to do so via anonymous functions?
Clarification: If you recall from C++, you could pass a function via pointers:
void call(void (*func)(void)){
func();
}
Or in Python:
def call(func):
func()
That's what i'm trying to accomplish.
For what it's worth, how about giving something like this a shot? (Yes, I know it's an anonymous function which was mentioned in the post, but I was disgruntled at the abundance of replies that did not mention closures/function-objects at all so this is mostly a note for people running across this post.)
I don't use PHP, but using a closure appears to work in PHP 5.3 (but not PHP 5.2) as demonstrated here. I am not sure what the limitations, if any, there are. (For all I know the closure will eat your children. You have been warned.)
function doIt ($fn) {
echo "doIt\n";
return $fn();
}
function doMe () {
echo "doMe\n";
}
// I am using a closure here.
// There may be a more clever way to "get the function-object" representing a given
// named function, but I do not know what it is. Again, I *don't use PHP* :-)
echo doIt(function () { doMe(); });
Happy coding.
The problem with call_user_func() is that you're passing the return value of the function called, not the function itself.
I've run into this problem before too and here's the solution I came up with.
function funcRef($func){
return create_function('', "return call_user_func_array('{$func}', func_get_args());");
}
function foo($a, $b, $c){
return sprintf("A:%s B:%s C:%s", $a, $b, $c);
}
$b = funcRef("foo");
echo $b("hello", "world", 123);
//=> A:hello B:world C:123
ideone.com demo
No, functions are not first class values in PHP, they cannot be passed by their name literal (which is what you're asking for). Even anonymous functions or functions created via create_function are passed by an object or string reference.
You can pass a name of a function as string, the name of an object method as (object, string) array or an anonymous function as object. None of these pass pointers or references, they just pass on the name of the function. All of these methods are known as the callback pseudo-type: http://php.net/callback
function func1(){
echo 'echo1 ';
return 'return1';
}
function func2($func){
echo 'echo2 ' . $func();
}
func2('func1');
Result:
echo1 echo2 return1
In PHP 5.4.4 (haven't tested lower or other versions), you can do exactly as you suggested.
Take this as an example:
function test ($func) {
$func('moo');
}
function aFunctionToPass ($str) {
echo $str;
}
test('aFunctionToPass');
The script will echo "moo" as if you called "aFunctionToPass" directly.
A similar pattern of this Javascript first class function:
function add(first, second, callback){
console.log(first+second);
if (callback) callback();
}
function logDone(){
console.log('done');
}
function logDoneAgain(){
console.log('done Again');
}
add(2,3, logDone);
add(3,5, logDoneAgain);
Can be done in PHP (Tested with 5.5.9-1ubuntu on C9 IDE) in the following way:
// first class function
$add = function($first, $second, $callback) {
echo "\n\n". $first+$second . "\n\n";
if ($callback) $callback();
};
function logDone(){
echo "\n\n done \n\n";
}
call_user_func_array($add, array(2, 3, logDone));
call_user_func_array($add, array(3, 6, function(){
echo "\n\n done executing an anonymous function!";
}));
Result: 5 done 9 done executing an anonymous function!
Reference: https://github.com/zenithtekla/unitycloud/commit/873659c46c10c1fe5312f5cde55490490191e168
You can create a reference by assigning the function to a local variable when you declare it:
$test = function() {
echo "hello world!";
};
function call($func){
$func();
}
call($test);
You can say
$fun = 'test';
call($fun);
Instead of call(test);, use call_user_func('test');.
As of PHP 8.1, you can use First-class callables:
call(test(...));
You can even use methods:
call($obj->test(...));
As simple as it is.
It appears a bit unclear why do you want to pass functions by reference? Usually things are passed by reference only when the referenced data needs to be (potentially) modified by the function.
As PHP uses arrays or strings to refer functions, you could just pass an array or a string by reference and that would allow the function reference to be modified.
For example, you could do something like
<?php
$mysort = function($a, b) { return ($a < $b) ? 1 : -1; };
adjust_sort_from_config($mysort); // modifies $mysort
do_something_with_data($mysort);
where
<?php
function load_my_configuration(&$fun)
{
$sort_memory = new ...;
...
$fun = [$sort_memory, "customSort"];
// or simply
$fun = function($a, b) { return (rand(1,10) < 4 ? 1 : -1; };
}
This works because there are three ways to refer to function in PHP via a variable:
$name – the string $name contains the name of the function in global namespace that should be called
array($object, $name) – refers to method called string $name of object $object.
array($class, $name) – refers to static function string $name of class $class.
If I remember correctly, the methods and static functions pointed by these constructs must be public. The "First-class callable syntax" should improve this restriction given recent enough PHP version but it seems to be just some syntactic sugar around Closure::fromCallable().
Anonymous functions work the same behind the scenes. You just don't see the literal random names of those functions anywhere but the reference to an anonymous function is just a value of a variable, too.

Categories