Lazy PHP: Part 2

Tweet

Following on from Lazy PHP: Part 1, it’s time to get lazy again, with some Lazy Evaluation.

Variable Functions
A lesser known feature of PHP is it’s ability to put a function name in a variable and call the function via the variable, as explained here in the manual.

For example, using the strtolower() function;


$function = 'strtolower';

$string = 'HELLO WORLD!';

echo $function($string); // displays 'hello world!'

Nice but what’s the point? Well recently I needed a native PHP implementation of the in built array_change_key_case() function, for PHP versions below 4.2.0 (which is when it became available). Thanks to the first user submitted comment from 05-Feb-2004, didn’t even need to engage my brain. Had this straight away;


function array_change_key_case($array, $changeCase = CASE_LOWER) {
	$return = array();
	foreach($array as $key => $value) {
		switch($changeCase) {
			case CASE_LOWER:
				$return[strtolower($key)] = $value;
			break;
			case CASE_UPPER:
			default:
				$return[strtoupper($key)] = $value;
			break;
		}
	}
	return $return;
}

[small]Note the use of the $return array is because the in built implementation of array_change_key_case() eliminates duplicate keys.[/small]

But there’s a performance issue here. For each element in the array, the switch condition has to evaluated. For a large array, that could become a significant overhead. What I want is to evaluate the condition once and only once.

Here’s where a variable function can help;


function array_change_key_case($array, $changeCase = CASE_LOWER) {
	switch($changeCase) {
		case CASE_LOWER:
			$caseFunc = 'strtolower';
		break;
		case CASE_UPPER:
                default:
			$caseFunc = 'strtoupper';
		break;
	}
	$return = array();
	foreach($array as $key => $value) {
		$return[$caseFunc($key)] = $value;
	}
	return $return;
}

By placing the function name I want to use (strtolower() or strtoupper()) in a variable, before I start looping through the array, the condition is only evaluated once. For a small array, the performance benefit is insignificant but when you’re handling alot of data you might start noticing the difference…

Lambda Functions
Now being able to place just a function name in a variable is nice but life get’s even more interesting when you look at create_function(), a PHP function for creating other functions!

In essence, it allows you to create a function and store it in a variable, without having to give the function a name.

Here’s a simple example;


$lambda = create_function('$str', 'return strtolower($str);');

$string = 'HELLO WORLD!';

$string = $lambda($string);

echo $string; // Displays 'hello world!'

What the first line is actually saying to PHP is create a function something like this;


function "anonymous"($str) {
    return strtolower($str);
}

…but without giving the function a name (I used “anonymous” for sake of example).

You can also use references with create_function() e.g.;


$lambda = create_function('& $str', '$str = strtolower($str);');

$string = 'HELLO WORLD!';

$lambda($string);

echo $string; // Displays 'hello world!'

The first line, if you declared it normally as a function, would look like this;


function "anonymous"(& $str) {
    $str = strtolower($str);
}

Where this comes in handy is, again, when processing large sets of data.

Using a similar, hypothetical example to the one above, let’s say you need a function for calculating sales tax (VAT) from your online shop (nRonOnline.com). Let’s say VAT is set at 17% so this needs to be added to the price of items you are selling.

Being a sharky individual, you spot a chance for some extra profit with some dubious fiddling of numbers. For customers you’ll calculate tax and round up to the nearest whole number. But when it comes to reporting to the tax man, you’ll round down the figure that includes tax (don’t try this at home!).

At first attempt, here’s a function you write to solve this;


function calcTax($array, $round = ROUND_DOWN) {
	foreach ( $array as $key => $value ) {
		switch ( $round ) {
			case ROUND_UP:
				$array[$key] = ceil($number * 1.17);
			break;
			case ROUND_DOWN:
			default:
				$array[$key] = floor($number * 1.17);
			break;
		}
	}
	return $array;
}

The problem again though is performance. As your shop’s been selling like crazy, you’re dealing with some very big arrays and it’s taking ages.

Instead of using that switch condition inside the loop, you can use a a lambda function and evaluation the condition once at the start;


function calcTax($array, $round = ROUND_DOWN) {
    switch ( $round ) {
        case ROUND_UP:
            $func = create_function('$number', 'return ceil($number * 1.17);');
        break;
        case ROUND_DOWN:
        default:
            $func = create_function('$number', 'return floor($number * 1.17);');
        break;
    }
    foreach ( $array as $key => $value ) {
        $array[$key] = $func($value);
    }

    return $array;
}

If you want to know more about lambda functions, the best person to ask is your nearest Python programmer. For example Mark Pilgrim does a great job explaining lambda functions in Dive into Python.

Anyway, enough Lazy PHP for today.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Keith

    Is it just me or do these examples seem pointless. The main point the article made was, don’t put code in a loop when it can be placed outisde of it. I like the feature of being able to create a function name as a variable, but this article didn’t seem to hit that point very well.

  • Raven

    Thanks for the article. Just one comment on your solution for the builtin function based on the version. Here is another way to address that specific issue.

    if (!function_exists(array_change_key_case)) {
    function array_change_key_case() {
    }
    }

    That way you are always safe in including it in all scripts.

  • http://www.casualcode.com eXplosive

    I make use of variables functions all the time. I never tried anything with “create_function” though.

  • Jordan

    You forgot to mention the most useful application of create_function(): callbacks. When using functions like array_filter() that call for a callback function, you can just drop in a create_function() instead of writing a complete function. Supremely useful.

  • Ren

    I’ve recently started using experimenting with variable functions to replace large switches. (In the order of the 100s of cases).
    It seems PHP switch() { } peforms as O(N) (where N is the number of cases).

    Whereas using an array of function names (or return values from create_function() ) $jumpTable[$i](); gives O(1).

    The problem seems to be that PHP switch does a basic linear search through each case statement to find the appropriate code to execute.

  • http://www.jeroenmulder.com/ JMulder

    I knew variable functions were possible, but I never looked at using it like that. I would have probably been stuck with the lesser performing function, instead of the revision.

    Thanks, keep them coming :)

  • http://www.phppatterns.com HarryF

    [QUOTE=Anonymous]Is it just me or do these examples seem pointless. The main point the article made was, don’t put code in a loop when it can be placed outisde of it. I like the feature of being able to create a function name as a variable, but this article didn’t seem to hit that point very well.[/QUOTE]

    Was avoiding a discussion of a computer science and, in fact, although I suggested this was lazy evalution at the start, on reflection it’s actually lazy initialization – delaying defining the function until it’s actually needed and the initializing code has had a chance to determine what’s required.

    You’re right that using two similar examples based on loops doesn’t explore the theory – that just happened to be the example in mind at that moment. Will look at another forms of laziness in later blogs. Just an ongoing notepad…

    Continuing briefly on the idea of lazy initialization, here’s another situation that can crop up in PHP – you need to access the results of a DB query more than once but where, and how many times depends on the particular page a use is requesting.

    Avoiding objects and classes for a moment, a simple approach using a function might be;


    function & getLatestArticles (& $dbConn) {
    static $articles = NULL;

    // Initialize $articles if it's still NULL
    if ( !$articles ) {
    $sql = "SELECT * FROM articles ORDER BY title LIMIT 0,10";
    $articles = mysql_query($sql,$dbConn);
    }

    return $articles;
    }

    The $dbConn variable contains the resource connecting to the MySQL server. Putting the result resource in a static variable means I can call this function as many times as I like but the SQL query itself is only executed once.

  • xpfusrqwkr

    Hello! Good Site! Thanks you! viuirjjbpzuzq