Wednesday, September 10, 2008

Beware of References

I got tripped up by the following code:
// This data actually comes from csv file
$data = array(
    array('Area1', null, null),
    array(null, 'Section1', null),
    array(null, null, 'Location1'),
    array('Area2', null, null),
    array(null, 'Section2', null),
    array(null, null, 'Location2')
);
$root = array();
foreach ($data as $row) {
    if ($row[0]) { // Is Area
        $area = array();
        $root[$row[0]] =& $area;
    } elseif ($row[1]) { // Is Section
        $section = array();
        $area[$row[1]] =& $section;
    } elseif ($row[2]) { // Is Location
        $section[] = $row[2];
    }
}
print_r($root); 
Expected result:
Array(
[Area1] => Array(                     
        [Section1] => Array(
                [0] => Location1
            )               
    )
[Area2] => Array(       
        [Section2] => Array(          
                [0] => Location2
            )               
    )
)
Actual result:
Array(
[Area1] => Array(                     
        [Section2] => Array(
                [0] => Location2
            )               
    )
[Area2] => Array(       
        [Section2] => Array(          
                [0] => Location2
            )               
    )
)
So what did I do wrong? To answer this lets look at a simpler example:
$a = array();
$b =& $a;
$a[] = 'hello';
echo implode(' ', $b); // Outputs 'hello'
$a = array('world');
echo implode(' ', $b); // Outputs 'world' 
See I was expecting the last line to output 'hello' because I was thinking references were like C pointers. That is:
void* a = array();
void* b = *a;
So looking up the PHP manual it says:
They are not like C pointers; instead, they are symbol table aliases... References can be thought of as hardlinking in Unix filesystem.

Monday, September 1, 2008

Generate random password

/**
 * Generate a random character array from the list of $possible characters
 *
 * @param array  $possible Array of possible characters
 * @param int    $length   Length of string to generate
 * @return array An array of $length random characters
 */
function randomStringArray($possible, $length) {
    $stringArray = array();
    $possibleCount = count($possible);

    $i = 0;
    while ($i < $length) {
        // pick a random character from the possible ones
        $char = $possible[mt_rand(0, $possibleCount - 1)];

        // we don't want this character if it's already in the password
        if (!in_array($char, $stringArray)) {
            $stringArray[] = $char;
            $i++;
        }
    }

    return $stringArray;
}

/**
 * Generate password that contains 6 letters and 2 numbers
 */
function generatePassword() {
    $letters = array_merge(range('a', 'z'), range('A', 'Z'));
    $numbers = range('0', '9');
    $chars = array_merge(
        randomStringArray($letters, 6), randomStringArray($numbers, 2));
    shuffle($chars);
    return implode('', $chars);
}