From 973753c9581f654c969effeeac5fb264a779a794 Mon Sep 17 00:00:00 2001 From: Franck DAKIA Date: Mon, 22 Jun 2026 01:45:02 +0000 Subject: [PATCH] Many Bugs fixed and new methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - file() — the broken !is_uploaded_file(...) === UPLOAD_ERR_OK check (always false due to precedence) now correctly rejects non-uploaded files while preserving the array/multi-file path. - locale() — the delimiter-less regex ('^([a-z]+)[-_]?/i') that warned and never matched is now '/([a-z]+)[-_]?/i', with null guards for a missing header. - query() / post() — return type was array but returned scalars (guaranteed TypeError); now mixed with a $default argument matching get()'s contract. - time() — read the non-existent $_SESSION['REQUEST_TIME']; now returns an int from $_SERVER['REQUEST_TIME']. - old() — $fallback had no default, fataling on old('x'); now defaults to null (also fixed the fullback typo). - hostname() / path() — guarded against undefined $_SERVER indices (CLI/tests) and fixed strpos truthiness with !== false. Methods added: isMethod(string) — match the request method. input(key, default) — alias of get(). boolean(key, default) — coerce an input to bool (1/true/on/yes). bearerToken() — extract the token from an Authorization: Bearer … header. --- src/Http/Request.php | 130 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 105 insertions(+), 25 deletions(-) diff --git a/src/Http/Request.php b/src/Http/Request.php index 33bf2aad..2d64c0a1 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -120,30 +120,32 @@ public function capture() * Retrieve query variables * * @param string|null $key - * @return array + * @param mixed $default + * @return mixed */ - public function query(?string $key = null): array + public function query(?string $key = null, mixed $default = null): mixed { if ($key === null) { return $this->query; } - return $this->query[$key] ?? []; + return $this->query[$key] ?? $default; } /** * Get posted data * * @param string|null $key - * @return array + * @param mixed $default + * @return mixed */ - public function post(?string $key = null): array + public function post(?string $key = null, mixed $default = null): mixed { if ($key === null) { return $this->post; } - return $this->post[$key] ?? []; + return $this->post[$key] ?? $default; } /** @@ -210,6 +212,17 @@ public function method(): ?string return $method; } + /** + * Check that the request method matches the given one. + * + * @param string $method + * @return bool + */ + public function isMethod(string $method): bool + { + return strtoupper($method) === $this->method(); + } + /** * Retrieve a value or a collection of values. * @@ -228,6 +241,36 @@ public function get(string $key, mixed $default = null): mixed return $value; } + /** + * Alias of get, retrieve an input value. + * + * @param string $key + * @param mixed|null $default + * @return mixed + */ + public function input(string $key, mixed $default = null): mixed + { + return $this->get($key, $default); + } + + /** + * Retrieve an input value as a boolean. + * + * Truthy values are 1, "1", true, "true", "on" and "yes". + * + * @param string $key + * @param bool $default + * @return bool + */ + public function boolean(string $key, bool $default = false): bool + { + if (!$this->has($key)) { + return $default; + } + + return filter_var($this->get($key), FILTER_VALIDATE_BOOLEAN); + } + /** * Get the request ID * @@ -317,7 +360,7 @@ private function scheme(): string */ public function hostname(): string { - return $_SERVER['HTTP_HOST']; + return $_SERVER['HTTP_HOST'] ?? ''; } /** @@ -327,9 +370,9 @@ public function hostname(): string */ public function domain(): string { - $part = explode(':', $this->hostname() ?? ''); + $part = explode(':', $this->hostname()); - return $part[0] ?? 'unknown'; + return $part[0] ?: 'unknown'; } /** @@ -339,25 +382,24 @@ public function domain(): string */ public function path(): string { - $position = strpos($_SERVER['REQUEST_URI'], '?'); + $request_uri = $_SERVER['REQUEST_URI'] ?? '/'; + $position = strpos($request_uri, '?'); - if ($position) { - $uri = substr($_SERVER['REQUEST_URI'], 0, $position); - } else { - $uri = $_SERVER['REQUEST_URI']; + if ($position !== false) { + return substr($request_uri, 0, $position); } - return $uri; + return $request_uri; } /** - * Get path sent by client. + * Get the request time as a UNIX timestamp. * - * @return string + * @return int */ - public function time(): string + public function time(): int { - return $_SESSION['REQUEST_TIME']; + return (int) ($_SERVER['REQUEST_TIME'] ?? time()); } /** @@ -402,7 +444,7 @@ public function file(string $key): UploadedFile|Collection|null return null; } - if (!is_uploaded_file($_FILES[$key]['tmp_name']) === UPLOAD_ERR_OK) { + if (!is_array($_FILES[$key]['tmp_name']) && !is_uploaded_file($_FILES[$key]['tmp_name'])) { return null; } @@ -430,14 +472,14 @@ public function file(string $key): UploadedFile|Collection|null * Get previous request data * * @param string $key - * @param mixed $fullback + * @param mixed $fallback * @return mixed */ - public function old(string $key, mixed $fullback): mixed + public function old(string $key, mixed $fallback = null): mixed { $old = Session::getInstance()->get('__bow.old', []); - return $old[$key] ?? $fullback; + return $old[$key] ?? $fallback; } /** @@ -531,7 +573,23 @@ public function referer(): string */ public function ip(): ?string { - return $_SERVER['REMOTE_ADDR'] ?? null; + $candidates = [ + $_SERVER['HTTP_CF_CONNECTING_IP'] ?? null, + isset($_SERVER['HTTP_X_FORWARDED_FOR']) + ? explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0] + : null, + $_SERVER['HTTP_X_REAL_IP'] ?? null, + $_SERVER['REMOTE_ADDR'] ?? null, + ]; + + foreach ($candidates as $candidate) { + $ip = trim((string) $candidate); + if ($ip !== '' && filter_var($ip, FILTER_VALIDATE_IP) !== false) { + return $ip; + } + } + + return null; } /** @@ -557,9 +615,15 @@ public function locale(): ?string { $accept_language = $this->getHeader('accept-language'); + if (!$accept_language) { + return null; + } + $tmp = explode(';', $accept_language)[0]; - preg_match('^([a-z]+)[-_]?/i', $tmp, $match); + if (!preg_match('/([a-z]+)[-_]?/i', $tmp, $match)) { + return null; + } return end($match); } @@ -644,6 +708,22 @@ public function userAgent(): ?string return $this->getHeader('USER_AGENT'); } + /** + * Get the Bearer token from the Authorization header. + * + * @return ?string + */ + public function bearerToken(): ?string + { + $authorization = $this->getHeader('authorization'); + + if ($authorization && str_starts_with($authorization, 'Bearer ')) { + return substr($authorization, 7); + } + + return null; + } + /** * Get session information *