Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Self-calling anonymous function without declaring as variable

I want to call an anonymous function without declaring a variable for it.

I know that this is a working example:

$foo = function ($bar) use ($foo) {
    if (is_array($bar)) {
        foreach ($bar AS $current) {
             $foo($current);
        }
    }
    else {
        print($bar);
    }
};

$foo($input);

# Unset variable cause we won't need it anymore
# and we like to have a lot of free memory.
unset($foo);

But I want to call and unset it automatically:

call_user_func(function ($bar) {
    if (is_array($bar)) {
         foreach ($bar AS $current) {
             # This won't work
             # as our function doesn't have any name.
             call_user_func(__FUNCTION__, $current);
         }
    }
    else {
         print($bar);
    }
}, $input);

But this example won't work, because our function does not have a name. Is there any solution or do you know any solution to solve this problem?


PS: Let's suppose that $input is the following array: ["Hello, ", "World!"].

As of that, the output should be:

Hello, 
World!

Update #1:

As this is just an example, call_user_func_array(function () { ... }, $input) is not the solution I am looking for.

And it wouldn't be working if I have an $input like [["Hello, ", "World"], "!"].


Update #2:

Another solution I'm not looking for is debug_backtrace()[1]['args'][0]->__invoke($current);. I think it is ugly enough to use for debug only. :) Thanks to @fschmengler.

And the other form of this is call_user_func(debug_backtrace()[1]['args'][0], $current));.


Update #3:

Another solution written by @UlrichEckhardt is embedding the anonymous function into another anonymous function. I think, unsetting the previously declared function variable - eg. first example - is both cleaner and shorter. But this is a solution too.

function () {
    $f = function ($param) use ($f) {
        // use $f here
    }
    return $f;
}()
like image 992
balintant Avatar asked Feb 01 '15 13:02

balintant


2 Answers

It's possible to access the closure with debug_backtrace() and use __invoke() to call it call it with call_user_func() as before:

$input = ["Hello, ", "World!"];

call_user_func(function ($bar) {
    if (is_array($bar)) {
         foreach ($bar AS $current) {
             call_user_func(debug_backtrace()[1]['args'][0], $current));
         }
    }
    else {
         print($bar);
    }
}, $input);

But in my opinion, the first version where you assign the closure to a variable is more readable and I don't see any arguments against it, other than personal taste.

Update: it has to be call_user_func() again, not __invoke(), so that args[0] references the closure for every level of recursion

like image 107
Fabian Schmengler Avatar answered Oct 31 '22 22:10

Fabian Schmengler


Write a closure returning the closure you want and call that:

function () {
    $f = function ($param) use ($f) {
        // use $f here
    }
    return $f;
}()

I'm actually not convinced that PHP is capable of defining and calling a function in one step, you may have to use call_user_func() instead to work around your language's shortcomings here. That aside, there are much better ways to write obfuscated code and if that is surprisingly not your goal, there might even be clearer alternatives. ;-)

like image 23
Ulrich Eckhardt Avatar answered Oct 31 '22 23:10

Ulrich Eckhardt