Skip to content

Commit

Permalink
kernel exception handler; termination middleware; request+response cl…
Browse files Browse the repository at this point in the history
…ass updates
  • Loading branch information
donwilson committed Sep 1, 2023
1 parent 6135fb8 commit b5ac871
Show file tree
Hide file tree
Showing 9 changed files with 519 additions and 64 deletions.
2 changes: 1 addition & 1 deletion src/Magnetar/Helpers/Facades/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use Magnetar\Helpers\Facades\Facade;

/**
* @method getPath(): string
* @method path(): string
* @method assignOverrideParameters(array $parameters): void
* @method getParameter(string $name, mixed $default=null): mixed
* @method getParameters(): array
Expand Down
64 changes: 64 additions & 0 deletions src/Magnetar/Http/ExceptionHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);

namespace Magnetar\Http;

use Throwable;

use Magnetar\Application;
use Magnetar\Http\Request;
use Magnetar\Http\Response;
use Magnetar\Router\RouteUnassignedException;

class ExceptionHandler {
protected Throwable $caughtException;

public function __construct(
/**
* The application instance
* @var Application
*/
protected Application $app
) {

}

/**
* Record the exception
* @param Throwable $e
* @return void
*/
public function record(Throwable $e): void {
$this->caughtException = $e;
}

/**
* Render the exception
* @param Request $request
* @param Throwable $e
* @return Response
*/
public function render(Request $request, Throwable $e): Response {
$this->record($e);

$theme_file = 'errors/503';
$response_status = 503;

if($e instanceof RouteUnassignedException) {
$theme_file = 'errors/404';
$response_status = 404;
}

$response = (new Response())->status($response_status)->setBody(
$this->app->make('theme')->tpl($theme_file, [
'request' => $request,
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString()
])
);

return $response;
}
}
116 changes: 111 additions & 5 deletions src/Magnetar/Http/HeaderCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,25 @@ class HeaderCollection {
* @param bool|int $replace Whether to replace an existing header with the same name
* @param int|null $response_code The HTTP response code to send
*/
public function add(string $header, bool|int $replace=true, int|null $response_code=0): self {
public function add(
string $header_key,
string $header_value='',
bool|int $replace=true,
int|null $response_code=0
): self {
if('' === ($header_key = $this->sanitizeHeaderKey($header_key))) {
return $this;
}

$this->headers[] = [
'header' => $header,
'header' => $header_key,
'value' => $this->sanitizeHeaderValue($header_value),
'replace' => $replace,
'response_code' => $response_code
];

// @TODO add validation support
// @TODO add sanitization
// @TODO add sanitization (trim, lowercase header name, etc)
// @TODO utilize replace logic in add()

return $this;
Expand All @@ -31,8 +41,104 @@ public function add(string $header, bool|int $replace=true, int|null $response_c
* @return void
*/
public function send(): void {
foreach ($this->headers as $header) {
header($header['header'], $header['replace'], $header['response_code']);
foreach($this->headers as $header) {
header($header['header'] .':'. $header['value'], $header['replace'], $header['response_code']);
}
}

/**
* Get all headers
* @return array
*/
public function all(): array {
return $this->headers;
}

/**
* Sanitize a header key (everything before the header's first colon)
* @param string $header_key
* @return string
*/
public function sanitizeHeaderKey(string $header_key): string {
return strtolower(trim($header_key));
}

/**
* Sanitize a header value (everything after the header's first colon)
* @param string $header_value
* @return string
*/
public function sanitizeHeaderValue(string $header_value): string {
return trim($header_value);
}

/**
* Get a header by name
* @param string $name The header name
* @return string|null
*/
public function get(string $name): ?string {
// sanitize
if('' === ($name = $this->sanitizeHeaderKey($name))) {
return null;
}

foreach($this->headers as $header) {
if($header['header'] === $name) {
return $header['value'];
}
}

return null;
}

/**
* Remove a header by name
* @param string $name The header name
* @return self
*/
public function remove(string $name): self {
// sanitize
if('' === ($name = $this->sanitizeHeaderKey($name))) {
return $this;
}

foreach($this->headers as $key => $header) {
if($header['header'] === $name) {
unset($this->headers[ $key ]);
}
}

return $this;
}

/**
* Check if a header exists
* @param string $name The header name
* @return bool
*/
public function has(string $name): bool {
// sanitize
if('' === ($name = $this->sanitizeHeaderKey($name))) {
return false;
}

foreach($this->headers as $header) {
if($header['header'] === $name) {
return true;
}
}

return false;
}

/**
* Clear all headers
* @return self
*/
public function clear(): self {
$this->headers = [];

return $this;
}
}
2 changes: 1 addition & 1 deletion src/Magnetar/Http/JsonResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function setJson(string $json): self {
}

public function send(): self {
$this->header("Content-Type: application/json; charset=UTF-8", true);
$this->header('Content-Type', 'application/json; charset=UTF-8', true);

$this->sendHeaders();
$this->sendBody();
Expand Down
Loading

0 comments on commit b5ac871

Please sign in to comment.