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

WorkerPrepareAdvice::processAdvice()   A

Complexity

Conditions 4
Paths 9

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 18
c 0
b 0
f 0
dl 0
loc 26
rs 9.6666
cc 4
nc 9
nop 1
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
use Generator;
23
use MikoPBX\Common\Handlers\CriticalErrorsHandler;
24
use MikoPBX\Common\Models\PbxSettings;
25
use MikoPBX\Common\Providers\ManagedCacheProvider;
26
use MikoPBX\Core\System\Processes;
27
use MikoPBX\Core\System\SystemMessages;
28
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvice\CheckConnection;
29
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvice\CheckCorruptedFiles;
30
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvice\CheckFirewalls;
31
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvice\CheckPasswords;
32
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvice\CheckSSHConfig;
33
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvice\CheckStorage;
34
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvice\CheckUpdates;
35
use Phalcon\Di;
36
use Recoil\React\ReactKernel;
37
use Throwable;
38
39
require_once 'Globals.php';
40
41
42
/**
43
 * WorkerPrepareAdvice is a worker class responsible for prepare system advice.
44
 *
45
 * @package MikoPBX\Core\Workers
46
 */
47
class WorkerPrepareAdvice extends WorkerBase
48
{
49
    public const ARR_ADVICE_TYPES = [
50
        ['type' => CheckConnection::class, 'cacheTime' => 120],
51
        ['type' => CheckCorruptedFiles::class, 'cacheTime' => 3600],
52
        ['type' => CheckPasswords::class, 'cacheTime' => 86400, 'dependent'=>PbxSettings::class],
53
        ['type' => CheckFirewalls::class, 'cacheTime' => 86400, 'dependent'=>PbxSettings::class],
54
        ['type' => CheckStorage::class, 'cacheTime' => 3600],
55
        ['type' => CheckUpdates::class, 'cacheTime' => 86400],
56
        ['type' => CheckSSHConfig::class, 'cacheTime' => 3600],
57
    ];
58
59
    // Array of generated advice
60
    public array $messages;
61
62
    /**
63
     * Starts processing advice types.
64
     *
65
     * @param array $argv The command-line arguments passed to the worker.
66
     *
67
     * @throws Throwable
68
     */
69
    public function start(array $argv): void
70
    {
71
        $adviceTypes = self::ARR_ADVICE_TYPES;
72
        // Use ReactKernel to start parallel execution
73
        ReactKernel::start(
74
            function () use ($adviceTypes) {
75
                // Parallel execution https://github.com/recoilphp/recoil
76
                foreach ($adviceTypes as $adviceType) {
77
                    yield $this->processAdvice($adviceType);
78
                }
79
            }
80
        );
81
    }
82
83
    /**
84
     * Processes advice of a specific type and caches the result.
85
     *
86
     * @param array $adviceType An array containing advice type and cache time.
87
     *
88
     * @return Generator|null A Generator object used for parallel execution.
89
     */
90
    private function processAdvice(array $adviceType): ?Generator
91
    {
92
        $start = microtime(true);
93
        $managedCache = $this->getDI()->getShared(ManagedCacheProvider::SERVICE_NAME);
94
        $currentAdviceClass = $adviceType['type'];
95
        $cacheKey = self::getCacheKey($currentAdviceClass);
96
        if (!$managedCache->has($cacheKey)) {
97
            // No cache - generate advice and store in cache
98
            try {
99
                $checkObj = new $currentAdviceClass();
100
                $newAdvice = $checkObj->process();
101
                $managedCache->set($cacheKey, $newAdvice, $adviceType['cacheTime']);
102
            } catch (\Throwable $e) {
103
                CriticalErrorsHandler::handleExceptionWithSyslog($e);
104
            }
105
            $timeElapsedSecs = round(microtime(true) - $start, 2);
106
            if ($timeElapsedSecs > 5) {
107
                SystemMessages::sysLogMsg(
108
                    __METHOD__,
109
                    "WARNING: Service WorkerPrepareAdvice:{$adviceType['type']} processed more than {$timeElapsedSecs} seconds",
110
                    LOG_WARNING
111
                );
112
            }
113
        }
114
        // Yield to allow parallel execution to continue
115
        yield;
116
    }
117
118
    /**
119
     * Prepares redis cache key for advice type
120
     * @param string $currentAdviceType current advice type
121
     * @return string cache key
122
     */
123
    public static function getCacheKey(string $currentAdviceType): string
124
    {
125
        return 'WorkerPrepareAdvice:' . $currentAdviceType;
126
    }
127
128
    /**
129
     * Cleanup cache for all advice types after change dependent models and PBX settings
130
     * on the WorkerModelsEvents worker.
131
     * @return void
132
     */
133
    public static function afterChangePBXSettings(): void
134
    {
135
        $di = Di::getDefault();
136
        $managedCache = $di->getShared(ManagedCacheProvider::SERVICE_NAME);
137
        foreach (self::ARR_ADVICE_TYPES as $adviceType) {
138
            if (array_key_exists('dependent', $adviceType) and $adviceType['dependent'] === PbxSettings::class) {
139
                $cacheKey = self::getCacheKey($adviceType['type']);
140
                $managedCache->delete($cacheKey);
141
            }
142
        }
143
        Processes::processPHPWorker(WorkerPrepareAdvice::class);
144
    }
145
146
    /**
147
     * Cleanup cache for all advice types after change SSH external configuration
148
     * @return void
149
     */
150
    public static function afterChangeSSHConf(): void
151
    {
152
        $di = Di::getDefault();
153
        $managedCache = $di->getShared(ManagedCacheProvider::SERVICE_NAME);
154
        $cacheKey = self::getCacheKey(CheckSSHConfig::class);
155
        $managedCache->delete($cacheKey);
156
    }
157
}
158
159
// Start worker process
160
WorkerPrepareAdvice::startWorker($argv ?? []);