update lock clucknut
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 14s
Build, Push and Deploy / build-and-push (push) Successful in 3m14s
Build, Push and Deploy / deploy-staging (push) Successful in 25s
Build, Push and Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-04-18 20:32:18 +07:00
parent 4554035227
commit dcaf267458
3359 changed files with 153185 additions and 205489 deletions

View File

@@ -1,5 +1,41 @@
# Changelog
## [1.42.2](https://github.com/googleapis/gax-php/compare/v1.42.1...v1.42.2) (2026-04-02)
### Bug Fixes
* Ensure safe minimum for protobuf v5 ([#661](https://github.com/googleapis/gax-php/issues/661)) ([e10a8c9](https://github.com/googleapis/gax-php/commit/e10a8c993b5d4861d80f2c702ae77373d8817622))
## [1.42.1](https://github.com/googleapis/gax-php/compare/v1.42.0...v1.42.1) (2026-03-12)
### Bug Fixes
* **deps:** Update dependency google/protobuf to v5 ([#655](https://github.com/googleapis/gax-php/issues/655)) ([1d89f50](https://github.com/googleapis/gax-php/commit/1d89f50510794e220fb036042612336717e8fedd))
* Suppress unpack exceptions ([#656](https://github.com/googleapis/gax-php/issues/656)) ([989cf37](https://github.com/googleapis/gax-php/commit/989cf3710c6285b3eb71b6fc0c83d2b50508f456))
## [1.42.0](https://github.com/googleapis/gax-php/compare/v1.41.0...v1.42.0) (2026-01-22)
### Features
* Support grpc-status-details-bin for ApiException::getErrorDetails ([#614](https://github.com/googleapis/gax-php/issues/614)) ([c44ad0c](https://github.com/googleapis/gax-php/commit/c44ad0c068586b635a3f6b325e157c116f68065e))
## [1.41.0](https://github.com/googleapis/gax-php/compare/v1.40.1...v1.41.0) (2026-01-16)
### Features
* Add ApiException::getErrorDetails() for retrieval of additional exception info ([#611](https://github.com/googleapis/gax-php/issues/611)) ([be7402a](https://github.com/googleapis/gax-php/commit/be7402a94d1c4201e1ca03cdd3bab6c01adb0c24))
## [1.40.1](https://github.com/googleapis/gax-php/compare/v1.40.0...v1.40.1) (2026-01-16)
### Bug Fixes
* Use self:: instead of $this-> for static validation methods in GapicClientTrait validation ([#643](https://github.com/googleapis/gax-php/issues/643)) ([5ba2b20](https://github.com/googleapis/gax-php/commit/5ba2b200c16b78b10e3dddf5ac6a2c5cd7eb8641))
## [1.40.0](https://github.com/googleapis/gax-php/compare/v1.39.0...v1.40.0) (2025-12-04)

View File

@@ -1 +1 @@
1.40.0
1.42.2

View File

@@ -10,7 +10,7 @@
"google/auth": "^1.49",
"google/grpc-gcp": "^0.4",
"grpc/grpc": "^1.13",
"google/protobuf": "^4.31",
"google/protobuf": "^4.31||^5.34",
"guzzlehttp/promises": "^2.0",
"guzzlehttp/psr7": "^2.0",
"google/common-protos": "^4.4",
@@ -19,9 +19,9 @@
},
"require-dev": {
"phpunit/phpunit": "^9.6",
"squizlabs/php_codesniffer": "4.*",
"phpspec/prophecy-phpunit": "^2.1",
"phpstan/phpstan": "^2.0"
"phpstan/phpstan": "^2.0",
"google/cloud-tools": "^0.16.1"
},
"conflict": {
"ext-protobuf": "<4.31.0"
@@ -36,13 +36,15 @@
"psr-4": {
"Google\\ApiCore\\Dev\\": "dev/src",
"Google\\ApiCore\\Tests\\": "tests",
"GPBMetadata\\Google\\": "metadata/Google"
"GPBMetadata\\Google\\": "metadata/Google",
"Google\\Showcase\\": "tests/Conformance/src",
"GPBMetadata\\Google\\Showcase\\": "tests/Conformance/metadata"
}
},
"scripts": {
"regenerate-test-protos": "dev/sh/regenerate-test-protos.sh",
"test": "./vendor/bin/phpunit",
"cs-lint": "vendor/bin/phpcs --standard=./ruleset.xml",
"cs-fix": "vendor/bin/phpcbf --standard=./ruleset.xml"
"cs-lint": "vendor/bin/php-tools cs-fixer googleapis/gax-php --ref $(git rev-parse --abbrev-ref HEAD) --flags \"\\-n --dry-run\"",
"cs-fix": "vendor/bin/php-tools cs-fixer googleapis/gax-php --ref $(git rev-parse --abbrev-ref HEAD) --flags \"\\-n\""
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="./vendor/autoload.php" colors="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<testsuites>
<testsuite name="conformance">
<directory>tests/Conformance</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src</directory>
<exclude>
<directory suffix=".php">src/V[!a-zA-Z]*</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@@ -46,6 +46,7 @@ class ApiException extends Exception
private $metadata;
private $basicMessage;
private $decodedMetadataErrorInfo;
private array $protobufErrors;
/**
* ApiException constructor.
@@ -62,7 +63,8 @@ public function __construct(
string $message,
int $code,
?string $status = null,
array $optionalArgs = []
array $optionalArgs = [],
array $protobufErrors = [],
) {
$optionalArgs += [
'previous' => null,
@@ -76,6 +78,7 @@ public function __construct(
if ($this->metadata) {
$this->decodedMetadataErrorInfo = self::decodeMetadataErrorInfo($this->metadata);
}
$this->protobufErrors = $protobufErrors;
}
public function getStatus()
@@ -137,6 +140,15 @@ public function getErrorInfoMetadata()
return ($this->decodedMetadataErrorInfo) ? $this->decodedMetadataErrorInfo['errorInfoMetadata'] : null;
}
/**
* Returns the unserialized errors
* @return array
*/
public function getErrorDetails(): array
{
return $this->protobufErrors;
}
/**
* @param stdClass $status
* @return ApiException
@@ -144,11 +156,14 @@ public function getErrorInfoMetadata()
public static function createFromStdClass(stdClass $status)
{
$metadata = property_exists($status, 'metadata') ? $status->metadata : null;
$errors = [];
return self::create(
$status->details,
$status->code,
$metadata,
Serializer::decodeMetadata((array) $metadata)
Serializer::decodeMetadata((array) $metadata, $errors),
$errors,
);
}
@@ -165,11 +180,13 @@ public static function createFromApiResponse(
?array $metadata = null,
?Exception $previous = null
) {
$errors = [];
return self::create(
$basicMessage,
$rpcCode,
$metadata,
Serializer::decodeMetadata((array) $metadata),
Serializer::decodeMetadata((array) $metadata, $errors),
$errors,
$previous
);
}
@@ -194,6 +211,7 @@ public static function createFromRestApiResponse(
$rpcCode,
$metadata,
is_null($metadata) ? [] : $metadata,
self::decodeMetadataToProtobufErrors($metadata ?? []),
$previous
);
}
@@ -235,6 +253,7 @@ private static function containsErrorInfo(array $decodedMetadata)
* @param int $rpcCode
* @param iterable|null $metadata
* @param array $decodedMetadata
* @param array|null $protobufErrors
* @param Exception|null $previous
* @return ApiException
*/
@@ -243,6 +262,7 @@ private static function create(
int $rpcCode,
$metadata,
array $decodedMetadata,
?array $protobufErrors = null,
?Exception $previous = null
) {
$containsErrorInfo = self::containsErrorInfo($decodedMetadata);
@@ -263,11 +283,51 @@ private static function create(
$metadata = iterator_to_array($metadata);
}
return new ApiException($message, $rpcCode, $rpcStatus, [
'previous' => $previous,
'metadata' => $metadata,
'basicMessage' => $basicMessage,
]);
return new ApiException(
$message,
$rpcCode,
$rpcStatus,
[
'previous' => $previous,
'metadata' => $metadata,
'basicMessage' => $basicMessage,
],
$protobufErrors ?? []
);
}
/**
* Encodes decoded metadata to the Protobuf error type
*
* @param array $metadata
* @return array
*/
private static function decodeMetadataToProtobufErrors(array $metadata): array
{
$result = [];
Serializer::loadKnownMetadataTypes();
foreach ($metadata as $error) {
$message = null;
if (!isset($error['@type'])) {
continue;
}
$type = $error['@type'];
if (!isset(KnownTypes::TYPE_URLS[$type])) {
continue;
}
$class = KnownTypes::TYPE_URLS[$type];
$message = new $class();
$jsonMessage = json_encode(array_diff_key($error, ['@type' => true]));
$message->mergeFromJsonString($jsonMessage);
$result[] = $message;
}
return $result;
}
/**

View File

@@ -121,7 +121,7 @@ public function addMiddleware(callable $middlewareCallable): void
$this->middlewareCallables[] = $middlewareCallable;
}
/**
/**
* Prepend a middleware to the call stack by providing a callable which will be
* invoked at the end of each call, and will return an instance of
* {@see MiddlewareInterface} when invoked.
@@ -261,7 +261,7 @@ private function setClientOptions(array $options)
if (isset($options['serviceAddress'])) {
$options['apiEndpoint'] = $this->pluck('serviceAddress', $options, false);
}
$this->validateNotNull($options, [
self::validateNotNull($options, [
'apiEndpoint',
'serviceName',
'descriptorsConfigPath',
@@ -270,7 +270,7 @@ private function setClientOptions(array $options)
'credentialsConfig',
'transportConfig',
]);
$this->traitValidate($options, [
self::traitValidate($options, [
'credentials',
'transport',
'gapicVersion',

87
vendor/google/gax/src/KnownTypes.php vendored Normal file
View File

@@ -0,0 +1,87 @@
<?php
/*
* Copyright 2025 Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Google\ApiCore;
/**
* @internal
*/
class KnownTypes
{
private static bool $initialized = false;
/** @deprecated use BIN_TYPES instead */
public const GRPC_TYPES = self::BIN_TYPES;
/** @deprecated use TYPE_URLS instead */
public const JSON_TYPES = self::TYPE_URLS;
public const BIN_TYPES = [
'google.rpc.retryinfo-bin' => \Google\Rpc\RetryInfo::class,
'google.rpc.debuginfo-bin' => \Google\Rpc\DebugInfo::class,
'google.rpc.quotafailure-bin' => \Google\Rpc\QuotaFailure::class,
'google.rpc.badrequest-bin' => \Google\Rpc\BadRequest::class,
'google.rpc.requestinfo-bin' => \Google\Rpc\RequestInfo::class,
'google.rpc.resourceinfo-bin' => \Google\Rpc\ResourceInfo::class,
'google.rpc.errorinfo-bin' => \Google\Rpc\ErrorInfo::class,
'google.rpc.help-bin' => \Google\Rpc\Help::class,
'google.rpc.localizedmessage-bin' => \Google\Rpc\LocalizedMessage::class,
'google.rpc.preconditionfailure-bin' => \Google\Rpc\PreconditionFailure::class,
];
public const TYPE_URLS = [
'type.googleapis.com/google.rpc.RetryInfo' => \Google\Rpc\RetryInfo::class,
'type.googleapis.com/google.rpc.DebugInfo' => \Google\Rpc\DebugInfo::class,
'type.googleapis.com/google.rpc.QuotaFailure' => \Google\Rpc\QuotaFailure::class,
'type.googleapis.com/google.rpc.BadRequest' => \Google\Rpc\BadRequest::class,
'type.googleapis.com/google.rpc.RequestInfo' => \Google\Rpc\RequestInfo::class,
'type.googleapis.com/google.rpc.ResourceInfo' => \Google\Rpc\ResourceInfo::class,
'type.googleapis.com/google.rpc.ErrorInfo' => \Google\Rpc\ErrorInfo::class,
'type.googleapis.com/google.rpc.Help' => \Google\Rpc\Help::class,
'type.googleapis.com/google.rpc.LocalizedMessage' => \Google\Rpc\LocalizedMessage::class,
'type.googleapis.com/google.rpc.PreconditionFailure' => \Google\Rpc\PreconditionFailure::class,
];
public static function allKnownTypes(): array
{
return array_values(self::TYPE_URLS);
}
public static function addKnownTypesToDescriptorPool()
{
if (self::$initialized) {
return;
}
// adds all the above protobuf classes to the descriptor pool
\GPBMetadata\Google\Rpc\ErrorDetails::initOnce();
self::$initialized = true;
}
}

View File

@@ -49,8 +49,8 @@ class TransportCallMiddleware implements MiddlewareInterface
public function __construct(
private TransportInterface $transport,
private array $transportCallMethods
)
{}
) {
}
public function __invoke(Call $call, array $options)
{

View File

@@ -269,7 +269,7 @@ class RetrySettings
*/
public function __construct(array $settings)
{
$this->validateNotNull($settings, [
self::validateNotNull($settings, [
'initialRetryDelayMillis',
'retryDelayMultiplier',
'maxRetryDelayMillis',

View File

@@ -31,6 +31,7 @@
*/
namespace Google\ApiCore;
use Exception;
use Google\Protobuf\Any;
use Google\Protobuf\Descriptor;
use Google\Protobuf\DescriptorPool;
@@ -53,18 +54,6 @@ class Serializer
private static array $snakeCaseMap = [];
private static array $camelCaseMap = [];
private static $metadataKnownTypes = [
'google.rpc.retryinfo-bin' => \Google\Rpc\RetryInfo::class,
'google.rpc.debuginfo-bin' => \Google\Rpc\DebugInfo::class,
'google.rpc.quotafailure-bin' => \Google\Rpc\QuotaFailure::class,
'google.rpc.badrequest-bin' => \Google\Rpc\BadRequest::class,
'google.rpc.requestinfo-bin' => \Google\Rpc\RequestInfo::class,
'google.rpc.resourceinfo-bin' => \Google\Rpc\ResourceInfo::class,
'google.rpc.errorinfo-bin' => \Google\Rpc\ErrorInfo::class,
'google.rpc.help-bin' => \Google\Rpc\Help::class,
'google.rpc.localizedmessage-bin' => \Google\Rpc\LocalizedMessage::class,
];
private $fieldTransformers;
private $messageTypeTransformers;
private $decodeFieldTransformers;
@@ -178,43 +167,69 @@ public static function serializeToPhpArray(Message $message)
* Decode metadata received from gRPC status object
*
* @param array $metadata
* @param null|array $errors
* @return array
*/
public static function decodeMetadata(array $metadata)
public static function decodeMetadata(array $metadata, ?array &$errors = null)
{
if (count($metadata) == 0) {
return [];
}
// ensure known types are available from the descriptor pool
KnownTypes::addKnownTypesToDescriptorPool();
$result = [];
// If metadata contains a "status" bin, use that instead
if (isset($metadata['grpc-status-details-bin'])) {
$status = new \Google\Rpc\Status();
$status->mergeFromString($metadata['grpc-status-details-bin'][0]);
foreach ($status->getDetails() as $any) {
if (isset(KnownTypes::TYPE_URLS[$any->getTypeUrl()])) {
$class = KnownTypes::TYPE_URLS[$any->getTypeUrl()];
new $class(); // add known types to descriptor pool
}
try {
$error = $any->unpack();
} catch (Exception $ex) {
// failed to unpack the $any object - keep the object as-is instead
$error = $any;
}
if (!is_null($errors)) {
$errors[] = $error;
}
$result[] = [
'@type' => $any->getTypeUrl(),
] + self::serializeToPhpArray($error);
}
return $result;
}
// look for individual error detail bins and decode those
// NOTE: This method SHOULD be superceeded by 'grpc-status-details-bin' in every case, but
// we are keeping it for now to be safe
foreach ($metadata as $key => $values) {
foreach ($values as $value) {
$decodedValue = [
'@type' => $key,
];
$decodedValue = ['@type' => $key];
if (self::hasBinaryHeaderSuffix($key)) {
if (isset(self::$metadataKnownTypes[$key])) {
$class = self::$metadataKnownTypes[$key];
if (isset(KnownTypes::BIN_TYPES[$key])) {
$class = KnownTypes::BIN_TYPES[$key];
/** @var Message $message */
$message = new $class();
try {
$message->mergeFromString($value);
$decodedValue += self::serializeToPhpArray($message);
if (!is_null($errors)) {
$errors[] = $message;
}
} catch (\Exception $e) {
// We encountered an error trying to deserialize the data
$decodedValue += [
'data' => '<Unable to deserialize data>',
];
$decodedValue['data'] = '<Unable to deserialize data>';
}
} else {
// The metadata contains an unexpected binary type
$decodedValue += [
'data' => '<Unknown Binary Data>',
];
$decodedValue['data'] = '<Unknown Binary Data>';
}
} else {
$decodedValue += [
'data' => $value,
];
$decodedValue['data'] = $value;
}
$result[] = $decodedValue;
}
@@ -238,7 +253,6 @@ public static function decodeAnyMessages($anyArray)
$unpacked = $any->unpack();
$results[] = self::serializeToPhpArray($unpacked);
} catch (\Exception $ex) {
echo "$ex\n";
// failed to unpack the $any object - show as unknown binary data
$results[] = [
'typeUrl' => $any->getTypeUrl(),
@@ -319,7 +333,7 @@ private function encodeMessageImpl(Message $message, Descriptor $messageType)
list($fields, $fieldsToOneof) = $this->getDescriptorMaps($messageType);
foreach ($fields as $field) {
$key = $field->getName();
$getter = $this->getGetter($key);
$getter = self::getGetter($key);
$v = $message->$getter();
if (is_null($v)) {
@@ -329,7 +343,7 @@ private function encodeMessageImpl(Message $message, Descriptor $messageType)
// Check and skip unset fields inside oneofs
if (isset($fieldsToOneof[$key])) {
$oneofName = $fieldsToOneof[$key];
$oneofGetter = $this->getGetter($oneofName);
$oneofGetter = self::getGetter($oneofName);
if ($message->$oneofGetter() !== $key) {
continue;
}
@@ -437,7 +451,7 @@ private function decodeMessageImpl(Message $message, Descriptor $messageType, ar
$value = $this->decodeElement($field, $v);
}
$setter = $this->getSetter($field->getName());
$setter = self::getSetter($field->getName());
$message->$setter($value);
// We must unset $value here, otherwise the protobuf c extension will mix up the references
@@ -453,9 +467,15 @@ private function decodeMessageImpl(Message $message, Descriptor $messageType, ar
*/
private function checkFieldRepeated(FieldDescriptor $field): bool
{
return method_exists($field, 'isRepeated')
? $field->isRepeated()
: $field->getLabel() === GPBLabel::REPEATED;
if (method_exists($field, 'isRepeated')) {
return $field->isRepeated();
}
if (method_exists($field, 'getLabel')) {
return $field->getLabel() === GPBLabel::REPEATED;
}
throw new \Exception('No field repeated method avaialble');
}
/**
@@ -527,7 +547,7 @@ private static function getPhpArraySerializer()
public static function loadKnownMetadataTypes()
{
foreach (self::$metadataKnownTypes as $key => $class) {
foreach (KnownTypes::allKnownTypes() as $key => $class) {
new $class();
}
}