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

daycry / cronjob / 15775568161

20 Jun 2025 09:13AM UTC coverage: 69.514% (+4.1%) from 65.424%
15775568161

push

github

daycry
- New Features

130 of 163 new or added lines in 9 files covered. (79.75%)

6 existing lines in 3 files now uncovered.

415 of 597 relevant lines covered (69.51%)

3.21 hits per line

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

76.81
/src/Job.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Daycry\CronJob;
6

7
use CodeIgniter\Events\Events;
8
use Config\Services;
9
use Daycry\CronJob\Config\CronJob as BaseConfig;
10
use Daycry\CronJob\Exceptions\CronJobException;
11
use Daycry\CronJob\Traits\ActivityTrait;
12
use Daycry\CronJob\Traits\FrequenciesTrait;
13
use Daycry\CronJob\Traits\InteractsWithSpark;
14
use Daycry\CronJob\Traits\LogTrait;
15
use Daycry\CronJob\Traits\StatusTrait;
16
use InvalidArgumentException;
17
use ReflectionException;
18
use ReflectionFunction;
19
use SplFileObject;
20

21
/**
22
 * Class Job
23
 *
24
 * Represents a single task that should be scheduled
25
 * and run periodically.
26
 *
27
 * @property-read mixed  $action
28
 * @property-read array  $environments
29
 * @property-read string $name
30
 * @property-read string $type
31
 * @property-read array  $types
32
 */
33
class Job
34
{
35
    use FrequenciesTrait;
36
    use LogTrait;
37
    use ActivityTrait;
38
    use StatusTrait;
39
    use InteractsWithSpark;
40

41
    protected BaseConfig $config;
42

43
    /**
44
     * Supported action types.
45
     *
46
     * @var list<string>
47
     */
48
    protected array $types = [
49
        'command',
50
        'shell',
51
        'closure',
52
        'event',
53
        'url',
54
    ];
55

56
    /**
57
     * The type of cron run.
58
     */
59
    protected string $runType = 'multiple';
60

61
    /**
62
     * If the job will run as a background process
63
     */
64
    protected bool $runInBackground = false;
65

66
    /**
67
     * The type of action.
68
     */
69
    protected string $type;
70

71
    /**
72
     * The actual content that should be run.
73
     */
74
    protected mixed $action;
75

76
    /**
77
     * If not empty, lists the allowed environments
78
     * this can run in.
79
     */
80
    protected array $environments = [];
81

82
    /**
83
     * The alias this task can be run by
84
     */
85
    protected ?string $name = null;
86

87
    /**
88
     * List of job dependencies
89
     *
90
     * @var list<string>|null
91
     */
92
    protected ?array $dependsOn = null;
93

94
    /**
95
     * The maximum number of retries for this job.
96
     */
97
    protected ?int $maxRetries = null;
98

99
    /**
100
     * The timeout (in seconds) for this job.
101
     */
102
    protected ?int $timeout = null;
103

104
    /**
105
     * Job constructor.
106
     *
107
     * @param mixed $action
108
     *
109
     * @throws CronJobException
110
     */
111
    public function __construct(string $type, $action)
112
    {
113
        if (! in_array($type, $this->types, true)) {
28✔
114
            throw CronJobException::forInvalidTaskType($type);
1✔
115
        }
116
        $this->config = config('CronJob');
27✔
117
        $this->type   = $type;
27✔
118
        $this->action = $action;
27✔
119
    }
120

121
    /**
122
     * Set the name to reference this task by
123
     *
124
     * @return $this
125
     */
126
    public function named(string $name): self
127
    {
128
        $this->name = $name;
12✔
129

130
        return $this;
12✔
131
    }
132

133
    /**
134
     * Returns the type.
135
     */
136
    public function getType(): string
137
    {
138
        return $this->type;
9✔
139
    }
140

141
    /**
142
     * Returns the saved action.
143
     */
144
    public function getAction(): mixed
145
    {
146
        return $this->action;
13✔
147
    }
148

149
    /**
150
     * Runs this Task's action.
151
     *
152
     * @throws CronJobException
153
     */
154
    public function run(): mixed
155
    {
156
        $method = 'run' . ucfirst($this->type);
7✔
157
        if (! method_exists($this, $method)) {
7✔
UNCOV
158
            throw CronJobException::forInvalidTaskType($this->type);
×
159
        }
160

161
        return $this->{$method}();
7✔
162
    }
163

164
    /**
165
     * Restricts this task to run within only specified environments.
166
     *
167
     * @param mixed ...$environments
168
     *
169
     * @return $this
170
     */
171
    public function environments(...$environments): self
172
    {
173
        $this->environments = $environments;
2✔
174

175
        return $this;
2✔
176
    }
177

178
    /**
179
     * Returns the environments.
180
     */
181
    public function getEnvironments(): array
182
    {
183
        return $this->environments;
1✔
184
    }
185

186
    /**
187
     * Checks if it runs within the specified environment.
188
     */
189
    protected function runsInEnvironment(string $environment): bool
190
    {
191
        if (empty($this->environments)) {
2✔
UNCOV
192
            return true;
×
193
        }
194

195
        return in_array($environment, $this->environments, true);
2✔
196
    }
197

198
    /**
199
     * Runs a framework Command.
200
     *
201
     * @return string Buffered output from the Command
202
     *
203
     * @throws InvalidArgumentException
204
     */
205
    protected function runCommand(): string
206
    {
207
        if (! $this->shouldRunInBackground()) {
1✔
208
            return command($this->getAction());
1✔
209
        }
210

211
        $output = $this->runCommandInBackground();
×
212

213
        return is_string($output) ? $output : '';
×
214
    }
215

216
    private function runCommandInBackground(): bool|string
217
    {
218
        $this->createFoldersIfNeeded();
×
219

220
        $runCommand = $this->sparkCommandInBackground($this->getAction());
×
221

222
        $afterRunCommand = $this->sparkCommandInBackground(
×
223
            "cronjob:finish --name {$this->getName()} --type {$this->getType()}",
×
224
        );
×
225

226
        return exec("{$runCommand} && {$afterRunCommand}");
×
227
    }
228

229
    /**
230
     * Executes a shell script.
231
     *
232
     * @return array Lines of output from exec
233
     */
234
    protected function runShell(): array
235
    {
236
        exec($this->getAction(), $output);
×
237

238
        return $output;
×
239
    }
240

241
    /**
242
     * Calls a Closure.
243
     *
244
     * @return mixed The result of the closure
245
     */
246
    protected function runClosure()
247
    {
248
        return $this->getAction()->__invoke();
5✔
249
    }
250

251
    /**
252
     * Triggers an Event.
253
     *
254
     * @return bool Result of the trigger
255
     */
256
    protected function runEvent(): bool
257
    {
258
        return Events::trigger($this->getAction());
×
259
    }
260

261
    /**
262
     * Queries a URL.
263
     *
264
     * @return mixed|string Body of the Response
265
     */
266
    protected function runUrl()
267
    {
268
        $response = Services::curlrequest()->request('GET', $this->getAction());
1✔
269

270
        return $response->getBody();
1✔
271
    }
272

273
    /**
274
     * Builds a unique name for the task.
275
     * Used when an existing name doesn't exist.
276
     *
277
     * @return string
278
     *
279
     * @throws ReflectionException
280
     */
281
    protected function buildName()
282
    {
283
        // Get a hash based on the action
284
        // Closures cannot be serialized so do it the hard way
285
        if ($this->getType() === 'closure') {
3✔
286
            $ref  = new ReflectionFunction($this->getAction());
1✔
287
            $file = new SplFileObject($ref->getFileName());
1✔
288
            $file->seek($ref->getStartLine() - 1);
1✔
289
            $content = '';
1✔
290

291
            while ($file->key() < $ref->getEndLine()) {
1✔
292
                $content .= $file->current();
1✔
293
                $file->next();
1✔
294
            }
295
            $actionString = json_encode([
1✔
296
                $content,
1✔
297
                $ref->getStaticVariables(),
1✔
298
            ]);
1✔
299
        } else {
300
            $actionString = serialize($this->getAction());
2✔
301
        }
302

303
        // Get a hash based on the expression
304
        $expHash = $this->getExpression();
3✔
305

306
        return $this->getType() . '_' . md5($actionString . '_' . $expHash);
3✔
307
    }
308

309
    /**
310
     * @return string
311
     */
312
    public function getName()
313
    {
314
        if (empty($this->name)) {
12✔
315
            return $this->buildName();
3✔
316
        }
317

318
        return $this->name;
12✔
319
    }
320

321
    /**
322
     * Set the runType of task
323
     *
324
     * @return $this
325
     */
326
    public function setRunType(string $runType): Job
327
    {
328
        $this->runType = $runType;
×
329

330
        return $this;
×
331
    }
332

333
    /**
334
     * Returns the runType.
335
     */
336
    public function getRunType(): string
337
    {
338
        return $this->runType;
×
339
    }
340

341
    /**
342
     * Mark job to run in background
343
     *
344
     * @return $this
345
     */
346
    public function runInBackground(): Job
347
    {
348
        // Only commands are currently able to execute in background
349
        if ($this->type === 'command') {
1✔
350
            $this->runInBackground = true;
1✔
351
        }
352

353
        return $this;
1✔
354
    }
355

356
    /**
357
     * If the job will run in the background
358
     */
359
    public function shouldRunInBackground(): bool
360
    {
361
        return $this->runInBackground;
8✔
362
    }
363

364
    /**
365
     * Set dependencies for this job.
366
     *
367
     * @return $this
368
     */
369
    public function dependsOn(array|string $jobNames): self
370
    {
371
        $this->dependsOn = is_array($jobNames) ? $jobNames : [$jobNames];
3✔
372

373
        return $this;
3✔
374
    }
375

376
    /**
377
     * Get dependencies for this job.
378
     */
379
    public function getDependsOn(): ?array
380
    {
381
        return $this->dependsOn;
10✔
382
    }
383

384
    /**
385
     * Set the maximum number of retries for this job.
386
     *
387
     * @return $this
388
     */
389
    public function maxRetries(int $retries): self
390
    {
391
        $this->maxRetries = $retries;
1✔
392

393
        return $this;
1✔
394
    }
395

396
    /**
397
     * Set the timeout (in seconds) for this job.
398
     *
399
     * @return $this
400
     */
401
    public function timeout(int $timeout): self
402
    {
403
        $this->timeout = $timeout;
1✔
404

405
        return $this;
1✔
406
    }
407

408
    /**
409
     * Get the maximum number of retries for this job.
410
     */
411
    public function getMaxRetries(): ?int
412
    {
413
        return $this->maxRetries ?? null;
6✔
414
    }
415

416
    /**
417
     * Get the timeout (in seconds) for this job.
418
     */
419
    public function getTimeout(): ?int
420
    {
421
        return $this->timeout ?? null;
1✔
422
    }
423
}
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