$_FILES Superglobal fix for multiple file uploads
There comes a time in every developers career when they are given the task to create a multiple file upload system. “Okay” you say, “Too easy!”.
If you have a set number of file-input fields to put on the page, you might simply name each like so: “myfile_upload_1″, “myfile_upload_2″, etc… But, if you want to give your site visitors the option to select how many fields to display or you love working with arrays (like myself), you may do something more like this: “myfile_upload[]“.
As stated, I’m a big fan of the second method (the array version); Not only does it give me a nice little array to work with, but if the client sees the four fields I’ve put in the new form and suddenly decides we need a fifth, it’s no sweat at all. Essentially, I’m making life easier for myself if the spec changes (which it almost invariably does).
There is one sour-plum in our punch however and it lies within the $_FILES Super global. Going back to our first illustration of the file-input names: “myfile_upload_1″; On submission of the form, we would expect $_FILES to be populated like so:
Array
{
[myfile_upload_1] => Array
{
[name] => myfile.txt
[type] => text/plain
[tmp_name] => /tmp/php/php9GHPab
[error] => 0
[size] => 1
}
}
Pretty straight forward, nothing out of the ordinary. So, what about our second example? Well, this is where things get a little messy:
Array
{
[myfile_upload] => Array
{
[name] => Array
{
[0] => myfile.txt
}
[type] => Array
{
[0] => text/plain
}
[tmp_name] => Array
{
[0] => /tmp/php/php9GHPab
}
[error] => Array
{
[0] => 0
}
[size] => Array
{
[0] => 1
}
}
}
Can you see what’s going on? It’s pretty hairy. What I would have expected is something more like this:
Array
{
[myfile_upload] => Array
{
[0] => Array
{
[name] => myfile.txt
[type] => text/plain
[tmp_name] => /tmp/php/php9GHPab
[error] => 0
[size] => 1
}
}
}
A lot neater, I think you’ll agree! It’s also much more inline with the way that multidimensional arrays normally work in PHP, yet for some reason this issue has gone un-noticed (or at least un-changed) for 5 versions of PHP now.
I must admit, that at times I can be a little stubborn and in regards to this particular problem, I’d really like the $_FILES super global to be predictable (as I’m sure anyone would). So, what can be done? Well, it just so happens that I’ve dealt with this bad-boy before and I put together a function that often finds its’ way into the components we develop at work:
/**
* PHP $_FILES array builds in an augmented fashion when uploading images from array-style input fields
* i.e. <input type="file" name="myuploads[]” />
* This function repairs the $_FILES array so it is more usable
*
* @param $array The array of uploaded file information. $_FILES is expected
* @return array
*/
function repairFileUpload($array){
$return = array();
if (is_array($array)) {
foreach($array AS $field => $val) {
if (is_array($array[$field]['name'])) {
foreach(array_keys($array[$field]['name']) AS $key) {
$return[$field][$key]['name'] = strtolower($array[$field]['name'][$key]);
$return[$field][$key]['type'] = $array[$field]['type'][$key];
$return[$field][$key]['size'] = $array[$field]['size'][$key];
$return[$field][$key]['tmp_name'] = $array[$field]['tmp_name'][$key];
$return[$field][$key]['error'] = $array[$field]['error'][$key];
}
} else {
$key = 0;
$return[$field][$key]['name'] = strtolower($array[$field]['name']);
$return[$field][$key]['type'] = $array[$field]['type'];
$return[$field][$key]['size'] = $array[$field]['size'];
$return[$field][$key]['tmp_name'] = $array[$field]['tmp_name'];
$return[$field][$key]['error'] = $array[$field]['error'];
}
}
}
return $return;
} // repairFileUpload()
It’s a little “thicker” than I’d like it to be, but it fixes the issue and that’s the important part! Obviously, you simply pass the $_FILES super global to this function fairly early on and you can either re-populate $_FILES:
$files = repairFileUpload($_FILES); $_FILES = $files;
Or use $files throughout your project. Either way, I hope this snippet of code finds you well and soothes your aching head (if you’ve been beating it against a wall like I did before this function).