diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index af77654..81876ef 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,21 +7,24 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Cache Composer dependencies
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: /tmp/composer-cache
key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
- - uses: php-actions/composer@v5
+ - name: Composer
+ uses: php-actions/composer@v6
+ with:
+ php_version: '8.1'
- name: Archive build
run: mkdir /tmp/github-actions/ && tar -cvf /tmp/github-actions/build.tar ./
- name: Upload build archive for test runners
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: build-artifact
path: /tmp/github-actions
@@ -31,7 +34,7 @@ jobs:
needs: [composer]
steps:
- - uses: actions/download-artifact@v2
+ - uses: actions/download-artifact@v3
with:
name: build-artifact
path: /tmp/github-actions
@@ -42,7 +45,7 @@ jobs:
- name: PHP Unit tests
uses: php-actions/phpunit@v3
with:
- php_version: 8.0
+ php_version: '8.1'
php_extensions: xdebug
memory_limit: 256M
configuration: test/phpunit/phpunit.xml
@@ -53,7 +56,7 @@ jobs:
needs: [composer]
steps:
- - uses: actions/download-artifact@v2
+ - uses: actions/download-artifact@v3
with:
name: build-artifact
path: /tmp/github-actions
@@ -64,5 +67,6 @@ jobs:
- name: PHP Static Analysis
uses: php-actions/phpstan@v3
with:
+ php_version: '8.1'
path: src/
level: 6
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
index 0e7d790..4d528e7 100644
--- a/.scrutinizer.yml
+++ b/.scrutinizer.yml
@@ -1,6 +1,6 @@
build:
environment:
- php: 8.0.0
+ php: 8.1.0
nodes:
analysis:
@@ -29,4 +29,4 @@ checks:
filter:
excluded_paths:
- test/*
- - vendor/*
\ No newline at end of file
+ - vendor/*
diff --git a/README.md b/README.md
index 0981496..ab97b88 100644
--- a/README.md
+++ b/README.md
@@ -10,19 +10,19 @@ This repository is an implementation of the PSR-7 interfaces for use within PHP.
***
-
+
-
+
-
+
-
+
-
+
-[fig]: https://www.php-fig.org/psr/psr-7/
\ No newline at end of file
+[fig]: https://www.php-fig.org/psr/psr-7/
diff --git a/composer.json b/composer.json
index da09a2c..772b800 100644
--- a/composer.json
+++ b/composer.json
@@ -4,7 +4,7 @@
"license": "MIT",
"require": {
- "php": ">=8.0",
+ "php": ">=8.1",
"phpgt/input": "^v1",
"psr/http-message": "^v1.0.1",
"willdurand/negotiation": "v3.1.0"
diff --git a/composer.lock b/composer.lock
index ae4a7f1..f7a450b 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "6b8734513d92d49afb4bfcd4222f4cd9",
+ "content-hash": "23bb9a9a013fa0210c1022f7e5397f2d",
"packages": [
{
"name": "phpgt/input",
@@ -452,16 +452,16 @@
},
{
"name": "phpstan/phpstan",
- "version": "1.8.5",
+ "version": "1.9.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
- "reference": "f6598a5ff12ca4499a836815e08b4d77a2ddeb20"
+ "reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f6598a5ff12ca4499a836815e08b4d77a2ddeb20",
- "reference": "f6598a5ff12ca4499a836815e08b4d77a2ddeb20",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d03bccee595e2146b7c9d174486b84f4dc61b0f2",
+ "reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2",
"shasum": ""
},
"require": {
@@ -491,7 +491,7 @@
],
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
- "source": "https://github.com/phpstan/phpstan/tree/1.8.5"
+ "source": "https://github.com/phpstan/phpstan/tree/1.9.4"
},
"funding": [
{
@@ -507,7 +507,7 @@
"type": "tidelift"
}
],
- "time": "2022-09-07T16:05:32+00:00"
+ "time": "2022-12-17T13:33:52+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -829,16 +829,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "9.5.24",
+ "version": "9.5.25",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5"
+ "reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d0aa6097bef9fd42458a9b3c49da32c6ce6129c5",
- "reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d",
+ "reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d",
"shasum": ""
},
"require": {
@@ -860,14 +860,14 @@
"phpunit/php-timer": "^5.0.2",
"sebastian/cli-parser": "^1.0.1",
"sebastian/code-unit": "^1.0.6",
- "sebastian/comparator": "^4.0.5",
+ "sebastian/comparator": "^4.0.8",
"sebastian/diff": "^4.0.3",
"sebastian/environment": "^5.1.3",
- "sebastian/exporter": "^4.0.3",
+ "sebastian/exporter": "^4.0.5",
"sebastian/global-state": "^5.0.1",
"sebastian/object-enumerator": "^4.0.3",
"sebastian/resource-operations": "^3.0.3",
- "sebastian/type": "^3.1",
+ "sebastian/type": "^3.2",
"sebastian/version": "^3.0.2"
},
"suggest": {
@@ -911,7 +911,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.24"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.25"
},
"funding": [
{
@@ -921,9 +921,13 @@
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
}
],
- "time": "2022-08-30T07:42:16+00:00"
+ "time": "2022-09-25T03:44:45+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -1094,16 +1098,16 @@
},
{
"name": "sebastian/comparator",
- "version": "4.0.7",
+ "version": "4.0.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "7fa545db548c90bdebeb9da0583001a252be5578"
+ "reference": "fa0f136dd2334583309d32b62544682ee972b51a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/7fa545db548c90bdebeb9da0583001a252be5578",
- "reference": "7fa545db548c90bdebeb9da0583001a252be5578",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a",
+ "reference": "fa0f136dd2334583309d32b62544682ee972b51a",
"shasum": ""
},
"require": {
@@ -1156,7 +1160,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
- "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.7"
+ "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8"
},
"funding": [
{
@@ -1164,7 +1168,7 @@
"type": "github"
}
],
- "time": "2022-09-14T06:33:43+00:00"
+ "time": "2022-09-14T12:41:17+00:00"
},
{
"name": "sebastian/complexity",
@@ -1946,8 +1950,8 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": ">=8.0"
+ "php": ">=8.1"
},
"platform-dev": [],
- "plugin-api-version": "2.2.0"
+ "plugin-api-version": "2.3.0"
}
diff --git a/src/Message.php b/src/Message.php
index 2a9a023..6625384 100644
--- a/src/Message.php
+++ b/src/Message.php
@@ -5,7 +5,7 @@
use Psr\Http\Message\StreamInterface;
trait Message {
- protected Headers $headers;
+ public readonly Headers $headers;
protected string $protocol;
protected StreamInterface $stream;
@@ -17,7 +17,7 @@ trait Message {
* @return string HTTP protocol version.
*/
public function getProtocolVersion():string {
- if(strstr($this->protocol, "/")) {
+ if(str_contains($this->protocol, "/")) {
return substr(
$this->protocol,
strpos($this->protocol, "/") + 1
@@ -41,7 +41,7 @@ public function getProtocolVersion():string {
* @return static
* @throws InvalidProtocolHttpException
*/
- public function withProtocolVersion($version) {
+ public function withProtocolVersion($version):self {
if(!is_numeric($version)) {
throw new InvalidProtocolHttpException($version);
}
diff --git a/src/RequestFactory.php b/src/RequestFactory.php
index 8010f40..6207fc0 100644
--- a/src/RequestFactory.php
+++ b/src/RequestFactory.php
@@ -52,6 +52,26 @@ public function createServerRequestFromGlobalState(
$uri = new Uri($server["REQUEST_URI"] ?? null);
$headers = new RequestHeaders();
+ if($secure = $server["HTTPS"] ?? null) {
+ $uri = $uri->withScheme("https");
+ }
+ else {
+ $uri = $uri->withScheme("http");
+ }
+
+ if($port = $server["SERVER_PORT"] ?? null) {
+ $uri = $uri->withPort((int)$port);
+ }
+
+ if($host = $server["HTTP_HOST"] ?? null) {
+ $host = strtok($host, ":");
+ $uri = $uri->withHost($host);
+ }
+
+ if($query = $server["QUERY_STRING"] ?? null) {
+ $uri = $uri->withQuery($query);
+ }
+
foreach($server as $key => $value) {
if(str_starts_with($key, "HTTP_")) {
$headerKey = substr($key, strlen("HTTP_"));
diff --git a/src/Response.php b/src/Response.php
index 1656243..0cb3fd4 100644
--- a/src/Response.php
+++ b/src/Response.php
@@ -19,6 +19,7 @@ class Response implements ResponseInterface {
public function __construct(
private ?int $statusCode = null,
ResponseHeaders $headers = null,
+ private ?Request $request = null,
) {
$this->headers = $headers ?? new ResponseHeaders();
$this->stream = new Stream();
@@ -28,13 +29,18 @@ public function setExitCallback(callable $callback):void {
$this->exitCallback = $callback;
}
- public function reload():void {
- $this->redirect("./");
+ public function reload(bool $keepQuery = true):void {
+ $uri = $this->request?->getUri() ?? new Uri();
+ $uri = $uri->withPath("./");
+ if(!$keepQuery) {
+ $uri = $uri->withQuery("");
+ }
+ $this->redirect($uri);
}
public function redirect(string|UriInterface $uri, int $statusCode = 303):void {
$this->statusCode = $statusCode;
- $this->headers->set("Location", $uri);
+ $this->headers->set("Location", (string)$uri);
if(isset($this->exitCallback)) {
call_user_func($this->exitCallback);
}
diff --git a/src/Uri.php b/src/Uri.php
index 34713ec..b9cdc51 100644
--- a/src/Uri.php
+++ b/src/Uri.php
@@ -152,7 +152,7 @@ protected function rawurlencodeMatchZero(array $match):string {
protected function filterUserInfo(string $user, string $pass = null):string {
$userInfo = $user;
- if(strlen($pass) > 0) {
+ if(strlen($pass ?? "") > 0) {
$userInfo .= ":";
$userInfo .= $pass;
}
diff --git a/test/phpunit/RequestFactoryTest.php b/test/phpunit/RequestFactoryTest.php
index 129587c..31227be 100644
--- a/test/phpunit/RequestFactoryTest.php
+++ b/test/phpunit/RequestFactoryTest.php
@@ -48,6 +48,22 @@ public function testCreateServerRequestFromGlobals_uri():void {
self::assertEquals("/path/to/somewhere", $request->getUri()->getPath());
}
+ public function testCreateServerRequestFromGlobals_uri_withAllParts():void {
+ $sut = new RequestFactory();
+ $request = $sut->createServerRequestFromGlobalState([
+ "REQUEST_METHOD" => "POST",
+ "REQUEST_URI" => "/path/to/somewhere/",
+ "SERVER_PORT" => 8080,
+ "HTTP_HOST" => "localhost:8080",
+ "QUERY_STRING" => "example=123",
+ ], [], [], []);
+
+ self::assertSame(
+ "http://localhost:8080/path/to/somewhere/?example=123",
+ (string)$request->getUri()
+ );
+ }
+
public function testCreateServerRequestFromGlobals_header():void {
$sut = new RequestFactory();
$request = $sut->createServerRequestFromGlobalState([
diff --git a/test/phpunit/ResponseTest.php b/test/phpunit/ResponseTest.php
index e21c49b..059fb79 100644
--- a/test/phpunit/ResponseTest.php
+++ b/test/phpunit/ResponseTest.php
@@ -2,7 +2,10 @@
namespace Gt\Http\Test;
use Gt\Http\Header\ResponseHeaders;
+use Gt\Http\Request;
use Gt\Http\Response;
+use Gt\Http\StatusCode;
+use Gt\Http\Uri;
use PHPUnit\Framework\TestCase;
class ResponseTest extends TestCase {
@@ -55,4 +58,53 @@ public function testRedirect() {
$sut->getHeaderLine("Location")
);
}
+
+ public function testReloadKeepsQuery() {
+ $expectedRelativePath = "./?test=123";
+
+ $uri = self::createMock(Uri::class);
+ $uri->expects(self::once())
+ ->method("withPath")
+ ->with("./")
+ ->willReturn($uri);
+ $uri->expects(self::never())
+ ->method("withQuery");
+ $uri->method("__toString")
+ ->willReturn($expectedRelativePath);
+
+ $request = self::createMock(Request::class);
+ $request->method("getUri")
+ ->willReturn($uri);
+
+ $sut = new Response(200, request: $request);
+ self::assertSame(StatusCode::OK, $sut->getStatusCode());
+ $sut->reload();
+ self::assertSame(StatusCode::SEE_OTHER, $sut->getStatusCode());
+ self::assertSame($expectedRelativePath, $sut->getHeaderLine("Location"));
+ }
+
+ public function testReloadWithoutKeepQuery() {
+ $expectedRelativePath = "./";
+
+ $uri = self::createMock(Uri::class);
+ $uri->expects(self::once())
+ ->method("withPath")
+ ->with("./")
+ ->willReturn($uri);
+ $uri->expects(self::once())
+ ->method("withQuery")
+ ->with("")
+ ->willReturn($uri);
+ $uri->method("__toString")
+ ->willReturn($expectedRelativePath);
+
+ $request = self::createMock(Request::class);
+ $request->method("getUri")
+ ->willReturn($uri);
+
+ $sut = new Response(200, request: $request);
+ $sut->reload(false);
+ self::assertSame(StatusCode::SEE_OTHER, $sut->getStatusCode());
+ self::assertSame($expectedRelativePath, $sut->getHeaderLine("Location"));
+ }
}