Thursday, January 27, 2011

PHP Closures - a practical example - the recursive way

Something fresh and new at PHP are Closures: anonymous functions assigned to variables. Most common usage for Closures is the handling of events as Callback functions, because you can easily pass them as parameters.

In my practical example I created a function that converts any object to a simple XML string, getting the node names from the property names of the objects. Since the object structure is a tree, nothing better than a recursive function to get the job done. I use a Closure as a recursive function, which makes the code much simpler, cleaner and elegant.



/**
 *
 * @param StdClass $obj
 * @param string $baseName
 * @return string
 */
function object2xml($obj, $baseName) {
    $ret = '<' . $baseName . ">\n";

    $recursiveWalk = function( $baseObject, $level ) use (&$recursiveWalk) {
        $recRet = '';
        foreach($baseObject as $k => $child) {
            $next = $recursiveWalk($child, $level + 1);
            if ($next === '') {
                $recRet .= str_repeat(' ', $level * 4) . '<' . $k . "/>\n";
            } else {
                $recRet .= str_repeat(' ', $level * 4) . '<' . $k . ">\n" . $next;
                $recRet .= str_repeat(' ', $level * 4) . ' . $k . ">\n";
            }
            
        }
        return $recRet;
    };
    $ret .= $recursiveWalk($obj, 1);


    return $ret . " . $baseName . ">\n";
}
?>
The example usage for this code follows:



$obj = (object) array(
    'level1_1' => (object) array(
        'level2_1' => (object) array(
            'level3_1' => (object) array(

            ),
            'level3_2' => (object) array(

            )
        ),
        'level2_2' => (object) array(

        )
    )
);
print object2xml($obj, 'root')
?>

 Notice the "use (&$recursiveWalk)"! To be able to call the function inside itself you need to pass it by reference to the Closure.

No comments: