Passed
Pull Request — main (#8)
by
unknown
15:18
created

UploadedFileFactory::createUploadedFile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
nc 2
nop 5
dl 0
loc 12
ccs 2
cts 2
cp 1
crap 2
rs 10
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Http;
13
14
use BlitzPHP\Filesystem\Files\UploadedFile;
15
use InvalidArgumentException;
16
use Psr\Http\Message\StreamInterface;
17
use Psr\Http\Message\UploadedFileFactoryInterface;
18
use Psr\Http\Message\UploadedFileInterface;
19
20
/**
21
 * Classe d'usine pour la création d'instances de fichiers téléchargés.
22
 *
23
 * @credit <a href="https://docs.laminas.dev/laminas-diactoros/">Laminas\Diactoros</a>
24
 */
25
class UploadedFileFactory implements UploadedFileFactoryInterface
26
{
27
    /**
28
     * Créer un nouveau fichier téléchargé.
29
     *
30
     * Si une taille n'est pas fournie, elle sera déterminée en vérifiant la taille du flux.
31
     *
32
     * @see http://php.net/manual/features.file-upload.post-method.php
33
     * @see http://php.net/manual/features.file-upload.errors.php
34
     *
35
     * @param StreamInterface $stream          Le flux sous-jacent représentant le contenu du fichier téléchargé.
36
     * @param int|null        $size            La taille du fichier en octets.
37
     * @param int             $error           L'erreur de téléchargement du fichier PHP.
38
     * @param string|null     $clientFilename  Le nom du fichier tel qu'il est fourni par le client, le cas échéant.
39
     * @param string|null     $clientMediaType Le type de média tel qu'il est fourni par le client, le cas échéant.
40
     *
41
     * @throws InvalidArgumentException Si la ressource du fichier n'est pas lisible.
42
     */
43
    public function createUploadedFile(
44
        StreamInterface $stream,
45
        ?int $size = null,
46
        int $error = UPLOAD_ERR_OK,
47
        ?string $clientFilename = null,
48
        ?string $clientMediaType = null
49
    ): UploadedFileInterface {
50
        if ($size === null) {
51 2
            $size = $stream->getSize() ?? 0;
52
        }
53
54 2
        return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType);
55
    }
56
57
    /**
58
     * Créer une instance de fichier téléchargé à partir d'un tableau de valeurs.
59
     *
60
     * @param array $spec Une seule entrée $_FILES.
61
     *
62
     * @throws InvalidArgumentException Si une ou plusieurs des clés tmp_name, size ou error sont manquantes dans $spec.
63
     */
64
    public static function makeUploadedFile(array $spec): UploadedFile
65
    {
66
        if (! isset($spec['tmp_name']) || ! isset($spec['size']) || ! isset($spec['error'])) {
67
            throw new InvalidArgumentException(sprintf(
68
                '$spec fourni à %s DOIT contenir chacune des clés "tmp_name", "size", et "error" ; une ou plusieurs étaient manquantes',
69
                __FUNCTION__
70 2
            ));
71
        }
72
73
        return new UploadedFile(
74
            $spec['tmp_name'],
75
            (int) $spec['size'],
76
            $spec['error'],
77
            $spec['name'] ?? null,
78
            $spec['type'] ?? null
79 8
        );
80
    }
81
82
    /**
83
     * Normaliser les fichiers téléchargés
84
     *
85
     * Transforme chaque valeur en une instance UploadedFile, et s'assure que les tableaux imbriqués sont normalisés.
86
     *
87
     * @see https://github.com/laminas/laminas-diactoros/blob/3.4.x/src/functions/normalize_uploaded_files.php
88
     *
89
     * @return UploadedFileInterface[]
90
     *
91
     * @throws InvalidArgumentException Pour les valeurs non reconnues.
92
     */
93
    public static function normalizeUploadedFiles(array $files): array
94
    {
95
        /**
96
         * Traverse une arborescence imbriquée de spécifications de fichiers téléchargés.
97
         *
98
         * @param array[]|string[]      $tmpNameTree
99
         * @param array[]|int[]         $sizeTree
100
         * @param array[]|int[]         $errorTree
101
         * @param array[]|string[]|null $nameTree
102
         * @param array[]|string[]|null $typeTree
103
         *
104
         * @return array[]|UploadedFile[]
105
         */
106
        $recursiveNormalize = static function (
107
            array $tmpNameTree,
108
            array $sizeTree,
109
            array $errorTree,
110
            ?array $nameTree = null,
111
            ?array $typeTree = null
112
        ) use (&$recursiveNormalize): array {
113 4
            $normalized = [];
114
115
            foreach ($tmpNameTree as $key => $value) {
116
                if (is_array($value)) {
117
                    // Traverse
118
                    $normalized[$key] = $recursiveNormalize(
119
                        $tmpNameTree[$key],
120
                        $sizeTree[$key],
121
                        $errorTree[$key],
122
                        $nameTree[$key] ?? null,
123
                        $typeTree[$key] ?? null
124 4
                    );
125
126 4
                    continue;
127
                }
128
129
                $normalized[$key] = static::makeUploadedFile([
130
                    'tmp_name' => $tmpNameTree[$key],
131
                    'size'     => $sizeTree[$key],
132
                    'error'    => $errorTree[$key],
133
                    'name'     => $nameTree[$key] ?? null,
134
                    'type'     => $typeTree[$key] ?? null,
135 4
                ]);
136
            }
137
138 4
            return $normalized;
139
        };
140
141
        /**
142
         * Normaliser un tableau de spécifications de fichiers.
143
         *
144
         * Boucle sur tous les fichiers imbriqués (déterminés par la réception d'un tableau à la clé `tmp_name` d'une spécification `$_FILES`) et renvoie un tableau normalisé d'instances UploadedFile.
145
         *
146
         * Cette fonction normalise un tableau `$_FILES` représentant un ensemble imbriqué de fichiers téléchargés tels que produits par les SAPI php-fpm, CGI SAPI, ou mod_php SAPI.
147
         *
148
         * @return UploadedFile[]
149
         */
150
        $normalizeUploadedFileSpecification = static function (array $files = []) use (&$recursiveNormalize): array {
151
            if (
152
                ! isset($files['tmp_name']) || ! is_array($files['tmp_name'])
153
                                            || ! isset($files['size']) || ! is_array($files['size'])
154
                                            || ! isset($files['error']) || ! is_array($files['error'])
155
            ) {
156
                throw new InvalidArgumentException(sprintf(
157
                    'Les fichiers fournis à %s DOIVENT contenir chacune des clés "tmp_name", "size" et "error",
158
				chacune étant représentée sous la forme d\'un tableau ;
159
				une ou plusieurs valeurs manquaient ou n\'étaient pas des tableaux.',
160
                    __FUNCTION__
161
                ));
162
            }
163
164
            return $recursiveNormalize(
165
                $files['tmp_name'],
166
                $files['size'],
167
                $files['error'],
168
                $files['name'] ?? null,
169
                $files['type'] ?? null
170 4
            );
171
        };
172
173 10
        $normalized = [];
174
175
        foreach ($files as $key => $value) {
176
            if ($value instanceof UploadedFileInterface) {
177 6
                $normalized[$key] = $value;
178
179
                continue;
180
            }
181
182
            if (is_array($value) && isset($value['tmp_name']) && is_array($value['tmp_name'])) {
183 4
                $normalized[$key] = $normalizeUploadedFileSpecification($value);
184
185 4
                continue;
186
            }
187
188
            if (is_array($value) && isset($value['tmp_name'])) {
189 6
                $normalized[$key] = self::makeUploadedFile($value);
190
191 6
                continue;
192
            }
193
194
            if (is_array($value)) {
195 4
                $normalized[$key] = self::normalizeUploadedFiles($value);
196
197 2
                continue;
198
            }
199
200 2
            throw new InvalidArgumentException('Valeur non valide dans la spécification des fichiers');
201
        }
202
203 10
        return $normalized;
204
    }
205
}
206