method = $method; $this->path = $path; $this->params = $params; $this->cookies = $cookies; parent::__construct($version, $headers, $content); } public function getMethod(): string { return $this->method; } public function getPath(): string { return $this->path; } public function getParamString(bool $spacesAsPlus = false): string { return http_build_query($this->params, '', '&', $spacesAsPlus ? PHP_QUERY_RFC1738 : PHP_QUERY_RFC3986); } public function getParams(): array { return $this->params; } public function getParam(string $name, int $filter = FILTER_DEFAULT, array|int $options = 0): mixed { if(!isset($this->params[$name])) return null; return filter_var($this->params[$name] ?? null, $filter, $options); } public function hasParam(string $name): bool { return isset($this->params[$name]); } public function getCookies(): array { return $this->cookies; } public function getCookie(string $name, int $filter = FILTER_DEFAULT, array|int $options = 0): mixed { if(!isset($this->cookies[$name])) return null; return filter_var($this->cookies[$name] ?? null, $filter, $options); } public function hasCookie(string $name): bool { return isset($this->cookies[$name]); } public static function fromRequest(): HttpRequest { $build = new HttpRequestBuilder; $build->setHttpVersion(new Version(...array_map('intval', explode('.', substr($_SERVER['SERVER_PROTOCOL'], 5))))); $build->setMethod($_SERVER['REQUEST_METHOD']); // this currently doesn't "properly" support the scenario where a full url is specified in the http request $path = $_SERVER['REQUEST_URI']; $pathQueryOffset = strpos($path, '?'); if($pathQueryOffset !== false) $path = substr($path, 0, $pathQueryOffset); else { $pathHashOffset = strpos($path, '#'); if($pathHashOffset !== false) $path = substr($path, 0, $pathHashOffset); } if(!str_starts_with($path, '/')) $path = '/' . $path; $build->setPath($path); $build->setParams($_GET); $build->setCookies($_COOKIE); $contentType = null; $contentLength = 0; $headers = self::getRawRequestHeaders(); foreach($headers as $name => $value) { $nameLower = strtolower($name); if($nameLower === 'content-type') try { $contentType = MediaType::parse($value); } catch(InvalidArgumentException $ex) { $contentType = null; } elseif($nameLower === 'content-length') $contentLength = (int)$value; $build->setHeader($name, $value); } if($contentType !== null && ($contentType->equals('application/json') || $contentType->equals('application/x-json'))) $build->setContent(JsonContent::fromRequest()); elseif($contentType !== null && ($contentType->equals('application/x-www-form-urlencoded') || $contentType->equals('multipart/form-data'))) $build->setContent(FormContent::fromRequest()); elseif($contentLength > 0) $build->setContent(StreamContent::fromRequest()); return $build->toRequest(); } private static function getRawRequestHeaders(): array { if(function_exists('getallheaders')) return getallheaders(); $headers = []; foreach($_SERVER as $key => $value) { if(substr($key, 0, 5) === 'HTTP_') { $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5))))); $headers[$key] = $value; } elseif($key === 'CONTENT_TYPE') { $headers['Content-Type'] = $value; } elseif($key === 'CONTENT_LENGTH') { $headers['Content-Length'] = $value; } } if(!isset($headers['Authorization'])) { if(isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; } elseif(isset($_SERVER['PHP_AUTH_USER'])) { $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . ($_SERVER['PHP_AUTH_PW'] ?? '')); } elseif(isset($_SERVER['PHP_AUTH_DIGEST'])) { $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST']; } } return $headers; } }