2
\$\begingroup\$

Client

From a POST request I get a mixed structure of an array and JSON. To handle multiple type of elements I am using var_dump to get the passes. For $_POST I get this:

array(2) {
  ["json_data"]=>
  string(677) "[{"firstname":""},{"lastname":""},{"email":""},{"countryCode":""},{"phone":""},{"i_signup_password":""},{"i_signup_password_rep":""},{"email":""},{"i_signin_password":""},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"}]",
  ["other_data"]=>
  string(11) "Other_Data"
}

Server

In PHP server side I am executing a function that reduce this $_POST to this array:

array(2) {
  ["JsonData"]=>
  array(10) {
    ["firstname"]=>
    string(0) ""
    ["lastname"]=>
    string(0) ""
    ["email"]=>
    string(0) ""
    ["countryCode"]=>
    string(0) ""
    ["phone"]=>
    string(0) ""
    ["i_signup_password"]=>
    string(0) ""
    ["i_signup_password_rep"]=>
    string(0) ""
    ["i_signin_password"]=>
    string(0) ""
    ["form"]=>
    string(11) "d-sys-login"
    ["process"]=>
    string(8) "e-signin"
  }
  ["otherdata"]=>
  string(9) "otherdata"
}

Code

You can see this runs online.

Can someone help me simplify or improve the script?

  • Application of best practices and design pattern usage
  • Potential security issues
  • Performance
  • Correctness in unanticipated cases

The script used to meet this output is this:

<?php
    function buildVirtualData($data)
    {
        if (is_array($data)) { //check if is an array Walk trough to rebuild
            $temp = [];
            foreach ($data as $key => $value) {
                $temp[$key] = buildVirtualData($value);
            }
            return reduArray($temp);
        } elseif (valJson($data)) { //check if is an JSON, Walk through to rebuild as an array
            $json_obj = json_decode($data, true);
            foreach ($json_obj as $key1 => $json_sub_obj) {
                foreach ($json_sub_obj as $key2 => $value2) {
                    if (is_array($value2)) {
                        $temp = [];
                        foreach ($value2 as $keyof => $valueof) {
                            $temp[$keyof] = buildVirtualData($valueof);
                        }
                        $json_obj[$key1][$key2] = $temp;
                    } else {
                        if ('true' === $value2 || true === $value2) {
                            $json_obj[$key1][$key2] = true;
                        } elseif ('false' === $value2 || false === $value2) {
                            $json_obj[$key1][$key2] = false;
                        } else {
                            $json_obj[$key1][$key2] = $value2;
                        }
                    }
                }
                return reduArray($json_obj);
            }
        } else { // if it is not an array or a JSON; evaluate the type if it is text and meets possible boolean values
            if ('true' === $data || true === $data) {
                $data = true;
            } elseif ('false' === $data || false === $data) {
                $data = false;
            }
            return $data;
        }
    }
    
    function valJson($var) //JSON Validator
    {
        if (!is_array($var)) {
            return ((json_decode($var) != null) &&
                (is_object(json_decode($var)) || is_array(json_decode($var)))) ? true : false;
        } else {
            return false;
        }
    }
    function reduArray($array) //array Reductor
    {
        $result = $array;
        if (is_array($array)) {
            $check = true;
            foreach ($array as $key => $value) {
                if (!is_array($value)) {
                    $check = false;
                    break;
                }
            }
            if ($check) {
                $result = array_reduce($array, 'array_merge', []);
            }
        }
        return $result;
    }
    //Example Data
    $_POST=[];
    $_POST['JsonData']='[{"firstname":""},{"lastname":""},{"email":""},{"countryCode":""},{"phone":""},{"i_signup_password":""},{"i_signup_password_rep":""},{"email":""},{"i_signin_password":""},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"}]';
    $_POST['otherdata']='otherdata';
    //Execution of Function hover $_POST Variable.
    $_POST=buildVirtualData($_POST);
    $_POST=reduArray($_POST);
    echo var_dump($_POST);

Examples and Explanation:

Main function is buildVirtualData

This function tries to parse the $_POST variable; and seeks to reduce it; eliminating the excess of levels in the resulting arrays.

if you check the examples for this variable (arrays plus JSON) 2 arrays + JSON String:

    $_POST=[];
    $_POST['JsonData']='[{"firstname":""},{"lastname":""},{"email":""},{"countryCode":""},{"phone":""},{"i_signup_password":""},{"i_signup_password_rep":""},{"email":""},{"i_signin_password":""},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"}]';
    $_POST['otherdata']='otherdata';

the output, Check that this include 2 Main array :: JsonDataand otherdata:

array(2) {
  ["JsonData"]=>
  array(10) {
    ["firstname"]=>
    string(0) ""
    ["lastname"]=>
    string(0) ""
    ["email"]=>
    string(0) ""
    ["countryCode"]=>
    string(0) ""
    ["phone"]=>
    string(0) ""
    ["i_signup_password"]=>
    string(0) ""
    ["i_signup_password_rep"]=>
    string(0) ""
    ["i_signin_password"]=>
    string(0) ""
    ["form"]=>
    string(11) "d-sys-login"
    ["process"]=>
    string(8) "e-signin"
  }
  ["otherdata"]=>
  string(9) "otherdata"
}

While for this other variable (Only one array with JSON String):

    $_POST=[];
    $_POST['JsonData']='[{"firstname":""},{"lastname":""},{"email":""},{"countryCode":""},{"phone":""},{"i_signup_password":""},{"i_signup_password_rep":""},{"email":""},{"i_signin_password":""},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"},{"form":"d-sys-login"},{"process":"e-signin"}]';

the result is one less array level in this case (removing the JsonData index which is unnecessary in this scope):

array(10) {
  ["firstname"]=>
  string(0) ""
  ["lastname"]=>
  string(0) ""
  ["email"]=>
  string(0) ""
  ["countryCode"]=>
  string(0) ""
  ["phone"]=>
  string(0) ""
  ["i_signup_password"]=>
  string(0) ""
  ["i_signup_password_rep"]=>
  string(0) ""
  ["i_signin_password"]=>
  string(0) ""
  ["form"]=>
  string(11) "d-sys-login"
  ["process"]=>
  string(8) "e-signin"
}

the other 2 Function used:

valJson is to validate if value is an JSON String and can be used as Object or Array.

reduArray is the function that performs a reduction of each Array.

Why a function that does all this?

I don't have control of the javascript code, I can only offer solutions in php code; What I can do is verify each scenario of what the server receives and this is the slightly more complex script I have.

in fact the JsonData index is not relevant take care of this: according to the documentation, everything that comes inside JsonData (the String Json) will be the inputs filled in a form and their value associated, so in reality the string is more important; and that the indexes and string values become indexes of $_POST ...

for example: there is a form with multiple checkboxes, they can be around 600 in total! Let's say they are to manage the permissions of the process screen and each process screen has 10 possible permission buttons, which are defined according to whether the checkbox is checked or not; There is no way that the server supports sending 600+ inputs in a single request without manipulating the server...

so the ingenious javascript programmer decided to put the 600 input and their filled values (formatted as string) inside a JSON variable (JsonData index / array) to be able to pass them to the server. I assure you, I do not share this idea; but need work around this.

These results are correct, but I am looking to improve the code based on the objectives of this community.

\$\endgroup\$
  • 1
    \$\begingroup\$ So how convoluted might your incoming data be? Do you actually need to have recursion while converting arrays of arrays to associative arrays? or will your data only have a maximum depth of 2? Knowing the business requirements will go a long way in lightening the load of the code logic. \$\endgroup\$ – mickmackusa Aug 5 at 21:07
  • 1
    \$\begingroup\$ I need to be more sure about your incoming data and your requirements. Will this simple handling suffice? 3v4l.org/4ms0W If so, I can post an educational explanation. \$\endgroup\$ – mickmackusa Aug 5 at 21:20
  • \$\begingroup\$ for your first comment: I don't know how deep the arrays are, even knowing this point as a dynamic part I resorted to recursion for ... this is possible for arrays, I don't know if JSON string can be nested. \$\endgroup\$ – walter nunez Aug 6 at 2:45
  • 1
    \$\begingroup\$ "the last 2 array fixes lost the index" ... no they didn't, I just wasn't printing them. See this change: 3v4l.org/g5TVf Perhaps provide a battery of sample inputs that isolate all of the behaviours of your script. Some booleans, some mixed data, some multi-dimensional data -- then we can run your script and see what output is expected. \$\endgroup\$ – mickmackusa Aug 6 at 2:51
  • 1
    \$\begingroup\$ Then you are developing a "lossy" data flattener. You are asking for the script to kill an associative key -- which should be, by definition, "valuable data". I do not have time to discuss right now, please edit your question. \$\endgroup\$ – mickmackusa Aug 6 at 3:02
1
\$\begingroup\$

The way I see it, virtually all of that over-engineered convolution can be scrapped.

You only need to take special action when you process the JsonData value.

It needs to be decoded, flattened, and merged with the other non-encoded data.

Simply use something like this: (Demo)

$post = [];
foreach ($_POST as $key => $value) {
    if ($key !== 'JsonData') {
        $post[$key] = $value;
    } else {
        $post = array_merge($post, ...json_decode($value, true));
    }
}
var_export($post);

If you are concerned about redundant subarray keys (that were previously json encoded), then that is more of a problem with the incoming data rather than a problem with this process (my script provides the same handling as in your posted script).

Now that you can see how simply the data can be unpacked, you won't need to bag so hard on the other developer.


I don't think I endorse the practice of key-ignorant json decoding true/false strings to booleans because it will potentially convert strings that shouldn't be converted.

Amyhow, here's one way of handling the conditional boolean conversion (Demo):

function mergeAndBoolify($posted) {
    $result = [];
    foreach ($posted as $key1 => $value1) {
        if ($key1 === 'JsonData') {
            foreach (json_decode($value1, true) as $item) {
                foreach ($item as $key2 => $value2) {
                    if (in_array($value2, ['true', 'false'])) {
                        $value2 = json_decode($value2);
                    }
                    $result[$key2] = $value2;
                }
            }
        } else {
            $result[$key1] = $value1;
        }
    }
    return $result;
}

$_POST = [
    'JsonData' => '[{"firstname":"false"},{"lastname":"true"},{"email":""}]',
    'otherdata' => 'otherdata'
];

var_export(mergeAndBoolify($_POST));

Output:

array (
  'firstname' => false,
  'lastname' => true,
  'email' => '',
  'otherdata' => 'otherdata',
)
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ ok but how i can get it with every value evaluated is a bolean (true, false), including string like 'true', 'false'... \$\endgroup\$ – walter nunez Aug 8 at 8:54
  • 1
    \$\begingroup\$ Please provide sample data for this case and your exact required result. I can only understand the variability of the data based on what you provide. \$\endgroup\$ – mickmackusa Aug 8 at 12:44
  • \$\begingroup\$ @walter, I have updated my answer to covert boolean strings within the jsondata to boolean type. \$\endgroup\$ – mickmackusa Aug 9 at 4:10
  • \$\begingroup\$ thank ypur for the great help. \$\endgroup\$ – walter nunez Aug 10 at 21:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.