Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Console\Scheduling;
4
 
5
use Closure;
6
use Cron\CronExpression;
7
use GuzzleHttp\Client as HttpClient;
8
use GuzzleHttp\Exception\TransferException;
9
use Illuminate\Contracts\Container\Container;
10
use Illuminate\Contracts\Debug\ExceptionHandler;
11
use Illuminate\Contracts\Mail\Mailer;
12
use Illuminate\Support\Arr;
13
use Illuminate\Support\Facades\Date;
14
use Illuminate\Support\Reflector;
15
use Illuminate\Support\Stringable;
16
use Illuminate\Support\Traits\Macroable;
17
use Illuminate\Support\Traits\ReflectsClosures;
18
use Psr\Http\Client\ClientExceptionInterface;
19
use Symfony\Component\Process\Process;
20
use Throwable;
21
 
22
class Event
23
{
24
    use Macroable, ManagesFrequencies, ReflectsClosures;
25
 
26
    /**
27
     * The command string.
28
     *
29
     * @var string|null
30
     */
31
    public $command;
32
 
33
    /**
34
     * The cron expression representing the event's frequency.
35
     *
36
     * @var string
37
     */
38
    public $expression = '* * * * *';
39
 
40
    /**
41
     * The timezone the date should be evaluated on.
42
     *
43
     * @var \DateTimeZone|string
44
     */
45
    public $timezone;
46
 
47
    /**
48
     * The user the command should run as.
49
     *
50
     * @var string|null
51
     */
52
    public $user;
53
 
54
    /**
55
     * The list of environments the command should run under.
56
     *
57
     * @var array
58
     */
59
    public $environments = [];
60
 
61
    /**
62
     * Indicates if the command should run in maintenance mode.
63
     *
64
     * @var bool
65
     */
66
    public $evenInMaintenanceMode = false;
67
 
68
    /**
69
     * Indicates if the command should not overlap itself.
70
     *
71
     * @var bool
72
     */
73
    public $withoutOverlapping = false;
74
 
75
    /**
76
     * Indicates if the command should only be allowed to run on one server for each cron expression.
77
     *
78
     * @var bool
79
     */
80
    public $onOneServer = false;
81
 
82
    /**
621 lars 83
     * The number of minutes the mutex should be valid.
148 lars 84
     *
85
     * @var int
86
     */
87
    public $expiresAt = 1440;
88
 
89
    /**
90
     * Indicates if the command should run in the background.
91
     *
92
     * @var bool
93
     */
94
    public $runInBackground = false;
95
 
96
    /**
97
     * The array of filter callbacks.
98
     *
99
     * @var array
100
     */
101
    protected $filters = [];
102
 
103
    /**
104
     * The array of reject callbacks.
105
     *
106
     * @var array
107
     */
108
    protected $rejects = [];
109
 
110
    /**
111
     * The location that output should be sent to.
112
     *
113
     * @var string
114
     */
115
    public $output = '/dev/null';
116
 
117
    /**
118
     * Indicates whether output should be appended.
119
     *
120
     * @var bool
121
     */
122
    public $shouldAppendOutput = false;
123
 
124
    /**
125
     * The array of callbacks to be run before the event is started.
126
     *
127
     * @var array
128
     */
129
    protected $beforeCallbacks = [];
130
 
131
    /**
132
     * The array of callbacks to be run after the event is finished.
133
     *
134
     * @var array
135
     */
136
    protected $afterCallbacks = [];
137
 
138
    /**
139
     * The human readable description of the event.
140
     *
141
     * @var string|null
142
     */
143
    public $description;
144
 
145
    /**
146
     * The event mutex implementation.
147
     *
148
     * @var \Illuminate\Console\Scheduling\EventMutex
149
     */
150
    public $mutex;
151
 
152
    /**
153
     * The mutex name resolver callback.
154
     *
155
     * @var \Closure|null
156
     */
157
    public $mutexNameResolver;
158
 
159
    /**
160
     * The exit status code of the command.
161
     *
162
     * @var int|null
163
     */
164
    public $exitCode;
165
 
166
    /**
167
     * Create a new event instance.
168
     *
169
     * @param  \Illuminate\Console\Scheduling\EventMutex  $mutex
170
     * @param  string  $command
171
     * @param  \DateTimeZone|string|null  $timezone
172
     * @return void
173
     */
174
    public function __construct(EventMutex $mutex, $command, $timezone = null)
175
    {
176
        $this->mutex = $mutex;
177
        $this->command = $command;
178
        $this->timezone = $timezone;
179
 
180
        $this->output = $this->getDefaultOutput();
181
    }
182
 
183
    /**
184
     * Get the default output depending on the OS.
185
     *
186
     * @return string
187
     */
188
    public function getDefaultOutput()
189
    {
190
        return (DIRECTORY_SEPARATOR === '\\') ? 'NUL' : '/dev/null';
191
    }
192
 
193
    /**
194
     * Run the given event.
195
     *
196
     * @param  \Illuminate\Contracts\Container\Container  $container
197
     * @return void
198
     *
199
     * @throws \Throwable
200
     */
201
    public function run(Container $container)
202
    {
203
        if ($this->shouldSkipDueToOverlapping()) {
204
            return;
205
        }
206
 
207
        $exitCode = $this->start($container);
208
 
209
        if (! $this->runInBackground) {
210
            $this->finish($container, $exitCode);
211
        }
212
    }
213
 
214
    /**
215
     * Determine if the event should skip because another process is overlapping.
216
     *
217
     * @return bool
218
     */
219
    public function shouldSkipDueToOverlapping()
220
    {
221
        return $this->withoutOverlapping && ! $this->mutex->create($this);
222
    }
223
 
224
    /**
225
     * Run the command process.
226
     *
227
     * @param  \Illuminate\Contracts\Container\Container  $container
228
     * @return int
229
     *
230
     * @throws \Throwable
231
     */
232
    protected function start($container)
233
    {
234
        try {
235
            $this->callBeforeCallbacks($container);
236
 
237
            return $this->execute($container);
238
        } catch (Throwable $exception) {
239
            $this->removeMutex();
240
 
241
            throw $exception;
242
        }
243
    }
244
 
245
    /**
246
     * Run the command process.
247
     *
248
     * @param  \Illuminate\Contracts\Container\Container  $container
249
     * @return int
250
     */
251
    protected function execute($container)
252
    {
253
        return Process::fromShellCommandline(
254
            $this->buildCommand(), base_path(), null, null, null
255
        )->run();
256
    }
257
 
258
    /**
259
     * Mark the command process as finished and run callbacks/cleanup.
260
     *
261
     * @param  \Illuminate\Contracts\Container\Container  $container
262
     * @param  int  $exitCode
263
     * @return void
264
     */
265
    public function finish(Container $container, $exitCode)
266
    {
267
        $this->exitCode = (int) $exitCode;
268
 
269
        try {
270
            $this->callAfterCallbacks($container);
271
        } finally {
272
            $this->removeMutex();
273
        }
274
    }
275
 
276
    /**
277
     * Call all of the "before" callbacks for the event.
278
     *
279
     * @param  \Illuminate\Contracts\Container\Container  $container
280
     * @return void
281
     */
282
    public function callBeforeCallbacks(Container $container)
283
    {
284
        foreach ($this->beforeCallbacks as $callback) {
285
            $container->call($callback);
286
        }
287
    }
288
 
289
    /**
290
     * Call all of the "after" callbacks for the event.
291
     *
292
     * @param  \Illuminate\Contracts\Container\Container  $container
293
     * @return void
294
     */
295
    public function callAfterCallbacks(Container $container)
296
    {
297
        foreach ($this->afterCallbacks as $callback) {
298
            $container->call($callback);
299
        }
300
    }
301
 
302
    /**
303
     * Build the command string.
304
     *
305
     * @return string
306
     */
307
    public function buildCommand()
308
    {
309
        return (new CommandBuilder)->buildCommand($this);
310
    }
311
 
312
    /**
313
     * Determine if the given event should run based on the Cron expression.
314
     *
315
     * @param  \Illuminate\Contracts\Foundation\Application  $app
316
     * @return bool
317
     */
318
    public function isDue($app)
319
    {
320
        if (! $this->runsInMaintenanceMode() && $app->isDownForMaintenance()) {
321
            return false;
322
        }
323
 
324
        return $this->expressionPasses() &&
325
               $this->runsInEnvironment($app->environment());
326
    }
327
 
328
    /**
329
     * Determine if the event runs in maintenance mode.
330
     *
331
     * @return bool
332
     */
333
    public function runsInMaintenanceMode()
334
    {
335
        return $this->evenInMaintenanceMode;
336
    }
337
 
338
    /**
339
     * Determine if the Cron expression passes.
340
     *
341
     * @return bool
342
     */
343
    protected function expressionPasses()
344
    {
345
        $date = Date::now();
346
 
347
        if ($this->timezone) {
348
            $date = $date->setTimezone($this->timezone);
349
        }
350
 
351
        return (new CronExpression($this->expression))->isDue($date->toDateTimeString());
352
    }
353
 
354
    /**
355
     * Determine if the event runs in the given environment.
356
     *
357
     * @param  string  $environment
358
     * @return bool
359
     */
360
    public function runsInEnvironment($environment)
361
    {
362
        return empty($this->environments) || in_array($environment, $this->environments);
363
    }
364
 
365
    /**
366
     * Determine if the filters pass for the event.
367
     *
368
     * @param  \Illuminate\Contracts\Foundation\Application  $app
369
     * @return bool
370
     */
371
    public function filtersPass($app)
372
    {
373
        foreach ($this->filters as $callback) {
374
            if (! $app->call($callback)) {
375
                return false;
376
            }
377
        }
378
 
379
        foreach ($this->rejects as $callback) {
380
            if ($app->call($callback)) {
381
                return false;
382
            }
383
        }
384
 
385
        return true;
386
    }
387
 
388
    /**
389
     * Ensure that the output is stored on disk in a log file.
390
     *
391
     * @return $this
392
     */
393
    public function storeOutput()
394
    {
395
        $this->ensureOutputIsBeingCaptured();
396
 
397
        return $this;
398
    }
399
 
400
    /**
401
     * Send the output of the command to a given location.
402
     *
403
     * @param  string  $location
404
     * @param  bool  $append
405
     * @return $this
406
     */
407
    public function sendOutputTo($location, $append = false)
408
    {
409
        $this->output = $location;
410
 
411
        $this->shouldAppendOutput = $append;
412
 
413
        return $this;
414
    }
415
 
416
    /**
417
     * Append the output of the command to a given location.
418
     *
419
     * @param  string  $location
420
     * @return $this
421
     */
422
    public function appendOutputTo($location)
423
    {
424
        return $this->sendOutputTo($location, true);
425
    }
426
 
427
    /**
428
     * E-mail the results of the scheduled operation.
429
     *
430
     * @param  array|mixed  $addresses
431
     * @param  bool  $onlyIfOutputExists
432
     * @return $this
433
     *
434
     * @throws \LogicException
435
     */
436
    public function emailOutputTo($addresses, $onlyIfOutputExists = false)
437
    {
438
        $this->ensureOutputIsBeingCaptured();
439
 
440
        $addresses = Arr::wrap($addresses);
441
 
442
        return $this->then(function (Mailer $mailer) use ($addresses, $onlyIfOutputExists) {
443
            $this->emailOutput($mailer, $addresses, $onlyIfOutputExists);
444
        });
445
    }
446
 
447
    /**
448
     * E-mail the results of the scheduled operation if it produces output.
449
     *
450
     * @param  array|mixed  $addresses
451
     * @return $this
452
     *
453
     * @throws \LogicException
454
     */
455
    public function emailWrittenOutputTo($addresses)
456
    {
457
        return $this->emailOutputTo($addresses, true);
458
    }
459
 
460
    /**
461
     * E-mail the results of the scheduled operation if it fails.
462
     *
463
     * @param  array|mixed  $addresses
464
     * @return $this
465
     */
466
    public function emailOutputOnFailure($addresses)
467
    {
468
        $this->ensureOutputIsBeingCaptured();
469
 
470
        $addresses = Arr::wrap($addresses);
471
 
472
        return $this->onFailure(function (Mailer $mailer) use ($addresses) {
473
            $this->emailOutput($mailer, $addresses, false);
474
        });
475
    }
476
 
477
    /**
478
     * Ensure that the command output is being captured.
479
     *
480
     * @return void
481
     */
482
    protected function ensureOutputIsBeingCaptured()
483
    {
484
        if (is_null($this->output) || $this->output == $this->getDefaultOutput()) {
485
            $this->sendOutputTo(storage_path('logs/schedule-'.sha1($this->mutexName()).'.log'));
486
        }
487
    }
488
 
489
    /**
490
     * E-mail the output of the event to the recipients.
491
     *
492
     * @param  \Illuminate\Contracts\Mail\Mailer  $mailer
493
     * @param  array  $addresses
494
     * @param  bool  $onlyIfOutputExists
495
     * @return void
496
     */
497
    protected function emailOutput(Mailer $mailer, $addresses, $onlyIfOutputExists = false)
498
    {
499
        $text = is_file($this->output) ? file_get_contents($this->output) : '';
500
 
501
        if ($onlyIfOutputExists && empty($text)) {
502
            return;
503
        }
504
 
505
        $mailer->raw($text, function ($m) use ($addresses) {
506
            $m->to($addresses)->subject($this->getEmailSubject());
507
        });
508
    }
509
 
510
    /**
511
     * Get the e-mail subject line for output results.
512
     *
513
     * @return string
514
     */
515
    protected function getEmailSubject()
516
    {
517
        if ($this->description) {
518
            return $this->description;
519
        }
520
 
521
        return "Scheduled Job Output For [{$this->command}]";
522
    }
523
 
524
    /**
525
     * Register a callback to ping a given URL before the job runs.
526
     *
527
     * @param  string  $url
528
     * @return $this
529
     */
530
    public function pingBefore($url)
531
    {
532
        return $this->before($this->pingCallback($url));
533
    }
534
 
535
    /**
536
     * Register a callback to ping a given URL before the job runs if the given condition is true.
537
     *
538
     * @param  bool  $value
539
     * @param  string  $url
540
     * @return $this
541
     */
542
    public function pingBeforeIf($value, $url)
543
    {
544
        return $value ? $this->pingBefore($url) : $this;
545
    }
546
 
547
    /**
548
     * Register a callback to ping a given URL after the job runs.
549
     *
550
     * @param  string  $url
551
     * @return $this
552
     */
553
    public function thenPing($url)
554
    {
555
        return $this->then($this->pingCallback($url));
556
    }
557
 
558
    /**
559
     * Register a callback to ping a given URL after the job runs if the given condition is true.
560
     *
561
     * @param  bool  $value
562
     * @param  string  $url
563
     * @return $this
564
     */
565
    public function thenPingIf($value, $url)
566
    {
567
        return $value ? $this->thenPing($url) : $this;
568
    }
569
 
570
    /**
571
     * Register a callback to ping a given URL if the operation succeeds.
572
     *
573
     * @param  string  $url
574
     * @return $this
575
     */
576
    public function pingOnSuccess($url)
577
    {
578
        return $this->onSuccess($this->pingCallback($url));
579
    }
580
 
581
    /**
582
     * Register a callback to ping a given URL if the operation fails.
583
     *
584
     * @param  string  $url
585
     * @return $this
586
     */
587
    public function pingOnFailure($url)
588
    {
589
        return $this->onFailure($this->pingCallback($url));
590
    }
591
 
592
    /**
593
     * Get the callback that pings the given URL.
594
     *
595
     * @param  string  $url
596
     * @return \Closure
597
     */
598
    protected function pingCallback($url)
599
    {
600
        return function (Container $container, HttpClient $http) use ($url) {
601
            try {
602
                $http->request('GET', $url);
603
            } catch (ClientExceptionInterface|TransferException $e) {
604
                $container->make(ExceptionHandler::class)->report($e);
605
            }
606
        };
607
    }
608
 
609
    /**
610
     * State that the command should run in the background.
611
     *
612
     * @return $this
613
     */
614
    public function runInBackground()
615
    {
616
        $this->runInBackground = true;
617
 
618
        return $this;
619
    }
620
 
621
    /**
622
     * Set which user the command should run as.
623
     *
624
     * @param  string  $user
625
     * @return $this
626
     */
627
    public function user($user)
628
    {
629
        $this->user = $user;
630
 
631
        return $this;
632
    }
633
 
634
    /**
635
     * Limit the environments the command should run in.
636
     *
637
     * @param  array|mixed  $environments
638
     * @return $this
639
     */
640
    public function environments($environments)
641
    {
642
        $this->environments = is_array($environments) ? $environments : func_get_args();
643
 
644
        return $this;
645
    }
646
 
647
    /**
648
     * State that the command should run even in maintenance mode.
649
     *
650
     * @return $this
651
     */
652
    public function evenInMaintenanceMode()
653
    {
654
        $this->evenInMaintenanceMode = true;
655
 
656
        return $this;
657
    }
658
 
659
    /**
660
     * Do not allow the event to overlap each other.
661
     *
621 lars 662
     * The expiration time of the underlying cache lock may be specified in minutes.
663
     *
148 lars 664
     * @param  int  $expiresAt
665
     * @return $this
666
     */
667
    public function withoutOverlapping($expiresAt = 1440)
668
    {
669
        $this->withoutOverlapping = true;
670
 
671
        $this->expiresAt = $expiresAt;
672
 
673
        return $this->skip(function () {
674
            return $this->mutex->exists($this);
675
        });
676
    }
677
 
678
    /**
679
     * Allow the event to only run on one server for each cron expression.
680
     *
681
     * @return $this
682
     */
683
    public function onOneServer()
684
    {
685
        $this->onOneServer = true;
686
 
687
        return $this;
688
    }
689
 
690
    /**
691
     * Register a callback to further filter the schedule.
692
     *
693
     * @param  \Closure|bool  $callback
694
     * @return $this
695
     */
696
    public function when($callback)
697
    {
698
        $this->filters[] = Reflector::isCallable($callback) ? $callback : function () use ($callback) {
699
            return $callback;
700
        };
701
 
702
        return $this;
703
    }
704
 
705
    /**
706
     * Register a callback to further filter the schedule.
707
     *
708
     * @param  \Closure|bool  $callback
709
     * @return $this
710
     */
711
    public function skip($callback)
712
    {
713
        $this->rejects[] = Reflector::isCallable($callback) ? $callback : function () use ($callback) {
714
            return $callback;
715
        };
716
 
717
        return $this;
718
    }
719
 
720
    /**
721
     * Register a callback to be called before the operation.
722
     *
723
     * @param  \Closure  $callback
724
     * @return $this
725
     */
726
    public function before(Closure $callback)
727
    {
728
        $this->beforeCallbacks[] = $callback;
729
 
730
        return $this;
731
    }
732
 
733
    /**
734
     * Register a callback to be called after the operation.
735
     *
736
     * @param  \Closure  $callback
737
     * @return $this
738
     */
739
    public function after(Closure $callback)
740
    {
741
        return $this->then($callback);
742
    }
743
 
744
    /**
745
     * Register a callback to be called after the operation.
746
     *
747
     * @param  \Closure  $callback
748
     * @return $this
749
     */
750
    public function then(Closure $callback)
751
    {
752
        $parameters = $this->closureParameterTypes($callback);
753
 
754
        if (Arr::get($parameters, 'output') === Stringable::class) {
755
            return $this->thenWithOutput($callback);
756
        }
757
 
758
        $this->afterCallbacks[] = $callback;
759
 
760
        return $this;
761
    }
762
 
763
    /**
764
     * Register a callback that uses the output after the job runs.
765
     *
766
     * @param  \Closure  $callback
767
     * @param  bool  $onlyIfOutputExists
768
     * @return $this
769
     */
770
    public function thenWithOutput(Closure $callback, $onlyIfOutputExists = false)
771
    {
772
        $this->ensureOutputIsBeingCaptured();
773
 
774
        return $this->then($this->withOutputCallback($callback, $onlyIfOutputExists));
775
    }
776
 
777
    /**
778
     * Register a callback to be called if the operation succeeds.
779
     *
780
     * @param  \Closure  $callback
781
     * @return $this
782
     */
783
    public function onSuccess(Closure $callback)
784
    {
785
        $parameters = $this->closureParameterTypes($callback);
786
 
787
        if (Arr::get($parameters, 'output') === Stringable::class) {
788
            return $this->onSuccessWithOutput($callback);
789
        }
790
 
791
        return $this->then(function (Container $container) use ($callback) {
792
            if ($this->exitCode === 0) {
793
                $container->call($callback);
794
            }
795
        });
796
    }
797
 
798
    /**
799
     * Register a callback that uses the output if the operation succeeds.
800
     *
801
     * @param  \Closure  $callback
802
     * @param  bool  $onlyIfOutputExists
803
     * @return $this
804
     */
805
    public function onSuccessWithOutput(Closure $callback, $onlyIfOutputExists = false)
806
    {
807
        $this->ensureOutputIsBeingCaptured();
808
 
809
        return $this->onSuccess($this->withOutputCallback($callback, $onlyIfOutputExists));
810
    }
811
 
812
    /**
813
     * Register a callback to be called if the operation fails.
814
     *
815
     * @param  \Closure  $callback
816
     * @return $this
817
     */
818
    public function onFailure(Closure $callback)
819
    {
820
        $parameters = $this->closureParameterTypes($callback);
821
 
822
        if (Arr::get($parameters, 'output') === Stringable::class) {
823
            return $this->onFailureWithOutput($callback);
824
        }
825
 
826
        return $this->then(function (Container $container) use ($callback) {
827
            if ($this->exitCode !== 0) {
828
                $container->call($callback);
829
            }
830
        });
831
    }
832
 
833
    /**
834
     * Register a callback that uses the output if the operation fails.
835
     *
836
     * @param  \Closure  $callback
837
     * @param  bool  $onlyIfOutputExists
838
     * @return $this
839
     */
840
    public function onFailureWithOutput(Closure $callback, $onlyIfOutputExists = false)
841
    {
842
        $this->ensureOutputIsBeingCaptured();
843
 
844
        return $this->onFailure($this->withOutputCallback($callback, $onlyIfOutputExists));
845
    }
846
 
847
    /**
848
     * Get a callback that provides output.
849
     *
850
     * @param  \Closure  $callback
851
     * @param  bool  $onlyIfOutputExists
852
     * @return \Closure
853
     */
854
    protected function withOutputCallback(Closure $callback, $onlyIfOutputExists = false)
855
    {
856
        return function (Container $container) use ($callback, $onlyIfOutputExists) {
857
            $output = $this->output && is_file($this->output) ? file_get_contents($this->output) : '';
858
 
859
            return $onlyIfOutputExists && empty($output)
860
                            ? null
861
                            : $container->call($callback, ['output' => new Stringable($output)]);
862
        };
863
    }
864
 
865
    /**
866
     * Set the human-friendly description of the event.
867
     *
868
     * @param  string  $description
869
     * @return $this
870
     */
871
    public function name($description)
872
    {
873
        return $this->description($description);
874
    }
875
 
876
    /**
877
     * Set the human-friendly description of the event.
878
     *
879
     * @param  string  $description
880
     * @return $this
881
     */
882
    public function description($description)
883
    {
884
        $this->description = $description;
885
 
886
        return $this;
887
    }
888
 
889
    /**
890
     * Get the summary of the event for display.
891
     *
892
     * @return string
893
     */
894
    public function getSummaryForDisplay()
895
    {
896
        if (is_string($this->description)) {
897
            return $this->description;
898
        }
899
 
900
        return $this->buildCommand();
901
    }
902
 
903
    /**
904
     * Determine the next due date for an event.
905
     *
906
     * @param  \DateTimeInterface|string  $currentTime
907
     * @param  int  $nth
908
     * @param  bool  $allowCurrentDate
909
     * @return \Illuminate\Support\Carbon
910
     */
911
    public function nextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
912
    {
913
        return Date::instance((new CronExpression($this->getExpression()))
914
            ->getNextRunDate($currentTime, $nth, $allowCurrentDate, $this->timezone));
915
    }
916
 
917
    /**
918
     * Get the Cron expression for the event.
919
     *
920
     * @return string
921
     */
922
    public function getExpression()
923
    {
924
        return $this->expression;
925
    }
926
 
927
    /**
928
     * Set the event mutex implementation to be used.
929
     *
930
     * @param  \Illuminate\Console\Scheduling\EventMutex  $mutex
931
     * @return $this
932
     */
933
    public function preventOverlapsUsing(EventMutex $mutex)
934
    {
935
        $this->mutex = $mutex;
936
 
937
        return $this;
938
    }
939
 
940
    /**
941
     * Get the mutex name for the scheduled command.
942
     *
943
     * @return string
944
     */
945
    public function mutexName()
946
    {
947
        $mutexNameResolver = $this->mutexNameResolver;
948
 
949
        if (! is_null($mutexNameResolver) && is_callable($mutexNameResolver)) {
950
            return $mutexNameResolver($this);
951
        }
952
 
953
        return 'framework'.DIRECTORY_SEPARATOR.'schedule-'.sha1($this->expression.$this->command);
954
    }
955
 
956
    /**
957
     * Set the mutex name or name resolver callback.
958
     *
959
     * @param  \Closure|string  $mutexName
960
     * @return $this
961
     */
962
    public function createMutexNameUsing(Closure|string $mutexName)
963
    {
964
        $this->mutexNameResolver = is_string($mutexName) ? fn () => $mutexName : $mutexName;
965
 
966
        return $this;
967
    }
968
 
969
    /**
970
     * Delete the mutex for the event.
971
     *
972
     * @return void
973
     */
974
    protected function removeMutex()
975
    {
976
        if ($this->withoutOverlapping) {
977
            $this->mutex->forget($this);
978
        }
979
    }
980
}