allow vendord
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s

This commit is contained in:
2026-03-30 14:54:57 +07:00
parent 66aed7c4e8
commit b5e3a778ce
21316 changed files with 2777892 additions and 13 deletions

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Kreait\Firebase\Http;
use Beste\Json;
use Psr\Http\Message\ResponseInterface;
use UnexpectedValueException;
use function is_string;
/**
* @internal
*/
final class ErrorResponseParser
{
public function getErrorReasonFromResponse(ResponseInterface $response): string
{
$responseBody = (string) $response->getBody();
try {
$data = Json::decode($responseBody, true);
} catch (UnexpectedValueException) {
return $responseBody;
}
if (is_string($data['error']['message'] ?? null)) {
return $data['error']['message'];
}
if (is_string($data['error'] ?? null)) {
return $data['error'];
}
return $responseBody;
}
/**
* @return array<mixed>
*/
public function getErrorsFromResponse(ResponseInterface $response): array
{
try {
return Json::decode((string) $response->getBody(), true);
} catch (UnexpectedValueException) {
return [];
}
}
}

View File

@@ -0,0 +1,204 @@
<?php
declare(strict_types=1);
namespace Kreait\Firebase\Http;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\RequestOptions;
use Kreait\Firebase\Exception\InvalidArgumentException;
use Psr\Http\Message\RequestInterface;
use function is_callable;
final class HttpClientOptions
{
/**
* @param array<non-empty-string, mixed> $guzzleConfig
* @param list<array{middleware: callable|callable-string, name: string}> $guzzleMiddlewares
*/
private function __construct(
private readonly array $guzzleConfig,
private readonly array $guzzleMiddlewares,
) {
}
public static function default(): self
{
return new self([], []);
}
/**
* The amount of seconds to wait while connecting to a server.
*
* @see RequestOptions::CONNECT_TIMEOUT
*/
public function connectTimeout(): ?float
{
return $this->guzzleConfig[RequestOptions::CONNECT_TIMEOUT] ?? null;
}
/**
* @param float $value the amount of seconds to wait while connecting to a server
*
* @see RequestOptions::CONNECT_TIMEOUT
*/
public function withConnectTimeout(float $value): self
{
if ($value < 0) {
throw new InvalidArgumentException('The connect timeout cannot be smaller than zero.');
}
return $this->withGuzzleConfigOption(RequestOptions::CONNECT_TIMEOUT, $value);
}
/**
* The amount of seconds to wait while reading a streamed body.
*
* @see RequestOptions::READ_TIMEOUT
*/
public function readTimeout(): ?float
{
return $this->guzzleConfig[RequestOptions::READ_TIMEOUT] ?? null;
}
/**
* @param float $value the amount of seconds to wait while reading a streamed body
*
* @see RequestOptions::READ_TIMEOUT
*/
public function withReadTimeout(float $value): self
{
if ($value < 0) {
throw new InvalidArgumentException('The read timeout cannot be smaller than zero.');
}
return $this->withGuzzleConfigOption(RequestOptions::READ_TIMEOUT, $value);
}
/**
* The amount of seconds to wait for a full request (connect + transfer + read) to complete.
*
* @see RequestOptions::TIMEOUT
*/
public function timeout(): ?float
{
return $this->guzzleConfig[RequestOptions::TIMEOUT] ?? null;
}
/**
* @param float $value the amount of seconds to wait while reading a streamed body
*
* @see RequestOptions::TIMEOUT
*/
public function withTimeout(float $value): self
{
if ($value < 0) {
throw new InvalidArgumentException('The total timeout cannot be smaller than zero.');
}
return $this->withGuzzleConfigOption(RequestOptions::TIMEOUT, $value);
}
/**
* The proxy that all requests should be passed through.
*
* @see RequestOptions::PROXY
*/
public function proxy(): ?string
{
return $this->guzzleConfig[RequestOptions::PROXY] ?? null;
}
/**
* @param non-empty-string $value the proxy that all requests should be passed through
*
* @see RequestOptions::PROXY
*/
public function withProxy(string $value): self
{
return $this->withGuzzleConfigOption(RequestOptions::PROXY, $value);
}
/**
* @param non-empty-string $name
*/
public function withGuzzleConfigOption(string $name, mixed $option): self
{
$config = $this->guzzleConfig;
$config[$name] = $option;
return new self($config, $this->guzzleMiddlewares);
}
/**
* @param array<non-empty-string, mixed> $options
*/
public function withGuzzleConfigOptions(array $options): self
{
$config = $this->guzzleConfig;
foreach ($options as $name => $option) {
$config[$name] = $option;
}
return new self($config, $this->guzzleMiddlewares);
}
/**
* @return array<non-empty-string, mixed>
*/
public function guzzleConfig(): array
{
return $this->guzzleConfig;
}
/**
* @return list<array{middleware: callable|callable-string, name: string}>
*/
public function guzzleMiddlewares(): array
{
return $this->guzzleMiddlewares;
}
/**
* @param callable|callable-string $middleware
* @param non-empty-string|null $name
*/
public function withGuzzleMiddleware(callable|string $middleware, ?string $name = null): self
{
return $this->withGuzzleMiddlewares([
['middleware' => $middleware, 'name' => $name ?? ''],
]);
}
/**
* @param list<array{
* middleware: callable|callable-string,
* name: string
* }|callable|callable-string> $middlewares
*/
public function withGuzzleMiddlewares(array $middlewares): self
{
$newMiddlewares = $this->guzzleMiddlewares;
foreach ($middlewares as $definition) {
if (is_callable($definition)) {
$newMiddlewares[] = ['middleware' => $definition, 'name' => ''];
continue;
}
$newMiddlewares[] = $definition;
}
return new self($this->guzzleConfig, $newMiddlewares);
}
/**
* @param callable(RequestInterface, array<mixed>): PromiseInterface $handler
*/
public function withGuzzleHandler(callable $handler): self
{
return $this->withGuzzleConfigOption('handler', $handler);
}
}

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace Kreait\Firebase\Http;
use Beste\Json;
use Closure;
use Exception;
use Fig\Http\Message\StatusCodeInterface as StatusCode;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Promise\Create;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7\Query;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use function array_merge;
use function ltrim;
use function str_ends_with;
/**
* @internal
*/
final class Middleware
{
/**
* Ensures that the ".json" suffix is added to URIs and that the content type is set correctly.
*
* @return callable(callable): Closure
*/
public static function ensureJsonSuffix(): callable
{
return static fn(callable $handler): Closure => static function (RequestInterface $request, ?array $options = null) use ($handler) {
$uri = $request->getUri();
$path = '/'.ltrim($uri->getPath(), '/');
if (!str_ends_with($path, '.json')) {
$uri = $uri->withPath($path.'.json');
$request = $request->withUri($uri);
}
return $handler($request, $options);
};
}
/**
* @param array<string, mixed>|null $override
*
* @return callable(callable): Closure
*/
public static function addDatabaseAuthVariableOverride(?array $override): callable
{
return static fn(callable $handler): Closure => static function (RequestInterface $request, ?array $options = null) use ($handler, $override) {
$uri = $request->getUri();
$uri = $uri->withQuery(Query::build(
array_merge(Query::parse($uri->getQuery()), ['auth_variable_override' => Json::encode($override)]),
));
return $handler($request->withUri($uri), $options);
};
}
/**
* @return callable(callable): Closure
*/
public static function log(LoggerInterface $logger, MessageFormatter $formatter, string $logLevel, string $errorLogLevel): callable
{
return static fn(callable $handler): Closure => static fn($request, array $options) => $handler($request, $options)->then(
static function (ResponseInterface $response) use ($logger, $request, $formatter, $logLevel, $errorLogLevel): ResponseInterface {
$message = $formatter->format($request, $response);
$messageLogLevel = $response->getStatusCode() >= StatusCode::STATUS_BAD_REQUEST ? $errorLogLevel : $logLevel;
$logger->log($messageLogLevel, $message);
return $response;
},
static function (Exception $reason) use ($logger, $request, $formatter, $errorLogLevel): PromiseInterface {
$response = $reason instanceof RequestException ? $reason->getResponse() : null;
$message = $formatter->format($request, $response, $reason);
$logger->log($errorLogLevel, $message, ['request' => $request, 'response' => $response]);
return Create::rejectionFor($reason);
},
);
}
}