From 8978cbd7bafe9b9bd62c7120158e2600c0f5e398 Mon Sep 17 00:00:00 2001 From: Terry Lin Date: Wed, 15 Jul 2020 12:30:10 +0800 Subject: [PATCH] Add supports for Restful. --- README.md | 33 +++++++++++++- src/Psr17/Utils/SuperGlobal.php | 12 +++++ src/Psr7/ServerRequest.php | 78 ++++++++++++++++++++++++++++++-- tests/Psr7/ServerRequestTest.php | 1 - 4 files changed, 117 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 275bf32..6cde107 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ echo $uploadFileArr['foo']->getClientFilename(); - [withQueryParams](https://github.com/terrylinooo/psr-http/wiki/ServerRequest:-withQueryParams-Example) - [getUploadedFiles](https://github.com/terrylinooo/psr-http/wiki/ServerRequest:-getUploadedFiles-Example) - [withUploadedFiles](https://github.com/terrylinooo/psr-http/wiki/ServerRequest:-withUploadedFiles-Example) - - [getParsedBody](https://github.com/terrylinooo/psr-http/wiki/ServerRequest:-getParsedBody-Example) + - [getParsedBody](https://github.com/terrylinooo/psr-http/wiki/ServerRequest:-getParsedBody-Example) (See explanation below) - [withParsedBody](https://github.com/terrylinooo/psr-http/wiki/ServerRequest:-withParsedBody-Example) - [getAttributes](https://github.com/terrylinooo/psr-http/wiki/ServerRequest:-getAttributes-Example) - [getAttribute](https://github.com/terrylinooo/psr-http/wiki/ServerRequest:-getAttribute-Example) @@ -225,6 +225,37 @@ echo $uploadFileArr['foo']->getClientFilename(); If you are looking for combined examples, see [unit testing](https://github.com/terrylinooo/psr-http/tree/master/tests). +### The Behavior of Handling Request Body + +Shieldon PSR-HTTP is ready for RESTful, the following content explains how PRS-HTTP deals with the request body. + +- **A.** The `getParsedBody` method returns an array of the superglobal *$_POST* if the request request method is `POST` and the Content-Type is one of the following type: + - `multipart/form-data` + - `application/x-www-form-urlencode` + + +- **B.** The `getParsedBody` method returns a JSON object if the request fit to the following conditions. + - The request Content-Type is `application/json` + - The request body is a valid *JSON-formatted* string. + - The request method is not `GET`. + +- **C.** The `getParsedBody` method returns a array parsed from HTTP build query: + - The condition is neither A or B. + - The request method is not `GET`. + +- **D.** The `getParsedBody` method returns `null` if the condition is not one of above. + +#### Summary + +| Condition| Method | Content-type | Parsed-body | +| --- | --- | --- | --- | +| A | POST | multipart/form-data
application/x-www-form-urlencode | array | +| B | ALL excepts GET | application/json | object | +| C | ALL excepts GET | Not A or B | array | +| D | - | - | null | + +Hope this helps. + --- ## Author diff --git a/src/Psr17/Utils/SuperGlobal.php b/src/Psr17/Utils/SuperGlobal.php index 39e1929..d2e2eb9 100644 --- a/src/Psr17/Utils/SuperGlobal.php +++ b/src/Psr17/Utils/SuperGlobal.php @@ -35,6 +35,18 @@ public static function extract(): array self::mockCliEnvironment(); } + // Here we add the HTTP prefix by ourselves... + $headerParamsWithoutHttpPrefix = [ + 'CONTENT_TYPE', + 'CONTENT_LENGTH', + ]; + + foreach ($headerParamsWithoutHttpPrefix as $value) { + if (isset($_SERVER[$value])) { + $_SERVER['HTTP_' . $value] = $_SERVER[$value]; + } + } + $headerParams = []; $serverParams = $_SERVER ?? []; $cookieParams = $_COOKIE ?? []; diff --git a/src/Psr7/ServerRequest.php b/src/Psr7/ServerRequest.php index bcddb45..17d21b9 100644 --- a/src/Psr7/ServerRequest.php +++ b/src/Psr7/ServerRequest.php @@ -19,12 +19,19 @@ use Shieldon\Psr7\Request; use Shieldon\Psr7\Utils\UploadedFileHelper; use InvalidArgumentException; - +use function file_get_contents; +use function gettype; use function is_array; use function is_null; use function is_object; +use function json_decode; +use function json_last_error; +use function parse_str; +use function preg_split; use function sprintf; -use function gettype; +use function strtolower; +use function strtoupper; +use const JSON_ERROR_NONE; /* * Representation of an incoming, server-side HTTP request. @@ -111,9 +118,7 @@ public function __construct( $this->queryParams = $getParams; $this->attributes = []; - if (!empty($postParams)) { - $this->parsedBody = $postParams; - } + $this->determineParsedBody($postParams); // This property will be assigned to a parsed array that contains // the UploadedFile instance(s) as the $filesParams is given. @@ -308,4 +313,67 @@ protected function assertParsedBody($data): void ); } } + + /** + * Confirm the content type and post values whether fit the requirement. + * + * @param array $postParams + * @return void + */ + protected function determineParsedBody(array $postParams) + { + $headerContentType = $this->getHeaderLine('Content-Type'); + $contentTypeArr = preg_split('/\s*[;,]\s*/', $headerContentType); + $contentType = strtolower($contentTypeArr[0]); + $httpMethod = strtoupper($this->getMethod()); + + // Is it a form submit or not. + $isForm = false; + + if ($httpMethod === 'POST' && !empty($postParams)) { + + // If the request Content-Type is either application/x-www-form-urlencoded + // or multipart/form-data, and the request method is POST, this method MUST + // return the contents of $_POST. + $postRequiredContentTypes = [ + '', // For unit testing purpose. + 'application/x-www-form-urlencoded', + 'multipart/form-data', + ]; + + if (in_array($contentType, $postRequiredContentTypes)) { + $this->parsedBody = $postParams ?: null; + $isForm = true; + } + } + + // Maybe other http methods such as PUT, DELETE, etc... + if ($httpMethod !== 'GET' && !$isForm) { + + // If it a JSON formatted string? + $isJson = false; + + // Receive content from PHP stdin input, if exists. + $rawText = file_get_contents('php://input'); + + if (!empty($rawText)) { + + if ($contentType === 'application/json') { + $jsonParsedBody = json_decode($rawText); + $isJson = (json_last_error() === JSON_ERROR_NONE); + } + + // Condition 1 - It's a JSON, now the body is a JSON object. + if ($isJson) { + $this->parsedBody = $jsonParsedBody ?: null; + } + + // Condition 2 - It's not a JSON, might be a http build query. + if (!$isJson) { + parse_str($rawText, $parsedStr); + $this->parsedBody = $parsedStr ?: null; + } + } + } + } } diff --git a/tests/Psr7/ServerRequestTest.php b/tests/Psr7/ServerRequestTest.php index 4e76a13..31e066e 100644 --- a/tests/Psr7/ServerRequestTest.php +++ b/tests/Psr7/ServerRequestTest.php @@ -75,7 +75,6 @@ public function test_GetPrefixMethods() $this->assertSame($serverRequest->getAttributes(), []);; // Test 2 - $serverRequest = self::getServerRequest( 'POST', ['foo' => 'bar'],