Completed
Push — master ( d89245...98d38e )
by Tobias
02:16
created

createServerRequestFromGlobals()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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