Completed
Push — master ( 92abf0...8df3bb )
by
unknown
02:46
created

createServerRequestFromArrays()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 0
cts 9
cp 0
rs 9.3142
c 0
b 0
f 0
cc 2
eloc 17
nc 2
nop 6
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Nyholm\Psr7\Factory;
6
7
use InvalidArgumentException;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Psr\Http\Message\UploadedFileInterface;
10
use Interop\Http\Factory\ServerRequestFactoryInterface;
11
use Nyholm\Psr7\ServerRequest;
12
use Nyholm\Psr7\UploadedFile;
13
14
/**
15
 * @author Tobias Nyholm <[email protected]>
16
 * @author Martijn van der Ven <[email protected]>
17
 */
18
class ServerRequestFactory implements ServerRequestFactoryInterface
19
{
20 16
    public function createServerRequest($method, $uri): ServerRequestInterface
21
    {
22 16
        return new ServerRequest($method, $uri);
23
    }
24
25 7
    public function createServerRequestFromArray(array $server): ServerRequestInterface
26
    {
27 7
        return new ServerRequest(
28 7
            $this->getMethodFromEnvironment($server),
29 7
            $this->getUriFromEnvironmentWithHTTP($server),
30 7
            [],
31 7
            null,
32 7
            '1.1',
33 7
            $server
34
        );
35
    }
36
37
    /**
38
     * Create a new server request from a set of arrays.
39
     *
40
     * @param array $server  Typically $_SERVER or similar structure.
41
     * @param array $headers Typically the output of getallheaders() or similar structure.
42
     * @param array $cookie  Typically $_COOKIE or similar structure.
43
     * @param array $get     Typically $_GET or similar structure.
44
     * @param array $post    Typically $_POST or similar structure.
45
     * @param array $files   Typically $_FILES or similar structure.
46
     *
47
     * @throws InvalidArgumentException If no valid method or URI can be determined.
48
     *
49
     * @return ServerRequestInterface
50
     */
51
    public function createServerRequestFromArrays(
52
        array $server,
53
        array $headers,
54
        array $cookie,
55
        array $get,
56
        array $post,
57
        array $files
58
    ): ServerRequestInterface {
59
        $method = $this->getMethodFromEnvironment($server);
60
        $uri = $this->getUriFromEnvironmentWithHTTP($server);
61
62
        $protocol = isset($server['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $server['SERVER_PROTOCOL']) : '1.1';
63
64
        $serverRequest = new ServerRequest($method, $uri, $headers, null, $protocol, $server);
65
66
        return $serverRequest
67
            ->withCookieParams($cookie)
68
            ->withQueryParams($get)
69
            ->withParsedBody($post)
70
            ->withUploadedFiles(self::normalizeFiles($files));
71
    }
72
73
    /**
74
     * Create a new server request from the current environment variables.
75
     * Defaults to a GET request to minimise the risk of an InvalidArgumentException.
76
     * Includes the current request headers as supplied by the server through `getallheaders()`.
77
     *
78
     * @throws InvalidArgumentException If no valid method or URI can be determined.
79
     *
80
     * @return ServerRequestInterface
81
     */
82
    public function createServerRequestFromGlobals(): ServerRequestInterface
83
    {
84
        $server = $_SERVER;
85
        if (false === isset($server['REQUEST_METHOD'])) {
86
            $server['REQUEST_METHOD'] = 'GET';
87
        }
88
        $headers = function_exists('getallheaders') ? getallheaders() : [];
89
90
        return $this->createServerRequestFromArrays($_SERVER, $headers, $_COOKIE, $_GET, $_POST, $_FILES);
91
    }
92
93 7
    private function getMethodFromEnvironment(array $environment): string
94
    {
95 7
        if (false === isset($environment['REQUEST_METHOD'])) {
96
            throw new InvalidArgumentException('Cannot determine HTTP method');
97
        }
98
99 7
        return $environment['REQUEST_METHOD'];
100
    }
101
102 7
    private function getUriFromEnvironmentWithHTTP(array $environment): \Psr\Http\Message\UriInterface
103
    {
104 7
        $uri = (new UriFactory())->createUriFromArray($environment);
105 7
        if ($uri->getScheme() === '') {
106 7
            $uri = $uri->withScheme('http');
107
        }
108
109 7
        return $uri;
110
    }
111
112
    /**
113
     * Return an UploadedFile instance array.
114
     *
115
     * @param array $files A array which respect $_FILES structure
116
     *
117
     * @throws InvalidArgumentException for unrecognized values
118
     *
119
     * @return array
120
     */
121
    private static function normalizeFiles(array $files): array
122
    {
123
        $normalized = [];
124
125
        foreach ($files as $key => $value) {
126
            if ($value instanceof UploadedFileInterface) {
127
                $normalized[$key] = $value;
128
            } elseif (is_array($value) && isset($value['tmp_name'])) {
129
                $normalized[$key] = self::createUploadedFileFromSpec($value);
130
            } elseif (is_array($value)) {
131
                $normalized[$key] = self::normalizeFiles($value);
132
                continue;
133
            } else {
134
                throw new InvalidArgumentException('Invalid value in files specification');
135
            }
136
        }
137
138
        return $normalized;
139
    }
140
141
    /**
142
     * Create and return an UploadedFile instance from a $_FILES specification.
143
     *
144
     * If the specification represents an array of values, this method will
145
     * delegate to normalizeNestedFileSpec() and return that return value.
146
     *
147
     * @param array $value $_FILES struct
148
     *
149
     * @return array|UploadedFileInterface
150
     */
151
    private static function createUploadedFileFromSpec(array $value)
152
    {
153
        if (is_array($value['tmp_name'])) {
154
            return self::normalizeNestedFileSpec($value);
155
        }
156
157
        return new UploadedFile(
158
            $value['tmp_name'],
159
            (int) $value['size'],
160
            (int) $value['error'],
161
            $value['name'],
162
            $value['type']
163
        );
164
    }
165
166
    /**
167
     * Normalize an array of file specifications.
168
     *
169
     * Loops through all nested files and returns a normalized array of
170
     * UploadedFileInterface instances.
171
     *
172
     * @param array $files
173
     *
174
     * @return UploadedFileInterface[]
175
     */
176
    private static function normalizeNestedFileSpec(array $files = []): array
177
    {
178
        $normalizedFiles = [];
179
180
        foreach (array_keys($files['tmp_name']) as $key) {
181
            $spec = [
182
                'tmp_name' => $files['tmp_name'][$key],
183
                'size' => $files['size'][$key],
184
                'error' => $files['error'][$key],
185
                'name' => $files['name'][$key],
186
                'type' => $files['type'][$key],
187
            ];
188
            $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
189
        }
190
191
        return $normalizedFiles;
192
    }
193
}
194