6

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;
}()
Community
  • 1
  • 1
balintant
  • 2,774
  • 1
  • 19
  • 33
  • @MichaelBerkowski The `(function() {...})();` solution would be interesting, but not actual for now. Maybe the only available solution is making a `call_user_anonym_func()` for PHP 5.5 - 5.6. (?) – balintant Feb 01 '15 at 14:04
  • @MichaelBerkowski The first example is a working one, so it does. But I don't want to declare a variable for the function and store it in it (or `unset()` if it finished. (It doesn't really make sense, but is an interesting question, I think.) – balintant Feb 01 '15 at 14:08
  • Sorry, sorry. When I went off to search for related questions I forgot you said the top example works. I'll delete earlier comments. – Michael Berkowski Feb 01 '15 at 14:12
  • @MichaelBerkowski no problemo! :D – balintant Feb 01 '15 at 14:13
  • Before reinventing wheels, have you checked the `array_walk_recursive()`? It iterates a tree of nested arrays and calls a function on every leaf(!) element. – Ulrich Eckhardt Feb 01 '15 at 15:29
  • How can you say that unsetting a variable of an anonymous function frees a lot of memory? What if your foremost assumption just is wrong? AFAIK you can't unload class definitions that way in PHP. – hakre Aug 14 '15 at 16:41

2 Answers2

4

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

Fabian Schmengler
  • 24,155
  • 9
  • 79
  • 111
  • Yeah, this is a solution near to the one I'm looking for. But we should use it only for debug(`_backtrace ...`), so I won't mark it as the solution _I am looking for_. Thanks! – balintant Feb 01 '15 at 14:31
2

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. ;-)

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
  • I wrote a helper function which works like this called `call_user_anonym_func()`. I'll post that asap. :) Thanks! – balintant Feb 01 '15 at 15:10
  • Btw, this is just an interesting question for me. It has no goal other then getting to know alternative solutions. :) (+ and choosing the best as answer if there is some) – balintant Feb 01 '15 at 17:43