Test Failed
Pull Request — main (#4)
by Dimitri
02:06
created

Task::timezone()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 5
ccs 0
cts 2
cp 0
rs 10
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of BlitzPHP Tasks.
7
 *
8
 * (c) 2025 Dimitri Sitchet Tomkeu <[email protected]>
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 BlitzPHP\Tasks;
15
16
use BlitzPHP\Contracts\Container\ContainerInterface;
17
use BlitzPHP\Tasks\Exceptions\TasksException;
18
use BlitzPHP\Utilities\Date;
19
use InvalidArgumentException;
20
use ReflectionException;
21
use ReflectionFunction;
22
use SplFileObject;
23
24
/**
25
 * Représente une tâche unique qui doit être planifiée et exécutée périodiquement.
26
 *
27
 * @property mixed        $action
28
 * @property list<string> $environments
29
 * @property string       $name
30
 * @property string       $type
31
 * @property list<string> $types
32
 *
33
 * @credit <a href="https://tasks.codeigniter.com">CodeIgniter4 - CodeIgniter\Tasks\Task</a>
34
 */
35
class Task
36
{
37
    use FrequenciesTrait;
38
39
    /**
40
     * Types d'action supportés
41
     *
42
     * @var list<string>
0 ignored issues
show
Bug introduced by
The type BlitzPHP\Tasks\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
43
     */
44
    protected array $types = [
45
        'command',
46
        'shell',
47
        'closure',
48
        'event',
49
        'url',
50
    ];
51
52
    /**
53
     * S'il n'est pas vide, liste les environnements autorisés dans lesquels le programme peut être exécuté.
54
     *
55
     * @var list<string>
56
     */
57
    protected array $environments = [];
58
59
	/**
60
     * Timezone dans lequel la tâche doit être traitée.
61
     */
62
    protected ?string $timezone = null;
63
64
    /**
65
     * Proprietés magiques emulées
66
     *
67
     * @var array<string,mixed>
68
     */
69
    protected array $attributes = [];
70
71
	protected ContainerInterface $container;
72
73
    /**
74
     * @param string $type  Type de l'action en cours.
75
	 * @param mixed $action Le contenu actuel qu'on souhaite executer.
76
     *
77
     * @throws TasksException
78
     */
79
    public function __construct(protected string $type, protected mixed $action, protected array $parameters = [])
80
    {
81
        if (! in_array($type, $this->types, true)) {
0 ignored issues
show
Bug introduced by
$this->types of type BlitzPHP\Tasks\list is incompatible with the type array expected by parameter $haystack of in_array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

81
        if (! in_array($type, /** @scrutinizer ignore-type */ $this->types, true)) {
Loading history...
82
            throw TasksException::invalidTaskType($type);
83
        }
84
85
		$this->container = service('container');
86
    }
87
88
    /**
89
     * Définissez le nom par lequel sera référencé cette tâche
90
     */
91
    public function named(string $name): self
92
    {
93
        $this->attributes['name'] = $name;
94
95
        return $this;
96
    }
97
98
    /**
99
     * Renvoie le type de la tache.
100
     */
101
    public function getType(): string
102
    {
103
        return $this->type;
104
    }
105
106
    /**
107
     * Renvoie l'action enregistrée.
108
     */
109
    public function getAction(): mixed
110
    {
111
        return $this->action;
112
    }
113
114
    /**
115
     * Exécute l'action de cette tâche.
116
     *
117
     * @return mixed
118
     *
119
     * @throws TasksException
120
     */
121
    public function run()
122
    {
123
        $method = 'run' . ucfirst($this->type);
124
        if (! method_exists($this, $method)) {
125
            throw TasksException::invalidTaskType($this->type);
126
        }
127
128
        return $this->{$method}();
129
    }
130
131
    /**
132
     * Détermine si cette tâche doit être exécutée maintenant en fonction de sa planification et de son environnement.
133
     */
134
    public function shouldRun(?string $testTime = null): bool
135
    {
136
        $cron = service('cronExpression')->setTimezone($this->timezone);
137
138
        // Autoriser le réglage des heures pendant les tests
139
        if (! empty($testTime)) {
140
            $cron->testTime($testTime);
141
        }
142
143
        // Sommes-nous limités aux environnements?
144
        if (! empty($this->environments) && ! $this->runsInEnvironment(environment())) {
145
            return false;
146
        }
147
148
        return $cron->shouldRun($this->getExpression());
149
    }
150
151
    /**
152
     * Limite l'exécution de cette tâche uniquement dans des environnements spécifiés.
153
     */
154
    public function environments(string ...$environments): self
155
    {
156
        $this->environments = $environments;
0 ignored issues
show
Documentation Bug introduced by
It seems like $environments of type array<integer,string> is incompatible with the declared type BlitzPHP\Tasks\list of property $environments.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
157
158
        return $this;
159
    }
160
161
	/**
162
	 * Définit le fuseau horaire pour l'exécution de la tâche.
163
	 *
164
	 * @param string $timezone L'identifiant du fuseau horaire à utiliser pour la tâche.
165
	 * 						   Il doit s'agir d'une chaîne de caractères PHP valide (par exemple, 'America/New_York', 'Europe/Paris').
166
	*/
167
	public function timezone(string $timezone): self
168
	{
169
		$this->timezone = $timezone;
170
171
		return $this;
172
	}
173
174
    /**
175
     * Renvoie la date à laquelle cette tâche a été exécutée pour la dernière fois.
176
     *
177
     * @return Date|string
178
     */
179
    public function lastRun()
180
    {
181
        if (parametre('tasks.log_performance') === false) {
182
            return '--';
183
        }
184
185
        // Recupere les logs
186
        $logs = parametre("tasks.log-{$this->name}");
187
188
        if (empty($logs)) {
189
            return '--';
190
        }
191
192
        $log = array_shift($logs);
193
194
        return Date::parse($log['start']);
195
    }
196
197
    /**
198
     * Vérifie s'il peut s'exécute dans l'environnement spécifié.
199
     */
200
    protected function runsInEnvironment(string $environment): bool
201
    {
202
        // Si rien n'est spécifié, il doit s'exécuter
203
        if (empty($this->environments)) {
204
            return true;
205
        }
206
207
        return in_array($environment, $this->environments, true);
0 ignored issues
show
Bug introduced by
$this->environments of type BlitzPHP\Tasks\list is incompatible with the type array expected by parameter $haystack of in_array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

207
        return in_array($environment, /** @scrutinizer ignore-type */ $this->environments, true);
Loading history...
208
    }
209
210
    /**
211
     * Execute une commande Klinge.
212
     *
213
     * @return string Sortie tamponnée de la commande
214
     *
215
     * @throws InvalidArgumentException
216
     */
217
    protected function runCommand(): string
218
    {
219
        return command($this->getAction());
220
    }
221
222
    /**
223
     * Execute un script shell.
224
     *
225
     * @return list<string> Lignes de la sortie de l'execution
226
     */
227
    protected function runShell(): array
228
    {
229
        exec($this->getAction(), $output);
230
231
        return $output;
232
    }
233
234
    /**
235
     * Execute un Closure.
236
     *
237
     * @return mixed Le resultat de la closure
238
     */
239
    protected function runClosure(): mixed
240
    {
241
        return $this->container->call($this->getAction(), $this->parameters);
242
    }
243
244
    /**
245
     * Declanche un evenement.
246
     *
247
     * @return bool Resultat du declanchement
248
     */
249
    protected function runEvent(): bool
250
    {
251
        return ! (false === service('event')->emit($this->getAction()));
252
    }
253
254
    /**
255
     * Interroge une URL.
256
     *
257
     * @return string Corps de la response
258
     */
259
    protected function runUrl(): string
260
    {
261
        $response = service('httpclient')->get($this->getAction());
262
263
        return $response->body();
264
    }
265
266
    /**
267
     * Crée un nom unique pour la tâche.
268
     * Utilisé lorsqu'un nom existant n'existe pas.
269
     *
270
     * @throws ReflectionException
271
     */
272
    protected function buildName(): string
273
    {
274
        // Obtenez un hachage basé sur l'action
275
        // Les closure ne peuvent pas être sérialisées, alors faites-le à la dure
276
        if ($this->getType() === 'closure') {
277
            $ref  = new ReflectionFunction($this->getAction());
278
            $file = new SplFileObject($ref->getFileName());
279
            $file->seek($ref->getStartLine() - 1);
280
            $content = '';
281
282
            while ($file->key() < $ref->getEndLine()) {
283
                $content .= $file->current();
284
                $file->next();
285
            }
286
            $actionString = json_encode([
287
                $content,
288
                $ref->getStaticVariables(),
289
            ]);
290
        } else {
291
            $actionString = serialize($this->getAction());
292
        }
293
294
        // Obtenir un hachage basé sur l'expression
295
        $expHash = $this->getExpression();
296
297
        return $this->getType() . '_' . md5($actionString . '_' . $expHash);
298
    }
299
300
    /**
301
     * Getter magique
302
     *
303
     * @return mixed
304
     */
305
    public function __get(string $key)
306
    {
307
        if ($key === 'name' && empty($this->attributes['name'])) {
308
            return $this->buildName();
309
        }
310
311
        if (property_exists($this, $key)) {
312
            return $this->{$key};
313
        }
314
315
        return $this->attributes[$key] ?? null;
316
    }
317
318
    /**
319
     * Setter magique
320
     */
321
    public function __set(string $name, mixed $value): void
322
    {
323
        $this->attributes[$name] = $value;
324
    }
325
}
326