= 2 && in_array($value[0], ['"', "'"], true) && $value[-1] === $value[0]) { $value = substr($value, 1, -1); } $values[$match[1]] = $value; } } return new self(array_replace($values, getenv()), $root); } public function get(string $key, string $default = ''): string { return (string) ($this->values[$key] ?? $default); } public function bool(string $key, bool $default = false): bool { $raw = $this->get($key, $default ? 'true' : 'false'); $value = filter_var($raw, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); if ($value === null) { throw new \RuntimeException('Invalid boolean configuration'); } return $value; } public function number(string $key, int $default): int { $value = filter_var($this->get($key, (string) $default), FILTER_VALIDATE_INT); if ($value === false || $value < 1 || $value > 86400) { throw new \RuntimeException('Invalid rate limit configuration'); } return $value; } public function canonical(string $path): string { $url = rtrim($this->get('APP_URL'), '/'); if ($url === '') { return ''; } if (!filter_var($url, FILTER_VALIDATE_URL) || !in_array(parse_url($url, PHP_URL_SCHEME), ['https', 'http'], true) || parse_url($url, PHP_URL_USER) || parse_url($url, PHP_URL_QUERY) || parse_url($url, PHP_URL_FRAGMENT) || !in_array(parse_url($url, PHP_URL_PATH), [null, '', '/'], true)) { throw new \RuntimeException('Invalid APP_URL'); } return $url . $path; } }