Skip to content

Commit

Permalink
Request: add getBody (interface BC break)
Browse files Browse the repository at this point in the history
  • Loading branch information
JanTvrdik committed Jun 5, 2016
1 parent 4fb8860 commit 48a3e5f
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 4 deletions.
6 changes: 6 additions & 0 deletions src/Http/IRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,10 @@ function getRemoteHost();
*/
function getRawBody();

/**
* Returns parsed content of HTTP request body.
* @return mixed
* @throws InvalidRequestBodyException
*/
function getBody();
}
24 changes: 21 additions & 3 deletions src/Http/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* @property-read string|NULL $remoteAddress
* @property-read string|NULL $remoteHost
* @property-read string|NULL $rawBody
* @property-read string|NULL $body
*/
class Request implements IRequest
{
Expand Down Expand Up @@ -58,9 +59,14 @@ class Request implements IRequest
/** @var callable|NULL */
private $rawBodyCallback;

/** @var callable|NULL */
private $bodyCallback;


public function __construct(UrlScript $url, $query = NULL, $post = NULL, $files = NULL, $cookies = NULL,
$headers = NULL, $method = NULL, $remoteAddress = NULL, $remoteHost = NULL, $rawBodyCallback = NULL)
public function __construct(
UrlScript $url, $query = NULL, $post = NULL, $files = NULL, $cookies = NULL,
$headers = NULL, $method = NULL, $remoteAddress = NULL, $remoteHost = NULL,
$rawBodyCallback = NULL, $bodyCallback = NULL)
{
$this->url = $url;
if ($query !== NULL) {
Expand All @@ -75,6 +81,7 @@ public function __construct(UrlScript $url, $query = NULL, $post = NULL, $files
$this->remoteAddress = $remoteAddress;
$this->remoteHost = $remoteHost;
$this->rawBodyCallback = $rawBodyCallback;
$this->bodyCallback = $bodyCallback;
}


Expand Down Expand Up @@ -289,7 +296,18 @@ public function getRemoteHost()
*/
public function getRawBody()
{
return $this->rawBodyCallback ? call_user_func($this->rawBodyCallback) : NULL;
return $this->rawBodyCallback ? call_user_func($this->rawBodyCallback, $this) : NULL;
}


/**
* Returns parsed content of HTTP request body.
* @return mixed
* @throws InvalidRequestBodyException
*/
public function getBody()
{
return $this->bodyCallback ? call_user_func($this->bodyCallback, $this) : NULL;
}


Expand Down
49 changes: 48 additions & 1 deletion src/Http/RequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace Nette\Http;

use Nette;
use Nette\Utils\Json;
use Nette\Utils\Strings;


Expand All @@ -33,6 +34,25 @@ class RequestFactory
/** @var array */
private $proxies = [];

/** @var callable[] of function (Request $request): mixed */
private $bodyParsers = [];


public function __construct()
{
$this->addBodyParser('application/x-www-form-urlencoded', function (Request $request) {
return $request->getPost();
});

$this->addBodyParser('application/json', function (Request $request) {
try {
return Json::decode($request->getRawBody());
} catch (Nette\Utils\JsonException $e) {
throw new InvalidRequestBodyException('Body is not a valid JSON', 0, $e);
}
});
}


/**
* @param bool
Expand All @@ -56,6 +76,18 @@ public function setProxy($proxy)
}


/**
* @param string
* @param callable function(Request $request): mixed|NULL
* @return self
*/
public function addBodyParser($contentType, $callback)
{
$this->bodyParsers[$contentType] = $callback;
return $this;
}


/**
* Creates current HttpRequest object.
* @return Request
Expand Down Expand Up @@ -235,7 +267,22 @@ public function createHttpRequest()
return file_get_contents('php://input');
};

return new Request($url, NULL, $post, $files, $cookies, $headers, $method, $remoteAddr, $remoteHost, $rawBodyCallback);
// parsed body
$bodyCallback = function (Request $request) use (& $body) {
if ($body === NULL) {
$contentType = $request->getHeader('Content-Type');
foreach ($this->bodyParsers as $parserContentType => $parser) {
if (stripos($contentType, $parserContentType) === 0) {
$body = $parser($request);
break;
}
}
}

return $body;
};

return new Request($url, NULL, $post, $files, $cookies, $headers, $method, $remoteAddr, $remoteHost, $rawBodyCallback, $bodyCallback);
}

}
18 changes: 18 additions & 0 deletions src/Http/exceptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/

namespace Nette\Http;

use Nette;


/**
* Exception is thrown when a request body can not be parsed.
*/
class InvalidRequestBodyException extends \RuntimeException
{
}
63 changes: 63 additions & 0 deletions tests/Http/RequestFactory.body.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/**
* Test: Nette\Http\RequestFactory body parsing.
*/

use Nette\Http\InvalidRequestBodyException;
use Nette\Http\RequestFactory;
use Nette\Utils\JsonException;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';


$factory = new RequestFactory;

$setRawBody = function ($rawBody) {
$this->rawBodyCallback = function () use ($rawBody) {
return $rawBody;
};
};


test(function () use ($factory, $setRawBody) {
$_SERVER = [
'CONTENT_TYPE' => 'application/json',
];

$request = $factory->createHttpRequest();
$setRawBody->bindTo($request, $request)->__invoke('[1, 2.0, "3", true, false, null, {}]');
Assert::same('[1, 2.0, "3", true, false, null, {}]', $request->getRawBody());
Assert::equal([1, 2.0, '3', TRUE, FALSE, NULL, new stdClass], $request->body);
});


test(function () use ($factory, $setRawBody) {
$_SERVER = [
'CONTENT_TYPE' => 'application/json',
];

$request = $factory->createHttpRequest();
$setRawBody->bindTo($request, $request)->__invoke('[');
Assert::same('[', $request->getRawBody());
$e = Assert::exception([$request, 'getBody'], InvalidRequestBodyException::class);
Assert::type(JsonException::class, $e->getPrevious());
});


test(function () use ($factory, $setRawBody) {
$_SERVER = [
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
];

$_POST = [
'a' => 'b',
];

$request = $factory->createHttpRequest();
$setRawBody->bindTo($request, $request)->__invoke('a=c');
Assert::same('a=c', $request->getRawBody());
Assert::equal(['a' => 'b'], $request->body);
});

0 comments on commit 48a3e5f

Please sign in to comment.