Programming - - By Harry Fuecks

Lazy PHP: Part 2

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.

Sponsors