Passed
Push — master ( b86ef6...5fed0d )
by Alexander
02:39
created

ServerRequestFactory::createFromGlobals()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 8
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 10
ccs 9
cts 9
cp 1
crap 2
rs 10
1
<?php
2
3
namespace Yiisoft\Yii\Web;
4
5
use Psr\Http\Message\ServerRequestFactoryInterface;
6
use Psr\Http\Message\ServerRequestInterface;
7
use Psr\Http\Message\StreamFactoryInterface;
8
use Psr\Http\Message\StreamInterface;
9
use Psr\Http\Message\UploadedFileFactoryInterface;
10
use Psr\Http\Message\UriFactoryInterface;
11
use Psr\Http\Message\UriInterface;
12
13
final class ServerRequestFactory
14
{
15
    private ServerRequestFactoryInterface $serverRequestFactory;
16
    private UriFactoryInterface $uriFactory;
17
    private UploadedFileFactoryInterface $uploadedFileFactory;
18
    private StreamFactoryInterface $streamFactory;
19
20 33
    public function __construct(
21
        ServerRequestFactoryInterface $serverRequestFactory,
22
        UriFactoryInterface $uriFactory,
23
        UploadedFileFactoryInterface $uploadedFileFactory,
24
        StreamFactoryInterface $streamFactory
25
    ) {
26 33
        $this->serverRequestFactory = $serverRequestFactory;
27 33
        $this->uriFactory = $uriFactory;
28 33
        $this->uploadedFileFactory = $uploadedFileFactory;
29 33
        $this->streamFactory = $streamFactory;
30 33
    }
31
32 16
    public function createFromGlobals(): ServerRequestInterface
33
    {
34 16
        return $this->createFromParameters(
35 16
            $_SERVER,
36 16
            $this->getHeadersFromGlobals(),
37 16
            $_COOKIE,
38 16
            $_GET,
39 16
            $_POST,
40 16
            $_FILES,
41 16
            \fopen('php://input', 'r') ?: null
42
        );
43
    }
44
45
    /**
46
     * @param array $server
47
     * @param array $headers
48
     * @param array $cookies
49
     * @param array $get
50
     * @param array $post
51
     * @param array $files
52
     * @param StreamInterface|resource|string|null $body
53
     * @return ServerRequestInterface
54
     */
55 33
    public function createFromParameters(array $server, array $headers = [], array $cookies = [], array $get = [], array $post = [], array $files = [], $body = null): ServerRequestInterface
56
    {
57 33
        $method = $server['REQUEST_METHOD'] ?? null;
58 33
        if ($method === null) {
59 1
            throw new \RuntimeException('Unable to determine HTTP request method.');
60
        }
61
62 32
        $uri = $this->getUri($server);
63
64 32
        $request = $this->serverRequestFactory->createServerRequest($method, $uri, $server);
65
66 32
        foreach ($headers as $name => $value) {
67 7
            $request = $request->withAddedHeader($name, $value);
68
        }
69
70 32
        $protocol = '1.1';
71 32
        if (array_key_exists('SERVER_PROTOCOL', $server) && $server['SERVER_PROTOCOL'] !== '') {
72 2
            $protocol = str_replace('HTTP/', '', $server['SERVER_PROTOCOL']);
73
        }
74
75
        $request = $request
76 32
            ->withProtocolVersion($protocol)
77 32
            ->withQueryParams($get)
78 32
            ->withParsedBody($post)
79 32
            ->withCookieParams($cookies)
80 32
            ->withUploadedFiles($this->getUploadedFilesArray($files));
81
82 32
        if ($body === null) {
83 16
            return $request;
84
        }
85
86 16
        if (\is_resource($body)) {
87 16
            $body = $this->streamFactory->createStreamFromResource($body);
88
        } elseif (\is_string($body)) {
89
            $body = $this->streamFactory->createStream($body);
90
        } elseif (!$body instanceof StreamInterface) {
0 ignored issues
show
introduced by
$body is always a sub-type of Psr\Http\Message\StreamInterface.
Loading history...
91
            throw new \InvalidArgumentException('Body parameter for ServerRequestFactory::createFromParameters() must be instance of StreamInterface, resource or null.');
92
        }
93
94 16
        return $request->withBody($body);
95
    }
96
97 32
    private function getUri(array $server): UriInterface
98
    {
99 32
        $uri = $this->uriFactory->createUri();
100
101 32
        if (array_key_exists('HTTPS', $server) && $server['HTTPS'] !== '' && $server['HTTPS'] !== 'off') {
102 4
            $uri = $uri->withScheme('https');
103
        } else {
104 28
            $uri = $uri->withScheme('http');
105
        }
106
107 32
        if (isset($server['HTTP_HOST'])) {
108 14
            if (1 === \preg_match('/^(.+)\:(\d+)$/', $server['HTTP_HOST'], $matches)) {
109 6
                $uri = $uri->withHost($matches[1])->withPort($matches[2]);
110
            } else {
111 14
                $uri = $uri->withHost($server['HTTP_HOST']);
112
            }
113 18
        } elseif (isset($server['SERVER_NAME'])) {
114 2
            $uri = $uri->withHost($server['SERVER_NAME']);
115
        }
116
117 32
        if (isset($server['SERVER_PORT'])) {
118 2
            $uri = $uri->withPort($server['SERVER_PORT']);
119
        }
120
121 32
        if (isset($server['REQUEST_URI'])) {
122 2
            $uri = $uri->withPath(\explode('?', $server['REQUEST_URI'])[0]);
123
        }
124
125 32
        if (isset($server['QUERY_STRING'])) {
126 2
            $uri = $uri->withQuery($server['QUERY_STRING']);
127
        }
128
129 32
        return $uri;
130
    }
131
132 16
    private function getHeadersFromGlobals(): array
133
    {
134 16
        if (\function_exists('getallheaders')) {
135
            $headers = getallheaders();
136
            if ($headers === false) {
137
                $headers = [];
138
            }
139
        } else {
140 16
            $headers = [];
141 16
            foreach ($_SERVER as $name => $value) {
142 16
                if (strncmp($name, 'HTTP_', 5) === 0) {
143 7
                    $name = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($name, 5))));
144 7
                    $headers[$name] = $value;
145
                }
146
            }
147
        }
148
149 16
        return $headers;
150
    }
151
152 32
    private function getUploadedFilesArray(array $filesArray): array
153
    {
154
        // TODO: simplify?
155 32
        $files = [];
156 32
        foreach ($filesArray as $class => $info) {
157
            $files[$class] = [];
158
            $this->populateUploadedFileRecursive($files[$class], $info['name'], $info['tmp_name'], $info['type'], $info['size'], $info['error']);
159
        }
160 32
        return $files;
161
    }
162
163
    /**
164
     * Populates uploaded files array from $_FILE data structure recursively.
165
     * @param array $files uploaded files array to be populated.
166
     * @param mixed $names file names provided by PHP
167
     * @param mixed $tempNames temporary file names provided by PHP
168
     * @param mixed $types file types provided by PHP
169
     * @param mixed $sizes file sizes provided by PHP
170
     * @param mixed $errors uploading issues provided by PHP
171
     */
172
    private function populateUploadedFileRecursive(array &$files, $names, $tempNames, $types, $sizes, $errors): void
173
    {
174
        if (\is_array($names)) {
175
            foreach ($names as $i => $name) {
176
                $files[$i] = [];
177
                $this->populateUploadedFileRecursive($files[$i], $name, $tempNames[$i], $types[$i], $sizes[$i], $errors[$i]);
178
            }
179
        } else {
180
            try {
181
                $stream = $this->streamFactory->createStreamFromFile($tempNames);
182
            } catch (\RuntimeException $e) {
183
                $stream = $this->streamFactory->createStream();
184
            }
185
186
            $files = $this->uploadedFileFactory->createUploadedFile(
187
                $stream,
188
                (int)$sizes,
189
                (int)$errors,
190
                $names,
191
                $types
192
            );
193
        }
194
    }
195
}
196