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]);
}