Anonymous function calls in PHP

6 Comments

Anonymous function calls are a well-known pattern in JavaScript but there are also use cases in PHP where they make sense. Of course PHP 5.3 with its Lambda Functions is required!

But let me first introduce the pattern shortly:

In JavaScript you often have code that just has to be executed when loaded but you really don't want to pollute the global namespace. The solution is to create an anonymous function and call it directly:


(function() {
  var some, local, variables;
  // do something
})();

Why should you want this in PHP?

Imagine an application that is not fully object oriented (yes this is the reality and yes, sometimes that even makes sense) and has some include files which execute code directly. Now if you don't unset all local variables at the end you leave a big mess in the global namespace. See the analogy?

Unfortunately the following is not posible with PHP Lambda Functions:


(function() {
  $localvar = 'foo';
  // do something
})(); // Parse error: syntax error, unexpected '('

But there is still good old call_user_func(). Since Lambda Functions are objects of the Closure class which again is of the callable "type", it fits perfectly our needs:


call_user_func(function() {
  $localvar = 'foo';
  // do something
});

Now what if there are variables from the outer scope that you need or want to change? Of course you could use the global keyword but that only works if you are refering to the global scope and there is a better way: The use keyword.

Look at this example:


// test.php
function test() {
  $readMe = 'Hello';
  $writeMe = 'World';
  include 'include.php';
  echo 'index.php: ', $readMe, ' ', $writeMe, "\n";
}
test();

// include.php
call_user_func(function() use ($readMe, &$writeMe) {
  $temp = $readMe . ' ' . $writeMe;
  echo 'include.php: ', $temp, "\n";
  $readMe = 'Good Bye';
  $writeMe = 'Internet';
});

test.php results in the following output:

include.php: Hello World
index.php: Hello Internet

Let's go through it step-by-step:

The $readMe and $writeMe variables are present in the local scope of test(). Since we also include include.php there, the scope stays the same within this file.

Using the anonymous function call we then open a new scope but take over $readMe and $writeMe with the use statement. Any other local variable from test() will not be present!

It is important to know that variables passed with use work similar to function parameters, so a copy is used by default (call-by-value). You can change this behaviour to call-by-reference exactly like in function declarations with a "&" denoting a reference.

Now our function prints the contents of $readMe and $writeMe to the screen ("Hello World") and assigns new values to both variables.

Afterwards, back in test() the $writeMe variable that was passed by reference will hold this new value whereas $readMe is still the same because it was passed by value. Therefore the output is "Hello Internet".

Of course $temp will not be set, it was a local variable in the anonymous function scope and destroyed after its execution.

One step further

To keep code maintainable the inclusion of files should not have side effects on variables which is on one hand assured by the anonymous function call but on the other hand bypassed again with use parameters by reference. To keep this transparent I recommend doing the wrapping around the include statement instead of (or even additional to) inside the included file:


// test.php
// ...
  call_user_func(function() use (&$writeMe) {
    include 'include.php';
  });
// ...

Now regardless what include.php contains it is clear that it will only have effects on $writeMe

Conclusion

If you work with procedural code and include files (and let it be just configuration files) anonymous function calls are a good way to keep the code more maintainable. They constrain unwanted side effects and make wanted side effects more visible without further documentation. So the two extra lines should really be worth their effort!

Update

Someone at reddit pointed out namespaces so let me clarify: Namespaces do not solve this problem!

Only classes, functions and constants are namespaced, a namespace does not have its own scope, so they share any variables within the parent scope!

Fabian Schmengler’s Blog: Anonymous function calls in PHP | Development Blog With Code Updates : Developercast.com

...lopment" rel="category tag">Development   Fabian Schmengler has a new post today looking a using anonymous function calls in PHP. He relates to to another popular language that allows for dynamic anonymous functions - Jav...

2011-02-25 5:15 pm

Jani Hartikainen

Interesting, I never considered using anonymous functions in a case like this.

I did use functions to work around an issue as you describe though: I was refactoring some legacy code, which had the issue of variable names easily colliding, so I simply created entire file-sized functions and called them.

Now that I think of it, anon functions would've worked quite well for it. Too bad we didn't use 5.3 yet back then :)

2011-02-25 6:01 pm

Fabian Schmengler’s Blog: Anonymous function calls in PHP

...ss="addthis_button_expanded">More Destinations Fabian Schmengler has a new post today looking a using anonymous function calls in PHP. He relates to to another popular language that allows for dynamic anonymous functions &#821...

2011-02-26 11:20 am

PHP Closures and the global namespace

...PHP Closures and the global namespace">No Comments I found Fabian Schmengler’s recent post on Anonymous function calls in PHP really interesting, drawing a comparison between Javascript’s closures to keep the global namespa...

2011-03-01 1:44 am

Mitch Pronschinske

Really nice post. I see you already use the DZone widget. Would you be interested in an invite to the MVB program? Send me an email and I can give you some more info.

Best,
Mitch

2011-03-14 2:47 am

Programowanie w PHP » Blog Archive » Fabian Schmengler’s Blog: Anonymous function calls in PHP

...gler’s Blog: Anonymous function calls in PHP Fabian Schmengler has a new post today looking a using anonymous function calls in PHP. He relates to to another popular language that allows for dynamic anonymous functions &#821...

2012-03-30 3:08 pm