1<?php
2/**
3 * @see       https://github.com/zendframework/zend-diactoros for the canonical source repository
4 * @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5 * @license   https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
6 */
7
8namespace Zend\Diactoros;
9
10use InvalidArgumentException;
11use Psr\Http\Message\UploadedFileInterface;
12
13use function is_array;
14
15/**
16 * Normalize uploaded files
17 *
18 * Transforms each value into an UploadedFile instance, and ensures that nested
19 * arrays are normalized.
20 *
21 * @param array $files
22 * @return UploadedFileInterface[]
23 * @throws InvalidArgumentException for unrecognized values
24 */
25function normalizeUploadedFiles(array $files)
26{
27    /**
28     * Traverse a nested tree of uploaded file specifications.
29     *
30     * @param string[]|array[] $tmpNameTree
31     * @param int[]|array[] $sizeTree
32     * @param int[]|array[] $errorTree
33     * @param string[]|array[]|null $nameTree
34     * @param string[]|array[]|null $typeTree
35     * @return UploadedFile[]|array[]
36     */
37    $recursiveNormalize = function (
38        array $tmpNameTree,
39        array $sizeTree,
40        array $errorTree,
41        array $nameTree = null,
42        array $typeTree = null
43    ) use (&$recursiveNormalize) {
44        $normalized = [];
45        foreach ($tmpNameTree as $key => $value) {
46            if (is_array($value)) {
47                // Traverse
48                $normalized[$key] = $recursiveNormalize(
49                    $tmpNameTree[$key],
50                    $sizeTree[$key],
51                    $errorTree[$key],
52                    isset($nameTree[$key]) ? $nameTree[$key] : null,
53                    isset($typeTree[$key]) ? $typeTree[$key] : null
54                );
55                continue;
56            }
57            $normalized[$key] = createUploadedFile([
58                'tmp_name' => $tmpNameTree[$key],
59                'size' => $sizeTree[$key],
60                'error' => $errorTree[$key],
61                'name' => isset($nameTree[$key]) ? $nameTree[$key] : null,
62                'type' => isset($typeTree[$key]) ? $typeTree[$key] : null
63            ]);
64        }
65        return $normalized;
66    };
67
68    /**
69     * Normalize an array of file specifications.
70     *
71     * Loops through all nested files (as determined by receiving an array to the
72     * `tmp_name` key of a `$_FILES` specification) and returns a normalized array
73     * of UploadedFile instances.
74     *
75     * This function normalizes a `$_FILES` array representing a nested set of
76     * uploaded files as produced by the php-fpm SAPI, CGI SAPI, or mod_php
77     * SAPI.
78     *
79     * @param array $files
80     * @return UploadedFile[]
81     */
82    $normalizeUploadedFileSpecification = function (array $files = []) use (&$recursiveNormalize) {
83        if (! isset($files['tmp_name']) || ! is_array($files['tmp_name'])
84            || ! isset($files['size']) || ! is_array($files['size'])
85            || ! isset($files['error']) || ! is_array($files['error'])
86        ) {
87            throw new InvalidArgumentException(sprintf(
88                '$files provided to %s MUST contain each of the keys "tmp_name",'
89                . ' "size", and "error", with each represented as an array;'
90                . ' one or more were missing or non-array values',
91                __FUNCTION__
92            ));
93        }
94
95        return $recursiveNormalize(
96            $files['tmp_name'],
97            $files['size'],
98            $files['error'],
99            isset($files['name']) ? $files['name'] : null,
100            isset($files['type']) ? $files['type'] : null
101        );
102    };
103
104    $normalized = [];
105    foreach ($files as $key => $value) {
106        if ($value instanceof UploadedFileInterface) {
107            $normalized[$key] = $value;
108            continue;
109        }
110
111        if (is_array($value) && isset($value['tmp_name']) && is_array($value['tmp_name'])) {
112            $normalized[$key] = $normalizeUploadedFileSpecification($value);
113            continue;
114        }
115
116        if (is_array($value) && isset($value['tmp_name'])) {
117            $normalized[$key] = createUploadedFile($value);
118            continue;
119        }
120
121        if (is_array($value)) {
122            $normalized[$key] = normalizeUploadedFiles($value);
123            continue;
124        }
125
126        throw new InvalidArgumentException('Invalid value in files specification');
127    }
128    return $normalized;
129}
130