Passed
Push — develop ( a04425...8843c6 )
by Nikolay
05:16 queued 14s
created

fillModifiedTablesFromCustomFilesData()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
c 0
b 0
f 0
dl 0
loc 24
rs 8.4444
cc 8
nc 6
nop 2
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright © 2017-2023 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\Workers;
21
22
require_once 'Globals.php';
23
24
use MikoPBX\Common\Handlers\CriticalErrorsHandler;
25
use MikoPBX\Common\Models\{CustomFiles, ModelsBase, PbxSettings};
26
use MikoPBX\Common\Providers\BeanstalkConnectionModelsProvider;
27
use MikoPBX\Common\Providers\PBXConfModulesProvider;
28
use MikoPBX\Core\Asterisk\Configs\AsteriskConfigInterface;
29
use MikoPBX\Core\Providers\AsteriskConfModulesProvider;
30
use MikoPBX\Core\System\{BeanstalkClient, SystemMessages};
31
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadAdviceAction;
32
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadCloudDescriptionAction;
33
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadCloudParametersAction;
34
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadCrondAction;
35
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadDialplanAction;
36
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadFail2BanConfAction;
37
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadFeaturesAction;
38
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadH323Action;
39
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadHepAction;
40
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadIAXAction;
41
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadLicenseAction;
42
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadManagerAction;
43
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadModulesConfAction;
44
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadModuleStateAction;
45
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadMOHAction;
46
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadNatsAction;
47
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadNetworkAction;
48
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadNginxAction;
49
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadNginxConfAction;
50
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadNTPAction;
51
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadParkingAction;
52
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadPBXCoreAction;
53
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadPHPFPMAction;
54
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadPJSIPAction;
55
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadQueuesAction;
56
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadRecordSavePeriodAction;
57
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadRestAPIWorkerAction;
58
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadRTPAction;
59
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadSentryAction;
60
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadSSHAction;
61
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadSyslogDAction;
62
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadTimezoneAction;
63
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadVoicemailAction;
64
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadWorkerCallEventsAction;
65
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\RestartPBXCoreAction;
66
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\ProcessOtherModels;
67
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\ProcessPBXSettings;
68
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadFirewallAction;
69
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\ProcessCustomFiles;
70
use MikoPBX\Modules\Config\SystemConfigInterface;
71
use Phalcon\Di;
72
use Pheanstalk\Contract\PheanstalkInterface;
73
use RuntimeException;
74
use Throwable;
75
76
ini_set('error_reporting', E_ALL);
77
ini_set('display_startup_errors', 1);
78
79
/**
80
 * WorkerModelsEvents.
81
 *
82
 * @package MikoPBX\Core\Workers
83
 */
84
class WorkerModelsEvents extends WorkerBase
85
{
86
    private int $last_change;
87
88
    // Array of planned reload actions that need to be started
89
    private array $plannedReloadActions = [];
90
91
    private int $timeout = 5;
92
93
    // Array of core conf objects
94
    private array $arrAsteriskConfObjects;
95
96
    // Array of reload actions sorted by its priority
97
    private array $reloadActions = [];
98
99
    private array $otherModelsDependencyTable = [];
100
    private array $pbxSettingsDependencyTable = [];
101
    private array $customFilesDependencyTable = [];
102
103
    private BeanstalkClient $beanstalkClient;
104
105
106
    /**
107
     * Starts the model events worker.
108
     *
109
     * This method initializes the worker, subscribes to necessary events, and enters a loop waiting for these events.
110
     * It acts as the main entry point for the worker's lifecycle.
111
     *
112
     * @param array $argv The command-line arguments passed to the worker.
113
     * @return void
114
     */
115
    public function start(array $argv): void
116
    {
117
        $this->initializeWorker();
118
        $this->subscribeToEvents();
119
        $this->waitForEvents();
120
    }
121
122
    /**
123
     * Initializes the worker by setting up initial state, loading configurations, and preparing dependencies.
124
     *
125
     * It sets the last change time, gets shared instances from the DI container, initializes PBX settings and model dependency tables,
126
     * and sets up priority actions for reload.
127
     *
128
     * @return void
129
     */
130
    private function initializeWorker(): void
131
    {
132
133
        $this->beanstalkClient = $this->getBeanstalkClient();
134
135
        $this->last_change = time() - $this->timeout;
136
137
        // Array of core conf objects
138
        $this->arrAsteriskConfObjects = $this->di->getShared(AsteriskConfModulesProvider::SERVICE_NAME);
139
140
        // Initializes the PBX settings model dependency table.
141
        $this->pbxSettingsDependencyTable = ProcessPBXSettings::getDependencyTable();
142
143
        // Initializes the custom files models dependency table.
144
        $this->customFilesDependencyTable = ProcessCustomFiles::getDependencyTable();
145
146
        // Initializes the models dependency table.
147
        $this->otherModelsDependencyTable = ProcessOtherModels::getDependencyTable();
148
149
        // Initializes the possible reload actions table.
150
        $this->reloadActions = $this->getReloadActionsWithPriority();
151
152
        $this->plannedReloadActions = [];
153
    }
154
155
    /**
156
     * Create BeanstalkClient connection
157
     * @return BeanstalkClient
158
     */
159
    private function getBeanstalkClient(): BeanstalkClient
160
    {
161
        $di = Di::getDefault();
162
        if (!$di) {
163
            throw new RuntimeException("Dependency Injection container is not set.");
164
        }
165
        return $di->getShared(BeanstalkConnectionModelsProvider::SERVICE_NAME);
166
    }
167
168
    /**
169
     * Get priority reload actions
170
     * @return array
171
     */
172
    private function getReloadActionsWithPriority(): array
173
    {
174
        return [
175
            ReloadModuleStateAction::class,
176
            ReloadTimezoneAction::class,
177
            ReloadSyslogDAction::class,
178
            ReloadRestAPIWorkerAction::class,
179
            ReloadNetworkAction::class,
180
            ReloadFirewallAction::class,
181
            ReloadFail2BanConfAction::class,
182
            ReloadSSHAction::class,
183
            ReloadLicenseAction::class,
184
            ReloadSentryAction::class,
185
            ReloadNatsAction::class,
186
            ReloadNTPAction::class,
187
            ReloadPHPFPMAction::class,
188
            ReloadNginxAction::class,
189
            ReloadNginxConfAction::class,
190
            ReloadCrondAction::class,
191
            RestartPBXCoreAction::class,
192
            ReloadPBXCoreAction::class,
193
            ReloadModulesConfAction::class,
194
            ReloadFeaturesAction::class,
195
            ReloadPJSIPAction::class,
196
            ReloadRTPAction::class,
197
            ReloadIAXAction::class,
198
            ReloadH323Action::class,
199
            ReloadHepAction::class,
200
            ReloadDialplanAction::class,
201
            ReloadParkingAction::class,
202
            ReloadQueuesAction::class,
203
            ReloadManagerAction::class,
204
            ReloadVoicemailAction::class,
205
            ReloadMOHAction::class,
206
            ReloadWorkerCallEventsAction::class,
207
            ReloadRecordSavePeriodAction::class,
208
            ReloadAdviceAction::class,
209
            ReloadCloudDescriptionAction::class,
210
            ReloadCloudParametersAction::class
211
        ];
212
    }
213
214
    /**
215
     * Subscribes the worker to relevant Beanstalk queues for processing model changes and handling pings.
216
     *
217
     * It ensures that the worker listens for incoming messages related to model changes and system pings,
218
     * setting up appropriate callbacks for each.
219
     *
220
     * @return void
221
     */
222
    private function subscribeToEvents(): void
223
    {
224
        $this->beanstalkClient->subscribe(self::class, [$this, 'processModelChanges']);
225
        $this->beanstalkClient->subscribe($this->makePingTubeName(self::class), [$this, 'pingCallBack']);
226
        $this->beanstalkClient->setTimeoutHandler([$this, 'timeoutHandler']);
227
    }
228
229
    /**
230
     * Waits for events in a loop until a restart condition is met.
231
     *
232
     * This method keeps the worker in a loop, processing incoming events from the Beanstalk queue.
233
     * The loop continues until an external condition triggers the need to restart the worker.
234
     *
235
     * @return void
236
     */
237
    private function waitForEvents(): void
238
    {
239
        while ($this->needRestart === false) {
240
            $this->beanstalkClient->wait();
241
        }
242
        $this->timeoutHandler(); // Execute all collected changes before exit
243
    }
244
245
    /**
246
     * Timeout handles
247
     */
248
    public function timeoutHandler(): void
249
    {
250
        $this->last_change = time() - $this->timeout;
251
        $this->startReload();
252
    }
253
254
    /**
255
     * Starts the reload process if there are modified tables.
256
     *
257
     * @return void
258
     */
259
    private function startReload(): void
260
    {
261
        // Check if there aren't any planned reload actions
262
        if (count($this->plannedReloadActions) === 0) {
263
            SystemMessages::sysLogMsg(__METHOD__, "No planed actions for reload", LOG_DEBUG);
264
            return;
265
        }
266
267
        // Check if enough time has passed since the last change
268
        $delta = time() - $this->last_change;
269
        if ($delta < $this->timeout) {
270
            SystemMessages::sysLogMsg(__METHOD__, "Wait more time before starting the reload.", LOG_DEBUG);
271
            return;
272
        }
273
274
        $executedActions = [];
275
        // Process changes for each method in priority order
276
        foreach ($this->reloadActions as $actionClassName) {
277
            // Skip if there is no change for this method
278
            if (!array_key_exists($actionClassName, $this->plannedReloadActions)) {
279
                continue;
280
            }
281
            // Call the method if it exists
282
            try {
283
                $parameters = $this->plannedReloadActions[$actionClassName]['parameters'];
284
                $hashes = array_keys($parameters);
285
                SystemMessages::sysLogMsg($actionClassName, "Start action for the next parameters hashes: ".PHP_EOL . json_encode($hashes, JSON_PRETTY_PRINT), LOG_DEBUG);
286
287
                $actionObject = new $actionClassName();
288
                $actionObject->execute($parameters);
289
                $executedActions[] = $actionClassName;
290
            } catch (Throwable $exception) {
291
                CriticalErrorsHandler::handleExceptionWithSyslog($exception);
292
            }
293
294
        }
295
        if (count($executedActions)>0){
296
            SystemMessages::sysLogMsg(__METHOD__, "Reload actions were executed in the next order: ".PHP_EOL . json_encode($executedActions, JSON_PRETTY_PRINT), LOG_DEBUG);
297
        }
298
299
        // Send information about models changes to additional modules bulky without any details
300
        PBXConfModulesProvider::hookModulesMethod(SystemConfigInterface::MODELS_EVENT_NEED_RELOAD, [$this->plannedReloadActions]);
301
302
        // Reset the modified tables array
303
        $this->plannedReloadActions = [];
304
    }
305
306
    /**
307
     * Processes model changes received from the Beanstalk queue.
308
     *
309
     * @param BeanstalkClient $message The message received from the Beanstalk queue.
310
     * @return void
311
     */
312
    public function processModelChanges(BeanstalkClient $message): void
313
    {
314
        try {
315
            // Decode the received message
316
            $receivedMessage = json_decode($message->getBody(), true, 512, JSON_THROW_ON_ERROR);
317
318
            // Check the source of the message and perform actions accordingly
319
            if ($receivedMessage['source'] === BeanstalkConnectionModelsProvider::SOURCE_INVOKE_ACTION
320
                && in_array($receivedMessage['action'], $this->reloadActions)) {
321
                // Store the modified table and its parameters
322
                $this->planReloadAction($receivedMessage['action'], $receivedMessage['parameters']);
323
324
            } elseif ($receivedMessage['source'] === BeanstalkConnectionModelsProvider::SOURCE_MODELS_CHANGED) {
325
326
                // Fill the modified tables array with the changes from the received message
327
                $this->fillModifiedTables($receivedMessage);
328
            }
329
330
            // Start the reload process if there are modified tables
331
            $this->startReload();
332
333
            if (!$receivedMessage) {
334
                return;
335
            }
336
337
            // Send information about model changes to additional modules with changed data details
338
            PBXConfModulesProvider::hookModulesMethod(SystemConfigInterface::MODELS_EVENT_CHANGE_DATA, [$receivedMessage]);
339
        } catch (Throwable $exception) {
340
            $this->needRestart = true;
341
            CriticalErrorsHandler::handleExceptionWithSyslog($exception);
342
        }
343
    }
344
345
    /**
346
     * Fills the modified tables array with changes based on the received data.
347
     *
348
     * @param array $data The data containing the changes.
349
     * @return void
350
     */
351
    private function fillModifiedTables(array $data): void
352
    {
353
        $countPlannedActions = count($this->plannedReloadActions);
354
        $modifiedModel = $data['model'] ?? '';
355
        if (empty($modifiedModel)){
356
            return;
357
        }
358
359
        SystemMessages::sysLogMsg(__METHOD__, "New changes received:".PHP_EOL . json_encode($data, JSON_PRETTY_PRINT), LOG_DEBUG);
360
361
        // Clear cache for the called class
362
        ModelsBase::clearCache($modifiedModel);
363
364
        // Get new settings for dependent modules
365
        $this->getNewSettingsForDependentModules($modifiedModel);
366
367
        // Plan new reload actions
368
        $this->planReloadActionsForCustomFiles($modifiedModel, $data);
369
        $this->planReloadActionsForPbxSettings($modifiedModel, $data);
370
        $this->planReloadActionsForOtherModels($modifiedModel, $data);
371
372
        // Start counting time when the new reload actions were received
373
        if ($countPlannedActions === 0 && count($this->plannedReloadActions) > 0) {
374
            $this->last_change = time();
375
        }
376
    }
377
378
    /**
379
     * Retrieves new settings for dependent modules based on the called class.
380
     *
381
     * @param string $modifiedModel The called class for which to retrieve settings.
382
     * @return void
383
     */
384
    private function getNewSettingsForDependentModules(string $modifiedModel): void
385
    {
386
        foreach ($this->arrAsteriskConfObjects as $configClassObj) {
387
            try {
388
                $dependencies = call_user_func([$configClassObj, AsteriskConfigInterface::GET_DEPENDENCE_MODELS]);
389
                // Check if the called class is a dependency and the config class has the GET_SETTINGS method
390
                if (in_array($modifiedModel, $dependencies, true) && method_exists($configClassObj, AsteriskConfigInterface::GET_SETTINGS)) {
391
                    // Retrieve the new settings for the config class
392
                    call_user_func([$configClassObj, AsteriskConfigInterface::GET_SETTINGS]);
393
                }
394
            } catch (Throwable $e) {
395
                CriticalErrorsHandler::handleExceptionWithSyslog($e);
396
                continue;
397
            }
398
        }
399
    }
400
401
    /**
402
     * Fills the modified tables array based on the models dependency table and the called class.
403
     *
404
     * @param string $called_class The called model class.
405
     * @param array $modelData Data received during model change.
406
     * @return void
407
     */
408
    private function planReloadActionsForOtherModels(string $modifiedModel, array $modelData): void
409
    {
410
        foreach ($this->otherModelsDependencyTable as $dependencyData) {
411
            if (!in_array($modifiedModel, $dependencyData['modelClasses'], true)) {
412
                continue;
413
            }
414
            foreach ($dependencyData['actions'] as $action) {
415
                $this->planReloadAction($action, $modelData);
416
            }
417
        }
418
    }
419
420
    /**
421
     * Fills the modified tables array based on the custom files data, the called class, and the record ID.
422
     *
423
     * @param string $modifiedModel The modified model class (Must be CustomFiles)
424
     * @param array $modelData Data received during model change.
425
     * @return void
426
     */
427
    private function planReloadActionsForCustomFiles(string $modifiedModel, array $modelData): void
428
    {
429
        // Check if the called class is not CustomFiles
430
        if (CustomFiles::class !== $modifiedModel || empty($modelData['recordId'])) {
431
            return;
432
        }
433
434
        $changedCustomFile = CustomFiles::findFirstById($modelData['recordId']);
435
        if ($changedCustomFile === null || $changedCustomFile->changed !== '1') {
436
            return;
437
        }
438
439
        foreach ($this->customFilesDependencyTable as $dependencyData) {
440
            // The rule for all files or the rule only for specific file
441
            if ($dependencyData['filePath'] === '*' || strcasecmp($changedCustomFile->filepath, $dependencyData['filePath']) === 0) {
442
                foreach ($dependencyData['actions'] as $action) {
443
                    $this->planReloadAction($action, $modelData);
444
                }
445
            }
446
        }
447
448
        // After actions are invoked, reset the changed status and save the file data
449
        $changedCustomFile->writeAttribute("changed", '0');
450
        $changedCustomFile->save();
451
    }
452
453
    /**
454
     * Add new reload action with parameters to the planned reload actions array.
455
     *
456
     * @param string $action The name of the action to be executed.
457
     * @param array $parameters The parameters to be passed to the action.
458
     * @return void
459
     */
460
    private function planReloadAction(string $action, array $parameters = []): void
461
    {
462
        $newHash = $this->createUniqueKeyFromArray($parameters);
463
        if (!array_key_exists($action, $this->plannedReloadActions)) {
464
            $this->plannedReloadActions[$action]['parameters'][$newHash] = $parameters;
465
            SystemMessages::sysLogMsg(__METHOD__, "New reload task $action planned with parameters (hash=$newHash):".PHP_EOL . json_encode($parameters, JSON_PRETTY_PRINT), LOG_DEBUG);
466
        } else {
467
            foreach ($this->plannedReloadActions[$action]['parameters'] as $oldHash=>$existParameters) {
468
                if ($newHash === $oldHash) {
469
                    return;
470
                }
471
                $this->plannedReloadActions[$action]['parameters'][$newHash] = $parameters;
472
                SystemMessages::sysLogMsg(__METHOD__, "Existing reload task $action received a new parameters (hash=$newHash)".PHP_EOL . json_encode($parameters, JSON_PRETTY_PRINT), LOG_DEBUG);
473
            }
474
        }
475
476
    }
477
478
    /**
479
     * Fills the modified tables array based on the PBX settings data, the called class, and the record ID.
480
     *
481
     * @param string $modifiedModel The modified model class (Must be PbxSettings)
482
     * @param array $modelData Data received during model change.
483
     * @return void
484
     */
485
    private function planReloadActionsForPbxSettings(string $modifiedModel, array $modelData): void
486
    {
487
        // Check if the called class is not PbxSettings
488
        if (PbxSettings::class !== $modifiedModel  || empty($modelData['recordId'])) {
489
            return;
490
        }
491
492
        // Clear cache for PbxSettings
493
        PbxSettings::clearCache(PbxSettings::class);
494
495
        // Find the PbxSettings record
496
        /** @var PbxSettings $pbxSettings */
497
        $pbxSettings = PbxSettings::findFirstByKey($modelData['recordId']);
498
        if ($pbxSettings === null) {
499
            return;
500
        }
501
        $key = $pbxSettings->key;
502
503
        // Iterate through the PBX settings dependency table and update the modified tables array
504
        foreach ($this->pbxSettingsDependencyTable as $data) {
505
            $additionalConditions = (isset($data['strPosKey']) && strpos($key, $data['strPosKey']) !== false);
506
507
            // Check additional conditions and the setting name
508
            if (!$additionalConditions && !in_array($key, $data['keys'], true)) {
509
                continue;
510
            }
511
512
            // Update the modified tables array for each function
513
            foreach ($data['actions'] as $action) {
514
                $this->planReloadAction($action, $modelData);
515
            }
516
        }
517
    }
518
519
    /**
520
     * Invokes an action by publishing a job to the Beanstalk queue.
521
     *
522
     * @param string $action The action to invoke.
523
     * @param array $parameters The parameters for the action.
524
     * @param int $priority The priority of the job.
525
     * @return void
526
     */
527
    public static function invokeAction(string $action, array $parameters = [], int $priority = 0): void
528
    {
529
        $di = Di::getDefault();
530
        if (!$di) {
531
            return;
532
        }
533
        /** @var BeanstalkClient $queue */
534
        $queue = $di->getShared(BeanstalkConnectionModelsProvider::SERVICE_NAME);
535
536
        // Prepare the job data
537
        $jobData = json_encode(['source' => BeanstalkConnectionModelsProvider::SOURCE_INVOKE_ACTION, 'action' => $action, 'parameters' => $parameters, 'model' => '']);
538
        // Publish the job to the Beanstalk queue
539
        $queue->publish($jobData, self::class, $priority, PheanstalkInterface::DEFAULT_DELAY, 3600);
540
    }
541
542
    /**
543
     * Callback for the ping to keep the connection alive.
544
     *
545
     * @param BeanstalkClient $message The received message.
546
     *
547
     * @return void
548
     */
549
    public function pingCallBack(BeanstalkClient $message): void
550
    {
551
        // Start the reload process if there are modified tables
552
        $this->startReload();
553
        $message->reply(json_encode($message->getBody() . ':pong'));
554
    }
555
556
    private function createUniqueKeyFromArray(array $array) {
557
        // Convert the array to JSON string
558
        $json = json_encode($array);
559
560
        // Create an MD5 hash of the JSON string
561
        return md5($json);
562
    }
563
}
564
565
/**
566
 * The start point
567
 */
568
WorkerModelsEvents::startWorker($argv ?? []);