From 2cf71d3d0328bf50f4d5ad7025af90d19c9cdf77 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Mon, 26 Sep 2022 19:22:26 +0100 Subject: [PATCH 1/7] tweak: safely check presence of current protocol (#171) --- src/Message.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Message.php b/src/Message.php index e027d98..2a9a023 100644 --- a/src/Message.php +++ b/src/Message.php @@ -48,7 +48,7 @@ public function withProtocolVersion($version) { $clone = clone $this; - if(strstr($clone->protocol, "/")) { + if(isset($clone->protocol) && strstr($clone->protocol, "/")) { $versionStartPos = strpos( $clone->protocol, "/" From 9ac259394b8fad2791032ea787bcce9f07a424fb Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Mon, 26 Sep 2022 20:54:31 +0100 Subject: [PATCH 2/7] tweak: pass request to response to redirect to current path (#173) --- src/Response.php | 4 +++- src/Uri.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Response.php b/src/Response.php index 1656243..aa99138 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(); @@ -29,7 +30,8 @@ public function setExitCallback(callable $callback):void { } public function reload():void { - $this->redirect("./"); + $uri = $this->request?->getUri() ?? new Uri(); + $this->redirect($uri->withPath("./")); } public function redirect(string|UriInterface $uri, int $statusCode = 303):void { 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; } From ec7a74dd32bd2f105684937b7650097a8f2d843b Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Mon, 26 Sep 2022 20:55:44 +0100 Subject: [PATCH 3/7] Update README.md (#174) --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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. *** - PHP.Gt/Http build status + PHP.Gt/Http build status - PHP.Gt/Http code quality + PHP.Gt/Http code quality - PHP.Gt/Http code coverage + PHP.Gt/Http code coverage - PHP.Gt/Http latest release + PHP.Gt/Http latest release - PHP.Gt/Http documentation + PHP.Gt/Http documentation -[fig]: https://www.php-fig.org/psr/psr-7/ \ No newline at end of file +[fig]: https://www.php-fig.org/psr/psr-7/ From 6d32a4cbf3efea655b3204591501fc84721b00ca Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 28 Sep 2022 17:04:19 +0100 Subject: [PATCH 4/7] feature: expose headers as public readonly property (#176) --- .github/workflows/ci.yml | 18 +++++++++------ .scrutinizer.yml | 4 ++-- composer.json | 2 +- composer.lock | 50 ++++++++++++++++++++++------------------ src/Message.php | 6 ++--- 5 files changed, 44 insertions(+), 36 deletions(-) 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/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..3d59fd9 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.8.6", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "f6598a5ff12ca4499a836815e08b4d77a2ddeb20" + "reference": "c386ab2741e64cc9e21729f891b28b2b10fe6618" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f6598a5ff12ca4499a836815e08b4d77a2ddeb20", - "reference": "f6598a5ff12ca4499a836815e08b4d77a2ddeb20", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c386ab2741e64cc9e21729f891b28b2b10fe6618", + "reference": "c386ab2741e64cc9e21729f891b28b2b10fe6618", "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.8.6" }, "funding": [ { @@ -507,7 +507,7 @@ "type": "tidelift" } ], - "time": "2022-09-07T16:05:32+00:00" + "time": "2022-09-23T09:54:39+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,7 +1950,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.0" + "php": ">=8.1" }, "platform-dev": [], "plugin-api-version": "2.2.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); } From eff90a4afc669e1ba45827c56708e755fe3532f6 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Sat, 5 Nov 2022 18:04:38 +0000 Subject: [PATCH 5/7] feature: reload and throw away query string (#178) closes #177 --- src/Response.php | 10 +++++-- test/phpunit/ResponseTest.php | 52 +++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/Response.php b/src/Response.php index aa99138..0cb3fd4 100644 --- a/src/Response.php +++ b/src/Response.php @@ -29,14 +29,18 @@ public function setExitCallback(callable $callback):void { $this->exitCallback = $callback; } - public function reload():void { + public function reload(bool $keepQuery = true):void { $uri = $this->request?->getUri() ?? new Uri(); - $this->redirect($uri->withPath("./")); + $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/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")); + } } From f0988f462ce260f037c1047d98d5d72ed8699566 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 31 Dec 2022 15:00:25 +0000 Subject: [PATCH 6/7] Bump phpstan/phpstan from 1.8.6 to 1.9.4 (#182) Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.8.6 to 1.9.4. - [Release notes](https://github.com/phpstan/phpstan/releases) - [Changelog](https://github.com/phpstan/phpstan/blob/1.9.x/CHANGELOG.md) - [Commits](https://github.com/phpstan/phpstan/compare/1.8.6...1.9.4) --- updated-dependencies: - dependency-name: phpstan/phpstan dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- composer.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index 3d59fd9..f7a450b 100644 --- a/composer.lock +++ b/composer.lock @@ -452,16 +452,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.8.6", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "c386ab2741e64cc9e21729f891b28b2b10fe6618" + "reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c386ab2741e64cc9e21729f891b28b2b10fe6618", - "reference": "c386ab2741e64cc9e21729f891b28b2b10fe6618", + "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.6" + "source": "https://github.com/phpstan/phpstan/tree/1.9.4" }, "funding": [ { @@ -507,7 +507,7 @@ "type": "tidelift" } ], - "time": "2022-09-23T09:54:39+00:00" + "time": "2022-12-17T13:33:52+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1953,5 +1953,5 @@ "php": ">=8.1" }, "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } From e3e4cf5eee74b96bc56063a802f9cfa85f96d42f Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Sat, 31 Dec 2022 15:04:57 +0000 Subject: [PATCH 7/7] feature: request factory creates uri with all parts (#183) * feature: request factory creates uri with all parts * test: fix existing tests * stan: cast int properly --- src/RequestFactory.php | 20 ++++++++++++++++++++ test/phpunit/RequestFactoryTest.php | 16 ++++++++++++++++ 2 files changed, 36 insertions(+) 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/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([