Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Cloud Vision Laravel Package

google/cloud-vision

The official PHP client library for Google Cloud Vision, making it easy to add image understanding to PHP apps. Detect labels, objects, faces, and landmarks; run OCR; and apply SafeSearch moderation through a simple, idiomatic API maintained by Google.

View on GitHub
Deep Wiki
Context7

Getting Started

google/cloud-vision is the official PHP client for the Google Cloud Vision API — it is not a Laravel package. Expect to write a thin wrapper yourself; there is no config file, facade, or service provider to publish.

Install and authenticate:

composer require google/cloud-vision

Create a service account in GCP with the Cloud Vision API User role, download the JSON key, and store the path (not the contents) in .env:

GOOGLE_APPLICATION_CREDENTIALS=/var/www/app/storage/keys/gcp-vision.json

If you cannot use the ambient env var (e.g. multi-tenant apps), pass credentials explicitly:

$client = new ImageAnnotatorClient([
    'credentials' => storage_path('keys/gcp-vision.json'),
]);

Enable the API in the console first — a perfectly valid key still throws PERMISSION_DENIED / SERVICE_DISABLED until you do.

Where to look first: the Google\Cloud\Vision\V1 namespace in vendor/google/cloud-vision/src/V1/, specifically ImageAnnotatorClient.php (every RPC), Feature/Type.php (the enum of detections), and Image.php (how to supply bytes vs. URIs). Ignore Google\Cloud\Vision\VisionClient and the image()/annotation() helpers — they are the legacy high-level API and are not what the generated client examples assume.

First use case — label detection on an uploaded file:

use Google\Cloud\Vision\V1\Client\ImageAnnotatorClient;
use Google\Cloud\Vision\V1\{BatchAnnotateImagesRequest, AnnotateImageRequest, Image, Feature};

$client = app(ImageAnnotatorClient::class);

$request = new AnnotateImageRequest([
    'image'    => new Image(['content' => file_get_contents($path)]),
    'features' => [new Feature(['type' => Feature\Type::LABEL_DETECTION, 'max_results' => 10])],
]);

$response = $client->annotateImage($request)->getLabelAnnotations();

foreach ($response as $label) {
    echo $label->getDescription(), ' ', $label->getScore(), PHP_EOL;
}

Implementation Patterns

Bind the client as a singleton. The client holds a gRPC/HTTP channel and is expensive to construct. Register it in a VisionServiceProvider:

$this->app->singleton(ImageAnnotatorClient::class, fn () => new ImageAnnotatorClient([
    'credentials' => config('services.vision.credentials'),
]));

Put the path in config/services.php rather than calling env() at runtime — env() returns null once config is cached.

Wrap it in your own service class. Never let controllers touch the generated client directly. A VisionService with methods like labels(string $path): array, ocr(string $path): string, safeSearch(string $path): array gives you one place to swap transports, add caching, and normalize the protobuf getters into plain arrays you can json_encode into a model column.

Batch instead of looping. batchAnnotateImages() accepts up to 16 images per call and is dramatically cheaper in latency than 16 sequential annotateImage() calls. Build an array of AnnotateImageRequest objects and read back $response->getResponses(), which is index-aligned with your input:

$batchResponse = $client->batchAnnotateImages(new BatchAnnotateImagesRequest([
    'requests' => $requests,
]));

foreach ($batchResponse->getResponses() as $i => $r) {
    if ($r->hasError()) { /* per-image failure, see Gotchas */ }
    $labels[$i] = array_map(fn ($l) => $l->getDescription(), iterator_to_array($r->getLabelAnnotations()));
}

Push heavy work to the queue. Vision calls are 200–2000 ms and occasionally much worse. A ProcessImage job on a dedicated queue keeps uploads snappy and gives you $tries, backoff, and failed() for free. Store the image in S3/GCS first, then dispatch with the path or gs:// URI.

Prefer gs:// URIs for anything sizeable. Image::fromUri('gs://bucket/key.jpg') avoids base64-inflating the payload by ~33% and sidesteps the request-size ceiling. Same for output: asyncBatchAnnotateFiles() writes OCR results for PDFs/TIFFs to a GCS bucket — you never get the text back in the response, you must read the JSON from GCS afterward.

Combine features in one request. DOCUMENT_TEXT_DETECTION + SAFE_SEARCH_DETECTION + LABEL_DETECTION on a single image costs one round trip. Use max_results on every feature; the default is 10 but FACE_DETECTION and WEB_DETECTION can return a lot of noise.

Cache aggressively by content hash. Vision output for identical bytes is deterministic enough to cache forever. Cache::remember('vision:labels:'.hash_file('xxh128', $path), now()->addYear(), ...) will pay for itself immediately given the free tier is only 1,000 units per feature per month.

Gotchas and Tips

  • HTTP 200 does not mean success. The RPC succeeds but individual images can fail with AnnotateImageResponse->getError() (corrupt image, INVALID_ARGUMENT, quota). Always check $r->getError() per response inside a batch. Only transport/auth failures throw ApiException.
  • Feature type is a string enum with magic constants. Feature\Type::TEXT_DETECTION etc. are plain strings in Feature\Type, not a PHP enum — typos fail at the API, not at compile time. Feature\Type::DOCUMENT_TEXT_DETECTION returns a TextAnnotation (with getPages(), structure, and blocks), while TEXT_DETECTION returns flat EntityAnnotations. Pick deliberately; DOCUMENT_TEXT_DETECTION is priced higher.
  • Getters return null/empty, not defaults. getFullTextAnnotation() is null when the feature wasn't requested or nothing was found. getTextAnnotations() includes one entry per word plus the whole-document entry at index 0 — [0]->getDescription() is the usual "give me the text" answer. Always null-guard before iterating.
  • gRPC vs REST transport. If ext-grpc is not installed, the client silently falls back to REST. REST has a hard ~10 MB request limit and different error shapes; gRPC gives faster cold starts. Force it with ['transport' => 'rest'] if you see gRPC channel errors (UNAVAILABLE: failed to connect to all addresses) in a container without proper networking.
  • Timeouts and retries are separate from Laravel's HTTP client. Configure per-call via $client->annotateImage($request, ['timeoutMillis' => 30000]) or set retrySettings in the client constructor. A queued job with $timeout = 60 and a 30 s RPC timeout is a reasonable pairing.
  • Credentials loading is order-sensitive. The library checks explicit credentials, then GOOGLE_APPLICATION_CREDENTIALS, then gcloud ADC, then the metadata server. In Docker you will often hit the metadata server attempt and wait ~2 s before failing — a common source of slow cold starts. Set the env var explicitly to avoid the probe.
  • max_results is per feature, not per request. Setting 50 on LABEL_DETECTION for every image will noticeably inflate response payloads and serialization time; the client parses every protobuf field.
  • Testing. The client is final-ish and hard to mock. Rather than Mockery on the generated class, bind a VisionService interface in the container and fake that; reserve one small integration test against a fixture image for the real client path.
  • Pricing is per feature per image. Multi-feature requests bill each feature separately. Add a vision_units counter or log line per call so a misbehaving upload path becomes visible before the invoice does.
  • Extension points are thin. There is no middleware layer. If you need retries with jitter, circuit breaking, or per-tenant quota, wrap the client — do not subclass ImageAnnotatorClient; its constructor signature and generated internals are not stable across minor versions.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
alphabalex/messaging-made-easy
rawphp/warp
shootkiran/merobug
narakode/fineauth
numencode/wn-syncops-plugin
ibnnajjaar/mib-global-pay
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel
vish4395/laravel-translation-manager
morningtrain/economic
teksite/systeminfo-laravel
atomcms/voting
run6/weather
siokas/laravelembeddirectives
adamlien/pkuuids
gilsonsouza/lara-doc-generator