使用指针遍历和更改多维数组

I've got non-human-readable urls on my site and need to parse urls for each products, categories and brands that I've got. This task led me to a code that needs to traverse multidimensional array and make changes in it (add nodes).

I've got the following array:

$a = array(
    'product' => array(
        'oven' => array(
            'url' => 'http',
            'category' => array(
                'single' => array(
                    'url' => 'http2'
                )
            )
        )
    )
);

I want to walk through it and make some changes in certain conditions, so I need to keep pointer.

My code now is:

$data = array('product' => 'oven', 'category' => 'single');
$pointer = &$a;
foreach ($data as $field => $value) {
    func($field, $value, $pointer);
    print_r($a);
    print_r($pointer);
}



function func($field, $value, &$pointer) {
    if ( isset($pointer[$field][$value]) ) {
        echo 'exists';
        // moving into
        $pointer = &$pointer[$field][$value];
    } else {
        echo 'does not exist';
        $pointer[$field][$value] = array('url' => 'someUrl');
        // moving into
        $pointer = &$pointer[$field][$value];
    }
}

The output shows that 'moving into' lines doesn't work.

exists
Array
(
    [product] => Array
        (
            [oven] => Array
                (
                    [url] => http
                    [category] => Array
                        (
                            [single] => Array
                                (
                                    [url] => http2
                                )

                        )

                )

        )

)
Array
(
    [product] => Array
        (
            [oven] => Array
                (
                    [url] => http
                    [category] => Array
                        (
                            [single] => Array
                                (
                                    [url] => http2
                                )

                        )

                )

        )

)
does not exist
Array
(
    [product] => Array
        (
            [oven] => Array
                (
                    [url] => http
                    [category] => Array
                        (
                            [single] => Array
                                (
                                    [url] => http2
                                )

                        )

                )

        )

    [category] => Array
        (
            [single] => Array
                (
                    [url] => someUrl
                )

        )

)
Array
(
    [product] => Array
        (
            [oven] => Array
                (
                    [url] => http
                    [category] => Array
                        (
                            [single] => Array
                                (
                                    [url] => http2
                                )

                        )

                )

        )

    [category] => Array
        (
            [single] => Array
                (
                    [url] => someUrl
                )

        )

)

The 'moving into' lines doesn't work because you need to use Returning References. In this case you have to use & in both places - to indicate that you want to return by reference, not a copy, and to indicate that reference binding, rather than usual assignment, should be done for $pointer. This will work:

$data = array('product' => 'oven', 'category' => 'single');
$pointer = &$a;

foreach ($data as $field => $value) {
    // returning by reference
    $pointer = &func($field, $value, $pointer);
    print_r($a);
    print_r($pointer);
}

function &func($field, $value, &$pointer) {
    $pointer = &$pointer[$field][$value];

    if (isset($pointer)) {
        echo 'exists';
    } else {
        echo 'does not exist';
        $pointer['url'] = 'someUrl';
    }

    // returning reference
    return $pointer;
}

Note: please remember that there is no pointers in PHP, only references.

Instead of going over $data with a foreach, I would suggest using recursion. It will need less of this reference business, that tends to make code harder to debug:

func($data, $a);

function func($data, &$a) { // the only place where you need a reference now
    if (!is_array($a) || !count($data)) return; // nothing more to do
    list($field, $value) = each($data); // get first data key/value
    if (!isset($a[$field][$value])) {
        echo 'does not exist';
        $a[$field][$value] = array('url' => 'someUrl');
    }
    // recursive call, without first element of $data, and going deeper in $a
    func(array_slice($data, 1), $a[$field][$value]);
}