Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions src/Http/Traits/Response/Body.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ public function get(string $key, ?string $default = null)
* Sets new key/value pair into response
* @param mixed $value
*/
public function set(string $key, $value): void
public function set(string $key, $value): self
{
$this->__response[$key] = $value;
return $this;
}

/**
Expand All @@ -94,7 +95,7 @@ public function delete(string $key): void
* Prepares the JSON response
* @param array<string, mixed>|null $data
*/
public function json(?array $data = null, ?int $code = null): void
public function json(?array $data = null, ?int $code = null): self
{
$this->setContentType(ContentType::JSON);

Expand All @@ -105,13 +106,15 @@ public function json(?array $data = null, ?int $code = null): void
if ($data) {
$this->__response = array_merge($this->__response, $data);
}

return $this;
}

/**
* Prepares the JSONP response
* @param array<string, mixed>|null $data
*/
public function jsonp(string $callback, ?array $data = null, ?int $code = null): void
public function jsonp(string $callback, ?array $data = null, ?int $code = null): self
{
$this->setContentType(ContentType::JSONP);

Expand All @@ -124,6 +127,8 @@ public function jsonp(string $callback, ?array $data = null, ?int $code = null):
if ($data) {
$this->__response = array_merge($this->__response, $data);
}

return $this;
}

/**
Expand All @@ -140,7 +145,7 @@ public function getJsonPData(array $data): string
* @param array<string, mixed>|null $data
* @param string $root
*/
public function xml(?array $data = null, $root = '<data></data>', ?int $code = null): void
public function xml(?array $data = null, $root = '<data></data>', ?int $code = null): self
{
$this->setContentType(ContentType::XML);

Expand All @@ -153,12 +158,14 @@ public function xml(?array $data = null, $root = '<data></data>', ?int $code = n
if ($data) {
$this->__response = array_merge($this->__response, $data);
}

return $this;
}

/**
* Prepares the HTML content
*/
public function html(string $html, ?int $code = null): void
public function html(string $html, ?int $code = null): self
{
$this->setContentType(ContentType::HTML);

Expand All @@ -167,6 +174,8 @@ public function html(string $html, ?int $code = null): void
}

$this->__response[ReservedKeys::RENDERED_VIEW] = $html;

return $this;
}

/**
Expand Down
7 changes: 4 additions & 3 deletions src/Http/Traits/Response/Header.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ public function getHeader(string $key): ?string
/**
* Sets the response header
*/
public function setHeader(string $key, string $value): void
public function setHeader(string $key, string $value): self
{
$this->__headers[$key] = $value;
return $this;
}

/**
Expand All @@ -77,9 +78,9 @@ public function deleteHeader(string $key): void
/**
* Sets the content type
*/
public function setContentType(string $contentType): void
public function setContentType(string $contentType): self
{
$this->setHeader('Content-Type', $contentType);
return $this->setHeader('Content-Type', $contentType);
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/Http/Traits/Response/Status.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,15 @@ public function getText(int $code): string
/**
* Sets the status code
*/
public function setStatusCode(int $code): void
public function setStatusCode(int $code): self
{
if (!isset($this->texts[$code])) {
throw new InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
}

$this->__statusCode = $code;

return $this;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/Router/Enums/ExceptionMessages.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ final class ExceptionMessages extends BaseExceptionMessages

public const ACTION_NOT_FOUND_ON_CONTROLLER = 'Action `{%1}` not found on controller `{%2}`';

public const INVALID_HANDLER_RESPONSE = 'Route handler `{%1}` must return `{%2}`';

public const ROUTE_NOT_CLOSURE = 'Route is not a closure';

public const NAME_BEFORE_ROUTE_DEFINITION = 'Names can not be set before route definition';
Expand Down
11 changes: 11 additions & 0 deletions src/Router/Exceptions/RouteException.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ public static function actionNotFound(string $action, string $controller): self
);
}

public static function invalidHandlerResponse(string $handler, string $expectedType): self
{
return new self(
_message(
ExceptionMessages::INVALID_HANDLER_RESPONSE,
[$handler, $expectedType]
),
E_ERROR
);
}

public static function notClosure(): self
{
return new self(
Expand Down
46 changes: 34 additions & 12 deletions src/Router/RouteDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
use Quantum\Router\Exceptions\RouteException;
use Quantum\Csrf\Exceptions\CsrfException;
use Quantum\Di\Exceptions\DiException;
use Quantum\Http\Response;
use Quantum\Http\Request;
use ReflectionException;
use Quantum\Csrf\Csrf;
use Quantum\Di\Di;
use Closure;

/**
* Class RouteDispatcher
Expand All @@ -34,7 +36,7 @@ final class RouteDispatcher
* Dispatch a matched route.
* @throws ReflectionException|CsrfException|DiException|RouteException
*/
public function dispatch(MatchedRoute $matched, Request $request): void
public function dispatch(MatchedRoute $matched, Request $request): Response
{
$route = $matched->getRoute();
$params = $matched->getParams();
Expand All @@ -46,8 +48,10 @@ public function dispatch(MatchedRoute $matched, Request $request): void
throw RouteException::closureHandlerMissing();
}

$this->invoke($closure, $params);
return;
return $this->requireResponse(
$this->invoke($closure, $params),
'closure route'
);
}

$callable = $this->resolveControllerCallable($route);
Expand All @@ -57,10 +61,14 @@ public function dispatch(MatchedRoute $matched, Request $request): void

$this->callHook($controller, '__before', $params);

/** @phpstan-ignore argument.type */
$this->invoke($callable, $params);
$response = $this->requireResponse(
$this->invoke($callable, $params),
$route->getController() . '::' . $route->getAction()
);

$this->callHook($controller, '__after', $params);

return $response;
}

/**
Expand Down Expand Up @@ -88,15 +96,30 @@ private function resolveControllerCallable(Route $route): array

/**
* Invoke a callable with parameters resolved via DI autowiring.
* @param callable|array{0: object, 1: string} $callable
* @param array<string, mixed> $params
* @throws DiException|ReflectionException
* @return mixed
*/
private function invoke(callable $callable, array $params): void
private function invoke($callable, array $params)
{
call_user_func_array(
$callable,
Di::autowire($callable, $params)
);
/** @var callable $callable */
$closure = Closure::fromCallable($callable);

return $closure(...Di::autowire($closure, $params));
}

/**
* Enforce a Response return contract for route handlers.
* @param mixed $result
* @throws RouteException
*/
private function requireResponse($result, string $handler): Response
{
if (!$result instanceof Response) {
throw RouteException::invalidHandlerResponse($handler, Response::class);
}

return $result;
Comment thread
armanist marked this conversation as resolved.
}

/**
Expand All @@ -107,7 +130,6 @@ private function invoke(callable $callable, array $params): void
private function callHook(object $controller, string $hook, array $params): void
{
if (method_exists($controller, $hook)) {
/** @phpstan-ignore argument.type */
$this->invoke([$controller, $hook], $params);
}
}
Expand Down
12 changes: 7 additions & 5 deletions tests/Unit/Http/Traits/Response/HttpResponseBodyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ public function testResponseSetHasGetAllDelete(): void

$this->assertFalse($response->has('name'));

$response->set('name', 'John');
$returned = $response->set('name', 'John');

$this->assertSame($response, $returned);

$this->assertTrue($response->has('name'));

Expand All @@ -49,7 +51,7 @@ public function testResponseJsonContent(): void

$response->set('lastname', 'Doe');

$response->json();
$this->assertSame($response, $response->json());

$this->assertEquals('{"firstname":"John","lastname":"Doe"}', $response->getContent());

Expand All @@ -75,7 +77,7 @@ public function testResponseJsonP(): void

$response->set('lastname', 'Doe');

$response->jsonp('myfunc');
$this->assertSame($response, $response->jsonp('myfunc'));

$this->assertEquals('myfunc({"firstname":"John","lastname":"Doe"})', $response->getContent());
}
Expand All @@ -88,7 +90,7 @@ public function testResponseXmlContent(): void

$response->set('lastname', 'Doe');

$response->xml();
$this->assertSame($response, $response->xml());

$xml = "<?xml version=\"1.0\"?>\n" .
"<data>\n" .
Expand Down Expand Up @@ -196,7 +198,7 @@ public function testResponseHtmlContent(): void
{
$response = response();

$response->html('<div>John Doe</div>');
$this->assertSame($response, $response->html('<div>John Doe</div>'));

$this->assertEquals('<div>John Doe</div>', $response->getContent());

Expand Down
4 changes: 3 additions & 1 deletion tests/Unit/Http/Traits/Response/HttpResponseHeaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ public function testResponseHeaderSetHasGetAllDelete(): void

$this->assertFalse($response->hasHeader('X-Frame-Options'));

$response->setHeader('X-Frame-Options', 'deny');
$returned = $response->setHeader('X-Frame-Options', 'deny');

$this->assertSame($response, $returned);

$this->assertTrue($response->hasHeader('X-Frame-Options'));

Expand Down
4 changes: 3 additions & 1 deletion tests/Unit/Http/Traits/Response/HttpResponseStatusTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public function testResponseStatus(): void

$this->assertEquals(200, $response->getStatusCode());

$response->setStatusCode(301);
$returned = $response->setStatusCode(301);

$this->assertSame($response, $returned);

$this->assertEquals(301, $response->getStatusCode());

Expand Down
Loading
Loading