Passed
Push — master ( 27873b...87f821 )
by Alexander
02:12
created

src/ServerRequestFactory.php (2 issues)

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 $serverRequestFactory;
16
    private $uriFactory;
17
    private $uploadedFileFactory;
18
    private $streamFactory;
19
20
21
    public function __construct(
22
        ServerRequestFactoryInterface $serverRequestFactory,
23
        UriFactoryInterface $uriFactory,
24
        UploadedFileFactoryInterface $uploadedFileFactory,
25
        StreamFactoryInterface $streamFactory
26
    ) {
27
        $this->serverRequestFactory = $serverRequestFactory;
28
        $this->uriFactory = $uriFactory;
29
        $this->uploadedFileFactory = $uploadedFileFactory;
30
        $this->streamFactory = $streamFactory;
31
    }
32
33
    public function createFromGlobals(): ServerRequestInterface
34
    {
35
        return $this->createFromParameters(
36
            $_SERVER,
37
            self::getHeadersFromGlobals(),
38
            $_COOKIE,
39
            $_GET,
40
            $_POST,
41
            $_FILES,
42
            \fopen('php://input', 'r') ?: null
43
        );
44
    }
45
46
    /**
47
     * @param array $server
48
     * @param array $headers
49
     * @param array $cookies
50
     * @param array $get
51
     * @param array $post
52
     * @param array $files
53
     * @param StreamInterface|resource|string|null $body
54
     * @return ServerRequestInterface
55
     */
56
    public function createFromParameters(array $server, array $headers = [], array $cookies = [], array $get = [], array $post = [], array $files = [], $body = null): ServerRequestInterface
57
    {
58
        $method = $server['REQUEST_METHOD'] ?? null;
59
        if ($method === null) {
60
            throw new \RuntimeException('Unable to determine HTTP request method.');
61
        }
62
63
        $uri = $this->getUri($server, $headers);
64
65
        $request = $this->serverRequestFactory->createServerRequest($method, $uri, $server);
66
67
        foreach ($headers as $name => $value) {
68
            $request = $request->withAddedHeader($name, $value);
69
        }
70
71
        $protocol = '1.1';
72
        if (!empty($_SERVER['SERVER_PROTOCOL'])) {
73
            $protocol = str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']);
74
        }
75
76
        $request = $request
77
            ->withProtocolVersion($protocol)
78
            ->withQueryParams($get)
79
            ->withParsedBody($post)
80
            ->withCookieParams($cookies)
81
            ->withUploadedFiles($this->getUploadedFilesArray($files));
82
83
        if ($body === null) {
84
            return $request;
85
        }
86
87
        if (\is_resource($body)) {
88
            $body = $this->streamFactory->createStreamFromResource($body);
89
        } elseif (\is_string($body)) {
90
            $body = $this->streamFactory->createStream($body);
91
        } elseif (!$body instanceof StreamInterface) {
92
            throw new \InvalidArgumentException('Body parameter for ServerRequestFactory::createFromParameters() must be instance of StreamInterface, resource or null.');
93
        }
94
95
96
        return $request->withBody($body);
97
    }
98
99
    private function getUri(array $server, array $headers): UriInterface
0 ignored issues
show
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

99
    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...
100
    {
101
        $uri = $this->uriFactory->createUri();
102
103
        if (isset($server['HTTPS'])) {
104
            $uri = $uri->withScheme($server['HTTPS'] === 'on' ? 'https' : 'http');
105
        }
106
107
        if (isset($server['HTTP_HOST'])) {
108
            if (1 === \preg_match('/^(.+)\:(\d+)$/', $server['HTTP_HOST'], $matches)) {
109
                $uri = $uri->withHost($matches[1])->withPort($matches[2]);
110
            } else {
111
                $uri = $uri->withHost($server['HTTP_HOST']);
112
            }
113
        } elseif (isset($server['SERVER_NAME'])) {
114
            $uri = $uri->withHost($server['SERVER_NAME']);
115
        }
116
117
        if (isset($server['SERVER_PORT'])) {
118
            $uri = $uri->withPort($server['SERVER_PORT']);
119
        }
120
121
        if (isset($server['REQUEST_URI'])) {
122
            $uri = $uri->withPath(\explode('?', $server['REQUEST_URI'])[0]);
123
        }
124
125
        if (isset($server['QUERY_STRING'])) {
126
            $uri = $uri->withQuery($server['QUERY_STRING']);
127
        }
128
129
        return $uri;
130
    }
131
132
    private static function getHeadersFromGlobals(): array
133
    {
134
        if (\function_exists('getallheaders')) {
135
            $headers = getallheaders();
136
        } else {
137
            $headers = [];
138
            foreach ($_SERVER as $name => $value) {
139
                if (strncmp($name, 'HTTP_', 5) === 0) {
140
                    $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
141
                    $headers[$name] = $value;
142
                }
143
            }
144
        }
145
146
        return $headers;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $headers could return the type false which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
147
    }
148
149
    private function getUploadedFilesArray(array $filesArray): array
150
    {
151
        // TODO: simplify?
152
        $files = [];
153
        foreach ($filesArray as $class => $info) {
154
            $files[$class] = [];
155
            $this->populateUploadedFileRecursive($files[$class], $info['name'], $info['tmp_name'], $info['type'], $info['size'], $info['error']);
156
        }
157
        return $files;
158
    }
159
160
    /**
161
     * Populates uploaded files array from $_FILE data structure recursively.
162
     * @param array $files uploaded files array to be populated.
163
     * @param mixed $names file names provided by PHP
164
     * @param mixed $tempNames temporary file names provided by PHP
165
     * @param mixed $types file types provided by PHP
166
     * @param mixed $sizes file sizes provided by PHP
167
     * @param mixed $errors uploading issues provided by PHP
168
     */
169
    private function populateUploadedFileRecursive(&$files, $names, $tempNames, $types, $sizes, $errors): void
170
    {
171
        if (\is_array($names)) {
172
            foreach ($names as $i => $name) {
173
                $files[$i] = [];
174
                $this->populateUploadedFileRecursive($files[$i], $name, $tempNames[$i], $types[$i], $sizes[$i], $errors[$i]);
175
            }
176
        } else {
177
            try {
178
                $stream = $this->streamFactory->createStreamFromFile($tempNames);
179
            } catch (\RuntimeException $e) {
180
                $stream = $this->streamFactory->createStream();
181
            }
182
183
            $files = $this->uploadedFileFactory->createUploadedFile(
184
                $stream,
185
                (int)$sizes,
186
                (int)$errors,
187
                $names,
188
                $types
189
            );
190
        }
191
    }
192
}
193