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

daycry / jobs / 24850441053

23 Apr 2026 05:54PM UTC coverage: 52.404% (-1.5%) from 53.938%
24850441053

push

github

daycry
Fixes

104 of 219 new or added lines in 42 files covered. (47.49%)

14 existing lines in 9 files now uncovered.

1210 of 2309 relevant lines covered (52.4%)

4.37 hits per line

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

91.49
/src/Loggers/FileHandler.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\Loggers;
15

16
use CodeIgniter\I18n\Time;
17
use CodeIgniter\Log\Handlers\BaseHandler;
18
use Daycry\Jobs\Config\Jobs as JobsConfig;
19
use Daycry\Jobs\Interfaces\LoggerHandlerInterface;
20

21
/**
22
 * File-based job execution history handler.
23
 * Stores a JSON array per job (<name>.json) with newest entries first.
24
 * Enforces maxLogsPerJob by trimming oldest entries.
25
 */
26
class FileHandler extends BaseHandler implements LoggerHandlerInterface
27
{
28
    private ?string $path = null;
29
    private string $name;
30

31
    public function __construct()
32
    {
33
        $configuration = config('Jobs');
12✔
34
        $base          = rtrim((string) $configuration->filePath, '/\\');
12✔
35
        $this->path    = $base;
12✔
36
        if ($this->path !== null && ! is_dir($this->path)) {
12✔
NEW
37
            mkdir($this->path, 0700, true);
×
38
        }
39
    }
40

41
    /**
42
     * Persist a structured log entry JSON string for the current job name.
43
     *
44
     * @param mixed $level
45
     * @param mixed $message
46
     */
47
    public function handle($level, $message): bool
48
    {
49
        /** @var JobsConfig config */
50
        $config = config('Jobs');
11✔
51
        // Intentar deducir nombre si no fue establecido aún vía setPath()
52
        if (!isset($this->name) || ($this->name === '' || $this->name === '0')) {
11✔
53
            $decoded = json_decode((string) $message, true);
2✔
54
            if (is_array($decoded) && ! empty($decoded['name'])) {
2✔
55
                $this->name = (string) $decoded['name'];
1✔
56
            }
57
        }
58
        if (!isset($this->name) || ($this->name === '' || $this->name === '0')) {
11✔
59
            $this->name = 'unnamed'; // fallback definitivo
1✔
60
        }
61
        // Sanitizar nombre para uso de archivo (sin espacios raros / separadores peligrosos)
62
        $safeName = $this->sanitizeName($this->name);
11✔
63
        $fileName = rtrim($config->filePath, '/\\') . '/' . $safeName . '.json';
11✔
64

65
        $logs = file_exists($fileName) ? \json_decode(\file_get_contents($fileName)) : [];
11✔
66

67
        // Make sure we have room for one more
68
        if ((is_countable($logs) ? count($logs) : 0) >= $config->maxLogsPerJob) {
11✔
69
            array_pop($logs);
1✔
70
        }
71

72
        // Add the log to the top of the array
73
        array_unshift($logs, json_decode((string) $message));
11✔
74

75
        file_put_contents(
11✔
76
            $fileName,
11✔
77
            json_encode(
11✔
78
                $logs,
11✔
79
                JSON_PRETTY_PRINT,
11✔
80
            ),
11✔
81
            LOCK_EX,
11✔
82
        );
11✔
83

84
        return true;
11✔
85
    }
86

87
    public function setPath(string $name): static
88
    {
89
        // Guardar el nombre crudo; la sanitización se hace al persistir
90
        $this->name = $name;
9✔
91

92
        return $this;
9✔
93
    }
94

95
    public function lastRun(string $name): string|Time
96
    {
97
        $safeName = $this->sanitizeName($name);
2✔
98
        $fileName = $this->path . '/' . $safeName . '.json';
2✔
99
        if (! file_exists($fileName)) {
2✔
100
            return '--';
2✔
101
        }
102
        $raw  = @file_get_contents($fileName);
1✔
103
        $logs = $raw ? json_decode($raw) : [];
1✔
104
        if (! is_array($logs) || $logs === [] || ! isset($logs[0]->start_at)) {
1✔
105
            return '--';
1✔
106
        }
107

108
        return Time::parse($logs[0]->start_at);
×
109
    }
110

111
    /**
112
     * Returns an array of recent executions for a job.
113
     * Each element is a stdClass decoded from stored JSON (already contains
114
     * name, job, payload, environment, start_at, end_at, duration, output, error, test_time).
115
     * The file stores newest first, so we slice directly.
116
     *
117
     * @return array<int, object>
118
     */
119
    public function history(string $name, int $limit = 10): array
120
    {
121
        $safeName = $this->sanitizeName($name);
1✔
122
        $fileName = $this->path . '/' . $safeName . '.json';
1✔
123
        if (! file_exists($fileName)) {
1✔
124
            return [];
×
125
        }
126
        $logs = json_decode(file_get_contents($fileName));
1✔
127
        if (! is_array($logs)) {
1✔
128
            return [];
×
129
        }
130

131
        return array_slice($logs, 0, $limit);
1✔
132
    }
133

134
    /**
135
     * Sanitize job name for safe filesystem usage.
136
     */
137
    private function sanitizeName(string $name): string
138
    {
139
        return preg_replace('/[^A-Za-z0-9._-]+/', '_', $name) ?? 'unnamed';
12✔
140
    }
141
}
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