0

I need to build complex multilevel array in callback only, one atomic update per call.

The reason for it: callback is called many times from iterative parser. Finally it should build deserialized PHP-array of binary format being parsed.

Here is runnable code:

const ACTION_VALUE = 1;
const ACTION_ENTER = 2;
const ACTION_LEAVE = 3;

function callback($action, $value, &$param)
{
   switch ($action) 
   {
    case ACTION_ENTER:
        $param['parent'][] = &$param['current'];
        $param['current'][] = [];

        end($param['current']);                
        $param['current'] = &$param['current'][key($param['current'])];
    break;

    case ACTION_LEAVE:
        unset($param['current']);
        $param['current'] = array_pop($param['parent']);
        end($param['current']);
    break;

    case ACTION_VALUE:
        $param['current'][] = $value;
    break;
   }
}

// prepare container
$arr = [];
$arr['data'] = [];
$arr['current'] = &$arr['data'];
$arr['parent'] = [];

// callback invocations
callback(ACTION_VALUE, 1, $arr);
callback(ACTION_VALUE, 2, $arr);
callback(ACTION_ENTER, 0, $arr);
callback(ACTION_VALUE, 10, $arr);
callback(ACTION_VALUE, 11, $arr);
callback(ACTION_LEAVE, 0, $arr);
callback(ACTION_VALUE, 3, $arr);
callback(ACTION_VALUE, 4, $arr);

// now see result
var_dump(json_encode($arr['data']));

Try it here

Above example prints:

[1,2,[10,11]], but should [1,2,[10,11],3,4].

Update: Multilevel means arbitrary arrays of random depth.

Update: The problem was with array_pop(), see below accepted answer for fixed version.

2
  • Could you try entering and leaving as a pointer? And on leave, add it to data and then point the pointer back to the main data? Commented Sep 28, 2018 at 0:38
  • If you mean, pass different "contexts" to callback? No, I can't since callback is called by parser, And parser knows nothing about how callback is used... and it just passthru &$param for all callback invocation, knows nothing about what &$param is. Commented Sep 28, 2018 at 0:49

2 Answers 2

2

You could do something like this, it definitely simplifies what you are trying to do. It just has different base array values, but it is similar enough, so I think it should work for your application of it.

Changes made: Restructured the array and used a addTo string as 'pointer'

const ACTION_VALUE = 1;
const ACTION_ENTER = 2;
const ACTION_LEAVE = 3;

// prepare container
$arr = [];
$arr['data'] = [];
$arr['addTo'] = 'data';
$arr['temp'] = [];

function callback($action, $value, &$param)
{
    switch ($action)
    {
        case ACTION_ENTER:
            $param['addTo'] = 'temp';
            break;

        case ACTION_LEAVE:
            $param['addTo'] = 'data';
            $param['data'][] = $param['temp'];
            $param['temp'] = [];
            break;

        case ACTION_VALUE:
            $param[$param['addTo']][] = $value;
            break;
    }
}



// callback invocations
callback(ACTION_VALUE, 1, $arr);
callback(ACTION_VALUE, 2, $arr);
callback(ACTION_ENTER, 0, $arr);
callback(ACTION_VALUE, 10, $arr);
callback(ACTION_VALUE, 11, $arr);
callback(ACTION_LEAVE, 0, $arr);
callback(ACTION_VALUE, 3, $arr);
callback(ACTION_VALUE, 4, $arr);

// now see result
var_dump(json_encode($arr['data']));
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, but unfortunately this approach do not work for 3+ nesting level. I can't build arbitrary arrays this way.
1

I think the problem is array_pop which doesn't return actual reference to the last element. This one works:

<?php

const ACTION_VALUE = 1;
const ACTION_ENTER = 2;
const ACTION_LEAVE = 3;

function callback($action, $value, &$param)
{
    switch ($action) {
        case ACTION_ENTER:
            $param['parent'][] = &$param['current'];
            $param['current'][] = [];

            end($param['current']);
            $param['current'] = &$param['current'][key($param['current'])];
            break;

        case ACTION_LEAVE:
            unset($param['current']);
            end($param['parent']);
            $param['current'] = &$param['parent'][key($param['parent'])];
            unset($param['parent'][key($param['parent'])]);
            end($param['current']);
            break;

        case ACTION_VALUE:
            $param['current'][] = $value;
            break;
    }
}

// prepare container
$arr = [];
$arr['data'] = [];
$arr['current'] = &$arr['data'];
$arr['parent'] = [];

// callback invocations
callback(ACTION_VALUE, 1, $arr);
callback(ACTION_VALUE, 2, $arr);
callback(ACTION_ENTER, 0, $arr);
callback(ACTION_VALUE, 10, $arr);
callback(ACTION_VALUE, 11, $arr);
callback(ACTION_ENTER, 0, $arr);
callback(ACTION_VALUE, 40, $arr);
callback(ACTION_VALUE, 41, $arr);
callback(ACTION_LEAVE, 0, $arr);
callback(ACTION_LEAVE, 0, $arr);
callback(ACTION_VALUE, 3, $arr);
callback(ACTION_VALUE, 4, $arr);

// now see result
var_dump(json_encode($arr['data']));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.