• 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

75.0
/src/Execution/Timeout.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\Execution;
15

16
use Daycry\Jobs\Exceptions\JobException;
17

18
/**
19
 * Runs a callable under a wall-clock timeout.
20
 *
21
 * When ext-pcntl is available the SIGALRM handler THROWS, so even CPU-bound or
22
 * blocking code is interrupted at the deadline (this is the v3 fix for the legacy
23
 * coordinator whose handler only set a flag and therefore never interrupted anything).
24
 * Without pcntl (e.g. Windows) it degrades to a soft, post-hoc check that cannot abort a
25
 * runaway job — documented as a limitation.
26
 *
27
 * A timeout surfaces as {@see JobException::forJobTimeout()}.
28
 */
29
final class Timeout
30
{
31
    /**
32
     * @template T
33
     *
34
     * @param callable():T $callback
35
     *
36
     * @return T
37
     */
38
    public function run(int $seconds, callable $callback, string $label = 'job'): mixed
39
    {
40
        if ($seconds <= 0) {
34✔
41
            return $callback();
27✔
42
        }
43

44
        if (function_exists('pcntl_alarm') && function_exists('pcntl_signal')) {
7✔
45
            return $this->runWithAlarm($seconds, $callback, $label);
7✔
46
        }
47

48
        // Soft fallback: cannot hard-interrupt; measure and fail if the deadline was exceeded.
NEW
49
        $start  = microtime(true);
×
NEW
50
        $result = $callback();
×
NEW
51
        if (microtime(true) - $start > $seconds) {
×
NEW
52
            throw JobException::forJobTimeout($label, $seconds);
×
53
        }
54

NEW
55
        return $result;
×
56
    }
57

58
    /**
59
     * @template T
60
     *
61
     * @param callable():T $callback
62
     *
63
     * @return T
64
     */
65
    private function runWithAlarm(int $seconds, callable $callback, string $label): mixed
66
    {
67
        $previousAsync = false;
7✔
68
        if (function_exists('pcntl_async_signals')) {
7✔
69
            $previousAsync = pcntl_async_signals(true);
7✔
70
        }
71

72
        $previousHandler = function_exists('pcntl_signal_get_handler')
7✔
73
            ? pcntl_signal_get_handler(SIGALRM)
7✔
NEW
74
            : SIG_DFL;
×
75

76
        // The handler THROWS so CPU-bound / blocking code is interrupted at the deadline.
77
        pcntl_signal(SIGALRM, static function () use ($label, $seconds): void {
7✔
78
            throw JobException::forJobTimeout($label, $seconds);
3✔
79
        });
7✔
80
        pcntl_alarm($seconds);
7✔
81

82
        try {
83
            return $callback();
7✔
84
        } finally {
85
            pcntl_alarm(0);
7✔
86
            pcntl_signal(SIGALRM, $previousHandler);
7✔
87
            if (function_exists('pcntl_async_signals')) {
7✔
88
                pcntl_async_signals($previousAsync);
7✔
89
            }
90
        }
91
    }
92
}
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