Test Failed
Push — main ( 57e80b...26fcea )
by Dimitri
04:42 queued 02:10
created

HooksTrait::after()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 0
cts 1
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\Contracts\Mail\MailerInterface;
18
use BlitzPHP\Utilities\Iterable\Arr;
19
use BlitzPHP\Utilities\String\Stringable;
20
use Closure;
21
use Throwable;
22
23
/**
24
 *
25
 */
26
trait HooksTrait
27
{
28
	/**
29
     * L'emplacement où la sortie doit être envoyée.
30
     */
31
    public ?string $location = null;
32
33
	/**
34
	 * Code de sortie de la tache
35
	 */
36
	protected ?int $exitCode = null;
37
38
	/**
39
     * Exception levée lors de l'exécution de la tâche.
40
     */
41
	protected ?Throwable $exception = null;
42
43
	/**
44
     * Indique si la sortie doit être ajoutée.
45
     */
46
    public bool $shouldAppendOutput = false;
47
48
    /**
49
     * Tableau de rappels à exécuter avant l'execution de la tâche.
50
	 *
51
	 * @var list<Closure>
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...
52
     */
53
    protected array $beforeCallbacks = [];
54
55
    /**
56
     * Tableau de rappels à exécuter après l'execution de la tâche.
57
	 *
58
	 * @var list<Closure>
59
     */
60
    protected $afterCallbacks = [];
61
62
	/**
63
     * Met la sortie de la tâche dans un fichier donné.
64
     */
65
    public function sendOutputTo(string $location, bool $append = false): self
66
    {
67
		$this->location           = $location;
68
		$this->shouldAppendOutput = $append;
69
70
        return $this;
71
    }
72
73
	/**
74
     * Ajoute la sortie de la tâche à la fin d'un fichier donné.
75
     */
76
    public function appendOutputTo(string $location): self
77
    {
78
        return $this->sendOutputTo($location, true);
79
    }
80
81
	/**
82
     * Envoi le resultat de l'execution de la tache par mail.
83
     *
84
     * @param  array|mixed  $addresses
85
     *
86
     * @throws \LogicException
87
     */
88
    public function emailOutputTo($addresses, bool $onlyIfOutputExists = false): self
89
    {
90
        $this->ensureOutputIsBeingCaptured();
91
92
        $addresses = Arr::wrap($addresses);
93
94
        return $this->then(function (MailerInterface $mailer) use ($addresses, $onlyIfOutputExists) {
95
            $this->emailOutput($mailer, $addresses, $onlyIfOutputExists);
96
        });
97
    }
98
99
    /**
100
     * Envoi le resultat de l'execution de la tache par mail si un resultat existe dans la sortie.
101
     *
102
     * @param  array|mixed  $addresses
103
     *
104
     * @throws \LogicException
105
     */
106
    public function emailWrittenOutputTo($addresses): self
107
    {
108
        return $this->emailOutputTo($addresses, true);
109
    }
110
111
    /**
112
     * Envoi le resultat de l'execution de la tache par mail si l'operation a echouée.
113
     *
114
     * @param  array|mixed  $addresses
115
     */
116
    public function emailOutputOnFailure($addresses): self
117
    {
118
        $this->ensureOutputIsBeingCaptured();
119
120
        $addresses = Arr::wrap($addresses);
121
122
        return $this->onFailure(function (MailerInterface $mailer) use ($addresses) {
123
            $this->emailOutput($mailer, $addresses, false);
124
        });
125
    }
126
127
	/**
128
     * Enregistre un callback à appeler avant l'opération.
129
     */
130
    public function before(Closure $callback): self
131
    {
132
        $this->beforeCallbacks[] = $callback;
133
134
        return $this;
135
    }
136
137
    /**
138
     * Enregistre un callback à appeler apres l'opération.
139
     */
140
    public function after(Closure $callback): self
141
    {
142
        return $this->then($callback);
143
    }
144
145
    /**
146
     * Enregistre un callback à appeler apres l'opération.
147
     */
148
    public function then(Closure $callback): self
149
    {
150
        $this->afterCallbacks[] = $callback;
151
152
        return $this;
153
    }
154
155
	/**
156
     * Enregistre un callback à appeler si l'opération se deroulle avec succes.
157
     */
158
    public function onSuccess(Closure $callback): self
159
    {
160
        return $this->then(function (ContainerInterface $container) use ($callback) {
161
            if ($this->exitCode === EXIT_SUCCESS) {
162
                $container->call($callback);
163
            }
164
        });
165
    }
166
167
	/**
168
     * Enregistre un callback à appeler si l'opération ne se deroulle pas correctement.
169
     */
170
    public function onFailure(Closure $callback): self
171
    {
172
        return $this->then(function (ContainerInterface $container) use ($callback) {
173
            if ($this->exitCode !== EXIT_SUCCESS) {
174
                $container->call($callback, array_filter([$this->exception]));
175
            }
176
        });
177
    }
178
179
	/**
180
	 * Procede a l'execution de la tache
181
	 */
182
	protected function process(ContainerInterface $container, $method): mixed
183
	{
184
		ob_start();
185
186
		$result = $this->start($this->container, $method);
187
188
		// if (! $this->runInBackground) {
189
        	$result = $this->finish($this->container, $result);
190
191
			ob_end_flush();
192
193
			return $result;
194
        // }
195
	}
196
197
	/**
198
     * Demarre l'execution de la tache
199
	 *
200
	 * @return mixed Le resultat de l'execution de la tache
201
     *
202
     * @throws Throwable
203
     */
204
    protected function start(ContainerInterface $container, string $runMethod): mixed
205
    {
206
        try {
207
            $this->callBeforeCallbacks($container);
208
209
            return $this->execute($container, $runMethod);
210
        } catch (Throwable $e) {
211
			$this->registerException($e);
212
        }
213
    }
214
215
	/**
216
     * Execute la tache.
217
	 *
218
	 * @return mixed Le resultat de l'execution de la tache
219
     */
220
    protected function execute(ContainerInterface $container, string $runMethod): mixed
221
    {
222
		try {
223
			$result = $this->{$runMethod}();
224
225
			if (is_int($result)) {
226
				$this->exitCode = $result;
227
			} else {
228
				$this->exitCode = EXIT_SUCCESS;
229
			}
230
		} catch (Throwable $e) {
231
			$this->registerException($e);
232
		}
233
234
		return $result ?? null;
235
    }
236
237
    /**
238
     * Marque l'execution de la tache comme terminée et lance les callbacks/nettoyages.
239
     */
240
    protected function finish(ContainerInterface $container, mixed $result): mixed
241
    {
242
        try {
243
            $output = $this->callAfterCallbacks($container, $result);
244
        } finally {
245
			if (isset($output) && $output !== '' && $this->location !== null) {
246
				@file_put_contents($this->location, $output, $this->shouldAppendOutput ? FILE_APPEND : 0);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for file_put_contents(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

246
				/** @scrutinizer ignore-unhandled */ @file_put_contents($this->location, $output, $this->shouldAppendOutput ? FILE_APPEND : 0);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
247
			}
248
        }
249
250
		return $result;
251
    }
252
253
    /**
254
     * Ensure that the command output is being captured.
255
     *
256
     * @return void
257
     */
258
    protected function ensureOutputIsBeingCaptured()
259
    {
260
        if (is_null($this->output) || $this->output == $this->getDefaultOutput()) {
0 ignored issues
show
Bug introduced by
It seems like getDefaultOutput() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

260
        if (is_null($this->output) || $this->output == $this->/** @scrutinizer ignore-call */ getDefaultOutput()) {
Loading history...
261
            $this->sendOutputTo(storage_path('logs/schedule-'.sha1($this->mutexName()).'.log'));
0 ignored issues
show
Bug introduced by
It seems like mutexName() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

261
            $this->sendOutputTo(storage_path('logs/schedule-'.sha1($this->/** @scrutinizer ignore-call */ mutexName()).'.log'));
Loading history...
262
        }
263
    }
264
265
    /**
266
     * Envoie du résultat de l'execution de la tache par mail aux destinataires.
267
     */
268
    protected function emailOutput(MailerInterface $mailer, array $addresses, bool $onlyIfOutputExists = false): void
269
    {
270
        $text = is_file($this->location) ? file_get_contents($this->location) : '';
0 ignored issues
show
Bug introduced by
It seems like $this->location can also be of type null; however, parameter $filename of file_get_contents() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

270
        $text = is_file($this->location) ? file_get_contents(/** @scrutinizer ignore-type */ $this->location) : '';
Loading history...
Bug introduced by
It seems like $this->location can also be of type null; however, parameter $filename of is_file() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

270
        $text = is_file(/** @scrutinizer ignore-type */ $this->location) ? file_get_contents($this->location) : '';
Loading history...
271
272
        if ($onlyIfOutputExists && empty($text)) {
273
            return;
274
        }
275
276
		$mailer->to($addresses)->subject($this->getEmailSubject())->text($text)->send();
277
    }
278
279
    /**
280
     * Objet de l'e-mail pour les résultats de sortie.
281
     */
282
    protected function getEmailSubject(): string
283
    {
284
        return "Sortie de la tâche planifiée pour [{$this->command}]";
285
    }
286
287
	/**
288
     * Appelle tous les callbacks qui doivent être lancer "avant" l'exécution de la tâche.
289
     */
290
    protected function callBeforeCallbacks(ContainerInterface $container): void
291
    {
292
        foreach ($this->beforeCallbacks as $callback) {
293
            $container->call($callback);
294
        }
295
    }
296
297
	/**
298
     * Appelle tous les callbacks qui doivent être lancer "apres" l'exécution de la tâche.
299
     */
300
    protected function callAfterCallbacks(ContainerInterface $container, mixed $result = null): string
301
    {
302
		$parameters = ['result' => $result];
303
304
		if ('' !== $output = ob_get_contents() ?: '') {
305
			$parameters['output'] = new Stringable($output);
306
		}
307
308
        foreach ($this->afterCallbacks as $callback) {
309
            $container->call($callback, $parameters);
310
        }
311
312
		return $output;
313
    }
314
315
	protected function registerException(Throwable $e)
316
	{
317
		$this->exception = $e;
318
		$this->exitCode  = EXIT_ERROR;
319
	}
320
}
321