Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Please update your remote URL if you have forked or cloned the repository.

## Unreleased

* Added support for `guzzlehttp/guzzle:^8.0`, `guzzlehttp/psr7:^3.0` and `guzzlehttp/promises:^3.0`.

## 8.3.0 - 2026-07-18

### Security improvements
Expand Down
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@
"firebase/php-jwt": "^6.10.2 || ^7.0.2",
"google/auth": "^1.45",
"google/cloud-storage": "^1.50.0 || ^2.0.0",
"guzzlehttp/guzzle": "^7.9.2",
"guzzlehttp/promises": "^2.0.4",
"guzzlehttp/psr7": "^2.7",
"guzzlehttp/guzzle": "^7.9.2 || ^8.0",
"guzzlehttp/promises": "^2.0.4 || ^3.0",
"guzzlehttp/psr7": "^2.7 || ^3.0",
"kreait/firebase-tokens": "^5.2",
"lcobucci/jwt": "^5.3",
"mtdowling/jmespath.php": "^2.9.2",
Expand Down
40 changes: 40 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,46 @@ parameters:
identifier: staticMethod.dynamicCall
path: tests

# The following errors depend on which Guzzle, Promises and PSR-7 majors are installed
-
message: '#^Call to function method_exists\(\) with GuzzleHttp\\Exception\\RequestException and ''getResponse'' will always evaluate to true\.$#'
path: src/Exception
reportUnmatched: false
-
message: '#^Parameter \#1 \$config of class GuzzleHttp\\Client constructor expects array#'
path: src/Factory.php
reportUnmatched: false
-
message: '#^Parameter \#3 \$config of class GuzzleHttp\\Pool constructor expects array#'
path: src/Messaging/ApiClient.php
reportUnmatched: false
-
message: '#^Casting to string something that''s already string\.$#'
paths:
- src/Messaging.php
- src/Messaging/AppInstanceApiClient.php
reportUnmatched: false
-
message: '#^Offset ''value'' might not exist on array#'
path: src/Messaging.php
reportUnmatched: false
-
message: '#^Method Kreait\\Firebase\\Messaging::unsubscribeFromAllTopics\(\) should return array<string, array<string, string>> but returns array<string, list<string>\|string>\.$#'
path: src/Messaging.php
reportUnmatched: false
-
message: '#^Provide more specific return type "GuzzleHttp\\Promise\\PromiseInterface" over abstract one$#'
paths:
- src/Messaging/ApiClient.php
- src/Messaging/AppInstanceApiClient.php
reportUnmatched: false

# Only reported with Guzzle 7; Guzzle 8's ResponseException extends RequestException
-
message: '#should return GuzzleHttp\\Exception\\RequestException but returns GuzzleHttp\\Exception\\ResponseException#'
path: tests/CreatesRequestExceptions.php
reportUnmatched: false

editorUrl: 'phpstorm://open?file=%%file%%&line=%%line%%'

exceptions:
Expand Down
2 changes: 1 addition & 1 deletion src/Exception/AppCheckApiExceptionConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private function convertGuzzleRequestException(RequestException $e): AppCheckExc
{
$message = $e->getMessage();
$code = $e->getCode();
$response = $e->getResponse();
$response = method_exists($e, 'getResponse') ? $e->getResponse() : null;

if ($response instanceof ResponseInterface) {
$message = $this->responseParser->getErrorReasonFromResponse($response);
Expand Down
2 changes: 1 addition & 1 deletion src/Exception/AuthApiExceptionConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private function convertGuzzleRequestException(RequestException $e): AuthExcepti
{
$message = $e->getMessage();
$code = $e->getCode();
$response = $e->getResponse();
$response = method_exists($e, 'getResponse') ? $e->getResponse() : null;

if ($response instanceof ResponseInterface) {
$message = $this->responseParser->getErrorReasonFromResponse($response);
Expand Down
2 changes: 1 addition & 1 deletion src/Exception/DatabaseApiExceptionConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private function convertGuzzleRequestException(RequestException $e): DatabaseExc
{
$message = $e->getMessage();
$code = $e->getCode();
$response = $e->getResponse();
$response = method_exists($e, 'getResponse') ? $e->getResponse() : null;

if ($response instanceof ResponseInterface) {
$message = $this->responseParser->getErrorReasonFromResponse($response);
Expand Down
2 changes: 1 addition & 1 deletion src/Exception/MessagingApiExceptionConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public function convertResponse(ResponseInterface $response, ?Throwable $previou

private function convertGuzzleRequestException(RequestException $e): MessagingException
{
$response = $e->getResponse();
$response = method_exists($e, 'getResponse') ? $e->getResponse() : null;

if ($response instanceof ResponseInterface) {
return $this->convertResponse($response, $e);
Expand Down
6 changes: 3 additions & 3 deletions src/Exception/RemoteConfigApiExceptionConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Kreait\Firebase\Exception;

use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use Kreait\Firebase\Exception\RemoteConfig\ApiConnectionFailed;
use Kreait\Firebase\Exception\RemoteConfig\OperationAborted;
Expand All @@ -13,6 +12,7 @@
use Kreait\Firebase\Exception\RemoteConfig\ValidationFailed;
use Kreait\Firebase\Exception\RemoteConfig\VersionMismatch;
use Kreait\Firebase\Http\ErrorResponseParser;
use Psr\Http\Client\NetworkExceptionInterface;
use Psr\Http\Message\ResponseInterface;
use Throwable;

Expand All @@ -33,7 +33,7 @@ public function convertException(Throwable $exception): RemoteConfigException
return $this->convertGuzzleRequestException($exception);
}

if ($exception instanceof ConnectException) {
if ($exception instanceof NetworkExceptionInterface) {
return new ApiConnectionFailed(
message: 'Unable to connect to the API: '.$exception->getMessage(),
previous: $exception
Expand All @@ -47,7 +47,7 @@ private function convertGuzzleRequestException(RequestException $e): RemoteConfi
{
$message = $e->getMessage();
$code = $e->getCode();
$response = $e->getResponse();
$response = method_exists($e, 'getResponse') ? $e->getResponse() : null;

if ($response instanceof ResponseInterface) {
$message = $this->responseParser->getErrorReasonFromResponse($response);
Expand Down
27 changes: 27 additions & 0 deletions tests/CreatesRequestExceptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Kreait\Firebase\Tests;

use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ResponseException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
* @internal
*/
trait CreatesRequestExceptions
{
private static function createGuzzleRequestException(string $message, RequestInterface $request, ResponseInterface $response): RequestException
{
if (class_exists(ResponseException::class)) {
// Guzzle 8: only the ResponseException branch of RequestException carries a response.
return new ResponseException($message, $request, $response);
}

// Guzzle 7: any RequestException can carry a response.
return new RequestException($message, $request, $response);
}
}
5 changes: 4 additions & 1 deletion tests/Unit/Exception/AppCheckApiExceptionConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Kreait\Firebase\Exception\AppCheck\PermissionDenied;
use Kreait\Firebase\Exception\AppCheckApiExceptionConverter;
use Kreait\Firebase\Http\ErrorResponseParser;
use Kreait\Firebase\Tests\CreatesRequestExceptions;
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
Expand All @@ -27,6 +28,8 @@
*/
final class AppCheckApiExceptionConverterTest extends TestCase
{
use CreatesRequestExceptions;

private AppCheckApiExceptionConverter $converter;

protected function setUp(): void
Expand Down Expand Up @@ -69,7 +72,7 @@ public static function exceptions(): Iterator

public static function createRequestException(int $code, string $identifier): RequestException
{
return new RequestException(
return self::createGuzzleRequestException(
'Firebase Error Test',
new Request('GET', 'https://example.com'),
new Response($code, [], Json::encode([
Expand Down
8 changes: 5 additions & 3 deletions tests/Unit/Exception/AuthApiExceptionConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use Beste\Json;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use Iterator;
Expand All @@ -28,6 +27,7 @@
use Kreait\Firebase\Exception\Auth\WeakPassword;
use Kreait\Firebase\Exception\AuthApiExceptionConverter;
use Kreait\Firebase\Http\ErrorResponseParser;
use Kreait\Firebase\Tests\CreatesRequestExceptions;
use Kreait\Firebase\Tests\UnitTestCase;
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
use PHPUnit\Framework\Attributes\DataProvider;
Expand All @@ -39,6 +39,8 @@
*/
final class AuthApiExceptionConverterTest extends UnitTestCase
{
use CreatesRequestExceptions;

private AuthApiExceptionConverter $converter;

protected function setUp(): void
Expand All @@ -48,7 +50,7 @@ protected function setUp(): void

public function testItConvertsARequestExceptionThatDoesNotIncludeValidJson(): void
{
$requestException = new RequestException(
$requestException = self::createGuzzleRequestException(
'Error without valid json',
new Request('GET', 'https://example.com'),
new Response(400, [], $responseBody = '{"what is this"'),
Expand Down Expand Up @@ -82,7 +84,7 @@ public function testItCanHandleUnknownExceptions(): void
#[DataProvider('requestErrors')]
public function testItConvertsRequestExceptionsBecause(string $identifier, string $expectedClass): void
{
$requestException = new RequestException(
$requestException = self::createGuzzleRequestException(
'Firebase Error Test',
new Request('GET', 'https://example.com'),
new Response(400, [], Json::encode([
Expand Down
6 changes: 4 additions & 2 deletions tests/Unit/Exception/DatabaseApiExceptionConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use Beste\Json;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use Kreait\Firebase\Exception\Database\ApiConnectionFailed;
Expand All @@ -16,6 +15,7 @@
use Kreait\Firebase\Exception\Database\PermissionDenied;
use Kreait\Firebase\Exception\DatabaseApiExceptionConverter;
use Kreait\Firebase\Http\ErrorResponseParser;
use Kreait\Firebase\Tests\CreatesRequestExceptions;
use Kreait\Firebase\Tests\UnitTestCase;
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;

Expand All @@ -24,6 +24,8 @@
*/
final class DatabaseApiExceptionConverterTest extends UnitTestCase
{
use CreatesRequestExceptions;

private DatabaseApiExceptionConverter $converter;

private Request $request;
Expand All @@ -36,7 +38,7 @@ protected function setUp(): void

public function testItConvertsARequestExceptionThatDoesNotIncludeValidJson(): void
{
$requestException = new RequestException(
$requestException = self::createGuzzleRequestException(
'Error without valid json',
$this->request,
new Response(400, [], $responseBody = '{"what is this"'),
Expand Down
5 changes: 4 additions & 1 deletion tests/Unit/Exception/MessagingApiExceptionConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Kreait\Firebase\Exception\Messaging\ServerError;
use Kreait\Firebase\Exception\Messaging\ServerUnavailable;
use Kreait\Firebase\Exception\MessagingApiExceptionConverter;
use Kreait\Firebase\Tests\CreatesRequestExceptions;
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
Expand All @@ -35,6 +36,8 @@
*/
final class MessagingApiExceptionConverterTest extends TestCase
{
use CreatesRequestExceptions;

private MessagingApiExceptionConverter $converter;

private FrozenClock $clock;
Expand Down Expand Up @@ -87,7 +90,7 @@ public static function exceptions(): Iterator

public static function createRequestException(int $code, string $identifier): RequestException
{
return new RequestException(
return self::createGuzzleRequestException(
'Firebase Error Test',
new Request('GET', 'https://example.com'),
new Response($code, [], Json::encode([
Expand Down