Skip to content

Commit 09162ce

Browse files
author
Ibrahim BinAlshikh
committed
feat: add ResponseEntity class for dynamic HTTP status codes with #[ResponseBody]
Introduce ResponseEntity that wraps body, status code, and content type, allowing methods to return different HTTP status codes based on runtime logic. Includes static factory methods: ok(), created(), noContent(), badRequest(), unauthorized(), forbidden(), notFound(), error(). When handleMethodResponse() detects a ResponseEntity return value, it uses its status and content type instead of the annotation defaults. Closes #107
1 parent 83caed4 commit 09162ce

6 files changed

Lines changed: 512 additions & 0 deletions

File tree

WebFiori/Http/ResponseEntity.php

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
3+
/**
4+
* This file is licensed under MIT License.
5+
*
6+
* Copyright (c) 2026-present WebFiori Framework
7+
*
8+
* For more information on the license, please visit:
9+
* https://github.com/WebFiori/.github/blob/main/LICENSE
10+
*
11+
*/
12+
namespace WebFiori\Http;
13+
14+
/**
15+
* A wrapper class that allows methods annotated with #[ResponseBody] to return
16+
* dynamic HTTP status codes and content types along with the response body.
17+
*
18+
* @author Ibrahim
19+
*/
20+
class ResponseEntity {
21+
/**
22+
* Creates a new ResponseEntity instance.
23+
*
24+
* @param mixed $body The response body content. Can be a Json object, array, string, or null.
25+
*
26+
* @param int $status The HTTP status code to send with the response. Default is 200.
27+
*
28+
* @param string $contentType The content type header value. Default is 'application/json'.
29+
*/
30+
public function __construct(
31+
private mixed $body,
32+
private int $status = 200,
33+
private string $contentType = 'application/json'
34+
) {
35+
}
36+
37+
/**
38+
* Returns the response body.
39+
*
40+
* @return mixed The body content of the response.
41+
*/
42+
public function getBody(): mixed {
43+
return $this->body;
44+
}
45+
46+
/**
47+
* Returns the HTTP status code.
48+
*
49+
* @return int The HTTP status code.
50+
*/
51+
public function getStatus(): int {
52+
return $this->status;
53+
}
54+
55+
/**
56+
* Returns the content type of the response.
57+
*
58+
* @return string The content type header value.
59+
*/
60+
public function getContentType(): string {
61+
return $this->contentType;
62+
}
63+
64+
/**
65+
* Creates a ResponseEntity with HTTP 200 OK status.
66+
*
67+
* @param mixed $body The response body content.
68+
*
69+
* @return self A new ResponseEntity instance with status 200.
70+
*/
71+
public static function ok(mixed $body): self {
72+
return new self($body, 200);
73+
}
74+
75+
/**
76+
* Creates a ResponseEntity with HTTP 201 Created status.
77+
*
78+
* @param mixed $body The response body content.
79+
*
80+
* @return self A new ResponseEntity instance with status 201.
81+
*/
82+
public static function created(mixed $body): self {
83+
return new self($body, 201);
84+
}
85+
86+
/**
87+
* Creates a ResponseEntity with HTTP 204 No Content status and null body.
88+
*
89+
* @return self A new ResponseEntity instance with status 204 and no body.
90+
*/
91+
public static function noContent(): self {
92+
return new self(null, 204);
93+
}
94+
95+
/**
96+
* Creates a ResponseEntity with HTTP 400 Bad Request status.
97+
*
98+
* @param mixed $body The response body content describing the error.
99+
*
100+
* @return self A new ResponseEntity instance with status 400.
101+
*/
102+
public static function badRequest(mixed $body): self {
103+
return new self($body, 400);
104+
}
105+
106+
/**
107+
* Creates a ResponseEntity with HTTP 401 Unauthorized status.
108+
*
109+
* @param mixed $body The response body content describing the authentication failure.
110+
*
111+
* @return self A new ResponseEntity instance with status 401.
112+
*/
113+
public static function unauthorized(mixed $body): self {
114+
return new self($body, 401);
115+
}
116+
117+
/**
118+
* Creates a ResponseEntity with HTTP 403 Forbidden status.
119+
*
120+
* @param mixed $body The response body content describing the authorization failure.
121+
*
122+
* @return self A new ResponseEntity instance with status 403.
123+
*/
124+
public static function forbidden(mixed $body): self {
125+
return new self($body, 403);
126+
}
127+
128+
/**
129+
* Creates a ResponseEntity with HTTP 404 Not Found status.
130+
*
131+
* @param mixed $body The response body content describing what was not found.
132+
*
133+
* @return self A new ResponseEntity instance with status 404.
134+
*/
135+
public static function notFound(mixed $body): self {
136+
return new self($body, 404);
137+
}
138+
139+
/**
140+
* Creates a ResponseEntity with HTTP 500 Internal Server Error status.
141+
*
142+
* @param mixed $body The response body content describing the error.
143+
*
144+
* @return self A new ResponseEntity instance with status 500.
145+
*/
146+
public static function error(mixed $body): self {
147+
return new self($body, 500);
148+
}
149+
}

WebFiori/Http/WebService.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,25 @@ protected function handleMethodResponse(mixed $result, string $methodName): void
14881488
return;
14891489
}
14901490

1491+
// Handle ResponseEntity for dynamic status codes
1492+
if ($result instanceof ResponseEntity) {
1493+
$body = $result->getBody();
1494+
if ($body === null) {
1495+
$this->send($result->getContentType(), "", $result->getStatus());
1496+
} else if ($body instanceof Json || $body instanceof JsonI) {
1497+
$json = $body instanceof JsonI ? $body->toJSON() : $body;
1498+
$this->send($result->getContentType(), $json, $result->getStatus());
1499+
} else if (is_array($body) || is_object($body)) {
1500+
$json = new Json();
1501+
$asObj = is_array($body) && !array_is_list($body);
1502+
$json->add("data", $body, $asObj);
1503+
$this->send($result->getContentType(), $json, $result->getStatus());
1504+
} else {
1505+
$this->send($result->getContentType(), $body, $result->getStatus());
1506+
}
1507+
return;
1508+
}
1509+
14911510
// Auto-convert return value to JSON response
14921511
if ($result === null) {
14931512
// Null return = empty response with configured status

0 commit comments

Comments
 (0)