forked from nategood/httpful
-
Notifications
You must be signed in to change notification settings - Fork 7
Add response debug/analysis helpers: status-range predicates, getErrorMessage(), and debugInfo() with full request context #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
voku
merged 2 commits into
master
from
copilot/add-helper-methods-debug-analyse-response
Apr 24, 2026
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -595,6 +595,153 @@ public function hasErrors(): bool | |
| return $this->code >= 400; | ||
| } | ||
|
|
||
| /** | ||
| * @return bool Did we receive a 1xx Informational response? | ||
| */ | ||
| public function isInformational(): bool | ||
| { | ||
| return $this->code >= 100 && $this->code < 200; | ||
| } | ||
|
|
||
| /** | ||
| * @return bool Did we receive a 2xx Successful response? | ||
| */ | ||
| public function isSuccess(): bool | ||
| { | ||
| return $this->code >= 200 && $this->code < 300; | ||
| } | ||
|
|
||
| /** | ||
| * @return bool Did we receive a 3xx Redirection response? | ||
| */ | ||
| public function isRedirect(): bool | ||
| { | ||
| return $this->code >= 300 && $this->code < 400; | ||
| } | ||
|
|
||
| /** | ||
| * @return bool Did we receive a 4xx Client Error response? | ||
| */ | ||
| public function isClientError(): bool | ||
| { | ||
| return $this->code >= 400 && $this->code < 500; | ||
| } | ||
|
|
||
| /** | ||
| * @return bool Did we receive a 5xx Server Error response? | ||
| */ | ||
| public function isServerError(): bool | ||
| { | ||
| return $this->code >= 500 && $this->code < 600; | ||
| } | ||
|
|
||
| /** | ||
| * Returns a human-readable hint / explanation for the current HTTP status code. | ||
| * | ||
| * Useful when displaying or logging error information without having to | ||
| * hard-code status-code ranges in calling code. | ||
| * | ||
| * @return string | ||
| */ | ||
| public function getErrorMessage(): string | ||
| { | ||
| if ($this->isSuccess()) { | ||
| return 'Request was successful (' . $this->code . ' ' . $this->reason . ').'; | ||
| } | ||
|
|
||
| if ($this->isInformational()) { | ||
| return 'Informational response (' . $this->code . ' ' . $this->reason . '): the server acknowledged the request.'; | ||
| } | ||
|
|
||
| if ($this->isRedirect()) { | ||
| return 'Redirect response (' . $this->code . ' ' . $this->reason . '): the resource has moved. Check the Location header.'; | ||
| } | ||
|
|
||
| if ($this->isClientError()) { | ||
| $hints = [ | ||
| 400 => 'Bad Request: the server could not understand the request due to invalid syntax.', | ||
| 401 => 'Unauthorized: authentication is required and has failed or not been provided.', | ||
| 403 => 'Forbidden: you do not have permission to access this resource.', | ||
| 404 => 'Not Found: the requested resource could not be found on the server.', | ||
| 405 => 'Method Not Allowed: the HTTP method used is not supported for this endpoint.', | ||
| 408 => 'Request Timeout: the server timed out waiting for the request.', | ||
| 409 => 'Conflict: the request conflicts with the current state of the resource.', | ||
| 410 => 'Gone: the resource has been permanently removed.', | ||
| 422 => 'Unprocessable Entity: the request was well-formed but contains semantic errors.', | ||
| 429 => 'Too Many Requests: you have exceeded the rate limit. Try again later.', | ||
| ]; | ||
|
|
||
| $hint = $hints[$this->code] ?? 'Client Error: the request could not be fulfilled due to a client-side problem. Check your request parameters, headers and authentication.'; | ||
|
|
||
| return $hint . ' (' . $this->code . ' ' . $this->reason . ')'; | ||
| } | ||
|
|
||
| if ($this->isServerError()) { | ||
| $hints = [ | ||
| 500 => 'Internal Server Error: the server encountered an unexpected error. This is a server-side problem.', | ||
| 501 => 'Not Implemented: the server does not support the functionality required to fulfil the request.', | ||
| 502 => 'Bad Gateway: the server received an invalid response from an upstream server.', | ||
| 503 => 'Service Unavailable: the server is temporarily unable to handle the request (overloaded or under maintenance).', | ||
| 504 => 'Gateway Timeout: the upstream server did not respond in time.', | ||
| ]; | ||
|
|
||
| $hint = $hints[$this->code] ?? 'Server Error: the server failed to fulfil the request. This is a server-side problem and is not caused by your request.'; | ||
|
|
||
| return $hint . ' (' . $this->code . ' ' . $this->reason . ')'; | ||
| } | ||
|
|
||
| return 'Unknown response status (' . $this->code . ' ' . $this->reason . ').'; | ||
| } | ||
|
|
||
| /** | ||
| * Returns a human-readable debug summary that includes the original | ||
| * request (method, full URL with query params, request headers, request | ||
| * body) as well as the response (status, hint, response headers, response | ||
| * body). Useful for logging, debugging, or building descriptive exception | ||
| * messages. | ||
| * | ||
| * @return string | ||
| */ | ||
| public function debugInfo(): string | ||
| { | ||
| $lines = []; | ||
|
|
||
| // ---- Request section (available when the response was produced by a Request) ---- | ||
| if ($this->request !== null) { | ||
| $lines[] = '--- Request ---'; | ||
| $lines[] = $this->request->getMethod() . ' ' . (string) $this->request->getUri(); | ||
|
|
||
| foreach ($this->request->getHeaders() as $name => $values) { | ||
| $lines[] = $name . ': ' . \implode(', ', (array) $values); | ||
| } | ||
|
|
||
| $requestBody = (string) $this->request->getBody(); | ||
| if ($requestBody !== '') { | ||
| $lines[] = ''; | ||
| $lines[] = $requestBody; | ||
| } | ||
|
|
||
| $lines[] = ''; | ||
| } | ||
|
|
||
| // ---- Response section ---- | ||
| $lines[] = '=== Response Debug Info ==='; | ||
| $lines[] = 'Status : ' . $this->code . ' ' . $this->reason; | ||
| $lines[] = 'Hint : ' . $this->getErrorMessage(); | ||
| $lines[] = ''; | ||
| $lines[] = '--- Response Headers ---'; | ||
|
|
||
| foreach ($this->headers->toArray() as $name => $values) { | ||
| $lines[] = $name . ': ' . \implode(', ', (array) $values); | ||
| } | ||
|
Comment on lines
+734
to
+736
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Response headers such as foreach ($this->headers->toArray() as $name => $values) {
$headerValue = \implode(', ', (array) $values);
if (\strtolower($name) === 'set-cookie') {
$headerValue = '******';
}
$lines[] = $name . ': ' . $headerValue;
} |
||
|
|
||
| $lines[] = ''; | ||
| $lines[] = '--- Response Body ---'; | ||
| $lines[] = (string) $this->body; | ||
|
|
||
| return \implode("\n", $lines); | ||
| } | ||
|
|
||
| /** | ||
| * @return bool | ||
| */ | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
debugInfo()method currently leaks sensitive credentials by including raw header values forAuthorization,Cookie, andProxy-Authorization. Although the pull request description shows these being masked (e.g.,Authorization: ******), the implementation does not actually perform redaction. For security reasons, sensitive headers should be redacted in debug output to prevent accidental exposure in logs.