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 | use HooksTrait; |
||||
39 | |||||
40 | /** |
||||
41 | * Types d'action supportés |
||||
42 | * |
||||
43 | * @var list<string> |
||||
0 ignored issues
–
show
|
|||||
44 | */ |
||||
45 | protected array $types = [ |
||||
46 | 'command', |
||||
47 | 'shell', |
||||
48 | 'closure', |
||||
49 | 'event', |
||||
50 | 'url', |
||||
51 | ]; |
||||
52 | |||||
53 | /** |
||||
54 | * S'il n'est pas vide, liste les environnements autorisés dans lesquels le programme peut être exécuté. |
||||
55 | * |
||||
56 | * @var list<string> |
||||
57 | */ |
||||
58 | protected array $environments = []; |
||||
59 | |||||
60 | /** |
||||
61 | * Timezone dans lequel la tâche doit être traitée. |
||||
62 | */ |
||||
63 | protected ?string $timezone = null; |
||||
64 | |||||
65 | /** |
||||
66 | * Proprietés magiques emulées |
||||
67 | * |
||||
68 | * @var array<string,mixed> |
||||
69 | */ |
||||
70 | protected array $attributes = []; |
||||
71 | |||||
72 | protected ContainerInterface $container; |
||||
73 | |||||
74 | /** |
||||
75 | * @param string $type Type de l'action en cours. |
||||
76 | * @param mixed $action Le contenu actuel qu'on souhaite executer. |
||||
77 | * @param list<mixed> $parameters Parametres eventuels de l'action |
||||
78 | * |
||||
79 | * @throws TasksException |
||||
80 | */ |
||||
81 | public function __construct(protected string $type, protected mixed $action, protected array $parameters = []) |
||||
82 | { |
||||
83 | if (! in_array($type, $this->types, true)) { |
||||
0 ignored issues
–
show
$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
![]() |
|||||
84 | throw TasksException::invalidTaskType($type); |
||||
85 | } |
||||
86 | |||||
87 | $this->container = service('container'); |
||||
88 | } |
||||
89 | |||||
90 | /** |
||||
91 | * Définissez le nom par lequel sera référencé cette tâche |
||||
92 | */ |
||||
93 | public function named(string $name): self |
||||
94 | { |
||||
95 | $this->attributes['name'] = $name; |
||||
96 | |||||
97 | return $this; |
||||
98 | } |
||||
99 | |||||
100 | /** |
||||
101 | * Renvoie le type de la tache. |
||||
102 | */ |
||||
103 | public function getType(): string |
||||
104 | { |
||||
105 | return $this->type; |
||||
106 | } |
||||
107 | |||||
108 | /** |
||||
109 | * Renvoie l'action enregistrée. |
||||
110 | */ |
||||
111 | public function getAction(): mixed |
||||
112 | { |
||||
113 | return $this->action; |
||||
114 | } |
||||
115 | |||||
116 | /** |
||||
117 | * Exécute l'action de cette tâche. |
||||
118 | * |
||||
119 | * @return mixed |
||||
120 | * |
||||
121 | * @throws TasksException |
||||
122 | */ |
||||
123 | public function run() |
||||
124 | { |
||||
125 | $method = 'run' . ucfirst($this->type); |
||||
126 | if (! method_exists($this, $method)) { |
||||
127 | throw TasksException::invalidTaskType($this->type); |
||||
128 | } |
||||
129 | |||||
130 | return $this->process($this->container, $method); |
||||
131 | } |
||||
132 | |||||
133 | /** |
||||
134 | * Détermine si cette tâche doit être exécutée maintenant en fonction de sa planification et de son environnement. |
||||
135 | */ |
||||
136 | public function shouldRun(?string $testTime = null): bool |
||||
137 | { |
||||
138 | $cron = service('cronExpression')->setTimezone($this->timezone); |
||||
139 | |||||
140 | // Autoriser le réglage des heures pendant les tests |
||||
141 | if (! empty($testTime)) { |
||||
142 | $cron->testTime($testTime); |
||||
143 | } |
||||
144 | |||||
145 | // Sommes-nous limités aux environnements? |
||||
146 | if (! $this->runsInEnvironment(environment())) { |
||||
147 | return false; |
||||
148 | } |
||||
149 | |||||
150 | return $cron->shouldRun($this->getExpression()); |
||||
151 | } |
||||
152 | |||||
153 | /** |
||||
154 | * Limite l'exécution de cette tâche uniquement dans des environnements spécifiés. |
||||
155 | */ |
||||
156 | public function environments(string ...$environments): self |
||||
157 | { |
||||
158 | $this->environments = $environments; |
||||
0 ignored issues
–
show
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.. ![]() |
|||||
159 | |||||
160 | return $this; |
||||
161 | } |
||||
162 | |||||
163 | /** |
||||
164 | * Définit le fuseau horaire pour l'exécution de la tâche. |
||||
165 | * |
||||
166 | * @param string $timezone L'identifiant du fuseau horaire à utiliser pour la tâche. |
||||
167 | * Il doit s'agir d'une chaîne de caractères PHP valide (par exemple, 'America/New_York', 'Europe/Paris'). |
||||
168 | */ |
||||
169 | public function timezone(string $timezone): self |
||||
170 | { |
||||
171 | $this->timezone = $timezone; |
||||
172 | |||||
173 | return $this; |
||||
174 | } |
||||
175 | |||||
176 | /** |
||||
177 | * Renvoie la date à laquelle cette tâche a été exécutée pour la dernière fois. |
||||
178 | * |
||||
179 | * @return Date|string |
||||
180 | */ |
||||
181 | public function lastRun() |
||||
182 | { |
||||
183 | if (parametre('tasks.log_performance') === false) { |
||||
184 | return '--'; |
||||
185 | } |
||||
186 | |||||
187 | // Recupere les logs |
||||
188 | $logs = parametre("tasks.log-{$this->name}"); |
||||
189 | |||||
190 | if (empty($logs)) { |
||||
191 | return '--'; |
||||
192 | } |
||||
193 | |||||
194 | $log = array_shift($logs); |
||||
195 | |||||
196 | return Date::parse($log['start']); |
||||
197 | } |
||||
198 | |||||
199 | /** |
||||
200 | * Vérifie s'il peut s'exécute dans l'environnement spécifié. |
||||
201 | */ |
||||
202 | protected function runsInEnvironment(string $environment): bool |
||||
203 | { |
||||
204 | return empty($this->environments) || in_array($environment, $this->environments, true); |
||||
0 ignored issues
–
show
$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
![]() |
|||||
205 | } |
||||
206 | |||||
207 | /** |
||||
208 | * Execute une commande Klinge. |
||||
209 | * |
||||
210 | * @return string Sortie tamponnée de la commande |
||||
211 | * |
||||
212 | * @throws InvalidArgumentException |
||||
213 | */ |
||||
214 | protected function runCommand(): string |
||||
215 | { |
||||
216 | if (class_exists($command = $this->getAction())) { |
||||
217 | } |
||||
218 | |||||
219 | return command($command); |
||||
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 |
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths