tencentcloud/common
tencentcloud/common provides shared foundation code for Tencent Cloud PHP SDK packages, including request signing, HTTP transport, credential handling, and common exceptions. Add it when integrating Tencent Cloud APIs or building service-specific clients in PHP.
tencentcloud/common is the transport core of the Tencent Cloud PHP SDK, not a usable SDK by itself. It ships credentials, request signing (TC3-HMAC-SHA256), the cURL connection layer, AbstractClient, AbstractModel, and TencentCloudSDKException. Every generated product client (CvmClient, SmsClient, CdnClient, …) extends AbstractClient from this package.
Because it has essentially no direct dependents and a single star, treat it as an internal dependency: you almost never composer require tencentcloud/common alone. You require the monolith (tencentcloud/tencentcloud-sdk-php) or a per-product package, and common arrives transitively. Only pull it directly if you are writing your own generated client or a thin custom HTTP wrapper.
Setup
composer require tencentcloud/tencentcloud-sdk-php # or a per-product package
php -m | grep curl # ext-curl is not always declared
composer show tencentcloud/common # note the resolved version
Where to look first
src/Common/AbstractClient.php — the _request() pipeline, endpoint resolution, response model dispatch.src/Common/Credential.php — secretId/secretKey/token.src/Common/Profile/HttpProfile.php and ClientProfile.php — timeouts, endpoint, sign method, language.src/Common/Exception/TencentCloudSDKException.php — getErrorCode(), getRequestId().*Request / *Response classes in the product package — these are your real API contract.First use case: SMS verification codes
use TencentCloud\Common\Credential;
use TencentCloud\Common\Profile\ClientProfile;
use TencentCloud\Common\Profile\HttpProfile;
use TencentCloud\Sms\V20210111\SmsClient;
use TencentCloud\Sms\V20210111\Models\SendSmsRequest;
$credential = new Credential(config('services.tencent.secret_id'), config('services.tencent.secret_key'));
$http = new HttpProfile();
$http->setEndpoint('sms.tencentcloudapi.com');
$http->setProtocol('https');
$http->setConnectTimeout(5);
$http->setReqTimeout(15);
$profile = new ClientProfile();
$profile->setHttpProfile($http);
$profile->setSignMethod('TC3-HMAC-SHA256');
$profile->setLanguage('en-US'); // readable error messages
$client = new SmsClient($credential, 'ap-guangzhou', $profile);
$req = new SendSmsRequest();
$req->SmsSdkAppId = config('services.tencent.sms_app_id');
$req->SignName = config('services.tencent.sms_sign');
$req->TemplateId = '1234567';
$req->TemplateParamSet = ['483920'];
$req->PhoneNumberSet = ['+8613800000000'];
$resp = $client->SendSms($req);
Namespace version segments (V20210111) and endpoint hosts vary per product — copy them from the generated client's docblock.
Bind a configured client per service in a service provider. Don't scatter new Credential(...) across controllers.
// app/Providers/TencentServiceProvider.php
$this->app->singleton(SmsClient::class, function () {
$cred = new Credential(config('services.tencent.secret_id'), config('services.tencent.secret_key'));
$http = new HttpProfile();
$http->setEndpoint(config('services.tencent.sms_endpoint'));
$http->setConnectTimeout(config('services.tencent.connect_timeout', 5));
$http->setReqTimeout(config('services.tencent.req_timeout', 15));
$profile = new ClientProfile();
$profile->setHttpProfile($http);
$profile->setSignMethod('TC3-HMAC-SHA256');
$profile->setLanguage('en-US');
return new SmsClient($cred, config('services.tencent.region'), $profile);
});
Keep secrets in config/services.php (which reads env), never env() directly in the closure — config:cache will otherwise hand you null.
Wrap every product client behind your own interface. AbstractClient methods are concrete and the response models are plain public-property bags, so mocking them in tests is painful. A one-method SmsSender interface with a real and a fake implementation gives you a clean testing seam and a place to attach logging, retries, and metrics.
Use retry() for throttling, but only on safe codes. The SDK historically does not retry for you.
$resp = retry(3, fn () => $client->SendSms($req), 200, function ($e) {
return $e instanceof TencentCloudSDKException
&& in_array($e->getErrorCode(), ['RequestLimitExceeded', 'InternalError'], true);
});
Never retry non-idempotent actions on ambiguous failures — a timeout may mean the SMS was already queued.
Hydrate request models from arrays when convenient. Generated request models take an optional params array in their constructor, so a DTO or validated form array can flow straight in — but confirm field names against the generated class rather than guessing.
Handle STS temporary credentials explicitly. Pass the token as the third Credential argument, cache it (Redis) with an expiry margin, and rotate it in a scheduled job. A stale token surfaces as AuthFailure.TokenFailure only at call time.
For queue jobs, make SDK timeouts strictly smaller than the worker timeout (connect 5s, req 15s, $timeout = 30), otherwise a hung cURL call takes the worker down with it. Also set $tries and use release() with backoff on RequestLimitExceeded.
Log RequestId on every failure. Tencent support will ask for it, and it's the only way to correlate with their side.
catch (TencentCloudSDKException $e) {
Log::warning('tencent.api.failed', [
'code' => $e->getErrorCode(),
'request_id' => $e->getRequestId(),
'msg' => $e->getMessage(),
]);
}
Poll for async operations deliberately. Many Describe*/*Status APIs are eventually consistent. Poll with a bounded loop and exponential sleep, not a tight while.
common and generated clients is the #1 breakage. Product packages pin a compatible common; a loose constraint plus a fresh composer update can yield signature errors or Class not found on response models. Pin exact versions together in composer.json and upgrade them as a set.AbstractClient::_request() derives the response class from the action name (FooRequest → FooResponse). If you subclass or wrap manually and the naming doesn't line up, you get a confusing class-not-found rather than an API error.HttpProfile::setEndpoint(), you may get an empty host or a generic client-side exception rather than "endpoint missing." Always set it explicitly per product; don't try to share one HttpProfile across services.HttpProfile / ClientProfile are mutable and are stored on the client. Don't reuse one instance across multiple clients in a long-running process — copy the settings instead.$resp->TotalCount, not $resp->getTotalCount(). Nested lists may come back as plain arrays or as hydrated models depending on the resolved SDK version — dd($resp->toJsonString()) once per new API and code against what you actually see.'' or 0 sends it. Half the "MissingParameter" errors are a typo'd property name on a request model — the SDK silently ignores unknown keys.AuthFailure.SignatureFailure after a server migration → check NTP. AuthFailure.SignatureExpire → the box is minutes off, not the key.RequestLimitExceeded is per-action and per-region, not a global quota. Fanning out a thousand DescribeInstances calls from one queue worker will throttle you regardless of how many workers you add.curl -v https://<endpoint>/ before touching credentials.qcloud/Qcloud\ SDK namespace can coexist with TencentCloud\Common\ if some legacy package is still installed. composer why tencentcloud/common will reveal duplicates; remove the legacy package rather than trying to bridge them.ClientProfile::setLanguage('en-US'). Error messages default to Chinese, which is fine for logs but slows down debugging at 2am.Credential object into logs or an exception handler payload — the secret key and token are private properties, but var_export/print_r of a surrounding client object can still leak them.How can I help you explore Laravel packages today?