• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daycry / jobs / 26886467550

03 Jun 2026 01:01PM UTC coverage: 88.948% (+14.0%) from 74.974%
26886467550

push

github

web-flow
v3.0: single clean architecture (remove V1, lease-based queues, secure-by-default)

Complete v3.0 rewrite into a single, clean architecture. The v1 API and the V2\ scaffolding
are removed (no facade, no dual code); the package passes PHPStan level 6 + strict-rules +
codeigniter with NO baseline.

- Definition: Jobs::define()->...->dispatch() fluent builder -> immutable JobDefinition.
- Handlers decoupled from the god-object (JobHandlerInterface / AbstractJobHandler / TypedJobHandler + JobContext).
- One QueueBackend contract (enqueue/fetch(lease)/ack/nack(delay)/abandon/reapExpired) with 5 backends:
  Sync, Database, Redis, Beanstalk, ServiceBus.
- Runtime: one attempt per fetch; real interrupting Timeout; opt-in idempotency; single-instance lock.
- Worker/Cron: jobs:queue:work, jobs:queue:reap, jobs:cronjob:run, jobs:queue:purge.
- Secure-by-default: HMAC-signed envelopes, per-queue handler allowlist, ShellHandler deny-by-default,
  EventHandler allowlist, UrlHandler anti-SSRF.

Resolves audit findings #1,#2,#3,#4,#5,#6,#7,#8,#10,#12,#13,#17,#18,#19,#20,#22.
Tests: 359 (Beanstalk live); line coverage 88.9%; PHPStan/Psalm/Rector/cs green on PHP 8.2-8.5.

BREAKING CHANGE: v1 API removed. See docs/MIGRATION-v1-to-v3.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

983 of 1103 new or added lines in 43 files covered. (89.12%)

15 existing lines in 3 files now uncovered.

1497 of 1683 relevant lines covered (88.95%)

7.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.91
/src/Libraries/DeadLetterQueue.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of Daycry Queues.
7
 *
8
 * (c) Daycry <daycry9@proton.me>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace Daycry\Jobs\Libraries;
15

16
use Daycry\Jobs\Definition\JobDefinition;
17
use Daycry\Jobs\Queues\BackendFactory;
18
use Throwable;
19

20
/**
21
 * Dead Letter Queue manager.
22
 * Handles jobs that have permanently failed after exhausting all retries.
23
 */
24
class DeadLetterQueue
25
{
26
    /**
27
     * Route a permanently-failed job's payload to the configured dead letter queue.
28
     *
29
     * @param mixed  $payload  The failed job payload (re-enqueued onto the DLQ for inspection).
30
     * @param string $handler  Handler key of the failed job.
31
     * @param string $reason   Failure reason (recorded in the DLQ payload metadata).
32
     * @param int    $attempts Number of attempts made before giving up.
33
     *
34
     * @return bool true when the payload was successfully persisted to the DLQ; false otherwise
35
     *              (DLQ disabled in config or enqueue to backend failed). Callers MUST act on a
36
     *              false return value to avoid silent job loss.
37
     */
38
    public function store(mixed $payload, string $handler, string $reason, int $attempts): bool
39
    {
40
        $config  = ConfigCache::get();
2✔
41
        $dlqName = $config->deadLetterQueue ?? null;
2✔
42

43
        if ($dlqName === null || $dlqName === '') {
2✔
44
            log_message('critical', "Job '{$handler}' permanently failed after {$attempts} attempts but DLQ is not configured — caller must decide whether to drop or requeue. Reason: {$reason}");
1✔
45

46
            return false;
1✔
47
        }
48

49
        $metadata = [
1✔
50
            'dlq_reason'    => $reason,
1✔
51
            'dlq_timestamp' => date('Y-m-d H:i:s'),
1✔
52
            'dlq_attempts'  => $attempts,
1✔
53
        ];
1✔
54

55
        $dlqPayload = $payload;
1✔
56
        if (is_array($dlqPayload)) {
1✔
57
            $dlqPayload['_dlq_metadata'] = $metadata;
1✔
58
        }
59

60
        $definition = new JobDefinition(
1✔
61
            handler: $handler,
1✔
62
            payload: $dlqPayload,
1✔
63
            queue: $dlqName,
1✔
64
            meta: $metadata,
1✔
65
        );
1✔
66

67
        try {
68
            BackendFactory::make($config)->enqueue($definition);
1✔
69
            log_message('info', "Job '{$handler}' moved to DLQ after {$attempts} attempts. Reason: {$reason}");
1✔
70

71
            return true;
1✔
UNCOV
72
        } catch (Throwable $e) {
×
NEW
73
            log_message('critical', "Failed to store job '{$handler}' in DLQ: {$e->getMessage()}");
×
74

UNCOV
75
            return false;
×
76
        }
77
    }
78

79
    /**
80
     * Get statistics about dead letter queue.
81
     *
82
     * @return array{enabled: bool, queue?: string}
83
     */
84
    public function getStats(): array
85
    {
86
        $config  = ConfigCache::get();
3✔
87
        $dlqName = $config->deadLetterQueue;
3✔
88

89
        if ($dlqName === null || $dlqName === '') {
3✔
90
            return ['enabled' => false];
2✔
91
        }
92

93
        // This would require queue backend support to count jobs
94
        return [
1✔
95
            'enabled' => true,
1✔
96
            'queue'   => $dlqName,
1✔
97
        ];
1✔
98
    }
99
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc