array_column : utilisation avec $_FILES

PHP array_columnLa sortie de PHP 5.5, actuellement en version 5.5.5, a amenée plusieurs nouveautés. Nous allons nous attarder ici sur une des nouvelles fonctions intégrées : array_column, et l’appliquer à l’upload multiple.

Pour comprendre ce que fait cette fonction un exemple sera plus parlant que des mots :

 

 

$records = array(
    array(
        'id' => 2135,
        'first_name' => 'John',
        'last_name' => 'Doe',
    ),
    array(
        'id' => 3245,
        'first_name' => 'Sally',
        'last_name' => 'Smith',
    ),
    array(
        'id' => 5342,
        'first_name' => 'Jane',
        'last_name' => 'Jones',
    ),
    array(
        'id' => 5623,
        'first_name' => 'Peter',
        'last_name' => 'Doe',
    )
);

$first_names = array_column($records, 'first_name');
print_r($first_names);
array (size=4)
  0 => string 'John' (length=4)
  1 => string 'Sally' (length=5)
  2 => string 'Jane' (length=4)
  3 => string 'Peter' (length=5)

Dans un tableau à 2 dimensions vous pouvez donc préciser une clef de tableau et obtenir ainsi les valeurs de chaque tableaux possédant cette clef.

 

Tableaux de plusieurs fichiers $_FILES avec array_column

Si vous avez déjà utilisé l’upload de plusieurs fichiers simultanément, vous êtes surement tombé sur un problème : la façon dont la variable superglobale $_FILES renvoie les éléments.

Voici un exemple ci-dessous du dump de $_FILES avec 2 fichiers envoyés :

<form action="" method="post" enctype="multipart/form-data">
    <input type="file" name="documents[]" multiple>
    <input type="submit" value="Envoyer">
</form>
array (size=5)
  'name' => 
    array (size=2)
      0 => string 'photo1.png' (length=12)
      1 => string 'photo2.png' (length=8)
  'type' => 
    array (size=2)
      0 => string 'image/png' (length=9)
      1 => string 'image/png' (length=9)
  'tmp_name' => 
    array (size=2)
      0 => string '/tmp/phpGZa2ya' (length=14)
      1 => string '/tmp/php52dYdV' (length=14)
  'error' => 
    array (size=2)
      0 => int 0
      1 => int 0
  'size' => 
    array (size=2)
      0 => int 246369
      1 => int 510519

C’est un tableau à 2 dimensions groupé par name, type etc. On ne peut donc pas boucler dessus pour avoir chaque fichier envoyé.

On peut s’en sortir grâce à une fonction du style :

function multiFileFormat(array $arrFiles)
{
    $files = array();
    $fdata = $arrFiles;
    if (is_array($fdata['name'])) {
        for ($i = 0 ; $i < count($fdata['name']) ; $i++) {
            $files[] = array(
                'name'     => $fdata['name'][$i],
                'type'     => $fdata['type'][$i],
                'tmp_name' => $fdata['tmp_name'][$i],
                'error'    => $fdata['error'][$i],
                'size'     => $fdata['size'][$i]
            );
        }
    } else {
        $files[] = $fdata;
    }
    return $files;
}

Grace à la nouvelle fonction array_column, nous allons pouvoir faire sensiblement la même chose avec moins de lignes :

$i = 0;
$documents = array();
while (!empty(array_column($_FILES['documents'], $i))) {
    $documents[] = array_column($_FILES['documents'], $i);
    $i++;
}

Petite explication de ces quelques lignes de codes : je boucle sur les index en partant de 0, ce qui permet de récupérer dans un tableau les 5 éléments du même index : name, type, tmp_name, error et size. Voila ce que donne le dump de la variable $documents.

array (size=2)
  0 => 
    array (size=5)
      0 => string 'photo1.png' (length=12)
      1 => string 'image/png' (length=9)
      2 => string '/tmp/phpGZa2ya' (length=14)
      3 => int 0
      4 => int 246369
  1 => 
    array (size=5)
      0 => string 'photo2.png' (length=8)
      1 => string 'image/png' (length=9)
      2 => string '/tmp/php52dYdV' (length=14)
      3 => int 0
      4 => int 510519

L’inconvénient de cette technique comparer à la fonction multiFileFormat est que les index name, type etc. sont remplacés par des numériques.

Si on veut améliorer ça et gérer nos fichiers façon objet, on peut utiliser une classe UploadedFile qui va nous rendre la vie plus facile pour gérer ces éléments par la suite.

$i = 0;
$documents = array();
while (!empty(array_column($_FILES['documents'], $i))) {
    list($name, $type, $tmp_name, $error, $size) = array_column($_FILES['documents'], $i);
    $documents[] = new UploadedFile($name, $type, $tmp_name, $error, $size);
    $i++;
}

Cette classe nous permet par la suite de gérer un objet $file (instance de UploadedFile) comme ceci :

echo $file->getSize(UploadedFile::SIZE_KO) . '<br>'; // affiche le poid en Ko
if ($file->hasError()) {
    echo $file->getErrorMessage() . '<br>'; // affiche éventuellement un message d'erreur
}
echo $file->getExtension() . '<br>'; // affiche l'extension
echo $file->getType() . '<br>'; // affiche le type de fichier
$file->move('/var/www/monsite/media/'); // enregistre le fichier où vous voulez

Pour aller voir le contenu de la classe UploadedFile direction le repository GitHub.

Ceci est une façon d’appliquer la fonction array_column pour se faciliter les choses. Le passage par la classe UploadedFile n’est pas obligatoire, c’est juste un moyen de pouvoir manipuler par la suite son fichier comme un objet et d’avoir accès à quelques fonctions pratiques pour récupérer un message d’erreur explicite, l’extension du fichier etc.

Si vous avez une version antérieur à PHP 5.5 et que vous êtes sur une Debian, vous pouvez consulter cet article pour savoir comment mettre à jour PHP et ainsi profiter des dernières améliorations apportées à cette mouture.

Pour information la version 5.5 est stable et il n’y a aucunes raisons de s’en priver !

Share Button

Laisser un commentaire.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.