Friday, January 28, 2011

Flatr - Example of references inside Closures in PHP

Last post I wrote a small example showing how to use Closures in PHP, the recursive way. Today I'll show you how to use a paramater passed by reference to convert a complex object structure in a flat array, with elegance and simplicity.

Without delay here's the Flatr function:

// Example structure
$obj = new stdClass();
$obj->prop1 = 'hello';
$obj->prop2 = new stdClass();
$obj->prop2->prop2 = 'there';
$obj->prop2->prop3 = 'world!';
/**
 *
 * @param stdClass $obj
 * @return array
 */
function flatr($obj) {
    $ret = array();

    $recursiveWalk = function( $baseObject ) use (&$recursiveWalk, &$ret) {
        if (is_object($baseObject)) {
            foreach($baseObject as $propValue) $recursiveWalk($propValue);
        } else {
            $ret[] = $baseObject;
        }
    };
    $recursiveWalk($obj);

    return $ret;
}
var_dump(flatr($obj));


This will output:

array(3) { [0]=> string(5) "hello" [1]=> string(5) "there" [2]=> string(6) "world!" }

This little function is not ready to handle arrays inside the object structure. But the required changes are not hard to make.

Next post I'll demonstrate a practical usage for this function.

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.