Params   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 316
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 120
dl 0
loc 316
rs 9.2
c 0
b 0
f 0
wmc 40

13 Methods

Rating   Name   Duplication   Size   Complexity  
A getParams() 0 9 3
A parsedParams() 0 28 5
A postParams() 0 9 3
A getBoundary() 0 15 3
A getBlocks() 0 5 1
A parseHeaders() 0 26 5
A parseFileData() 0 15 1
A getParsedStream() 0 5 1
B processBlocks() 0 40 8
A getParsedFile() 0 27 2
A parseRawInput() 0 13 2
A arrayParam() 0 9 3
A getParsedParameter() 0 13 3

How to fix   Complexity   

Complex Class

Complex classes like Params often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Params, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.7
13
 */
14
15
namespace Quantum\Http\Request;
16
17
use Quantum\Libraries\Storage\Factories\FileSystemFactory;
18
use Quantum\Libraries\Storage\UploadedFile;
19
use Quantum\Http\Exceptions\HttpException;
20
use Quantum\App\Exceptions\BaseException;
21
use Quantum\Di\Exceptions\DiException;
22
use Quantum\Environment\Server;
23
use ReflectionException;
24
25
/**
26
 * Trait Params
27
 * @package Quantum\Http\Request
28
 */
29
trait Params
30
{
31
32
    /**
33
     * Gets the GET params
34
     * @return array
35
     */
36
    private static function getParams(): array
37
    {
38
        $getParams = [];
39
40
        if (!empty($_GET)) {
41
            $getParams = filter_input_array(INPUT_GET) ?: [];
42
        }
43
44
        return $getParams;
45
    }
46
47
    /**
48
     * Gets the POST params
49
     * @return array
50
     */
51
    private static function postParams(): array
52
    {
53
        $postParams = [];
54
55
        if (!empty($_POST)) {
56
            $postParams = filter_input_array(INPUT_POST) ?: [];
57
        }
58
59
        return $postParams;
60
    }
61
62
    /**
63
     * Parses the raw input parameters sent via PUT, PATCH or DELETE methods
64
     * @return array[]
65
     * @throws DiException
66
     * @throws HttpException
67
     * @throws ReflectionException
68
     */
69
    private static function parsedParams(): array
70
    {
71
        $parsedParams = [
72
            'params' => [],
73
            'files' => []
74
        ];
75
76
        if (in_array(self::$__method, ['PUT', 'PATCH', 'POST'])) {
77
78
            $rawInput = file_get_contents('php://input');
79
80
            switch (self::$server->contentType(true)) {
81
                case self::CONTENT_FORM_DATA:
0 ignored issues
show
Bug introduced by
The constant Quantum\Http\Request\Params::CONTENT_FORM_DATA was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
82
                    $parsedParams = self::parseRawInput($rawInput);
83
                    break;
84
                case self::CONTENT_JSON_PAYLOAD:
0 ignored issues
show
Bug introduced by
The constant Quantum\Http\Request\Params::CONTENT_JSON_PAYLOAD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
85
                    $parsedParams['params'] = json_decode($rawInput, true);
86
                    break;
87
                case self::CONTENT_URL_ENCODED:
0 ignored issues
show
Bug introduced by
The constant Quantum\Http\Request\Params::CONTENT_URL_ENCODED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
88
                    parse_str(urldecode($rawInput), $result);
89
                    $parsedParams['params'] = $result;
90
                    break;
91
                default:
92
                    throw HttpException::contentTypeNotSupported();
93
            }
94
        }
95
96
        return $parsedParams;
97
    }
98
99
    /**
100
     * Parses the raw input
101
     * @param string $rawInput
102
     * @return array[]
103
     * @throws BaseException
104
     * @throws DiException
105
     * @throws ReflectionException
106
     */
107
    private static function parseRawInput(string $rawInput): array
108
    {
109
        $boundary = self::getBoundary();
110
111
        if ($boundary) {
112
            $blocks = self::getBlocks($boundary, $rawInput);
113
114
            return self::processBlocks($blocks);
115
        }
116
117
        return [
118
            'params' => [],
119
            'files' => []
120
        ];
121
122
    }
123
124
    /**
125
     * Gets the boundary
126
     * @return string|null
127
     */
128
    private static function getBoundary(): ?string
129
    {
130
        $contentType = Server::getInstance()->contentType();
131
132
        if (!$contentType) {
133
            return null;
134
        }
135
136
        preg_match('/boundary=(.*)$/', $contentType, $match);
137
138
        if (!count($match)) {
139
            return null;
140
        }
141
142
        return $match[1];
143
    }
144
145
    /**
146
     * Gets the blocks
147
     * @param string $boundary
148
     * @param string $rawInput
149
     * @return array
150
     */
151
    private static function getBlocks(string $boundary, string $rawInput): array
152
    {
153
        $result = preg_split("/-+$boundary/", $rawInput);
154
        array_pop($result);
155
        return $result;
156
    }
157
158
    /**
159
     * Process blocks
160
     * @param array $blocks
161
     * @return array
162
     * @throws BaseException
163
     * @throws DiException
164
     */
165
    private static function processBlocks(array $blocks): array
166
    {
167
        $params = [];
168
        $files = [];
169
170
        foreach ($blocks as $id => $block) {
171
            if (empty($block)) {
172
                continue;
173
            }
174
175
            if (strpos($block, 'filename') !== false) {
176
                list($nameParam, $file) = self::getParsedFile($block);
177
178
                if (!$file) {
179
                    continue;
180
                }
181
182
                $arrayParam = self::arrayParam($nameParam);
183
184
                if (is_array($arrayParam)) {
185
                    list($name, $key) = $arrayParam;
186
187
                    if ($key === '') {
188
                        $files[$name][] = $file;
189
                    } else {
190
                        $files[$name][$key] = $file;
191
                    }
192
                } else {
193
                    $files[$nameParam] = $file;
194
                }
195
            } else if (strpos($block, 'application/octet-stream') !== false) {
196
                $params = array_merge($params, self::getParsedStream($block));
197
            } else {
198
                $params = array_merge($params, self::getParsedParameter($block));
199
            }
200
        }
201
202
        return [
203
            'params' => $params,
204
            'files' => $files
205
        ];
206
    }
207
208
    /**
209
     * @param string $block
210
     * @return array|string[]
211
     */
212
    private static function getParsedStream(string $block): array
213
    {
214
        preg_match('/name=\"([^\"]*)\".*stream[\n|\r]+([^\n\r].*)?$/s', $block, $match);
215
216
        return [$match[1] => $match[2] ?? ''];
217
    }
218
219
    /**
220
     * Gets the parsed file
221
     * @param string $block
222
     * @return array|null
223
     * @throws DiException
224
     * @throws BaseException
225
     */
226
    private static function getParsedFile(string $block): ?array
227
    {
228
        list($name, $filename, $type, $content) = self::parseFileData($block);
229
230
        if (!$content) {
231
            return null;
232
        }
233
234
        $fs = FileSystemFactory::get();
235
236
        $tempName = tempnam(sys_get_temp_dir(), 'qt_');
237
238
        $fs->put($tempName, $content);
239
240
        $file = new UploadedFile([
241
            'name' => $filename,
242
            'type' => $type,
243
            'tmp_name' => $tempName,
244
            'error' => UPLOAD_ERR_OK,
245
            'size' => $fs->size($tempName),
246
        ]);
247
248
        register_shutdown_function(function () use ($fs, $tempName) {
249
            $fs->remove($tempName);
250
        });
251
252
        return [$name, $file];
253
    }
254
255
    /**
256
     * Parses the file string
257
     * @param string $block
258
     * @return array
259
     */
260
    private static function parseFileData(string $block): array
261
    {
262
        $block = ltrim($block, "\r\n");
263
264
        list($rawHeaders, $content) = explode("\r\n\r\n", $block);
265
266
        list($name, $filename, $contentType) = self::parseHeaders($rawHeaders);
267
268
        $content = substr($content, 0, strlen($content) - 2);
269
270
        return [
271
            $name,
272
            $filename,
273
            $contentType,
274
            $content
275
        ];
276
    }
277
278
    /**
279
     * Parses and returns the parameter
280
     * @param string $block
281
     * @return array
282
     */
283
    private static function getParsedParameter(string $block): array
284
    {
285
        $data = [];
286
287
        if (preg_match('/name=\"([^\"]*)\"[\n|\r]+([^\n\r].*)?\r$/s', $block, $match)) {
288
            if (preg_match('/^(.*)\[\]$/i', $match[1], $tmp)) {
289
                $data[$tmp[1]][] = $match[2] ?? '';
290
            } else {
291
                $data[$match[1]] = $match[2] ?? '';
292
            }
293
        }
294
295
        return $data;
296
    }
297
298
    /**
299
     * Parses the header information of the file string
300
     * @param string $rawHeaders
301
     * @return array
302
     */
303
    private static function parseHeaders(string $rawHeaders): array
304
    {
305
        $rawHeaders = explode("\r\n", $rawHeaders);
306
307
        $name = '-unknown-';
308
        $filename = '-unknown-';
309
        $contentType = 'application/octet-stream';
310
311
        foreach ($rawHeaders as $header) {
312
313
            list($key, $value) = explode(':', $header);
314
315
            if ($key == 'Content-Type') {
316
                $contentType = ltrim($value, ' ');
317
            }
318
319
            if (preg_match('/name=\"([^\"]*)\"/', $header, $match)) {
320
                $name = $match[1];
321
            }
322
323
            if (preg_match('/filename=\"([^\"]*)\"/', $header, $match)) {
324
                $filename = $match[1];
325
            }
326
        }
327
328
        return [$name, $filename, $contentType];
329
    }
330
331
    /**
332
     * Finds if the param is array type or not
333
     * @param string $parameter
334
     * @return array|string
335
     */
336
    private static function arrayParam(string $parameter)
337
    {
338
        if (strpos($parameter, '[') !== false) {
339
            if (preg_match('/^([^[]*)\[([^]]*)\](.*)$/', $parameter, $match)) {
340
                return [$match[1], $match[2]];
341
            }
342
        }
343
344
        return $parameter;
345
    }
346
}