0

I'm implementing a multi purpose html form posting code in PHP having able to post object graph structures.

So I created some naming rules and based on them, I parse indexes in the server side.

One of those rules is something like this:

<input name="myModels[children[0]]" type="text" />

Of course above naming is a little shortened to make it easy to get to the point.

That naming is working perfectly when I retrieved them from

$_POST

but when I use it in file inputs, they never appear in $_FILE global array and

print_r($_FILES);

is empty

According to file upload php manual

PHP supports HTML array feature even with files.

So why is that? Why that type of naming ([] inside []) works for POSTED data but does not for file data.

Please note that its not a problem with uploading file itself.
Uploading is working grate even with arrays. Even with arrays with key indexes.

The one I'm trying to figure out is:

<form action="/Test/uploadTest.php" method="post" enctype="multipart/form-data" >
    File1 <input name="myModels[postedFiles[0]]" type="file" /></br>
    File2 <input name="myModels[postedFiles[1]]" type="file" /></br>
    <input type="submit" value="post" />
</form> 

And at the server in uploadTest.php

print_r($_FILES);

Is empty.

My PHP version is a little old and it is PHP 5.3.6-13. Do you think its something about my PHP version or its some sort of a bug even in newer versions of PHP?

Thanks in advance.

Solution: Both the answers provided by @tozer83 and @alvaro were very helpful. Now that it turns out multipart forms are some sort of different in handling non scalar indexes and trying to get advantages of html arrays with file inputs in order to create an expression for creating Complex Object Graphs just like the other frameworks in other languages is not going to work in PHP. One should ignore html arrays or use other literals like parentheses to work around this. It is sort of hard to decide which ones check mark should be clicked! Considering this high rank that @alvoro already got, I decided to choose @tozer83's answer because he answered first and gave a solution. But @alvaro's answer was detailed and very helpful too. So I +1 his answer.

4
  • 2
    I can't figure out what output you expect. Do you mean myModels[postedFiles][0] by chance? Commented Jul 2, 2015 at 12:30
  • Here is the deal, I created above from and selected two files to upload then hit the submit. Now in the server side I want my files! but like I said, $_FILES is empty. Is not that weird? Why that naming is work for posted data? When I use myModels[children[0]] for a input text, it can be retrieved in $_POST by why not for $_FILES? AM I clear now? Commented Jul 2, 2015 at 12:48
  • And BTW I don't mean myModels[postedFiles][0] Commented Jul 2, 2015 at 12:49
  • All I'm suggesting is based upon this quote: "PHP supports HTML array feature even with files". If that naming is working for POSTED data, then I expect it to work with files too. I've written tons of codes based on this assumption that this naming would work for files too! Commented Jul 2, 2015 at 12:54

2 Answers 2

2

First of all (just to set things clear) PHP does not allow non scalar values as array keys. In fact, only strings and integers can be used. Thus there's no language structure that can hold stuff like myModels[postedFiles[0]]. And the documentation about HTML arrays doesn't really state what happens when unparseable field names come in.

What happens when we push it to the limit? Apparently, behaviour is different depending on what superglobal we talk about. In $_POST, PHP makes its best to create the array:

<input name="good[array][0]" type="text">
<input name="myModels[children[0]]" type="text">
array(2) {
  ["good"]=>
  array(1) {
    ["array"]=>
    array(1) {
      [0]=>
      string(0) ""
    }
  }
  ["myModels"]=>
  array(1) {
    ["children[0"]=>
    string(0) ""
  }
}

In $_FILES, however, invalid fields get omitted from the array:

<input name="good[file][0]" type="file">
<input name="myModels[postedFiles[0]]" type="file">
array(1) {
  ["good"]=>
  array(5) {
    ["name"]=>
    array(1) {
      ["file"]=>
      array(1) {
        [0]=>
        string(0) ""
      }
    }
    ["type"]=>
    array(1) {
      ["file"]=>
      array(1) {
        [0]=>
        string(0) ""
      }
    }
    ["tmp_name"]=>
    array(1) {
      ["file"]=>
      array(1) {
        [0]=>
        string(0) ""
      }
    }
    ["error"]=>
    array(1) {
      ["file"]=>
      array(1) {
        [0]=>
        int(4)
      }
    }
    ["size"]=>
    array(1) {
      ["file"]=>
      array(1) {
        [0]=>
        int(0)
      }
    }
  }
}

Since we're trying to get a structure that's not physically possible and the documentation doesn't explicitly say what should happen we're in the territory of undefined behaviour: we can neither complaint nor rely on it.

It's also worth nothing that regular post forms allow us to write our own parser:

var_dump(file_get_contents('php://input'));
string(111) "good%5Barray%5D%5B0%5D=&myModels%5Bchildren%5B0%5D%5D=&good%5Bfile%5D%5B0%5D=&myModels%5BpostedFiles%5B0%5D%5D="

... but not multipart/form-data (aka files) ones.

Why this blatant difference? My guess is that it's due to the particular structure of the $_FILES superglobal (IMHO, an error design that's too late to fix). When using simple names, you get the five automatic keys on top of each leaf:

Array
(
    [one] => Array
        (
            [name] => 
            [type] => 
            [tmp_name] => 
            [error] => 4
            [size] => 0
        )

    [two] => Array
        (
            [name] => 
            [type] => 
            [tmp_name] => 
            [error] => 4
            [size] => 0
        )
)

As soon as you use square brackets, structure changes entirely:

Array
(
    [one] => Array
        (
            [name] => Array
                (
                    [0] => 
                )

            [type] => Array
                (
                    [0] => 
                )

            [tmp_name] => Array
                (
                    [0] => 
                )

            [error] => Array
                (
                    [0] => 4
                )

            [size] => Array
                (
                    [0] => 0
                )

        )

    [two] => Array
        (
            [name] => Array
                (
                    [0] => 
                )

            [type] => Array
                (
                    [0] => 
                )

            [tmp_name] => Array
                (
                    [0] => 
                )

            [error] => Array
                (
                    [0] => 4
                )

            [size] => Array
                (
                    [0] => 0
                )

        )

)

This suggests that internal logic in $_POST and $_FILES is completely different, thus it isn't weird to see different workarounds on invalid input.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you so much Alvaro. So considering the answer of @tozer83 and his suggestion to use parentheses as another non scalar indexes, Do you think it will be dangerous to build a system based on these things? Other frameworks for other languages like Java and .net that I've worked with support something like myModel.x.y[0].z as the name of a input field. They can be easily parsed using an EL. But in php something like that will not work, because it'll consider the myModel.x.y as the name of the array and .z thing gets omitted.
And also I say dangerous because I have no idea what could happen in the next release of PHP
As I said, I don't really understand what array structure you want (seriously), neither do I know the rationale behind. Whatever, you can of course either use the HTML arrays feature as intended or skip it entirely by using a non special character (e.g. parenthesis).
OK no problem, thank you anyway. Yours and the other friend's answers helped me so much.
0

I attempted to make the same form but modified it a bit.

<form action="uploadTest.php" method="post" enctype="multipart/form-data" >
    File1 <input name="myModels[postedFiles(0)]" type="file" /></br>
    File2 <input name="myModels[postedFiles(1)]" type="file" /></br>
    <input type="submit" value="post" />
</form> 

print_r($_FILES) results when attempting to upload files:

Array ( [myModels] => Array ( [name] => Array ( [postedFiles(0)] => file1.txt [postedFiles(1)] => file2.txt ) [type] => Array ( [postedFiles(0)] => text/plain [postedFiles(1)] => text/plain ) [tmp_name] => Array ( [postedFiles(0)] => /tmp/phpiHJBb1 [postedFiles(1)] => /tmp/phpSFh4J0 ) [error] => Array ( [postedFiles(0)] => 0 [postedFiles(1)] => 0 ) [size] => Array ( [postedFiles(0)] => 4 [postedFiles(1)] => 4 ) ) )

Refer to PHP manual here: https://secure.php.net/manual/en/features.file-upload.post-method.php Check out example #3 for uploading an array of files.

This doesn't not seem to work however if the file is too large which would be dictated in your php.ini through upload_max_filesize or maybe even post_max_size.

4 Comments

Thank you so much for your effort, but I said that >Please note that its not a problem with uploading file itself. Uploading is working grate even with arrays. Even with arrays with key indexes. Yes this will work normally but this is not my case. I need some complex indexing in order to create Object Graph assignment.
Change the brackets around the key value and then it works: <input name="myModels[postedFiles(0)]" type="file" /> At least it did for me. I edited my post the reflect the results.
Thank you @toer83 Its a good point. I plussed that, But bracket it more symbolic to show arrays than others. And BTW I've written tons of codes based on [ thing. So you acknowledge that something like myModels[postedFiles[0]] did not work for you too? Could you please tell me what is the version of your PHP?
It did not work with [] brackets. PHP version is 5.5.26-1.

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.