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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ jobs:

- name: Upload to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}

phpstan:
runs-on: ubuntu-latest
Expand All @@ -115,6 +117,7 @@ jobs:
with:
php_version: ${{ matrix.php }}
path: src/
level: 7

phpmd:
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"phpgt/promise": "^2.4",
"phpgt/async": "^1.0",
"phpgt/json": "^2.2",
"phpgt/curl": "^3.1",
"phpgt/curl": "^3.2",
"phpgt/propfunc": "^1.0",
"psr/http-message": "^2.0",
"willdurand/negotiation": "3.1.0"
Expand All @@ -38,7 +38,7 @@

"scripts": {
"phpunit": "vendor/bin/phpunit --configuration phpunit.xml",
"phpstan": "vendor/bin/phpstan analyse --level 6 src",
"phpstan": "vendor/bin/phpstan analyse --memory-limit=512M --level 7 src",
"phpcs": "vendor/bin/phpcs src --standard=phpcs.xml",
"phpmd": "vendor/bin/phpmd src/ text phpmd.xml",
"test": [
Expand Down
21 changes: 11 additions & 10 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions src/Header/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@ public function getKeyValues():array {
protected function pregMatchProtocol(string $matchName):string {
$headerLine = strtok($this->rawHeaders, "\n") ?: "";
/** @noinspection RegExpRedundantEscape */
preg_match(
$matched = preg_match(
"/HTTP\/(?P<version>[0-9\.]+)\s*(?P<code>\d+)?/",
$headerLine,
$matches
);
/** @var array<int|string, string> $matches */
if($matched !== 1) {
return "";
}

return $matches[$matchName];
return (string)($matches[$matchName] ?? "");
}
}
12 changes: 6 additions & 6 deletions src/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function getProtocolVersion():string {
}

/** @inheritDoc */
public function withProtocolVersion(string $version):self {
public function withProtocolVersion(string $version):static {
if(!is_numeric($version)) {
throw new InvalidProtocolHttpException($version);
}
Expand Down Expand Up @@ -105,7 +105,7 @@ public function getHeaderLine(string $name):string {
*
* @param string|string[] $value Header value(s).
*/
public function withHeader(string $name, $value):self {
public function withHeader(string $name, $value):static {
if(!is_array($value)) {
$value = [$value];
}
Expand All @@ -120,7 +120,7 @@ public function withHeader(string $name, $value):self {
*
* @param string|string[] $value Header value(s).
*/
public function withAddedHeader(string $name, $value):self {
public function withAddedHeader(string $name, $value):static {
if(!is_array($value)) {
$value = [$value];
}
Expand All @@ -131,14 +131,14 @@ public function withAddedHeader(string $name, $value):self {
}

/** @inheritDoc */
public function withoutHeader(string $name):self {
public function withoutHeader(string $name):static {
$clone = clone $this;
$clone->headers->remove($name);
return $clone;
}

/** @param array<string, string|array<int, string>> $headers */
public function setHeaders(array $headers):self {
public function setHeaders(array $headers):static {
$clone = clone $this;
$clone->headers->fromArray($headers);
return $clone;
Expand All @@ -153,7 +153,7 @@ public function getBody():StreamInterface {
}

/** @inheritDoc */
public function withBody(StreamInterface $body):self {
public function withBody(StreamInterface $body):static {
$clone = clone $this;
$clone->stream = $body;
return $clone;
Expand Down
8 changes: 4 additions & 4 deletions src/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public function getRequestTarget():string {
}

/** @inheritDoc */
public function withRequestTarget($requestTarget):self {
public function withRequestTarget($requestTarget):static {
$clone = clone $this;
$clone->requestTarget = $requestTarget;
return $clone;
Expand All @@ -68,7 +68,7 @@ public function getMethod():string {
* @inheritDoc
* @SuppressWarnings("StaticAccess")
*/
public function withMethod(string $method):self {
public function withMethod(string $method):static {
$method = RequestMethod::filterMethodName($method);
$clone = clone $this;
$clone->method = $method;
Expand All @@ -81,7 +81,7 @@ public function getUri():UriInterface {
}

/** @inheritDoc */
public function withUri(UriInterface $uri, bool$preserveHost = false):self {
public function withUri(UriInterface $uri, bool$preserveHost = false):static {
$clone = clone $this;

$host = $uri->getHost();
Expand All @@ -97,7 +97,7 @@ public function withUri(UriInterface $uri, bool$preserveHost = false):self {
}

/** @inheritDoc */
public function withBody(StreamInterface|FormData $body):self {
public function withBody(StreamInterface|FormData $body):static {
if($body instanceof FormData) {
$stream = new Stream();
$stream->write((string)$body);
Expand Down
3 changes: 1 addition & 2 deletions src/RequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public function createServerRequestFromGlobalState(

$headers = $this->buildRequestHeaders($server);

/** @var ServerRequestInterface $serverRequest */
$serverRequest = $this->buildRequest(
$method,
$uri,
Expand Down Expand Up @@ -58,7 +57,7 @@ public function buildRequest(
array $get,
array $post,
string $inputPath
):ServerRequest|Request {
):ServerRequestInterface {
$request = new ServerRequest(
$method,
$uri,
Expand Down
4 changes: 2 additions & 2 deletions src/Response.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php /** @noinspection PhpUnusedPrivateMethodInspection */
namespace Gt\Http;

use Gt\Curl\CurlInterface;
use GT\Curl\CurlInterface;
use Gt\Http\Header\ResponseHeaders;
use Gt\Json\JsonObject;
use Gt\Json\JsonObjectBuilder;
Expand Down Expand Up @@ -166,7 +166,7 @@ public function withStatus(

/** @inheritDoc */
public function getReasonPhrase():string {
return StatusCode::REASON_PHRASE[$this->statusCode];
return StatusCode::REASON_PHRASE[$this->statusCode ?? 0] ?? "";
}

public function getResponseHeaders():ResponseHeaders {
Expand Down
13 changes: 6 additions & 7 deletions src/Stream.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,11 @@ public function __construct(
$this->stream = $stream;


$streamInfo = stream_get_meta_data($this->stream);
$this->isSeekable = $streamInfo["seekable"];
/** @phpstan-ignore-next-line */
$this->uri = $streamInfo["uri"] ?? "";
$this->isReadable = in_array($streamInfo["mode"], self::READABLE_MODES);
$this->isWritable = in_array($streamInfo["mode"], self::WRITABLE_MODES);
$streamInfo = stream_get_meta_data($this->stream);
$this->isSeekable = $streamInfo["seekable"];
$this->uri = $streamInfo["uri"] ?? "";
$this->isReadable = in_array($streamInfo["mode"], self::READABLE_MODES);
$this->isWritable = in_array($streamInfo["mode"], self::WRITABLE_MODES);
}

/** @inheritDoc */
Expand All @@ -63,7 +62,7 @@ public function close():void {
public function detach() {
/** @var resource|null $stream */
$stream = $this->stream;
unset($this->stream);
$this->stream = null;
return $stream;
}

Expand Down
71 changes: 70 additions & 1 deletion src/Uri.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ protected function parseUri(string $uri):false|array {
return $authorityStyleParts;
}

$hostLikeParts = $this->parseHostLikeParts($uri, $parts);
if(!is_null($hostLikeParts)) {
return $hostLikeParts;
}

return $parts;
}

Expand Down Expand Up @@ -77,6 +82,34 @@ protected function parseAuthorityStyleParts(
return $authorityParts;
}

/**
* @param array<string, int|string> $parts
* @return null|array<string, int|string>
*/
protected function parseHostLikeParts(
string $uri,
array $parts
):?array {
if(!$this->canBeHostLikeUri($uri, $parts)) {
return null;
}

$hostLikeParts = parse_url("//" . $uri);
if($hostLikeParts === false || !isset($hostLikeParts["host"])) {
return null;
}

if(!$this->isAuthorityPathValid($hostLikeParts)) {
return null;
}

if(!$this->isAuthorityHostLike($hostLikeParts)) {
return null;
}

return $hostLikeParts;
}

/** @param array<string, int|string> $parts */
protected function canBeAuthorityStyleUri(string $uri, array $parts):bool {
if(str_contains($uri, "://")) {
Expand All @@ -91,6 +124,33 @@ protected function canBeAuthorityStyleUri(string $uri, array $parts):bool {
return str_contains($path, "@");
}

/** @param array<string, int|string> $parts */
protected function canBeHostLikeUri(string $uri, array $parts):bool {
if(str_contains($uri, "://")) {
return false;
}

if(isset($parts["host"])) {
return false;
}

if(isset($parts["scheme"])) {
return $this->isHostLikeString((string)$parts["scheme"]);
}

if(!isset($parts["path"])) {
return false;
}

$path = (string)$parts["path"];
if($path === "" || str_starts_with($path, "/")) {
return false;
}

$firstSegment = explode("/", $path, 2)[0];
return $this->isHostLikeString($firstSegment);
}

/** @param array<string, int|string> $authorityParts */
protected function hasRequiredAuthorityParts(array $authorityParts):bool {
return isset($authorityParts["user"], $authorityParts["pass"], $authorityParts["host"]);
Expand All @@ -104,7 +164,16 @@ protected function isAuthorityPathValid(array $authorityParts):bool {

/** @param array<string, int|string> $authorityParts */
protected function isAuthorityHostLike(array $authorityParts):bool {
$host = (string)$authorityParts["host"];
return $this->isHostLikeString((string)$authorityParts["host"]);
}

protected function isHostLikeString(string $host):bool {
if($host === "."
|| $host === ".."
|| str_starts_with($host, ".")) {
return false;
}

if(filter_var($host, FILTER_VALIDATE_IP) !== false) {
return true;
}
Expand Down
Loading
Loading