Skip to content
Advertisement

POST-ing HTMLFormElements as Array Keep DOM Order Accross Browser

I am POST-ing three HTML input elements that has the PHP Array naming convention i.e. name="name[]" (the 3 inputs have different names “A[]”, “B[]” and “C[]”). The input elements are children of <td> elements of which there are anything from 7 to 1001 <td>‘s, meaning for each <td> there is 3 times the input elements i.e. 21 to 3003. The layout is similar to that of a Spreadsheet e.g.

<table>
    <--! ... -->

    <tbody>
        <tr>
            <td>
                <input type="text" name="A[]" placeholder="A1">
                <input type="hidden" name="B[]" value="A1">
                <input type="hidden" name="C[]" value="2021-10-03">
            </td>
            <td>
                <input type="text" name="A[]" placeholder="B1">
                <input type="hidden" name="B[]" value="B1">
                <input type="hidden" name="C[]" value="2021-10-04">
            </td>

            <--! ... -->
        </tr>

        <--! ... -->
    </tbody>

    <--! ... -->

When I POST them I noticed that each input‘ Array keys on the server side conforms to the order the input element appears within the HTMLFormElement.

What I want to know is the order of array keys true accross browser?

I could not find anything in the HTML Spec, but the PHP manual gives the following note when denoting an input name as name[] :

Specifying array keys is optional in HTML. If you do not specify the keys, the array gets filled in the order the elements appear in the form. Our first example will contain keys 0, 1, 2 and 3.

Advertisement

Answer

Thanks for your question, quite interesting, however I have problems to answer it specifically. That is because this depends on implementation and it can (or as we can see at least could) be different across browsers.

For the PHP part, this is encoded in PHPs source code and therefore defined, but that is only one side and my educated guess (have not looked into the sources) is that it depends on the input order.

This is how I understand it: Given the browsers build the form data set from successful controls and then encode it. For the PHP array keys (of which order you’re interested in) with the HTML form (excerpt) example:

  <form method="post">
    ...
            <td>
                <input type="text" name="A[]" placeholder="A1">
                <input type="hidden" name="B[]" value="A1">
                <input type="hidden" name="C[]" value="2021-10-03">
            </td>
            <td>
                <input type="text" name="A[]" placeholder="B1">
                <input type="hidden" name="B[]" value="B1">
                <input type="hidden" name="C[]" value="2021-10-04">
            </td>
     ...
  </form>

I would assume document order of the input elements to build the form data-set from. Problem with that is – IIRC – that in HTML there is no specification of document order. Within the DOM there is but from what I know a browser must not have a DOM. It’s perhaps stupid to not have a DOM browser internally (or some structure with similar semantics) but there is no requirement in the HTML specification to have one: From what I know DOM is Javascript specific and a browser must not support any client side scripting.


NOTE: The HTML living standard supports this interpretation (as of today 2021-10-03). It does not relate to the DOM in form data encoding, instead it has tree order which has similar semantics to DOM document order. And it is the tree from which the list of input elements is obtained from that is used to identify the successful controls for building and encoding the forms’ data. All lists have ordered sequence (first to last, add appends), are initialized empty and are traversed from start to end. So it is that this sequence is retained including in the encoding serializers’ encoding which becomes the input parsed by the PHP SAPI.

This confirms the observation that the input order is always the same. If not (the part you’re curious about), you could treat it as a flaw. Your standard form input validation should handle that case thought anyway, as any form data in the request could be composed elsewhere (e.g. different form, there is no same-origin policy for form submits) and therefore needs validation.


Considering there is DOM support and also used internally when creating the forms data-set and considering that the successful inputs are processed in document order when building the data-set, the following data-set in this top-down order (one control-name/current-value pair per line) is created:

A[]: A1
B[]: A1
C[]: 2021-10-03
A[]: B1
B[]: B1
C[]: 2021-10-04

If then the result of the encoding (not further specified for the order in the HTML 4.01 specs) of this data-set. Again it would be kind of counter-intuitive to implement it different to the processing order, but if in processing order this then defines the input-order as the PHP SAPI receives this data (HTTP request body) and forms the $_POST array out of it.

What then follows is the following (at least how I explain it to myself, not verified in the sources):

  1. The $_POST array is initialized empty.
  2. The first control-name is A[].
  3. PHP “realizes” the array access brackets [] and that no index or key is given within those brackets.
  4. Therefore the array is populated similar like in PHP code: $_POST['A'][] = 'A1'; (compare: array_push()).
  5. These steps 2. – 4. are done for each name/value pair.

Now this does not specifically answer your question. However if you’re asking because you’d like to set the indexes in stone (apart from any order), it should be possible to provide those within the HTML form control-names and it perhaps is what you’re looking for:

  <form method="post">
    ...
            <td>
                <input type="text" name="A[0]" placeholder="A1">
                <input type="hidden" name="B[0]" value="A1">
                <input type="hidden" name="C[0]" value="2021-10-03">
            </td>
            <td>
                <input type="text" name="A[1]" placeholder="B1">
                <input type="hidden" name="B[1]" value="B1">
                <input type="hidden" name="C[1]" value="2021-10-04">
            </td>
     ...
  </form>

Then at least on the level of accessing via $_POST the keys are defined as the control-names need to be preserved by the HTML standard. And they have the names you expect, as you can re-create the order via the numerical index.


If you’re concerned about rows, all you would need to do is to transpose those “named” parts (as PHP is handling the [...] array part) to their indexes:

// required input
$input = $_POST;

// define structure of result data
$fields = ['A' => null, 'B' => null, 'C' => null];
$count = 2;

// initialize the result
$result = array_fill(0, $count, $fields);

// map $input to $result
foreach (array_keys($result) as $index) {
    foreach ($fields as $name => $default) {
        $result[$index][$name] = $_POST[$name][$index] ?? $result[$index][$name];
    }
}

Such a part is also a good place to verify the input is complete. The resulting $result (pick the name that fits best in your case) are also better to map onto database fields (e.g. use A, B, C, … within the form when you generate the inputs from a structure and then map them back with the same structure onto database (or your applications internal entity/property names).

This is especially the case if you realized that the order was suddenly brittle – you can make the form creation/submission/data-handling a more distinct transaction even HTTP is stateless. At least more distinct on the data level.


Which brings me to another improvement/variant when writing the HTML of the form to have the data in the processing order already within $_POST. It won’t spare the needed validation steps, however this can be done within PHP:

            <td>
                <input type="text" name="set[0][D]" placeholder="A1">
                <input type="hidden" name="set[0][E]" value="A1">
                <input type="hidden" name="set[0][F]" value="2021-10-05">
            </td>
            <td>
                <input type="text" name="set[1][D]" placeholder="B1">
                <input type="hidden" name="set[1][E]" value="B1">
                <input type="hidden" name="set[1][F]" value="2021-10-06">
            </td>

Then $_POST['set'] already contains the form-data in an ordered array and the transposition is not necessary and to map the order, ksort may suffice already:

ksort($_POST['set']);

So this can help with the immediate processing for the form data (the numeric indexes can be left out as well if the input elements are in the correct order, no ksort() necessary then, PHP will take care of that).

It would be even possible to leave the first name out but it might not be useful because of other form fields not relating to the set:

            <td>
                <input type="text" name="0[G]" placeholder="A1">
                <input type="hidden" name="0[H]" value="A1">
                <input type="hidden" name="0[I]" value="2021-10-07">
            </td>
            <td>
                <input type="text" name="1[G]" placeholder="B1">
                <input type="hidden" name="1[H]" value="B1">
                <input type="hidden" name="1[I]" value="2021-10-08">
            </td>

Makes $_POST a numerically indexed (in undefined order) array of arrays.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement