HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/ai.naniguide.com/app/Jobs/Concerns/JobControlAdapter.php
<?php

namespace App\Jobs\Concerns;

use App\Library\Sending\JobControl;
use Illuminate\Bus\Batch;

/**
 * Bridges a Laravel queueable Job (`SendMessage` / `SendSingleMessage`) to the
 * pipeline's `JobControl` contract.
 *
 * ## Why this class exists
 *
 * The pipeline + every exception handler needs to call three job-side actions:
 * `release(int)`, `batch(): ?Batch`, `addToBatch(object)`. We declare those on
 * a small `JobControl` interface so the pipeline doesn't have to import
 * Laravel's `Batchable` / `InteractsWithQueue` traits, and so test stubs can
 * implement it in a few lines.
 *
 * The interface declares STRICT return types (`: void`, `: ?Batch`). Laravel's
 * underlying trait methods are intentionally LOOSE:
 *
 *   - `Illuminate\Queue\InteractsWithQueue::release($delay = 0)`   ← no return type
 *   - `Illuminate\Bus\Batchable::batch()`                          ← no return type
 *
 * If a Job class did `implements JobControl` directly while also `use Batchable`,
 * PHP enforces LSP between the trait method and the interface method and bails
 * with "Declaration must be compatible" — the trait's untyped return is wider
 * than the interface's `?Batch` / `void`.
 *
 * Two ways out:
 *   (a) rename interface methods to dodge the collision (`releaseJob`,
 *       `currentBatch`) and add proxy methods on every Job class, OR
 *   (b) **stop having Job classes implement the interface**; let an adapter do
 *       it. This file is option (b).
 *
 * Option (b) keeps the interface natural-named, contains the proxy logic in one
 * place, and lets new Job classes (e.g. a future `SendTestMessage` for the
 * test-email path) plug in by passing `new JobControlAdapter($this)` in their
 * `handle()` — no boilerplate copy.
 *
 * ## How it works
 *
 * The adapter holds a reference to the wrapping job and forwards each call to
 * the public trait method on it. `release()` and `batch()` on the wrapped job
 * are public (Laravel's traits expose them); we don't touch protected state.
 *
 * @internal Constructed by `SendMessage::handle()` / `SendSingleMessage::handle()`
 * — never instantiated outside Job classes.
 */
final class JobControlAdapter implements JobControl
{
    public function __construct(private object $job)
    {
    }

    public function release(int $seconds): void
    {
        // Forwards to Illuminate\Queue\InteractsWithQueue::release on the wrapped job.
        $this->job->release($seconds);
    }

    public function batch(): ?Batch
    {
        // Forwards to Illuminate\Bus\Batchable::batch on the wrapped job.
        // Returns null when the job is dispatched outside a Bus::batch context
        // (e.g. SendSingleMessage automation single-dispatch).
        return $this->job->batch();
    }

    public function addToBatch(object $job): void
    {
        $batch = $this->job->batch();
        if ($batch === null) {
            // Handlers should call currentBatch() === null first; this guard is
            // a last-resort safety net for caller bugs.
            throw new \LogicException(
                'JobControlAdapter::addToBatch called outside batch context for '.$this->job::class
            );
        }
        $batch->add([$job]);
    }
}