UploadedFileHelper::uploadedFileSpecsConvert()   A
last analyzed

Complexity

Conditions 5
Paths 2

Size

Total Lines 32
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 32
ccs 15
cts 15
cp 1
rs 9.5222
cc 5
nc 2
nop 1
crap 5
1
<?php 
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace Shieldon\Psr7\Utils;
14
15
use Shieldon\Psr7\UploadedFile;
16
17
use function array_merge_recursive;
18
use function rtrim;
19
use function is_array;
20
use function is_string;
21
use function is_numeric;
22
23
/*
24
 * The helper functions for converting $_FILES to an array of UploadedFile
25
 * instance, only used on ServerRequest class.
26
 * This class is not a part of PSR 7.
27
 */
28
class UploadedFileHelper
29
{
30
/**
31
     * Create an array for PSR-7 Uploaded File needed.
32
     * 
33
     * @param array $files     An array generally from $_FILES
34
     * @param bool  $isConvert To covert and return $files as an UploadedFile instance.
35
     * 
36
     * @return array|UploadedFile
37
     */
38 6
    public static function uploadedFileParse(array $files)
39
    {
40 6
        $specTree = [];
41
42 6
        $specFields = [
43 6
            0 => 'tmp_name',
44 6
            1 => 'name',
45 6
            2 => 'type',
46 6
            3 => 'size',
47 6
            4 => 'error',
48 6
        ];
49
50 6
        foreach ($files as $fileKey => $fileValue) {
51 6
            if (!isset($fileValue['tmp_name'])) {
52
                // @codeCoverageIgnoreStart
53
                return [];
54
                // @codeCoverageIgnoreEnd
55
            }
56
57 6
            if (is_string($fileValue['tmp_name']) || is_numeric($fileValue['tmp_name'])) {
58 6
                $specTree[$fileKey] = $fileValue;
59
60 2
            } elseif (is_array($fileValue['tmp_name'])) {
61
62 2
                $tmp = [];
63
64
                // We want to find out how many levels of array it has.
65 2
                foreach ($specFields as $i => $attr) {
66 2
                    $tmp[$i] = self::uploadedFileNestedFields($fileValue, $attr);
67
                }
68
69 2
                $parsedTree = array_merge_recursive(
70 2
                    $tmp[0], // tmp_name
71 2
                    $tmp[1], // name
72 2
                    $tmp[2], // type
73 2
                    $tmp[3], // size
74 2
                    $tmp[4]  // error
75 2
                );
76
  
77 2
                $specTree[$fileKey] = $parsedTree;
78 2
                unset($tmp, $parsedTree);
79
            }
80
        }
81
82 6
        return self::uploadedFileArrayTrim($specTree);
83
    }
84
85
    /**
86
     * Find out how many levels of an array it has.
87
     *
88
     * @param array  $files Data structure from $_FILES.
89
     * @param string $attr  The attributes of a file.
90
     *
91
     * @return array
92
     */
93 2
    public static function uploadedFileNestedFields(array $files, string $attr): array
94
    {
95 2
        $result = [];
96 2
        $values = $files;
97
98 2
        if (isset($files[$attr])) {
99 2
            $values = $files[$attr];
100
        }
101
102 2
        foreach ($values as $key => $value) {
103
104
            /**
105
             * Hereby to add `_` to be a part of the key for letting `array_merge_recursive`
106
             * method can deal with numeric keys as string keys. 
107
             * It will be restored in the next step.
108
             *
109
             * @see uploadedFileArrayTrim
110
             */
111 2
            if (is_numeric($key)) {
112 2
                $key .= '_';
113
            }
114
115 2
            if (is_array($value)) {
116 2
                $result[$key] = self::uploadedFileNestedFields($value, $attr);
117
            } else {
118 2
                $result[$key][$attr] = $value;
119
            }
120
        }
121
122 2
        return $result;
123
    }
124
125
    /**
126
     * That's because that PHP function `array_merge_recursive` has the different
127
     * results as dealing with string keys and numeric keys.
128
     * In the previous step, we made numeric keys to stringify, so that we want to
129
     * restore them back to numeric ones.
130
     *
131
     * @param array|string $values
132
     *
133
     * @return array|string
134
     */
135 6
    public static function uploadedFileArrayTrim($values)
136
    {
137 6
        $result = [];
138
139 6
        if (is_array($values)) {
140
141 6
            foreach ($values as $key => $value) {
142
143
                // Restore the keys back to the original ones.
144 6
                $key = rtrim($key, '_');
145
146 6
                if (is_array($value)) {
147 6
                    $result[$key] = self::uploadedFileArrayTrim($value);
148
                } else {
149 6
                    $result[$key] = $value;
150
                }
151
            }
152
        }
153
154 6
        return $result;
155
    }
156
157
    /**
158
     * Convert the parse-ready array into PSR-7 specs.
159
     *
160
     * @param string|array $values
161
     * 
162
     * @return array
163
     */
164 6
    public static function uploadedFileSpecsConvert($values) 
165
    {
166 6
        $result = [];
167
168 6
        if (is_array($values)) {
169
170 6
            foreach ($values as $key => $value) {
171
172 6
                if (is_array($value)) {
173
174
                    // Continue querying self, until a string is found.
175 6
                    $result[$key] = self::uploadedFileSpecsConvert($value);
176
177 6
                } elseif ($key === 'tmp_name') {
178
179
                    /**
180
                     * Once one of the keys on the same level has been found,
181
                     * then we can fetch the others at a time.
182
                     * In this case, the `tmp_name` found.
183
                     */
184 6
                    $result = new uploadedFile(
185 6
                        $values['tmp_name'],
186 6
                        $values['name'],
187 6
                        $values['type'],
188 6
                        $values['size'],
189 6
                        $values['error']
190 6
                    );
191
                }
192
            }
193
        }
194
195 6
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type Shieldon\Psr7\UploadedFile which is incompatible with the documented return type array.
Loading history...
196
    }
197
}
198