Recursive Closures in PHP 5.3

With the release of PHP 5.3.3 the other day and the announcement of the end of active support for the PHP 5.2 branch I thought it would be a good time to do a bit more experimenting with the new features in PHP 5.3. I would have done this sooner but the hosting company we use at work is still using the 5.2 branch. I’d also like to upgrade my Zend Certification to the 5.3 version when the new exam becomes available. Our hosting company also has the setting ‘magic_quotes_gpc’ enabled in php.ini and one thing all of my projects have in them is code to remove the quotes added so I can handle appropriate escaping myself. I use the following code from the PHP manual:

<?php
if (get_magic_quotes_gpc()) {
function stripslashes_deep($value) {
return (is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value));
}
$_POST = array_map('stripslashes_deep', $_POST);
$_GET = array_map('stripslashes_deep', $_GET);
$_COOKIE = array_map('stripslashes_deep', $_COOKIE);
}
?>

This works perfectly but it does end up creating a function in the global namespace which is only called once. A perfect job for a closure.

What is a closure?

A closure is a special type of anonymous function that encapsulates one or more variables from the scope it is created in when it is created. The name comes from the idea that this kind of function closes over these variables. This is not the same as passing an argument to a function or using the global keyword. Inside the closure the variable encapsulated contains the value its ‘parent’ had at the time the closure was created. Changes to the variable inside the closure do not affect the variable outside the closure. It’s this behaviour that differentiates a closure from an anonymous function (the PHP manual seems to use the terms interchangeably).

Stripping slashes the PHP 5.3 way

I found the technique for creating a recursive closure in PHP on Stack Overflow. Here’s my code to strip slashes added by magic_quotes_gpc using a recursive closure:

<?php

if (get_magic_quotes_gpc()) {
 $strip_slashes_deep = function ($value) use (&$strip_slashes_deep) {
return is_array($value) ? array_map($strip_slashes_deep, $value) : stripslashes($value);
 };
 $_GET = array_map($strip_slashes_deep, $_GET);
 $_POST = array_map($strip_slashes_deep, $_POST);
 $_COOKIE = array_map($strip_slashes_deep, $_COOKIE);
 }
?>

Variables are enclosed within a closure using the ‘use’ keyword. The thing to note here is that the closure is enclosed within itself (!) and that to achieve this it needs to be passed by reference. Without this the PHP parser generates a warning saying that the variable ‘$strip_slashes_deep’ does not exist. I think that this is because of the way values are encapsulated within closures. They are encapsulated at the point the closure is created and in the code above the variable $strip_slashes_deep does not exist at the point the closure is created. It is only created as the return value of creating the closure, ie. the closure itself. Passing the closure by reference avoids this issue entirely.

The real beauty of this code for me is that it avoids polluting the global namespace with a function that is used only once. As soon as the variable $strip_slashes_deep falls out of scope it, and the closure contained in it, are destroyed. This technique could be used to create any number of recursive closures. It just so happens that the example I could think of for this was to strip slashes added by magic quotes. I’d be curious to know of any other use cases anyone can think of for this technique or any other comments people may have. I still find the concept of recursive closures encapsulating themselves slightly mind boggling and may have muddled up some of my terminology as a result.

10 Comments
  1. Lars Steen says:

    Really nice article, I really enjoyed reading it. Keep ‘em coming :)
    Lars

    Reply
  2. Lode says:

    The idea is nice, but it’s a pretty poor example because magic_qoutes_gpc are (finally) deprecated in php5.3

    Please also be adviced that besides the root keys al keys should go through stripslashes as well!

    See the manual: http://www.php.net/manual/en/security.magicquotes.disabling.php

    Reply
    • Hi Lode,

      Thanks for the comment. I should have made it clearer in the post that the code is merely an example for the technique. I used the magic_quotes example because a) it’s the one that came to mind at the time and b) my hosting company has that directive enabled. Unfortunately although magic_quotes are deprecated I can’t see them disappearing any time soon, at least not until every hosting company and server is running PHP 6 (or whatever the next version is going to be called). Personally, I can’t wait for the time when magic_quotes are removed from PHP entirely.

      Thanks for the info about keys and magic_quotes-I wasn’t aware of that. I’m not sure if I’m too worried about that though. I can’t think of a time when I’ve written a POST, GET or Cookie key that contained characters that would need to be escaped by magic_quotes. One could argue that there’s a potential security vulnerability since there’s nothing to stop an attacker submitting arbitrary key => value pairs. I would argue though that it’s the job of the developer to only work with parameters that they’re expecting in the request, filtering these in the strongest ways possible. For me the code in the post gives a good start by removing any slashes before any parameters in the request are filtered.

      Reply
  3. Evert says:

    You can also use the y-combinator as a generic pattern for this.

    Reply
  4. Lode says:

    Even though magic_quotes_* are still enabled on most hosted servers.
    They are easely disabled in .htaccess 1 simple line:
    php_value magic_quotes_gpc Off

    A lot better then filtering them on demand. this will disable them a lot more globally.

    Anyways a nice example of using a reference closure recursion.

    Reply
    • Thanks for that Lode, good tip and definitely better than doing this in the code.

      Reply
  5. eDwaRd C.c. says:

    THANKS, this helped me!!

    Greetings from Monterrey, México =P

    Reply
Trackbacks / Pings
Leave a Reply




XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>