Passed
Push — master ( 9013bb...125cf1 )
by Kirill
03:56
created

SapiRequestFactory::fromGlobals()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 10
rs 10
cc 2
nc 1
nop 0
1
<?php
2
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Spiral\Http;
13
14
use Psr\Http\Message\ServerRequestFactoryInterface;
15
use Psr\Http\Message\ServerRequestInterface;
16
use Psr\Http\Message\StreamFactoryInterface;
17
use Psr\Http\Message\StreamInterface;
18
use Psr\Http\Message\UploadedFileFactoryInterface;
19
use Psr\Http\Message\UriFactoryInterface;
20
use Psr\Http\Message\UriInterface;
21
22
/**
23
 * @source https://github.com/yiisoft/yii-web/blob/master/src/ServerRequestFactory.php
24
 * Used by permission from Alex Makarov.
25
 *
26
 * Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com) All rights reserved.
27
 *
28
 * Redistribution and use in source and binary forms, with or without modification, are
29
 * permitted provided that the following conditions are met:
30
 *
31
 * Redistributions of source code must retain the above copyright notice, this list
32
 * of conditions and the following disclaimer.
33
 *
34
 * Redistributions in binary form must reproduce the above copyright notice, this list
35
 * of conditions and the following disclaimer in the documentation and/or other materials provided
36
 * with the distribution.
37
 *
38
 * Neither the name of Yii Software LLC nor the names of its contributors may be used to
39
 * endorse or promote products derived from this software without specific prior written permission.
40
 *
41
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
42
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
43
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
44
 *
45
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
47
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
48
 *
49
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
50
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
51
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52
 *
53
 * @codeCoverageIgnore
54
 */
55
final class SapiRequestFactory
56
{
57
    /** @var ServerRequestFactoryInterface */
58
    private $requestFactory;
59
60
    /** @var UriFactoryInterface */
61
    private $uriFactory;
62
63
    /** @var StreamFactoryInterface */
64
    private $streamFactory;
65
66
    /** @var UploadedFileFactoryInterface */
67
    private $uploadedFileFactory;
68
69
    /**
70
     * @param ServerRequestFactoryInterface $requestFactory
71
     * @param StreamFactoryInterface        $streamFactory
72
     * @param UploadedFileFactoryInterface  $uploadedFileFactory
73
     * @param UriFactoryInterface           $uriFactory
74
     */
75
    public function __construct(
76
        ServerRequestFactoryInterface $requestFactory,
77
        UriFactoryInterface $uriFactory,
78
        StreamFactoryInterface $streamFactory,
79
        UploadedFileFactoryInterface $uploadedFileFactory
80
    ) {
81
        $this->requestFactory = $requestFactory;
82
        $this->uriFactory = $uriFactory;
83
        $this->streamFactory = $streamFactory;
84
        $this->uploadedFileFactory = $uploadedFileFactory;
85
    }
86
87
    /**
88
     * @return ServerRequestInterface
89
     */
90
    public function fromGlobals(): ServerRequestInterface
91
    {
92
        return $this->createFromParameters(
93
            $_SERVER,
94
            self::getHeadersFromGlobals(),
95
            $_COOKIE,
96
            $_GET,
97
            $_POST,
98
            $_FILES,
99
            \fopen('php://input', 'r') ?: null
100
        );
101
    }
102
103
    /**
104
     * @param array                                $server
105
     * @param array                                $headers
106
     * @param array                                $cookies
107
     * @param array                                $get
108
     * @param array                                $post
109
     * @param array                                $files
110
     * @param StreamInterface|resource|string|null $body
111
     * @return ServerRequestInterface
112
     */
113
    public function createFromParameters(
114
        array $server,
115
        array $headers = [],
116
        array $cookies = [],
117
        array $get = [],
118
        array $post = [],
119
        array $files = [],
120
        $body = null
121
    ): ServerRequestInterface {
122
        $method = $server['REQUEST_METHOD'] ?? 'GET';
123
124
        $uri = $this->getUri($server, $headers);
125
126
        $request = $this->requestFactory->createServerRequest($method, $uri, $server);
127
        foreach ($headers as $name => $value) {
128
            $request = $request->withAddedHeader($name, $value);
129
        }
130
131
        $protocol = '1.1';
132
        if (!empty($_SERVER['SERVER_PROTOCOL'])) {
133
            $protocol = str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']);
134
        }
135
136
        $request = $request
137
            ->withProtocolVersion($protocol)
138
            ->withQueryParams($get)
139
            ->withParsedBody($post)
140
            ->withCookieParams($cookies)
141
            ->withUploadedFiles($this->getUploadedFilesArray($files));
142
143
        if ($body === null) {
144
            return $request;
145
        }
146
147
        if (\is_resource($body)) {
148
            $body = $this->streamFactory->createStreamFromResource($body);
149
        } elseif (\is_string($body)) {
150
            $body = $this->streamFactory->createStream($body);
151
        } elseif (!$body instanceof StreamInterface) {
0 ignored issues
show
introduced by
$body is always a sub-type of Psr\Http\Message\StreamInterface.
Loading history...
152
            throw new \InvalidArgumentException(
153
                'Body parameter for ServerRequestFactory::createFromParameters() '
154
                . 'must be instance of StreamInterface, resource or null.'
155
            );
156
        }
157
158
        return $request->withBody($body);
159
    }
160
161
    /**
162
     * @param array $server
163
     * @param array $headers
164
     * @return UriInterface
165
     */
166
    private function getUri(array $server, array $headers): UriInterface
0 ignored issues
show
Unused Code introduced by
The parameter $headers is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

166
    private function getUri(array $server, /** @scrutinizer ignore-unused */ array $headers): UriInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
167
    {
168
        $uri = $this->uriFactory->createUri();
169
        if (isset($server['HTTPS'])) {
170
            $uri = $uri->withScheme($server['HTTPS'] === 'on' ? 'https' : 'http');
171
        }
172
173
        if (isset($server['HTTP_HOST'])) {
174
            if (1 === \preg_match('/^(.+)\:(\d+)$/', $server['HTTP_HOST'], $matches)) {
175
                $uri = $uri->withHost($matches[1])->withPort($matches[2]);
176
            } else {
177
                $uri = $uri->withHost($server['HTTP_HOST']);
178
            }
179
        } elseif (isset($server['SERVER_NAME'])) {
180
            $uri = $uri->withHost($server['SERVER_NAME']);
181
        }
182
183
        if (isset($server['SERVER_PORT'])) {
184
            $uri = $uri->withPort($server['SERVER_PORT']);
185
        }
186
187
        if (isset($server['REQUEST_URI'])) {
188
            $uri = $uri->withPath(\explode('?', $server['REQUEST_URI'])[0]);
189
        }
190
191
        if (isset($server['QUERY_STRING'])) {
192
            $uri = $uri->withQuery($server['QUERY_STRING']);
193
        }
194
195
        return $uri;
196
    }
197
198
    /**
199
     * @return array
200
     */
201
    private static function getHeadersFromGlobals(): array
202
    {
203
        if (\function_exists('getallheaders')) {
204
            $headers = getallheaders();
205
        } else {
206
            $headers = [];
207
            foreach ($_SERVER as $name => $value) {
208
                if (strncmp($name, 'HTTP_', 5) === 0) {
209
                    $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
210
                    $headers[$name] = $value;
211
                }
212
            }
213
        }
214
215
        return $headers;
216
    }
217
218
    /**
219
     * @param array $filesArray
220
     * @return array
221
     */
222
    private function getUploadedFilesArray(array $filesArray): array
223
    {
224
        $files = [];
225
        foreach ($filesArray as $class => $info) {
226
            $files[$class] = [];
227
            $this->populateUploadedFileRecursive(
228
                $files[$class],
229
                $info['name'],
230
                $info['tmp_name'],
231
                $info['type'],
232
                $info['size'],
233
                $info['error']
234
            );
235
        }
236
237
        return $files;
238
    }
239
240
    /**
241
     * Populates uploaded files array from $_FILE data structure recursively.
242
     *
243
     * @param array $files     uploaded files array to be populated.
244
     * @param mixed $names     file names provided by PHP
245
     * @param mixed $tempNames temporary file names provided by PHP
246
     * @param mixed $types     file types provided by PHP
247
     * @param mixed $sizes     file sizes provided by PHP
248
     * @param mixed $errors    uploading issues provided by PHP
249
     * @since 3.0.0
250
     */
251
    private function populateUploadedFileRecursive(&$files, $names, $tempNames, $types, $sizes, $errors): void
252
    {
253
        if (\is_array($names)) {
254
            foreach ($names as $i => $name) {
255
                $files[$i] = [];
256
                $this->populateUploadedFileRecursive(
257
                    $files[$i],
258
                    $name,
259
                    $tempNames[$i],
260
                    $types[$i],
261
                    $sizes[$i],
262
                    $errors[$i]
263
                );
264
            }
265
        } else {
266
            try {
267
                $stream = $this->streamFactory->createStreamFromFile($tempNames);
268
            } catch (\RuntimeException $e) {
269
                $stream = $this->streamFactory->createStream();
270
            }
271
272
            $files = $this->uploadedFileFactory->createUploadedFile(
273
                $stream,
274
                (int)$sizes,
275
                (int)$errors,
276
                $names,
277
                $types
278
            );
279
        }
280
    }
281
}
282