superUntitled
superUntitled

Reputation: 22527

PHP: parse $_FILES[] data in multidimesional array

I have a form that allows for dynamic duplication of the form fields. The form allows for file uploads and text input, so the data is sent in both $_POST and $_FILES arrays.

The the initial set of inputs look like this:

<input type="text" name="question[1][text]"  />
<input  type="file" name="question[1][file]"  /> 

<input type="text" class="a" name="answer[1][text][]"  /> 
<input  type="file" name="answer[1][file][]"  /> 

When duplicated the fields are incremented, they look like this:

<input type="text" name="question[2][text]"  />
<input  type="file" name="question[2][file]"  /> 

<input type="text" class="a" name="answer[2][text][]"  /> 
<input  type="file" name="answer[2][file][]"  /> 

To complicate matters, the "answer" form fields can also be duplicated (thus the [] at the end of the 'answer' name array.

How can I parse the posted $_FILES array? I have tried something like this:

foreach ($_FILES['question'] as $p_num)
{ 
  echo  $p_num['file']['name'];

    foreach ($_FILES['answer'] as $a_num)
    { 
      echo  $a_num['file']['name'];
    }

}

but I get an "Undefined index: file... " error. How can I parse out the posted values.

Upvotes: 1

Views: 3027

Answers (4)

tvanc
tvanc

Reputation: 4294

The PHP $_FILES array gets crazy when you're dealing with more than one file at a time. It's worse when you're dealing with multiple inputs that can each take multiple files. I wrote a library that takes the $_FILES array and gives you back a copy of it rearranged the way you were imagining.

In fact, the situation that finally made me write a robust solution for the problem was very much like yours. See the section in the readme about dealing with multiple fields that take multiple files.

Install tvanc/files-array-organizer

composer require tvanc/files-array-organizer

Organize $_FILES

With some minor adjustment to your code you can loop over the files like you were trying to do.

use tvanc\FilesArrayOrganizer\FilesArrayOrganizer;

// Use composer's autoload.php
require 'vendor/autoload.php';

$organizedFiles = FilesArrayOrganizer::organize($_FILES);

foreach ($organizedFiles['question'] as $q_index => $question)
{ 
    echo  $question['file']['name'];

    foreach ($organizedFiles['answer'][$q_index]['file'] as $answer_file)
    { 
        echo  $answer_file['name'];
    }

}

Upvotes: 0

Rolf
Rolf

Reputation: 5743

PHP "reorders" the $_FILES array in an undesirable way. If you visit the PHP manual, at the page discussing multiple file uploads:

http://www.php.net/manual/en/features.file-upload.multiple.php

You can find a big variety of code snippets in the page comments, all dealing with this annoyance. Most comments are about that, actually!

Upvotes: 0

ajreal
ajreal

Reputation: 47311

updated

form:

<form method="post" enctype="multipart/form-data">
<input type="text" name="question[1][text]"  value="AAA"/>
<input type="file" name="question[1][file]"  />
<input type="text" name="answer[1][text][0]" value="bbb" />
<input type="file" name="answer[1][file][0]"  />
<input type="text" name="answer[1][text][1]" value="ccc" />
<input type="file" name="answer[1][file][1]"  />
<input type="submit"/>

php:

$rtn = array();
foreach ($_POST['question'] as $key=>$arr)
{
  if (!isset($rtn[$key])) 
  {
    $rtn[$key] = array('question'=>'', 'answer'=>array());
  }
  $rtn[$key]['question'] = $arr['text'];


  $tmp = array();
  foreach ($_FILES['question'] as $fkey=>$farr)
  {
    $tmp[$fkey] = $farr[$key]['file'];
  }
  $rtn[$key]['question_file']  = $tmp;

  $rtn[$key]['answer']['text'] = $_POST['answer'][$key]['text'];
}

foreach ($_FILES['answer'] as $key=>$arr)
{
  foreach ($arr as $answer_idx=>$farr)
  {
    foreach ($farr as $file_index=>$file)
    {
      $rtn[$answer_idx]['answer_file'][$file_index][$key] = $file;
    }
  }
}

output (upload using test.txt)

Array
(
    [1] => Array
        (
            [question] => AAA
            [answer] => Array
                (
                    [text] => Array
                        (
                            [0] => bbb
                            [1] => ccc
                        )

                )

            [question_file] => Array
                (
                    [name] => test.txt
                    [type] => text/plain
                    [tmp_name] => /tmp/phpqef5eL
                    [error] => 0
                    [size] => 0
                )

            [answer_file] => Array
                (
                    [file] => Array
                        (
                            [name] => Array
                                (
                                    [0] => test.txt
                                    [1] => test.txt
                                )

                            [type] => Array
                                (
                                    [0] => text/plain
                                    [1] => text/plain
                                )

                            [tmp_name] => Array
                                (
                                    [0] => /tmp/phpc2sdMs
                                    [1] => /tmp/phpzKNnja
                                )

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

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

                        )

                )

        )

)

Upvotes: 3

Marc B
Marc B

Reputation: 360572

PHP doesn't seem to like it when you upload files with filenames specified in multi-dimensional array format. Given that the POST and FILEs data are kept completely seperate in PHP, you should rejigger your form. The "question[1]" format is ok, but not "question[1][file]". Rather, try for something like:

<input type="text" name="question[1][text]"  />
<input  type="file" name="file[1]"  /> 

<input type="text" name="question[2][text]"  />
<input  type="file" name="file[2]"  /> 

As long as you keep the same index value for the file fields and all the other fields, can access the files during form processing like this:

foreach(array_keys($_POST['question']) as $key {
     $file_is = $_FILES['file'][$key]['tmp_name'];
     $question_text = $_POST['question][$key]['text'];
}

Upvotes: 2

Related Questions