tigusigalpa/yandex-cloud-client-php
A PHP client for Yandex Cloud APIs with a clean, Laravel-friendly interface. Easily connect your app to services like Object Storage, Translate, and SpeechKit, with simple configuration and straightforward authentication for common cloud operations.
1. Install and configure.
composer require tigusigalpa/yandex-cloud-client-php
php artisan vendor:publish --tag=yandex-cloud-config # tag may differ — check the readme/ServiceProvider
Set the two things that matter in .env and never anywhere else:
YANDEX_CLOUD_OAUTH_TOKEN=...
YANDEX_CLOUD_FOLDER_ID=b1g...
The OAuth token is account-scoped and long-lived — treat it like an AWS root key. Use a dedicated service account for the app, not a personal one, and keep it in the secret store (Vault/SSM/Doppler) with .env only pointing at it locally.
2. Look at the source before you look at the docs. The package is small and that's a feature — read src/ in this order:
curl with a Bearer header;Note the real FQCN and request helper signature there; small packages rename these between minor versions and the readme drifts.
3. First use case — a read-only listing, so you can prove auth works end to end. Something like:
$cloud = app(YandexCloudClient::class); // verify FQCN in src/
$instances = $cloud->request('GET', 'compute/v1/instances', [
'folderId' => config('yandex-cloud.folder_id'),
]);
If that returns a JSON body with an instances array, auth, token caching, and transport are all working, and you can build from there. Don't start with a mutating call — you want the first signal to be unambiguous.
Wrap it, always. This is a base/transport package, not a domain SDK. Put one thin adapter per Yandex service in your app and let the rest of the codebase depend on those:
namespace App\Services\YandexCloud;
class ComputeClient
{
public function __construct(private YandexCloudClient $cloud) {}
public function listInstances(): array
{
return $this->cloud
->request('GET', 'compute/v1/instances', ['folderId' => config('yandex-cloud.folder_id')])
->json()['instances'] ?? [];
}
}
Two payoffs: you can swap the underlying package without touching call sites (17 stars and 0 dependents means you may well end up forking it), and you get one seam to mock in tests instead of faking the network.
Bind the client as a singleton. The whole point is that the IAM token is fetched once and reused. If the container builds a new client per injection, you either refetch the token per request or rely on the package's cache store being shared. In AppServiceProvider::register():
$this->app->singleton(YandexCloudClient::class, fn () => new YandexCloudClient(config('yandex-cloud')));
Wrap long-running operations in a poller. Most Yandex Cloud mutations (create instance, create bucket policy, run a SpeechKit batch job) return an operation object, not the resource. done: false means "come back later." Build one helper and use it everywhere:
public function waitForOperation(string $id, int $timeout = 300): array
{
$deadline = now()->addSeconds($timeout);
do {
$op = $this->cloud->request('GET', "operation/{$id}")->json();
if ($op['done'] ?? false) {
return $op;
}
sleep(2);
} while (now()->lt($deadline));
throw new YandexOperationTimeout($id);
}
Push slow calls onto the queue. Anything hitting *.api.cloud.yandex.net crosses the public internet with variable latency. A ShouldQueue job with $tries = 5, exponential backoff(), and retryUntil(now()->addMinutes(30)) is the right default. On the retry, send an Idempotency-Key header on POSTs so a network timeout doesn't create a second VM/bucket/database.
Paginate explicitly. Yandex list endpoints are pageSize / pageToken → nextPageToken. A LazyCollection generator beats loading every instance into memory, and it composes:
foreach ($this->compute->paginate('compute/v1/instances', 'instances') as $instance) { ... }
Let it be the shared auth layer for other packages. If you pull in a service-specific PHP SDK from the same ecosystem, it should depend on this one — check that it resolves the same singleton rather than constructing its own token provider, or you'll burn duplicate IAM token requests.
Errors don't live in the HTTP status. Yandex Cloud's REST/JSON transcoding puts a gRPC status code in the body (code, message, details). A 200-looking envelope can still be a failure, and 3 (INVALID_ARGUMENT), 5 (NOT_FOUND), 7 (PERMISSION_DENIED), 8 (RESOURCE_EXHAUSTED), 14 (UNAVAILABLE), 16 (UNAUTHENTICATED) don't map to HTTP semantics you'd expect. Branch on the body code, not status(). Retry only on 8 and 14; retrying a 3 just burns quota.
The folderId omission is the most common first bug. Almost every resource-scoped call needs it, and the failure mode is a confusing PERMISSION_DENIED rather than a validation error.
Don't cache the IAM token yourself. It has a ~12h TTL and the package refreshes it. If you stash the bearer string in your own cache/Redis and pass it back in, you'll hold a dead token past expiry and get sporadic 16s at exactly the wrong time. Let the provider own it.
Queue workers and token stampede. Long-lived workers keep the token in memory; a fleet restart means N workers all hitting the IAM endpoint at once. If the package honors a Laravel cache store, point it at Redis so one worker refreshes and the rest read the cached token. Same story under Octane — singletons survive across requests, which is fine for an IAM token but a problem if any per-request state (folder overrides, tenant context) rides along on the same object.
config:cache will bite you if you read env('YANDEX_CLOUD_...') anywhere outside config/. Publish the config, reference config('yandex-cloud.*') only.
Confirm the auth mode fits production. The description advertises OAuth → IAM exchange. If your org standardizes on service-account authorized keys (JWT exchange), verify support in src/ before committing — that's the kind of gap that surfaces at deploy time, not at composer require time.
Instrument it once. Wrap the transport in a decorator that logs method, path, latency, and the gRPC code on failure. With a 17-star package you won't get observability for free, and when a request hangs at 3am you'll want to know whether it's auth, quota, or network.
Pin the version and expect to fork. Zero dependents, low star count, single maintainer. Read the whole source (it's short), composer.lock it, and keep your wrapper layer thin enough that replacing it is an afternoon's work rather than a refactor. The token provider is maybe 100 lines — that's the part worth owning if you ever need to.
How can I help you explore Laravel packages today?