Passed
Push — develop ( be2703...3032cd )
by Nikolay
04:21
created

WorkerPrepareAdvices::processAdvice()   A

Complexity

Conditions 4
Paths 9

Size

Total Lines 28
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 20
c 1
b 0
f 0
dl 0
loc 28
rs 9.6
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 MikoPBX\Common\Models\PbxSettings;
23
use MikoPBX\Common\Providers\ManagedCacheProvider;
24
use MikoPBX\Core\System\Notifications;
25
use MikoPBX\Core\System\Processes;
26
use MikoPBX\Core\System\Util;
27
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckConnection;
28
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckCorruptedFiles;
29
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckFirewalls;
30
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckPasswords;
31
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckSSHConfig;
32
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckStorage;
33
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckUpdates;
34
use Phalcon\Di;
35
use Recoil\React\ReactKernel;
36
use Throwable;
37
use Generator;
38
39
require_once 'Globals.php';
40
41
42
/**
43
 * WorkerPrepareAdvices is a worker class responsible for prepare system advices.
44
 *
45
 * @package MikoPBX\Core\Workers
46
 */
47
class WorkerPrepareAdvices 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 advices
60
    public array $messages;
61
62
    /**
63
     * Starts processing advice types.
64
     *
65
     * @param array $params An array of parameters for processing.
66
     *
67
     * @throws Throwable
68
     */
69
    public function start(array $params): 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
                global $errorLogger;
104
                $errorLogger->captureException($e);
105
                Util::sysLogMsg(__METHOD__, $e->getMessage(), LOG_ERR);
106
            }
107
            $timeElapsedSecs = round(microtime(true) - $start, 2);
108
            if ($timeElapsedSecs > 5) {
109
                Util::sysLogMsg(
110
                    __METHOD__,
111
                    "WARNING: Service WorkerPrepareAdvices:{$adviceType['type']} processed more than {$timeElapsedSecs} seconds",
112
                    LOG_WARNING
113
                );
114
            }
115
        }
116
        // Yield to allow parallel execution to continue
117
        yield;
118
    }
119
120
    /**
121
     * Prepares redis cache key for advice type
122
     * @param string $currentAdviceType current advice type
123
     * @return string cache key
124
     */
125
    public static function getCacheKey(string $currentAdviceType): string
126
    {
127
        return 'WorkerPrepareAdvices:' . $currentAdviceType;
128
    }
129
130
    /**
131
     * Cleanup cache for all advice types after change dependent models and PBX settings
132
     * on the WorkerModelsEvents worker.
133
     * @return void
134
     */
135
    public static function afterChangePBXSettings(): void
136
    {
137
        $di = Di::getDefault();
138
        $managedCache = $di->getShared(ManagedCacheProvider::SERVICE_NAME);
139
        foreach (self::ARR_ADVICE_TYPES as $adviceType) {
140
            if (array_key_exists('dependent', $adviceType) and $adviceType['dependent'] === PbxSettings::class) {
141
                $cacheKey = self::getCacheKey($adviceType['type']);
142
                $managedCache->delete($cacheKey);
143
            }
144
        }
145
        Processes::processPHPWorker(WorkerPrepareAdvices::class);
146
    }
147
148
    /**
149
     * Cleanup cache for all advice types after change SSH external configuration
150
     * @return void
151
     */
152
    public static function afterChangeSSHConf(): void
153
    {
154
        $di = Di::getDefault();
155
        $managedCache = $di->getShared(ManagedCacheProvider::SERVICE_NAME);
156
        $cacheKey = self::getCacheKey(CheckSSHConfig::class);
157
        $managedCache->delete($cacheKey);
158
    }
159
}
160
161
// Start worker process
162
WorkerPrepareAdvices::startWorker($argv ?? []);