Pull to refresh

Странный $_FILES или «проблема использования синтаксиса массива в полях формы типа файл»

Reading time 3 min
Views 17K
Меня всегда мучал вопрос по поводу того, почему так устроен массив $_FILES в PHP, точнее то, почему он очень странным образом формирует его. В случае, если имена полей формы оформлены с использованием синтаксиса массива, $_REQUEST, $_GET или $_POST будут содержать правильное представление, но… такое использование не подходит для $_FILES!

Проблема

Имеем форму:
<form action="" method="post" enctype="multipart/form-data">
  <input type="file" name="oneLevel[]">
  <input type="file" name="oneLevel[]">
  <input type="submit">
</form>

При загрузке файлов через эту форму получаем массив $_FILES следующего вида:
array(
 'files' => array (
  'name' => array (
   0 => 'Lighthouse.jpg',
   1 => 'Hydrangeas.jpg',
  ),
  'type' => array (
   0 => 'image/jpeg',
   1 => 'image/jpeg',
  ),
  'tmp_name' => array (
   0 => '/tmp/phpQR67Qp',
   1 => '/tmp/phpJjnAHA',
  ),
  'error' => array (
   0 => 0,
   1 => 0,
  ),
  'size' => array (
   0 => 561276,
   1 => 595284,
  ),
 ),
)

Мне эта «фича» в большинстве случаев не подходит и кажется нелогичной. Простейшее решение — использовать простые (строковые) имена для полей типа «файл», например file_0, file_1, ..., file_N, но это не так удобно. Если, все-таки, вас интересует решение этой проблемы — читаем далее…

Решение

В autoprepend-файле или где-нибудь в момент инициализации вашего приложения стоит обработать массив $_FILE следующим образом:
/**
* Рекурсивная функция для реструктуризации массива
*
* @param array   $arrayForFill          Массив для заполнения.
*                                       Этот массив будет содержать "правильное"
*                                       представление $_FILES
* @param string  $currentKey            Ключ текущей позиции
* @param mixed   $currentMixedValue     Значение текущей позиции
* @param string  $fileDescriptionParam  Текущий параметр описания файла
*                                       (name, type, tmp_name, error или size)
* @return void
*/
function rRestructuringFilesArray(&$arrayForFill, $currentKey,
  $currentMixedValue, $fileDescriptionParam)
{
  if (is_array($currentMixedValue)) {
    foreach ($currentMixedValue as $nameKey => $mixedValue) {
      rRestructuringFilesArray($arrayForFill[$currentKey],
                               $nameKey,
                               $mixedValue,
                               $fileDescriptionParam);
    }
  } else {
    $arrayForFill[$currentKey][$fileDescriptionParam] = $currentMixedValue;
  }
}

// массив, в котором будем формировать "правильный" $_FILES
$arrayForFill = array();

// первый уровень проходим без изменения
foreach ($_FILES as $firstNameKey => $arFileDescriptions) {

  // а вот со второго уровня интерпритатор делает то,
  // что мне в большинстве случаев не подходит
  foreach ($arFileDescriptions as $fileDescriptionParam => $mixedValue) {
    rRestructuringFilesArray($arrayForFill,
                             $firstNameKey,
                             $_FILES[$firstNameKey][$fileDescriptionParam],
                             $fileDescriptionParam);
  }
}
// перегружаем $_FILES сформированным массивом
$_FILES = $arrayForFill;

В итоге имеем более «логичный» миссив:
array(
 'files' => array (
  0 => array (
   'name' => 'Lighthouse.jpg',
   'type' => 'image/jpeg',
   'tmp_name' => '/tmp/phpKNqlsc',
   'error' => 0,
   'size' => 561276,
  ),
  1 => array (
   'name' => 'Hydrangeas.jpg',
   'type' => 'image/jpeg',
   'tmp_name' => '/tmp/phpB8X3E8',
   'error' => 0,
   'size' => 595284,
  ),
 ),
)

Разместил это решение комментарием в официальной документации, т.к. вопросов много, а конкретного примера нет. Но, на момент публикации здесь его там пока ещё не появилось (не знаю, появится ли?).

Спасибо за внимание, надеюсь кому-нибудь пригодится.

UPD: Глядя на то, как мои слова «В autoprepend-файле или где-нибудь в момент инициализации вашего приложения стоит обработать массив $_FILE» и перезапись в коде массива $_FILES вызвали некоторое негодование некоторой части сообщества, заявляю: это пример того, как можно реализовать необходимый функционал, и я не призываю использовать данный код «как есть» в боевых проектах.
Tags:
Hubs:
+6
Comments 40
Comments Comments 40

Articles