Completed
Push — master ( 267aac...08ed0c )
by Tobias
01:46
created

ServerRequestCreator::normalizeFiles()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

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