PHP Partial Evaluation and Functional Style Programming for Nested Arrays

An example of partial application in PHP, using closures to implement functional-style programming. This technique helps to collapse loops into functions, and shorten code.

This article is based on an older article that has been cleaned up and improved.

I’m doing wordpress programming, and one of the headaches is that they store serialized objects in the database with the update_option() and related functions.

This makes querying some data difficult. Here’s an example of the data structure I was dealing with (it stores sidebar settings).

Array
(
    [wp_inactive_widgets] => Array
        (
        )

    [sidebar-1] => Array
        (
            [0] => search-2
            [1] => recent-posts-2
            [2] => recent-comments-2
            [3] => archives-2
            [4] => categories-2
            [5] => meta-2
        )

)

I’m interested in sidebar-1 because it has an element with the value meta-*.

Here’s the code that returns the name of the sidebar in $the_sidebar:

foreach($sidebars as $key=>$sb) {
    foreach($sb as $widgets) {
        if (0===strpos($widgets, 'meta-')) {
            $the_sidebar = $key;
            break;
        }
    }
}

That code is pretty long.

There’s a technique to use functional programming style code to shorten the above, to this:

$function = F::filter(F::or(F::regexMatch('/meta-\d+/')));
$the_sidebar = array_keys($function($sidebars))[0];

The F class is a small library that generates functions. F::regexMatch takes a regex, and returns a function that returns true on matches.

F::or takes a function, and returns a function that takes an array argument. If the function evaluates to true for any of the values, it returns the entire array.

This is similar to in_array(), but the return values are different. F::or returns the entire array, in_array() returns true. When nothing matches, F::or returns the empty array, and in_array() returns false.

$function = F::or(F::regexMatch('/cat/'));
echo $function(['dog', 'bobcat', 'freebird']);
array(3) {
  [0] =>
  string(3) "dog"
  [1] =>
  string(6) "bobcat"
  [2] =>
  string(8) "freebird"
}

F::filter takes a function, applies it to each value, and gathers all the “truthy” or non-empty and non-null values, and returns them. It’s like “grep”, or a filter.

Stacking the functions like F::filter(F::or… creates a function that goes two levels deep into the nested array structure.

Each function is like a “query” that can be applied to the nested arrays.

Here’s the code for F::or and F::filter:

    /**
     * Returns a function that:
     * Returns the array if the $func evaluates to true for
     * any of the values. Otherwise, returns the empty array.
     */
    static function or($func) {
        return function($a) use ($func) {
            foreach($a as $k=>$v) {
                if ($func($v)) {
                    return $a;
                }
            }
            return [];
        };
    }

    /**
     * Returns a function that:
     * Returns an array of key value pairs where the
     * $func evaluates to true for the value.
     */
    static function filter($func) {
        return function($a) use ($func) {
            $output = [];
            foreach($a as $k=>$v) {
                $r = $func($v);
                if ($r) {
                    $output[$k] = $v;
                }
            }
            return $output;
        };
    }

It’s not that complex, but when the parts are combined, it’s pretty powerful.

There’s a github project for this, php-function-generators, and there are more examples and function generating functions there, but the project’s dormant. If I work on something that stores complex arrays in the WordPress options, I’ll add features. If you need more functions, drop me an email.

Leave a Reply