My new best friend - PHP's create_function()

Posted by Felix Geisendörfer, on May 18, 2007 - in PHP & CakePHP » Other

Deprecated post

The authors of this post have marked it as deprecated. This means the information displayed is most likely outdated, inaccurate, boring or a combination of all three.

Policy: We never delete deprecated posts, but they are not listed in our categories or show up in the search anymore.

Comments: You can continue to leave comments on this post, but please consult Google or our search first if you want to get an answer ; ).

Hey folks,

I meant to introduce you to my new best friend for quite a while but didn't get around to do it so far. His name is 'create_function' and he's a really useful co-worker. For those of you who just vaguely know him - don't worry, he's not so much like his ev(a|i)l cousin. Well, that doesn't mean he can't be harmful, but he's more likely to help you instead.

Alright enough of that non-sense ; ). But I really recommend everybody to give this function a closer look. One does not need it very often, but there are things that can be incredibly simplified and made very powerful using create_function. I mostly use it for quick conversion / manipulation functions that I just need temporally, for filtering items or for advanced mappings based on rules. So for those of you who haven't done any of this so far, or used verbose and complex solutions for it, here is a neat little example:

The task is the following, you are the project manager of a big PHP application and want to hire some new programmers. However all the ones on your list kind of sound the same and you don't really feel like reading their long résumés. So in order to speed things up you make yourself a little PHP array with the most important facts about all of them:

php
  1. $programmers = array(
  2.     'Jim' => array(
  3.         'languages' => array('PHP', 'C++', 'JavaScript')
  4.         , 'rate' => 50
  5.     ),
  6.     'John' => array(
  7.         'languages' => array('Java', 'Visual Basic', 'PHP', 'C++')
  8.         , 'rate' => 55
  9.     ),
  10.     'Jack' => array(
  11.         'languages' => array('ActionScript', 'PHP')
  12.         , 'rate' => 45
  13.     ),
  14.     'James' => array(
  15.         'languages' => array('C++', 'C', 'JavaScript', 'Python', 'Ruby', 'PHP', 'ActionScript', 'VB.NET')
  16.         , 'rate' => 75
  17.     ),
  18.     'Jeff' => array(
  19.         'languages' => array('PHP')
  20.         , 'rate' => 25
  21.     ),
  22.     'Joe' => array(
  23.         'languages' => array('Lisp', 'Haskell', 'Python', 'Ruby', 'Scheme', 'Ada')
  24.         , 'rate' => 60
  25.     )
  26. );

Now that you have that list you start to think about what kind of people you want to hire, and come up with the following criterias:

  • The programmer must know PHP
  • The programmer should have no Java background (no offense if you are one, this is just an example ^^)
  • The programmer should not charge too much, less or equal to $50 / hour is ok.
  • The programmer needs to know at least 2 different languages.

So most people (including me until a while ago) would solve this by looping through all $programmers and then nest a long if-else chain in it that will unset() the current programmer if one of the if statements evaluates to true. However, a much more readable and extendable approach is to define the $conditions in an array of short PHP code snippets that return true or false depending whether the condition is met. This way we can implement a neat little 'filter' function that can be used to filter items fairly comfortable from now on like this:

php
  1. function filter($items, $conditions = array()) {
  2.     foreach ($conditions as $name => $condition) {
  3.         $condition = create_function('$item', $condition);
  4.         foreach ($items as $i => $item) {
  5.             if ($condition($item) != true) {
  6.                 unset($items[$i]);
  7.             }
  8.         }
  9.     }
  10.     return $items;
  11. }
  12.  
  13. $conditions = array(
  14.     'knows-php' => 'return in_array("PHP", $item["languages"]);',
  15.     'no-java-background' => 'return !in_array("Java", $item["languages"]);',
  16.     'cheap' => 'return $item["rate"] <= 50;',
  17.     'multi-lingual' => 'return count($item["languages"]) >= 2;',
  18. );
  19.  
  20. $programmers = filter($programmers, $conditions);
  21. echo join(', ', array_keys($programmers)); // Outputs "Jim, Jack"

Of course this is just a very simple example but you should get my point: create_function allows you to create very flexible functions that help you to save many lines of code. Also note that the filter function I showed here just takes code snippets as conditions and takes care of calling create_function for you. While this is neat in certain situations, you get more flexibility if you allow your conditions to be callbacks so you can use new functions created using 'create_function' or existing functions to do your filtering.

Another neat way to use create_function is to create temporary shortcuts to already existing functions with some parameters being pre-populated:

php
  1. $formatDate= create_function('$date = null', 'return date('.$user["User"]["date_format"].', $date)');
  2.  
  3. echo "Hello ".$user['User']['name'].' today is '.$formatDate()."\n\n";
  4. echo "Your most recent emails are:\n";
  5. foreach ($user['Email'] as $email) {
  6.     echo $formatDate($email['date']).' - '.$email['Subject']."\n";
  7. }

Now of course this style of coding is not for the faint of heart and I would not recommend it to everybody / every environment. But if you decide to use it make sure to validate eventual user-enterable data for not containing PHP code to avoid security problems.

Alright, I hope some of you find this useful and I'd be interested in what ways you have discovered to use those lambda style functions in PHP.

-- Felix Geisendörfer aka the_undefined

PS: The filter function from above can be simplified further like this:

php
  1. function filter($items, $conditions = array()) {
  2.     foreach ($conditions as $name => $condition) {
  3.         $items = array_filter($items, create_function('$item', $condition));
  4.     }
  5.     return $items;
  6. }

I just wanted to keep things simple for the example.