Passed
Push — master ( 73ccaf...ffe39b )
by Evgeniy
06:43 queued 04:17
created

ServerRequestFactory::getHeadersFromGlobals()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5.5069

Importance

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