Passed
Push — develop ( 368e3d...115c7b )
by Nikolay
12:16
created

WorkerModelsEvents::subscribeToEvents()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 0
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\Core\Workers\Libs\WorkerModelsEvents\ProcessCustomFiles;
26
use MikoPBX\Common\Models\{CustomFiles, ModelsBase, PbxExtensionModules, PbxSettings};
27
use MikoPBX\Common\Providers\BeanstalkConnectionModelsProvider;
28
use MikoPBX\Common\Providers\PBXConfModulesProvider;
29
use MikoPBX\Core\Asterisk\Configs\AsteriskConfigInterface;
30
use MikoPBX\Core\Providers\AsteriskConfModulesProvider;
31
use MikoPBX\Core\System\{BeanstalkClient, SystemMessages};
32
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadAdviceAction;
33
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadCloudDescriptionAction;
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\ReloadFirewallAction;
39
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadIAXAction;
40
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadLicenseAction;
41
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadManagerAction;
42
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadModuleStateAction;
43
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadMOHAction;
44
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadNatsAction;
45
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadNetworkAction;
46
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadNginxAction;
47
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadNginxConfAction;
48
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadNTPAction;
49
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadParkingAction;
50
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\RestartPBXCoreAction;
51
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadPHPFPMAction;
52
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadPJSIPAction;
53
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadQueuesAction;
54
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadRecordSavePeriodAction;
55
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadRestAPIWorkerAction;
56
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadRTPAction;
57
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadSentryAction;
58
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadSSHAction;
59
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadSyslogDAction;
60
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadTimezoneAction;
61
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadVoicemailAction;
62
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadWorkerCallEventsAction;
63
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadCloudParametersAction;
64
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadH323Action;
65
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadHepAction;
66
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadModulesConfAction;
67
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\Actions\ReloadPBXCoreAction;
68
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\ProcessOtherModels;
69
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\ProcessPBXSettings;
70
use MikoPBX\Core\Workers\Libs\WorkerModelsEvents\ReloadManager;
71
use MikoPBX\Modules\Config\SystemConfigInterface;
72
use Phalcon\Di;
73
use Pheanstalk\Contract\PheanstalkInterface;
74
use RuntimeException;
75
use Throwable;
76
77
ini_set('error_reporting', E_ALL);
78
ini_set('display_startup_errors', 1);
79
80
/**
81
 * WorkerModelsEvents.
82
 *
83
 * @package MikoPBX\Core\Workers
84
 */
85
class WorkerModelsEvents extends WorkerBase
86
{
87
    public const R_MANAGERS = 'reloadManager';
88
    public const R_QUEUES = 'reloadQueues';
89
    public const R_MODULES_CONF = 'reloadModulesConf';
90
    public const R_DIALPLAN = 'reloadDialplan';
91
    public const R_PARKING = 'reloadParking';
92
    public const R_FIREWALL = 'reloadFirewall';
93
    public const R_NETWORK = 'networkReload';
94
    public const R_IAX = 'reloadIax';
95
    public const R_SIP = 'reloadSip';
96
    public const R_RTP = 'rtpReload';
97
    public const R_H323  = 'H323Reload';
98
    public const R_HEP   = 'HepReload';
99
    public const R_PBX_CORE_RESTART = 'pbxCoreRestart';
100
    public const R_PBX_CORE_RELOAD = 'pbxCoreReload';
101
    public const R_FEATURES = 'reloadFeatures';
102
    public const R_CRON = 'reloadCron';
103
    public const  R_NGINX = 'reloadNginx';
104
    public const  R_NGINX_CONF = 'reloadNginxConf';
105
    public const  R_FAIL2BAN_CONF = 'reloadFail2BanConf';
106
    public const R_PHP_FPM = 'reloadPHPFPM';
107
    public const R_TIMEZONE = 'updateTomeZone';
108
    public const R_SYSLOG = 'restartSyslogD';
109
    public const R_SSH = 'reloadSSH';
110
    public const R_LICENSE = 'reloadLicense';
111
    public const R_NATS = 'reloadNats';
112
    public const R_VOICEMAIL = 'reloadVoicemail';
113
    public const R_REST_API_WORKER = 'reloadRestAPIWorker';
114
    public const R_CALL_EVENTS_WORKER = 'reloadWorkerCallEvents';
115
    public const R_PBX_MODULE_STATE = 'afterModuleStateChanged';
116
    public const R_MOH = 'reloadMoh';
117
    public const R_NTP = 'reloadNtp';
118
    public const R_UPDATE_REC_SAVE_PERIOD = 'updateRecordSavePeriod';
119
    public const R_ADVICE = 'cleanupAdviceCache';
120
    public const R_RESET_DESCRIPTION = 'resetDescription';
121
    public const R_SENTRY = 'reloadSentry';
122
    public const R_CLOUD_PROVISION = 'ReloadCloudParametersAction';
123
124
    private int $last_change;
125
    private array $modified_tables = [];
126
127
    private int $timeout = 2;
128
129
    // Array of core conf objects
130
    private array $arrAsteriskConfObjects;
131
132
    // Array of reload actions sorted by its priority
133
    private array $reloadActions = [];
134
135
    private array $otherModelsDependencyTable = [];
136
    private array $pbxSettingsDependencyTable = [];
137
    private array $customFilesDependencyTable = [];
138
139
    private BeanstalkClient $beanstalkClient;
140
141
    private ReloadManager $reloadManager;
142
143
    /**
144
     * Starts the model events worker.
145
     *
146
     * This method initializes the worker, subscribes to necessary events, and enters a loop waiting for these events.
147
     * It acts as the main entry point for the worker's lifecycle.
148
     *
149
     * @param array $argv The command-line arguments passed to the worker.
150
     * @return void
151
     */
152
    public function start(array $argv): void
153
    {
154
        $this->initializeWorker();
155
        $this->subscribeToEvents();
156
        $this->waitForEvents();
157
    }
158
159
    /**
160
     * Initializes the worker by setting up initial state, loading configurations, and preparing dependencies.
161
     *
162
     * It sets the last change time, gets shared instances from the DI container, initializes PBX settings and model dependency tables,
163
     * and sets up priority actions for reload.
164
     *
165
     * @return void
166
     */
167
    private function initializeWorker(): void
168
    {
169
170
        $this->beanstalkClient = $this->getBeanstalkClient();
171
172
        $this->last_change = time() - 2;
173
174
        // Array of core conf objects
175
        $this->arrAsteriskConfObjects = $this->di->getShared(AsteriskConfModulesProvider::SERVICE_NAME);
176
177
        // Initializes the PBX settings model dependency table.
178
        $this->pbxSettingsDependencyTable = ProcessPBXSettings::getDependencyTable();
179
180
        // Initializes the custom files models dependency table.
181
        $this->customFilesDependencyTable = ProcessCustomFiles::getDependencyTable();
182
183
        // Initializes the models dependency table.
184
        $this->otherModelsDependencyTable = ProcessOtherModels::getDependencyTable();
185
186
        // Initializes the possible reload actions table.
187
        $this->reloadActions = $this->getReloadActions();
188
189
        // Initializes the reload manager.
190
        $this->reloadManager = new ReloadManager($this->reloadActions);
191
192
        $this->modified_tables = [];
193
    }
194
195
    /**
196
     * Create BeanstalkClient connection
197
     * @return BeanstalkClient
198
     */
199
    private function getBeanstalkClient(): BeanstalkClient
200
    {
201
        $di = Di::getDefault();
202
        if (!$di) {
203
            throw new RuntimeException("Dependency Injection container is not set.");
204
        }
205
        return $di->getShared(BeanstalkConnectionModelsProvider::SERVICE_NAME);
206
    }
207
208
    /**
209
     * Get priority reload actions
210
     * @return array
211
     */
212
    private function getReloadActions(): array
213
    {
214
        return [
215
            self::R_PBX_MODULE_STATE => new ReloadModuleStateAction(),
216
            self::R_TIMEZONE => new ReloadTimezoneAction(),
217
            self::R_SYSLOG => new ReloadSyslogDAction,
218
            self::R_REST_API_WORKER => new ReloadRestAPIWorkerAction(),
219
            self::R_NETWORK => new ReloadNetworkAction(),
220
            self::R_FIREWALL => new ReloadFirewallAction(),
221
            self::R_FAIL2BAN_CONF => new ReloadFail2BanConfAction(),
222
            self::R_SSH => new ReloadSSHAction(),
223
            self::R_LICENSE => new ReloadLicenseAction(),
224
            self::R_SENTRY => new ReloadSentryAction(),
225
            self::R_NATS => new ReloadNatsAction(),
226
            self::R_NTP => new ReloadNTPAction(),
227
            self::R_PHP_FPM => new ReloadPHPFPMAction(),
228
            self::R_NGINX => new ReloadNginxAction(),
229
            self::R_NGINX_CONF => new ReloadNginxConfAction(),
230
            self::R_CRON => new ReloadCrondAction(),
231
            self::R_PBX_CORE_RESTART => new RestartPBXCoreAction(),
232
            self::R_PBX_CORE_RELOAD => new ReloadPBXCoreAction(),
233
            self::R_MODULES_CONF  => new ReloadModulesConfAction(),
234
            self::R_FEATURES => new ReloadFeaturesAction(),
235
            self::R_SIP => new ReloadPJSIPAction(),
236
            self::R_RTP => new ReloadRTPAction(),
237
            self::R_IAX => new ReloadIAXAction(),
238
            self::R_H323  => new ReloadH323Action(),
239
            self::R_HEP  => new ReloadHepAction(),
240
            self::R_DIALPLAN => new ReloadDialplanAction(),
241
            self::R_PARKING => new ReloadParkingAction(),
242
            self::R_QUEUES => new ReloadQueuesAction(),
243
            self::R_MANAGERS => new ReloadManagerAction(),
244
            self::R_VOICEMAIL => new ReloadVoicemailAction(),
245
            self::R_MOH => new ReloadMOHAction(),
246
            self::R_CALL_EVENTS_WORKER => new ReloadWorkerCallEventsAction(),
247
            self::R_UPDATE_REC_SAVE_PERIOD => new ReloadRecordSavePeriodAction(),
248
            self::R_ADVICE => new ReloadAdviceAction(),
249
            self::R_RESET_DESCRIPTION => new ReloadCloudDescriptionAction(),
250
            self::R_CLOUD_PROVISION => new ReloadCloudParametersAction()
251
        ];
252
    }
253
254
    /**
255
     * Subscribes the worker to relevant Beanstalk queues for processing model changes and handling pings.
256
     *
257
     * It ensures that the worker listens for incoming messages related to model changes and system pings,
258
     * setting up appropriate callbacks for each.
259
     *
260
     * @return void
261
     */
262
    private function subscribeToEvents(): void
263
    {
264
        $this->beanstalkClient->subscribe(self::class, [$this, 'processModelChanges']);
265
        $this->beanstalkClient->subscribe($this->makePingTubeName(self::class), [$this, 'pingCallBack']);
266
        $this->beanstalkClient->setTimeoutHandler([$this, 'timeoutHandler']);
267
    }
268
269
    /**
270
     * Waits for events in a loop until a restart condition is met.
271
     *
272
     * This method keeps the worker in a loop, processing incoming events from the Beanstalk queue.
273
     * The loop continues until an external condition triggers the need to restart the worker.
274
     *
275
     * @return void
276
     */
277
    private function waitForEvents(): void
278
    {
279
        while ($this->needRestart === false) {
280
            $this->beanstalkClient->wait();
281
        }
282
        $this->timeoutHandler(); // Execute all collected changes before exit
283
    }
284
285
    /**
286
     * Timeout handles
287
     */
288
    public function timeoutHandler(): void
289
    {
290
        $this->last_change = time() - $this->timeout;
291
        $this->startReload();
292
    }
293
294
    /**
295
     * Starts the reload process if there are modified tables.
296
     *
297
     * @return void
298
     */
299
    private function startReload(): void
300
    {
301
        // Check if there are any modified tables
302
        if (count($this->modified_tables) === 0) {
303
            return;
304
        }
305
306
        // Check if enough time has passed since the last change
307
        $delta = time() - $this->last_change;
308
        if ($delta < $this->timeout) {
309
            return;
310
        }
311
312
        // Process changes for each method in priority order
313
        foreach ($this->reloadActions as $method_name => $calledClass) {
314
            $action = $this->modified_tables[$method_name] ?? null;
315
316
            // Skip if there is no change for this method
317
            if ($action === null) {
318
                continue;
319
            }
320
321
            // Call the method if it exists
322
            $className = get_class($calledClass);
323
            SystemMessages::sysLogMsg(__METHOD__, "Process reload action: {$className}", LOG_DEBUG);
324
            $parameters = $this->modified_tables['parameters'][$method_name] ?? [];
325
            $this->reloadManager->processReload($method_name, $parameters);
326
327
        }
328
329
        // Send information about models changes to additional modules bulky without any details
330
        PBXConfModulesProvider::hookModulesMethod(SystemConfigInterface::MODELS_EVENT_NEED_RELOAD, [$this->modified_tables]);
331
332
        // Reset the modified tables array
333
        $this->modified_tables = [];
334
    }
335
336
    /**
337
     * Processes model changes received from the Beanstalk queue.
338
     *
339
     * @param BeanstalkClient $message The message received from the Beanstalk queue.
340
     * @return void
341
     */
342
    public function processModelChanges(BeanstalkClient $message): void
343
    {
344
        try {
345
            // Decode the received message
346
            $receivedMessage = json_decode($message->getBody(), true, 512, JSON_THROW_ON_ERROR);
347
348
            // Check the source of the message and perform actions accordingly
349
            if ($receivedMessage['source'] === BeanstalkConnectionModelsProvider::SOURCE_INVOKE_ACTION
350
                && array_key_exists($receivedMessage['action'], $this->reloadActions))
351
            {
352
                // Store the modified table and its parameters
353
                $this->modified_tables[$receivedMessage['action']] = true;
354
                $this->modified_tables['parameters'][$receivedMessage['action']] = $receivedMessage['parameters'];
355
            } elseif ($receivedMessage['source'] === BeanstalkConnectionModelsProvider::SOURCE_MODELS_CHANGED) {
356
357
                // Fill the modified tables array with the changes from the received message
358
                $this->fillModifiedTables($receivedMessage);
359
            }
360
361
            // Start the reload process if there are modified tables
362
            $this->startReload();
363
364
            if (!$receivedMessage) {
365
                return;
366
            }
367
368
            // Send information about model changes to additional modules with changed data details
369
            PBXConfModulesProvider::hookModulesMethod(SystemConfigInterface::MODELS_EVENT_CHANGE_DATA, [$receivedMessage]);
370
        } catch (Throwable $exception) {
371
            $this->needRestart = true;
372
            CriticalErrorsHandler::handleExceptionWithSyslog($exception);
373
        }
374
    }
375
376
    /**
377
     * Fills the modified tables array with changes based on the received data.
378
     *
379
     * @param array $data The data containing the changes.
380
     * @return void
381
     */
382
    private function fillModifiedTables(array $data): void
383
    {
384
        $count_changes = count($this->modified_tables);
385
        $called_class = $data['model'] ?? '';
386
        SystemMessages::sysLogMsg(__METHOD__, "New changes " . json_encode($data), LOG_DEBUG);
387
388
        // Clear cache for the called class
389
        ModelsBase::clearCache($called_class);
390
391
        // Get new settings for dependent modules
392
        $this->getNewSettingsForDependentModules($called_class);
393
394
        // Fill modified tables from models
395
        $this->fillModifiedTablesFromOtherModels($called_class);
396
397
        // Fill modified tables from PBX settings data
398
        $this->fillModifiedTablesFromCustomFilesData($called_class, $data['recordId']);
399
400
        // Fill modified tables from PBX settings data
401
        $this->fillModifiedTablesFromPbxSettingsData($called_class, $data['recordId']);
402
403
        // Fill modified tables from PBX extension modules
404
        $this->fillModifiedTablesFromPbxExtensionModules($called_class, $data['recordId']);
405
406
        // Start counting time when the first task is received
407
        if ($count_changes === 0 && count($this->modified_tables) > 0) {
408
            $this->last_change = time();
409
        }
410
    }
411
412
    /**
413
     * Retrieves new settings for dependent modules based on the called class.
414
     *
415
     * @param string $called_class The called class for which to retrieve settings.
416
     * @return void
417
     */
418
    private function getNewSettingsForDependentModules(string $called_class): void
419
    {
420
        foreach ($this->arrAsteriskConfObjects as $configClassObj) {
421
            try {
422
                $dependencies = call_user_func([$configClassObj, AsteriskConfigInterface::GET_DEPENDENCE_MODELS]);
423
424
                // Check if the called class is a dependency and the config class has the GET_SETTINGS method
425
                if (in_array($called_class, $dependencies, true) && method_exists($configClassObj, AsteriskConfigInterface::GET_SETTINGS)) {
426
                    // Retrieve the new settings for the config class
427
                    call_user_func([$configClassObj, AsteriskConfigInterface::GET_SETTINGS]);
428
                }
429
            } catch (Throwable $e) {
430
                CriticalErrorsHandler::handleExceptionWithSyslog($e);
431
                continue;
432
            }
433
        }
434
    }
435
436
    /**
437
     * Fills the modified tables array based on the models dependency table and the called class.
438
     *
439
     * @param string $called_class The called class.
440
     * @return void
441
     */
442
    private function fillModifiedTablesFromOtherModels(string $called_class): void
443
    {
444
        foreach ($this->otherModelsDependencyTable as $dependencyData) {
445
            if (!in_array($called_class, $dependencyData['settingName'], true)) {
446
                continue;
447
            }
448
            foreach ($dependencyData['functions'] as $function) {
449
                $this->modified_tables[$function] = true;
450
            }
451
        }
452
    }
453
454
    /**
455
     * Fills the modified tables array based on the custom files data, the called class, and the record ID.
456
     *
457
     * @param string $called_class The called class (Must be CustomFiles)
458
     * @param string $recordId The record ID.
459
     * @return void
460
     */
461
    private function fillModifiedTablesFromCustomFilesData(string $called_class, string $recordId): void
462
    {
463
        // Check if the called class is not CustomFiles
464
        if (CustomFiles::class !== $called_class) {
465
            return;
466
        }
467
468
        $changedCustomFile = CustomFiles::findFirstById($recordId);
469
        if ($changedCustomFile === null || $changedCustomFile->changed!=='1') {
470
            return;
471
        }
472
473
        foreach ($this->customFilesDependencyTable as $dependencyData) {
474
            // The rule for all files or the rule only for specific file
475
            if ($dependencyData['filePath']==='*' || strcasecmp($changedCustomFile->filepath, $dependencyData['filePath']) === 0) {
476
                foreach ($dependencyData['functions'] as $function) {
477
                    $this->modified_tables[$function] = true;
478
                }
479
            }
480
        }
481
482
        // After actions are invoked, reset the changed status and save the file data
483
        $changedCustomFile->writeAttribute("changed", '0');
484
        $changedCustomFile->save();
485
    }
486
    /**
487
     * Fills the modified tables array based on the PBX settings data, the called class, and the record ID.
488
     *
489
     * @param string $called_class The called class (Must be PbxSettings)
490
     * @param string $recordId The record ID.
491
     * @return void
492
     */
493
    private function fillModifiedTablesFromPbxSettingsData(string $called_class, string $recordId): void
494
    {
495
        // Check if the called class is not PbxSettings
496
        if (PbxSettings::class !== $called_class) {
497
            return;
498
        }
499
500
        // Clear cache for PbxSettings
501
        PbxSettings::clearCache(PbxSettings::class);
502
503
        // Find the PbxSettings record
504
        /** @var PbxSettings $pbxSettings */
505
        $pbxSettings = PbxSettings::findFirstByKey($recordId);
506
        if ($pbxSettings === null) {
507
            return;
508
        }
509
        $settingName = $pbxSettings->key;
510
511
        // Iterate through the PBX settings dependency table and update the modified tables array
512
        foreach ($this->pbxSettingsDependencyTable as $data) {
513
            $additionalConditions = (isset($data['strPosKey']) && strpos($settingName, $data['strPosKey']) !== false);
514
515
            // Check additional conditions and the setting name
516
            if (!$additionalConditions && !in_array($settingName, $data['settingName'], true)) {
517
                continue;
518
            }
519
520
            // Update the modified tables array for each function
521
            foreach ($data['functions'] as $function) {
522
                $this->modified_tables[$function] = true;
523
            }
524
        }
525
    }
526
527
    /**
528
     * Fills the modified tables array based on the PBX extension modules data, the called class, and the record ID.
529
     *
530
     * @param string $called_class The called class.
531
     * @param string $recordId The record ID.
532
     * @return void
533
     */
534
    private function fillModifiedTablesFromPbxExtensionModules(string $called_class, string $recordId): void
535
    {
536
        // Check if the called class is not PbxExtensionModules
537
        if (PbxExtensionModules::class !== $called_class) {
538
            return;
539
        }
540
541
        // Find the module settings record
542
        $moduleSettings = PbxExtensionModules::findFirstById($recordId);
543
        if ($moduleSettings !== null) {
544
            // Invoke the action for the PBX module state with the module settings data
545
            self::invokeAction(self::R_PBX_MODULE_STATE, $moduleSettings->toArray(), 50);
546
        }
547
    }
548
549
    /**
550
     * Invokes an action by publishing a job to the Beanstalk queue.
551
     *
552
     * @param string $action The action to invoke.
553
     * @param array $parameters The parameters for the action.
554
     * @param int $priority The priority of the job.
555
     * @return void
556
     */
557
    public static function invokeAction(string $action, array $parameters = [], int $priority = 0): void
558
    {
559
        $di = Di::getDefault();
560
        if (!$di) {
561
            return;
562
        }
563
        /** @var BeanstalkClient $queue */
564
        $queue = $di->getShared(BeanstalkConnectionModelsProvider::SERVICE_NAME);
565
566
        // Prepare the job data
567
        $jobData = json_encode(['source' => BeanstalkConnectionModelsProvider::SOURCE_INVOKE_ACTION, 'action' => $action, 'parameters' => $parameters, 'model' => '']);
568
        // Publish the job to the Beanstalk queue
569
        $queue->publish($jobData, self::class, $priority, PheanstalkInterface::DEFAULT_DELAY, 3600);
570
    }
571
}
572
573
/**
574
 * The start point
575
 */
576
WorkerModelsEvents::startWorker($argv ?? []);