Skip to content

Commit 7534d04

Browse files
committed
feat: Refatorar ReactServerCompat para melhorar a extração e criação de requisições, além de adicionar testes abrangentes
1 parent edcf93a commit 7534d04

File tree

5 files changed

+688
-93
lines changed

5 files changed

+688
-93
lines changed

src/Security/RuntimeBlockingDetector.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ private function reportBlocking(float $duration): void
117117
return !isset($frame['class']) || $frame['class'] !== self::class;
118118
});
119119

120-
$relevantFrame = reset($filteredTrace) !== false ? reset($filteredTrace) : ['file' => 'unknown', 'line' => 0, 'function' => 'unknown'];
120+
$relevantFrame = reset($filteredTrace) !== false
121+
? reset($filteredTrace)
122+
: ['file' => 'unknown', 'line' => 0, 'function' => 'unknown'];
121123

122124
($this->callback)([
123125
'duration' => $duration,

src/Server/ReactServerCompat.php

Lines changed: 201 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -88,99 +88,19 @@ private function handleRequestCompat(\React\Http\Message\ServerRequest $reactReq
8888
{
8989
return new Promise(function ($resolve, $reject) use ($reactRequest) {
9090
try {
91-
// Extract request data without using PSR-7 interfaces
92-
$method = $reactRequest->getMethod();
93-
$uri = (string) $reactRequest->getUri();
94-
$headers = $reactRequest->getHeaders();
95-
$body = (string) $reactRequest->getBody();
96-
97-
// Parse URI
98-
$parsedUrl = parse_url($uri);
99-
$path = $parsedUrl['path'] ?? '/';
100-
$query = $parsedUrl['query'] ?? '';
101-
102-
// Create PivotPHP Request manually
103-
$pivotRequest = new \PivotPHP\Core\Http\Request($method, $path, $path);
104-
105-
// Set headers
106-
foreach ($headers as $name => $values) {
107-
$headerValue = is_array($values) ? implode(', ', $values) : $values;
108-
$pivotRequest = $pivotRequest->withHeader($name, $headerValue);
109-
}
110-
111-
// Parse query params
112-
if ($query !== '') {
113-
parse_str($query, $queryParams);
114-
$pivotRequest = $pivotRequest->withQueryParams($queryParams);
115-
}
116-
117-
// Parse body
118-
if ($body !== '') {
119-
$contentType = $pivotRequest->getHeaderLine('Content-Type');
120-
121-
if (str_contains($contentType, 'application/json')) {
122-
$parsedBody = json_decode($body, true);
123-
if (is_array($parsedBody)) {
124-
$pivotRequest = $pivotRequest->withParsedBody($parsedBody);
125-
}
126-
} elseif (str_contains($contentType, 'application/x-www-form-urlencoded')) {
127-
parse_str($body, $parsedBody);
128-
$pivotRequest = $pivotRequest->withParsedBody($parsedBody);
129-
}
130-
131-
// Set body stream
132-
$stream = new \PivotPHP\Core\Http\Psr7\Stream($body);
133-
$pivotRequest = $pivotRequest->withBody($stream);
134-
}
135-
136-
// Create PivotPHP Response
137-
$pivotResponse = new \PivotPHP\Core\Http\Response();
138-
139-
// Find and execute route
140-
$route = \PivotPHP\Core\Routing\Router::identify($method, $path);
141-
142-
if ($route !== null) {
143-
// Execute route handler
144-
$handler = $route['handler'];
145-
146-
// Capture output
147-
ob_start();
148-
$handler($pivotRequest, $pivotResponse);
149-
$output = ob_get_clean();
150-
151-
// Create React response manually
152-
$reactResponse = new \React\Http\Message\Response(
153-
200,
154-
['Content-Type' => 'application/json'],
155-
$output !== false ? $output : ''
156-
);
157-
158-
$resolve($reactResponse);
159-
} else {
160-
// 404 Not Found
161-
$notFoundBody = json_encode(['error' => 'Not Found']);
162-
$reactResponse = new \React\Http\Message\Response(
163-
404,
164-
['Content-Type' => 'application/json'],
165-
$notFoundBody !== false ? $notFoundBody : '{"error":"Not Found"}'
166-
);
167-
168-
$resolve($reactResponse);
169-
}
170-
} catch (\Exception $e) {
171-
$this->logger->error('Error handling request', [
172-
'error' => $e->getMessage(),
173-
'trace' => $e->getTraceAsString(),
174-
]);
175-
176-
$errorBody = json_encode(['error' => 'Internal Server Error']);
177-
$reactResponse = new \React\Http\Message\Response(
178-
500,
179-
['Content-Type' => 'application/json'],
180-
$errorBody !== false ? $errorBody : '{"error":"Internal Server Error"}'
181-
);
91+
// Extract basic request data from ReactPHP request
92+
$requestData = $this->extractRequestData($reactRequest);
93+
94+
// Create PivotPHP Request from extracted data
95+
$pivotRequest = $this->createPivotRequest($requestData);
96+
97+
// Process the request through routing and get response
98+
$reactResponse = $this->processRoute($pivotRequest, $requestData);
18299

183100
$resolve($reactResponse);
101+
} catch (\Exception $e) {
102+
$errorResponse = $this->createErrorResponse($e);
103+
$resolve($errorResponse);
184104
}
185105
});
186106
}
@@ -238,4 +158,194 @@ public function isRunning(): bool
238158
{
239159
return $this->running;
240160
}
161+
162+
/**
163+
* Extract basic request data from ReactPHP ServerRequest
164+
*/
165+
private function extractRequestData(\React\Http\Message\ServerRequest $reactRequest): array
166+
{
167+
$uri = (string) $reactRequest->getUri();
168+
$parsedUrl = parse_url($uri);
169+
170+
return [
171+
'method' => $reactRequest->getMethod(),
172+
'uri' => $uri,
173+
'path' => $parsedUrl['path'] ?? '/',
174+
'query' => $parsedUrl['query'] ?? '',
175+
'headers' => $reactRequest->getHeaders(),
176+
'body' => (string) $reactRequest->getBody(),
177+
];
178+
}
179+
180+
/**
181+
* Create PivotPHP Request from extracted request data
182+
*/
183+
private function createPivotRequest(array $requestData): \PivotPHP\Core\Http\Request
184+
{
185+
// Create base PivotPHP Request
186+
$pivotRequest = new \PivotPHP\Core\Http\Request(
187+
$requestData['method'],
188+
$requestData['path'],
189+
$requestData['path']
190+
);
191+
192+
// Apply headers
193+
$pivotRequest = $this->applyHeaders($pivotRequest, $requestData['headers']);
194+
195+
// Apply query parameters
196+
$pivotRequest = $this->applyQueryParameters($pivotRequest, $requestData['query']);
197+
198+
// Apply body data
199+
$pivotRequest = $this->applyBodyData($pivotRequest, $requestData['body']);
200+
201+
return $pivotRequest;
202+
}
203+
204+
/**
205+
* Apply headers to PivotPHP Request
206+
*/
207+
private function applyHeaders(
208+
\PivotPHP\Core\Http\Request $pivotRequest,
209+
array $headers
210+
): \PivotPHP\Core\Http\Request {
211+
foreach ($headers as $name => $values) {
212+
$headerValue = is_array($values) ? implode(', ', $values) : $values;
213+
$pivotRequest = $pivotRequest->withHeader($name, $headerValue);
214+
}
215+
216+
return $pivotRequest;
217+
}
218+
219+
/**
220+
* Apply query parameters to PivotPHP Request
221+
*/
222+
private function applyQueryParameters(
223+
\PivotPHP\Core\Http\Request $pivotRequest,
224+
string $query
225+
): \PivotPHP\Core\Http\Request {
226+
if ($query !== '') {
227+
parse_str($query, $queryParams);
228+
return $pivotRequest->withQueryParams($queryParams);
229+
}
230+
231+
return $pivotRequest;
232+
}
233+
234+
/**
235+
* Apply body data to PivotPHP Request based on content type
236+
*/
237+
private function applyBodyData(\PivotPHP\Core\Http\Request $pivotRequest, string $body): \PivotPHP\Core\Http\Request
238+
{
239+
if ($body === '') {
240+
return $pivotRequest;
241+
}
242+
243+
$contentType = $pivotRequest->getHeaderLine('Content-Type');
244+
245+
// Parse body based on content type
246+
if (str_contains($contentType, 'application/json')) {
247+
$pivotRequest = $this->parseJsonBody($pivotRequest, $body);
248+
} elseif (str_contains($contentType, 'application/x-www-form-urlencoded')) {
249+
$pivotRequest = $this->parseFormBody($pivotRequest, $body);
250+
}
251+
252+
// Set body stream using StreamFactory for consistency
253+
$streamFactory = new \PivotPHP\Core\Http\Psr7\Factory\StreamFactory();
254+
$stream = $streamFactory->createStream($body);
255+
$pivotRequest = $pivotRequest->withBody($stream);
256+
257+
return $pivotRequest;
258+
}
259+
260+
/**
261+
* Parse JSON body and apply to request
262+
*/
263+
private function parseJsonBody(\PivotPHP\Core\Http\Request $pivotRequest, string $body): \PivotPHP\Core\Http\Request
264+
{
265+
$parsedBody = json_decode($body, true);
266+
if (is_array($parsedBody)) {
267+
return $pivotRequest->withParsedBody($parsedBody);
268+
}
269+
270+
return $pivotRequest;
271+
}
272+
273+
/**
274+
* Parse form-encoded body and apply to request
275+
*/
276+
private function parseFormBody(\PivotPHP\Core\Http\Request $pivotRequest, string $body): \PivotPHP\Core\Http\Request
277+
{
278+
parse_str($body, $parsedBody);
279+
return $pivotRequest->withParsedBody($parsedBody);
280+
}
281+
282+
/**
283+
* Process route and create ReactPHP response
284+
*/
285+
private function processRoute(
286+
\PivotPHP\Core\Http\Request $pivotRequest,
287+
array $requestData
288+
): \React\Http\Message\Response {
289+
$route = \PivotPHP\Core\Routing\Router::identify($requestData['method'], $requestData['path']);
290+
291+
if ($route !== null) {
292+
return $this->executeRoute($route, $pivotRequest);
293+
}
294+
295+
return $this->createNotFoundResponse();
296+
}
297+
298+
/**
299+
* Execute route handler and create response
300+
*/
301+
private function executeRoute(array $route, \PivotPHP\Core\Http\Request $pivotRequest): \React\Http\Message\Response
302+
{
303+
$handler = $route['handler'];
304+
$pivotResponse = new \PivotPHP\Core\Http\Response();
305+
306+
// Capture output from route handler
307+
ob_start();
308+
$handler($pivotRequest, $pivotResponse);
309+
$output = ob_get_clean();
310+
311+
// Create ReactPHP response
312+
return new \React\Http\Message\Response(
313+
200,
314+
['Content-Type' => 'application/json'],
315+
$output !== false ? $output : ''
316+
);
317+
}
318+
319+
/**
320+
* Create 404 Not Found response
321+
*/
322+
private function createNotFoundResponse(): \React\Http\Message\Response
323+
{
324+
$notFoundBody = json_encode(['error' => 'Not Found']);
325+
326+
return new \React\Http\Message\Response(
327+
404,
328+
['Content-Type' => 'application/json'],
329+
$notFoundBody !== false ? $notFoundBody : '{"error":"Not Found"}'
330+
);
331+
}
332+
333+
/**
334+
* Create error response from exception
335+
*/
336+
private function createErrorResponse(\Exception $e): \React\Http\Message\Response
337+
{
338+
$this->logger->error('Error handling request', [
339+
'error' => $e->getMessage(),
340+
'trace' => $e->getTraceAsString(),
341+
]);
342+
343+
$errorBody = json_encode(['error' => 'Internal Server Error']);
344+
345+
return new \React\Http\Message\Response(
346+
500,
347+
['Content-Type' => 'application/json'],
348+
$errorBody !== false ? $errorBody : '{"error":"Internal Server Error"}'
349+
);
350+
}
241351
}

tests/Bridge/RequestFactoryTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,9 @@ public function testCreateRequestWithInvalidJson(): void
173173

174174
public function testCreateRequestWithComplexData(): void
175175
{
176-
$body = $this->streamFactory->createStream('{"user":{"name":"Alice","settings":{"theme":"dark","notifications":true}}}');
176+
$body = $this->streamFactory->createStream(
177+
'{"user":{"name":"Alice","settings":{"theme":"dark","notifications":true}}}'
178+
);
177179
$psrRequest = $this->serverRequestFactory->createServerRequest('PUT', '/profile')
178180
->withHeader('Content-Type', 'application/json')
179181
->withHeader('X-Request-ID', 'abc123')

0 commit comments

Comments
 (0)