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

daycry / cronjob / 18288101817

06 Oct 2025 04:45PM UTC coverage: 68.511% (-1.0%) from 69.514%
18288101817

push

github

daycry
Update README.md

483 of 705 relevant lines covered (68.51%)

5.53 hits per line

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

56.63
/src/Traits/StatusTrait.php
1
<?php
2

3
namespace Daycry\CronJob\Traits;
4

5
use DateTime;
6
use Daycry\CronJob\Job;
7

8
/**
9
 * Trait StatusTrait
10
 *
11
 * @mixin Job
12
 */
13
trait StatusTrait
14
{
15
    protected function isRunningFlagPath(): string
16
    {
17
        return $this->config->filePath . 'running/' . $this->getName();
17✔
18
    }
19

20
    protected function createFolderIfNotExists(string $folder): void
21
    {
22
        if (! is_dir($folder)) {
×
23
            mkdir($folder, 0777, true);
×
24
        }
25
    }
26

27
    protected function createFoldersIfNeeded(): void
28
    {
29
        $this->createFolderIfNotExists($this->config->filePath);
×
30
        $this->createFolderIfNotExists($this->config->filePath . 'running');
×
31
    }
32

33
    /**
34
     * Saves the running flag.
35
     *
36
     * @param mixed $flag
37
     *
38
     * @return array|false
39
     */
40
    public function saveRunningFlag($flag)
41
    {
42
        $path = $this->isRunningFlagPath();
17✔
43
        $lockDir = rtrim($this->config->lockPath ?? ($this->config->filePath . 'locks/'), '/\\') . DIRECTORY_SEPARATOR;
17✔
44
        if (! is_dir($lockDir)) {
17✔
45
            mkdir($lockDir, 0777, true);
×
46
        }
47
        $lockFile = $lockDir . md5($this->getName()) . '.lock';
17✔
48

49
        if ($flag) {
17✔
50
            $fh = @fopen($lockFile, 'c+');
17✔
51
            if (! $fh) {
17✔
52
                return false;
×
53
            }
54
            // Obtain exclusive non-blocking lock
55
            if (! @flock($fh, LOCK_EX | LOCK_NB)) {
17✔
56
                // Read existing metadata to decide if we can reclaim
57
                $raw = @file_get_contents($lockFile);
3✔
58
                $meta = @json_decode($raw ?? '', true) ?: [];
3✔
59
                $ttl = $this->config->lockTTL;
3✔
60
                $mtime = @filemtime($lockFile) ?: 0;
3✔
61
                $age = time() - $mtime;
3✔
62
                $pid = $meta['pid'] ?? null;
3✔
63
                $heartbeat = isset($meta['heartbeat']) ? strtotime($meta['heartbeat']) : null;
3✔
64
                $hbStale = $heartbeat ? (time() - $heartbeat) > ($ttl ?? 0) : false;
3✔
65
                $pidDead = $pid && PHP_OS_FAMILY !== 'Windows' && function_exists('posix_kill') ? ! @posix_kill((int) $pid, 0) : false;
3✔
66
                $expired = ($ttl !== null && $age > $ttl) || $pidDead || $hbStale;
3✔
67
                if ($expired && @flock($fh, LOCK_EX | LOCK_NB)) {
3✔
68
                    ftruncate($fh, 0);
×
69
                    $data = $this->buildLockMetadata($flag, true);
×
70
                    fwrite($fh, json_encode($data, JSON_PRETTY_PRINT));
×
71
                    fflush($fh);
×
72
                    $this->lockHandle = $fh;
×
73
                    return $data;
×
74
                }
75
                fclose($fh);
3✔
76
                return false; // Still locked and not stale
3✔
77
            }
78
            // Fresh lock acquired
79
            ftruncate($fh, 0);
17✔
80
            $data = $this->buildLockMetadata($flag, false);
17✔
81
            fwrite($fh, json_encode($data, JSON_PRETTY_PRINT));
17✔
82
            fflush($fh);
17✔
83
            $this->lockHandle = $fh;
17✔
84
            return $data;
17✔
85
        }
86

87
        // Release
88
        if (isset($this->lockHandle) && is_resource($this->lockHandle)) {
17✔
89
            @flock($this->lockHandle, LOCK_UN);
17✔
90
            @fclose($this->lockHandle);
17✔
91
            unset($this->lockHandle);
17✔
92
        }
93
        @unlink($lockFile);
17✔
94
        @unlink($path); // legacy path cleanup
17✔
95
        return false;
17✔
96
    }
97

98
    /**
99
     * Get cronjob status
100
     */
101
    public function status(): bool
102
    {
103
        $name = $this->getName();
11✔
104

105
        return ! is_dir($this->config->filePath) || ! is_dir($this->config->filePath . 'disable/') || ! file_exists($this->config->filePath . 'disable/' . $name);
11✔
106
    }
107

108
    /**
109
     * Disable cronjob
110
     */
111
    public function disable(): bool
112
    {
113
        $name        = $this->getName();
×
114
        $disablePath = $this->config->filePath . 'disable/' . $name;
×
115

116
        if (! file_exists($disablePath)) {
×
117
            $this->createFolderIfNotExists($this->config->filePath . 'disable');
×
118

119
            $data = [
×
120
                'name' => $name,
×
121
                'time' => (new DateTime())->format('Y-m-d H:i:s'),
×
122
            ];
×
123

124
            file_put_contents($disablePath, json_encode($data, JSON_PRETTY_PRINT));
×
125

126
            return true;
×
127
        }
128

129
        return false;
×
130
    }
131

132
    /**
133
     * Enable cronjob
134
     */
135
    public function enable(): bool
136
    {
137
        $name        = $this->getName();
×
138
        $disablePath = $this->config->filePath . 'disable/' . $name;
×
139

140
        if (file_exists($disablePath)) {
×
141
            @unlink($disablePath);
×
142

143
            return true;
×
144
        }
145

146
        return false;
×
147
    }
148

149
    /**
150
     * @return ?array{
151
     *     name: string,
152
     *     time: string,
153
     * }
154
     * @deprecated Legacy support for background command completion (used by cronjob:finish). Will be removed in a future major version; prefer lock metadata inspection.
155
     */
156
    public function getIsRunningInfo(): ?array
157
    {
158
        $path = $this->isRunningFlagPath();
×
159

160
        if (! file_exists($path)) {
×
161
            return null;
×
162
        }
163

164
        $content = file_get_contents($path);
×
165

166
        return json_decode($content, true);
×
167
    }
168

169
    private function getName(): string
170
    {
171
        return $this->name ?: $this->buildName();
×
172
    }
173

174
    /**
175
     * Build lock metadata array.
176
     *
177
     * @param mixed $flag
178
     * @return array<string,mixed>
179
     */
180
    private function buildLockMetadata($flag, bool $stolen): array
181
    {
182
        $now = new DateTime();
17✔
183
        $data = [
17✔
184
            'flag'      => $flag,
17✔
185
            'time'      => $now->format('Y-m-d H:i:s'),
17✔
186
            'pid'       => getmypid(),
17✔
187
            'heartbeat' => $now->format('c'),
17✔
188
            'job'       => $this->getName(),
17✔
189
        ];
17✔
190
        if ($stolen) {
17✔
191
            $data['stolen'] = true;
×
192
        }
193
        return $data;
17✔
194
    }
195
}
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