Skip to content

Commit c76a644

Browse files
committed
add server request creation from globals
1 parent efdb873 commit c76a644

File tree

3 files changed

+573
-14
lines changed

3 files changed

+573
-14
lines changed

quality/phpstan.neon.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
parameters:
2-
level: max
2+
level: 8
33
paths:
44
- ../src
55
- ../tests

src/ServerRequestFactory.php

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,26 @@
44

55
namespace IngeniozIT\Http\Message;
66

7+
use IngeniozIT\Http\Message\ValueObject\Message\Header;
78
use Psr\Http\Message\{
89
ServerRequestFactoryInterface,
910
StreamFactoryInterface,
11+
UploadedFileFactoryInterface,
12+
UploadedFileInterface,
1013
UriFactoryInterface,
1114
UriInterface,
12-
ServerRequestInterface,
15+
ServerRequestInterface
1316
};
1417
use IngeniozIT\Http\Message\ValueObject\Message\Headers;
1518
use IngeniozIT\Http\Message\ValueObject\Request\Method;
1619
use InvalidArgumentException;
1720

18-
readonly class ServerRequestFactory implements ServerRequestFactoryInterface
21+
readonly final class ServerRequestFactory implements ServerRequestFactoryInterface
1922
{
2023
public function __construct(
2124
private StreamFactoryInterface $streamFactory,
2225
private UriFactoryInterface $uriFactory,
26+
private UploadedFileFactoryInterface $uploadedFileFactory,
2327
) {
2428
}
2529

@@ -33,7 +37,7 @@ public function createServerRequest(string $method, $uri, array $serverParams =
3337
return new ServerRequest(
3438
protocolVersion: '',
3539
headers: new Headers([]),
36-
body: $this->streamFactory->createStream(),
40+
body: $this->streamFactory->createStreamFromFile('php://input'),
3741
method: Method::tryFrom($method) ?? throw new InvalidArgumentException("Invalid method: $method"),
3842
requestTarget: '',
3943
uri: is_string($uri) ? $this->uriFactory->createUri($uri) : $uri,
@@ -45,4 +49,143 @@ public function createServerRequest(string $method, $uri, array $serverParams =
4549
attributes: [],
4650
);
4751
}
52+
53+
/**
54+
* @param array<string, mixed> $globals
55+
*/
56+
public function createServerRequestFromGlobals(array $globals): ServerRequestInterface
57+
{
58+
/** @var array<string, mixed> $uploadedFiles */
59+
$uploadedFiles = $this->extractUploadedFiles($globals['_FILES'] ?? []);
60+
return new ServerRequest(
61+
protocolVersion: $this->extractProtocolVersion($globals['_SERVER'] ?? []),
62+
headers: new Headers($this->extractHeaders($globals['_SERVER'] ?? [])),
63+
body: $this->streamFactory->createStreamFromFile('php://input'),
64+
method: $this->extractMethod($globals['_SERVER'] ?? []),
65+
requestTarget: $this->extractRequestTarget($globals['_SERVER'] ?? []),
66+
uri: $this->extractUri($globals['_SERVER'] ?? []),
67+
serverParams: $globals['_SERVER'] ?? [],
68+
cookieParams: $this->extractCookies($globals['_COOKIE'] ?? [], $globals['_SERVER'] ?? []),
69+
queryParams: $globals['_GET'] ?? [],
70+
uploadedFiles: $uploadedFiles,
71+
parsedBody: $globals['_POST'] ?? null,
72+
attributes: [],
73+
);
74+
}
75+
76+
/**
77+
* @param array<string, mixed> $serverParams
78+
*/
79+
private function extractProtocolVersion(array $serverParams): string
80+
{
81+
$serverProtocol = $serverParams['SERVER_PROTOCOL'] ?? '';
82+
return explode('/', $serverProtocol)[1] ?? '1.1';
83+
}
84+
85+
/**
86+
* @param array<string, mixed> $serverParams
87+
* @return Header[]
88+
*/
89+
private function extractHeaders(array $serverParams): array
90+
{
91+
$headers = [];
92+
foreach ($serverParams as $key => $value) {
93+
if (!str_starts_with($key, 'HTTP_')) {
94+
continue;
95+
}
96+
$headerName = str_replace('_', '-', substr($key, 5));
97+
$headers[$headerName] = new Header($headerName, $this->formatHeaderValue($value));
98+
}
99+
return $headers;
100+
}
101+
102+
/**
103+
* @return string[]
104+
*/
105+
private function formatHeaderValue(string $value): array
106+
{
107+
return array_map('trim', explode(',', $value));
108+
}
109+
110+
/**
111+
* @param array<string, mixed> $serverParams
112+
* @SuppressWarnings(PHPMD.StaticAccess)
113+
*/
114+
private function extractMethod(array $serverParams): Method
115+
{
116+
$method = $serverParams['REQUEST_METHOD'] ?? 'GET';
117+
return Method::tryFrom($method) ?? throw new InvalidArgumentException("Invalid method: $method");
118+
}
119+
120+
/**
121+
* @param array<string, mixed> $serverParams
122+
*/
123+
private function extractRequestTarget(array $serverParams): string
124+
{
125+
return $serverParams['REQUEST_URI'] ?? '';
126+
}
127+
128+
/**
129+
* @param array<string, mixed> $serverParams
130+
*/
131+
private function extractUri(array $serverParams): UriInterface
132+
{
133+
return $this->uriFactory->createUri(
134+
($serverParams['HTTP_HOST'] ?? '') .
135+
($serverParams['REQUEST_URI'] ?? '')
136+
);
137+
}
138+
139+
/**
140+
* @param array<string, string> $cookies
141+
* @param array<string, mixed> $serverParams
142+
* @return array<string, string>
143+
*/
144+
private function extractCookies(array $cookies, array $serverParams): array
145+
{
146+
$headerCookies = array_filter(explode(';', $serverParams['HTTP_COOKIE'] ?? ''));
147+
foreach ($headerCookies as $headerCookie) {
148+
$cookieData = explode('=', $headerCookie, 2);
149+
$cookies[trim($cookieData[0])] = trim($cookieData[1] ?? '');
150+
}
151+
return $cookies;
152+
}
153+
154+
/**
155+
* @param array<string, mixed> $files
156+
* @return array<string, mixed>|UploadedFileInterface
157+
*/
158+
private function extractUploadedFiles(array $files): UploadedFileInterface|array
159+
{
160+
if (array_key_exists('tmp_name', $files)) {
161+
$files = !is_array($files['tmp_name']) ?
162+
$this->uploadedFileFactory->createUploadedFile(
163+
$this->streamFactory->createStreamFromFile($files['tmp_name']),
164+
$files['size'] ?? null,
165+
$files['error'] ?? UPLOAD_ERR_OK,
166+
$files['name'] ?? null,
167+
$files['type'] ?? null,
168+
) :
169+
$this->flipFileArray($files);
170+
}
171+
172+
return $files instanceof UploadedFileInterface ?
173+
$files :
174+
array_map($this->extractUploadedFiles(...), $files);
175+
}
176+
177+
/**
178+
* @param array<string, mixed[]> $files
179+
* @return mixed[]
180+
*/
181+
private function flipFileArray(array $files): array
182+
{
183+
foreach (array_keys($files['tmp_name']) as $index) {
184+
foreach (['tmp_name', 'size', 'error', 'name', 'type'] as $key) {
185+
$files[$index][$key] = $files[$key][$index];
186+
unset($files[$key][$index]);
187+
}
188+
}
189+
return $files;
190+
}
48191
}

0 commit comments

Comments
 (0)