1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\HttpFoundation; 13 14use Symfony\Component\HttpFoundation\File\UploadedFile; 15 16/** 17 * FileBag is a container for uploaded files. 18 * 19 * @author Fabien Potencier <fabien@symfony.com> 20 * @author Bulat Shakirzyanov <mallluhuct@gmail.com> 21 */ 22class FileBag extends ParameterBag 23{ 24 private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type'); 25 26 /** 27 * @param array $parameters An array of HTTP files 28 */ 29 public function __construct(array $parameters = array()) 30 { 31 $this->replace($parameters); 32 } 33 34 /** 35 * {@inheritdoc} 36 */ 37 public function replace(array $files = array()) 38 { 39 $this->parameters = array(); 40 $this->add($files); 41 } 42 43 /** 44 * {@inheritdoc} 45 */ 46 public function set($key, $value) 47 { 48 if (!is_array($value) && !$value instanceof UploadedFile) { 49 throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.'); 50 } 51 52 parent::set($key, $this->convertFileInformation($value)); 53 } 54 55 /** 56 * {@inheritdoc} 57 */ 58 public function add(array $files = array()) 59 { 60 foreach ($files as $key => $file) { 61 $this->set($key, $file); 62 } 63 } 64 65 /** 66 * Converts uploaded files to UploadedFile instances. 67 * 68 * @param array|UploadedFile $file A (multi-dimensional) array of uploaded file information 69 * 70 * @return UploadedFile[]|UploadedFile|null A (multi-dimensional) array of UploadedFile instances 71 */ 72 protected function convertFileInformation($file) 73 { 74 if ($file instanceof UploadedFile) { 75 return $file; 76 } 77 78 $file = $this->fixPhpFilesArray($file); 79 if (is_array($file)) { 80 $keys = array_keys($file); 81 sort($keys); 82 83 if ($keys == self::$fileKeys) { 84 if (UPLOAD_ERR_NO_FILE == $file['error']) { 85 $file = null; 86 } else { 87 $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']); 88 } 89 } else { 90 $file = array_map(array($this, 'convertFileInformation'), $file); 91 if (array_keys($keys) === $keys) { 92 $file = array_filter($file); 93 } 94 } 95 } 96 97 return $file; 98 } 99 100 /** 101 * Fixes a malformed PHP $_FILES array. 102 * 103 * PHP has a bug that the format of the $_FILES array differs, depending on 104 * whether the uploaded file fields had normal field names or array-like 105 * field names ("normal" vs. "parent[child]"). 106 * 107 * This method fixes the array to look like the "normal" $_FILES array. 108 * 109 * It's safe to pass an already converted array, in which case this method 110 * just returns the original array unmodified. 111 * 112 * @return array 113 */ 114 protected function fixPhpFilesArray($data) 115 { 116 if (!is_array($data)) { 117 return $data; 118 } 119 120 $keys = array_keys($data); 121 sort($keys); 122 123 if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) { 124 return $data; 125 } 126 127 $files = $data; 128 foreach (self::$fileKeys as $k) { 129 unset($files[$k]); 130 } 131 132 foreach ($data['name'] as $key => $name) { 133 $files[$key] = $this->fixPhpFilesArray(array( 134 'error' => $data['error'][$key], 135 'name' => $name, 136 'type' => $data['type'][$key], 137 'tmp_name' => $data['tmp_name'][$key], 138 'size' => $data['size'][$key], 139 )); 140 } 141 142 return $files; 143 } 144} 145