method = strtoupper($method); $this->uri = $uri; $this->setHeaders($headers); $this->protocol = $version; if (! $this->hasHeader('Host')) { $this->updateHostFromUri(); } if ($body !== '' && $body !== null) { $this->stream = ($body instanceof StreamInterface ? $body : new SwooleStream($body)); } } public function __toString(): string { return $this->toString(); } /** * Retrieves the message's request target. * Retrieves the message's request-target either as it will appear (for * clients), as it appeared at request (for servers), or as it was * specified for the instance (see withRequestTarget()). * In most cases, this will be the origin-form of the composed URI, * unless a value was provided to the concrete implementation (see * withRequestTarget() below). * If no URI is available, and no request-target has been specifically * provided, this method MUST return the string "/". */ public function getRequestTarget(): string { if ($this->requestTarget !== null) { return $this->requestTarget; } $target = $this->uri->getPath(); if ($target == '') { $target = '/'; } if ($this->uri->getQuery() != '') { $target .= '?' . $this->uri->getQuery(); } return $target; } /** * Return an instance with the specific request-target. * If the request needs a non-origin-form request-target — e.g., for * specifying an absolute-form, authority-form, or asterisk-form — * this method may be used to create an instance with the specified * request-target, verbatim. * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * changed request target. * * @see http://tools.ietf.org/html/rfc7230#section-5.3 (for the various * request-target forms allowed in request messages) * @param string $requestTarget */ public function withRequestTarget(mixed $requestTarget): static { if (preg_match('#\s#', $requestTarget)) { throw new InvalidArgumentException('Invalid request target provided; cannot contain whitespace'); } $new = clone $this; $new->requestTarget = $requestTarget; return $new; } /** * Retrieves the HTTP method of the request. * * @return string returns the request method */ public function getMethod(): string { return $this->method; } /** * Return an instance with the provided HTTP method. * While HTTP method names are typically all uppercase characters, HTTP * method names are case-sensitive and thus implementations SHOULD NOT * modify the given string. * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * changed request method. * * @param string $method case-sensitive method * @throws InvalidArgumentException for invalid HTTP methods */ public function withMethod(mixed $method): static { $method = strtoupper($method); $methods = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'HEAD']; if (! in_array($method, $methods)) { throw new InvalidArgumentException('Invalid Method'); } $new = clone $this; $new->method = $method; return $new; } /** * Retrieves the URI instance. * This method MUST return a UriInterface instance. * * @see http://tools.ietf.org/html/rfc3986#section-4.3 * @return UriInterface returns a UriInterface instance * representing the URI of the request */ public function getUri(): UriInterface { return $this->uri; } /** * Returns an instance with the provided URI. * This method MUST update the Host header of the returned request by * default if the URI contains a host component. If the URI does not * contain a host component, any pre-existing Host header MUST be carried * over to the returned request. * You can opt in to preserving the original state of the Host header by * setting `$preserveHost` to `true`. When `$preserveHost` is set to * `true`, this method interacts with the Host header in the following ways: * - If the Host header is missing or empty, and the new URI contains * a host component, this method MUST update the Host header in the returned * request. * - If the Host header is missing or empty, and the new URI does not contain a * host component, this method MUST NOT update the Host header in the returned * request. * - If a Host header is present and non-empty, this method MUST NOT update * the Host header in the returned request. * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new UriInterface instance. * * @see http://tools.ietf.org/html/rfc3986#section-4.3 * @param UriInterface $uri new request URI to use * @param bool $preserveHost preserve the original state of the Host header */ public function withUri(UriInterface $uri, $preserveHost = false): static { if ($uri === $this->uri) { return $this; } $new = clone $this; $new->uri = $uri; if (! $preserveHost) { $new->updateHostFromUri(); } return $new; } public function setMethod(string $method): static { $method = strtoupper($method); $methods = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'HEAD']; if (! in_array($method, $methods)) { throw new InvalidArgumentException('Invalid Method'); } $this->method = $method; return $this; } public function setUri(string|UriInterface $uri, ?bool $preserveHost = null): static { $this->uri = $uri; if (! $preserveHost) { $this->updateHostFromUri(); } return $this; } public function setRequestTarget(string $requestTarget): static { if (preg_match('#\s#', $requestTarget)) { throw new InvalidArgumentException('Invalid request target provided; cannot contain whitespace'); } $this->requestTarget = $requestTarget; return $this; } public function toString(bool $withoutBody = false): string { return Http::packRequest( $this->getMethod(), $this->getUri()->getPath(), $this->getStandardHeaders(), $withoutBody ? '' : (string) $this->getBody(), $this->getProtocolVersion() ); } /** * Update Host Header according to Uri. * * @see http://tools.ietf.org/html/rfc7230#section-5.4 */ private function updateHostFromUri(): void { $host = $this->uri->getHost(); if ($host === '') { return; } if (($port = $this->uri->getPort()) !== null) { $host .= ':' . $port; } $header = 'host'; if ($this->hasHeader('host')) { $host = $this->getHeaderLine('host'); } else { $this->headerNames['host'] = 'host'; } // Ensure Host is the first header. $this->headers = [$header => [$host]] + $this->headers; } }