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 Core Laravel Package

google/cloud-core

google/cloud-core provides shared infrastructure for Google Cloud PHP client libraries, including authentication, transport, and request handling. It is installed automatically as a dependency and is not intended for direct use. This GA component offers stable APIs for the rest of the Google Cloud P...

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require google/cloud-storage
    

    Note: google/cloud-core is not used directly—it's the shared runtime (auth, retries, pagination, HTTP transport) pulled in transitively by every google/cloud-* service package. You install the service package; Composer resolves core for you.

  2. Confirm the version you actually got — this matters for behaviour changes:

    composer show google/cloud-core | grep versions
    # v1.74.0+ ships GCS idempotency tokens for all Storage API operations
    

    Because core is transitive, upgrading core alone is not enough: bump the service package too (composer update google/cloud-storage google/cloud-core) so the two stay in lockstep. Commit composer.lock.

  3. First Use Case: When you integrate a Google Cloud service (Storage, BigQuery, Pub/Sub), core transparently provides:

    • Authentication: ADC / GOOGLE_APPLICATION_CREDENTIALS / metadata server detection.
    • Retry Logic: exponential backoff for transient failures (429, 503, connection errors).
    • Idempotency Tokens (v1.74.0+): every GCS operation carries a per-operation token, so an automatic retry of an insert cannot create a duplicate object/bucket.
    • Pagination & Streaming: lazy iterators instead of loading whole result sets.
    use Google\Cloud\Storage\StorageClient;
    
    $storage = new StorageClient(); // core handles auth, retries, idempotency tokens
    $bucket  = $storage->bucket('my-bucket');
    
    foreach ($bucket->objects() as $object) {   // streamed via core iterator
        echo $object->name();
    }
    
  4. Where to Look First:

    • Debugging Guide: HTTP logging, env setup, emulators.
    • CHANGELOG.md / releases page: core changes land here first and affect every service package you depend on.
    • Service-Specific Docs: google/cloud-core intentionally documents almost nothing—read the client class you actually instantiate.

Implementation Patterns

1. Authentication Workflows

  • Application Default Credentials (local dev):

    gcloud auth application-default login   # writes ~/.config/gcloud/application_default_credentials.json
    

    In Laravel, keep the path in .env and out of config caching surprises:

    // .env
    GOOGLE_APPLICATION_CREDENTIALS=/abs/path/keyfile.json
    

    Core reads this process env var directly, so run php artisan config:cache with the var exported in the runtime environment (not just in .env) for queued workers and Octane.

  • Explicit credentials (multi-tenant / multi-bucket apps):

    $client = new StorageClient([
        'projectId'   => config('services.gcs.project'),
        'credentials' => base64_decode(config('services.gcs.credentials_b64')),
    ]);
    

    Bind it once in a service provider so you don't build clients per request:

    $this->app->singleton(StorageClient::class, fn () => new StorageClient([...]));
    
  • Legacy keyFile / keyFilePath options: deprecated. They still work but emit deprecation noise; prefer the env var or the credentials array above.

2. Retry and Error Handling

  • Retries are enabled by default for transient status codes. Tune per client:
    $client = new StorageClient([
        'retry' => ['max_attempts' => 5, 'timeout' => 30.0],
    ]);
    
  • Prefer retrying at the job layer for long-running work, and let core retry only short-lived HTTP blips:
    public function handle(StorageClient $storage): void
    {
        $storage->bucket('uploads')->upload(fopen($this->path, 'r'), [
            'name' => $this->objectName,
        ]);
    }
    // public $tries = 3; public $backoff = [10, 60, 180];
    
    With v1.74.0+ this is safe even for creates: core reuses the same idempotency token across its internal retries, so a retried insert converges on one object instead of uploads/report.txt plus uploads/report.txt(1)-style surprises.

3. Idempotency Tokens (v1.74.0+)

  • You get this for free. Core now generates a token per API operation and attaches it to the outgoing GCS request, reusing that same token for every retry attempt of that operation. Nothing to configure.
  • Why it matters day-to-day: duplicate-file bugs after network timeouts are the single most common GCS complaint in Laravel apps (retried upload → two objects, two Pub/Sub notifications, double row in your DB tracker). Tokens make the client-side retry loop safe for non-idempotent verbs, not just reads/updates.
  • Where it does not help — your own outer retry loop that re-invokes the client from scratch:
    // ❌ Each call is a NEW operation → new token → genuine duplicate
    for ($i = 0; $i < 3; $i++) {
        try { $bucket->upload($stream, ['name' => $name]); break; }
        catch (\Exception $e) { sleep(2); }
    }
    
    If you must hand-roll retries, do the dedup yourself (deterministic object name, or if (! $bucket->object($name)->exists())) rather than hoping the token propagates across separate client calls.
  • Don't reuse a token you didn't generate for a different payload. The token identifies one operation; the service treats a repeat as "return the previous result", so reusing it for different content will silently return the first operation's outcome.
  • Not the same as the old idempotency upload option. Tokens are a transport-level concern now; drop ad-hoc 'idempotency' => '...' hacks from uploads and let core do it.

4. Options Validation

  • Options are validated before the request is sent; invalid keys/values throw Google\Cloud\Core\Exception\InvalidArgumentException (note: core's namespace is singular Exception, not Exceptions). Catch that separately from transport errors so a typo fails fast in tests instead of becoming a 400 in production:
    try {
        $object->update(['metadata' => ['custom' => 'value']]);
    } catch (\Google\Cloud\Core\Exception\InvalidArgumentException $e) {
        // programmer error — surface loudly
    }
    
  • Wrap service calls in a small adapter so your domain code never sees Google exceptions; that's also where you convert ServiceException (404/403) into your own types.

5. Streaming and Pagination

  • Iterators are lazy and page internally — don't iterator_to_array() a bucket listing.
    foreach ($bucket->objects(['prefix' => 'exports/']) as $object) { ... }
    
  • Large uploads: pass a stream resource (fopen), not a string, so core uses resumable/chunked transfer:
    $bucket->upload(fopen($path, 'r'), ['name' => $name, 'resumable' => true]);
    
  • For Laravel-land, expose listing as a LazyCollection when you need collection ergonomics:
    LazyCollection::make(fn () => yield from $bucket->objects());
    

6. Environment Awareness & Emulators

  • Project ID auto-detection falls back to the metadata server; on a laptop with no ADC this is the usual "could not detect project ID" failure. Always set projectId explicitly outside GCP.
  • Local dev / CI with an emulator:
    new StorageClient([
        'projectId'    => 'test-project',
        'apiEndpoint'  => 'http://localhost:4443',
    ]);
    
    Emulators generally ignore the idempotency token header — harmless, but don't write tests that assert on it, and don't rely on emulator dedup behaviour to prove token correctness.

7. Logging and Debugging

  • Quick look at raw traffic:
    export GOOGLE_CLOUD_DEBUG=1
    
  • Structured, Laravel-native:
    $client = new StorageClient([
        'logger' => \Log::channel('google-cloud')->getLogger(),
    ]);
    
    Give it a dedicated channel with daily rotation — debug logging is verbose and will flood your default stack channel.

Gotchas and Tips

Pitfalls

  1. Direct usage anti-pattern:

    • ❌ Instantiating or type-hinting Google\Cloud\Core\Client. It's an internal base class.
    • ✅ Use StorageClient, BigQueryClient, etc., and inject those.
  2. Version skew between core and service packages:

    • Idempotency tokens (v1.74.0) live in core but are exercised by google/cloud-storage. Pinning google/cloud-storage to an old version while core floats forward (or vice versa) is the most common source of "works locally, duplicates in prod". Constrain both, update both, and re-run your upload tests after any composer update.
  3. Double submission from your own retry layer:

    • Core's token covers core's retries. It does not cover a try/catch + re-call, a queue $tries re-run, or a user double-clicking upload. For those, enforce a deterministic object name or an exists() check.
    • With queues: mark jobs ShouldBeUnique when the payload is the same, otherwise a TimeoutExceeded retry legitimately starts a second operation.
  4. Deprecated keyFile / keyFilePath:

    • Migrate to ADC or the 'credentials' => base64_decode(...) pattern; long-lived JSON keys in a repo are both a deprecation and a security finding.
  5. Retry overrides can silently disable resilience:

    'retry' => ['max_attempts' => 1], // no retries at all — will surface as user-facing 503s
    

    Test override values in staging; service packages ship tuned defaults per method.

  6. PHP 8.4+ implicit-nullable deprecations: many core signatures were tightened (fixed around v1.62.2). If you subclass or mock core classes, declare nullable types explicitly or you'll get deprecations in the test suite.

  7. Timezone handling: core deliberately doesn't mutate DateTime objects. Normalise to UTC at the boundary (new \DateTimeImmutable('now', new \DateTimeZone('UTC'))) rather than expecting conversion.

  8. Emulator + transport mismatch: REST-only emulator setups can fatal if the client is configured for gRPC. Match the emulator's transport, or force REST via the client's transport option.

Debugging Tips

  1. Enable request logging (env var for a single process, or putenv() in a tinker session):

    GOOGLE_CLOUD_DEBUG=1 php artisan tinker
    
  2. Capture raw requests into your log channel:

    $client = new StorageClient([
        'logger' => \Log::channel('google-cloud')->getLogger(),
    ]);
    

    Then grep for the idempotency token header to confirm retries reuse the same value across attempts:

  3. Prove credential resolution before blaming your code:

    gcloud auth application-default print-access-token
    

    If that fails, the PHP side will too — no amount of client config fixes a missing ADC.

  4. Reproduce duplicate-upload reports deliberately: point the client at a proxy/emulator that drops the first response after the request lands, then confirm only one object exists. That's the fastest way to verify the v1.74.0 behaviour is actually in the build you deployed.

  5. Check the class namespace you're catching: Google\Cloud\Core\Exception\ServiceException (singular) — a use of Exceptions\... will silently never match and your error handling will look like it "does nothing".

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.
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table