44
55namespace IngeniozIT \Http \Message ;
66
7+ use IngeniozIT \Http \Message \ValueObject \Message \Header ;
78use Psr \Http \Message \{
89 ServerRequestFactoryInterface ,
910 StreamFactoryInterface ,
11+ UploadedFileFactoryInterface ,
12+ UploadedFileInterface ,
1013 UriFactoryInterface ,
1114 UriInterface ,
12- ServerRequestInterface ,
15+ ServerRequestInterface
1316};
1417use IngeniozIT \Http \Message \ValueObject \Message \Headers ;
1518use IngeniozIT \Http \Message \ValueObject \Request \Method ;
1619use 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