Passed
Push — develop ( 9d129a...447c71 )
by Nikolay
05:05 queued 13s
created

recreateModulesDBConnections()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright (C) 2017-2020 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
declare(strict_types=1);
21
22
namespace MikoPBX\Common\Providers;
23
24
25
use MikoPBX\Core\System\Processes;
26
use MikoPBX\Core\System\Util;
27
use Phalcon\Di;
28
use Phalcon\Di\DiInterface;
29
use Phalcon\Di\ServiceProviderInterface;
30
use Phalcon\Events\Manager;
31
use phpDocumentor\Reflection\DocBlock\Tags\See;
32
use ReflectionClass;
33
use ReflectionException;
34
35
36
/**
37
 * Class ModulesDBConnectionsProvider
38
 * Add to DI modules DB as sqlite3 connections
39
 *
40
 * @package Modules
41
 */
42
class ModulesDBConnectionsProvider extends DatabaseProviderBase implements ServiceProviderInterface
43
{
44
    public const SERVICE_NAME = '';
45
    /**
46
     * DiServicesInstall constructor
47
     *
48
     * @param $di DiInterface link to app dependency injector
49
     *
50
     */
51
    public function register(DiInterface $di): void
52
    {
53
        $registeredDBServices = [];
54
        $config               = $di->getShared('config');
55
        $modulesDir           = $config->path('core.modulesDir');
56
57
        $results = glob($modulesDir . '/*/module.json', GLOB_NOSORT);
58
59
        foreach ($results as $moduleJson) {
60
            $jsonString            = file_get_contents($moduleJson);
61
            if ($jsonString === false){
62
                continue;
63
            }
64
            $jsonModuleDescription = json_decode($jsonString, true);
65
            if ( ! is_array($jsonModuleDescription)
66
                || !array_key_exists('moduleUniqueID', $jsonModuleDescription)) {
67
                continue;
68
            }
69
70
            $moduleUniqueId = $jsonModuleDescription['moduleUniqueID'];
71
            if ( ! isset($moduleUniqueId)) {
72
                continue;
73
            }
74
75
            $modelsFiles = glob("{$modulesDir}/{$moduleUniqueId}/Models/*.php", GLOB_NOSORT);
76
            foreach ($modelsFiles as $file) {
77
                $className        = pathinfo($file)['filename'];
78
                $moduleModelClass = "\\Modules\\{$moduleUniqueId}\\Models\\{$className}";
79
80
                // Test whether this class abstract or not
81
                try {
82
                    $reflection = new ReflectionClass($moduleModelClass);
83
                    if ($reflection->isAbstract()) {
84
                        continue;
85
                    }
86
                } catch (ReflectionException $exception) {
87
                    continue;
88
                }
89
90
                if (
91
                    ! class_exists($moduleModelClass)
92
                    || count(get_class_vars($moduleModelClass)) === 0) {
93
                    continue;
94
                }
95
96
                $model                 = new $moduleModelClass();
97
                $connectionServiceName = $model->getReadConnectionService();
98
                if ( ! isset($connectionServiceName)) {
99
                    continue;
100
                }
101
                $registeredDBServices[] = $connectionServiceName;
102
                if ($di->has($connectionServiceName)) {
103
                    $di->remove($connectionServiceName);
104
                }
105
106
                // Create and connect database
107
                $dbDir = "{$config->path('core.modulesDir')}/{$moduleUniqueId}/db";
108
                if (!file_exists($dbDir)){
109
                    Util::mwMkdir($dbDir, true);
110
                }
111
                $dbFileName = "{$dbDir}/module.db";
112
                $dbFileExistBeforeAttachToConnection = file_exists($dbFileName);
113
114
                // Log
115
                $logDir = "{$config->path('core.logsDir')}/$moduleUniqueId/db";
116
                $logFileName = "{$logDir}/queries.log";
117
                if (!is_dir($logDir)){
118
                    Util::mwMkdir($logDir, true);
119
                    $touchPath = Util::which('touch');
120
                    Processes::mwExec("{$touchPath} {$logFileName}");
121
                    Util::addRegularWWWRights($logDir);
122
                }
123
124
                $params = [
125
                    "debugMode"    => $config->path('modulesDatabases.debugMode'),
126
                    "adapter"      => "Sqlite",
127
                    "dbfile"       => $dbFileName,
128
                    "debugLogFile" => $logFileName,
129
                ];
130
131
                $this->registerDBService($connectionServiceName, $di, $params);
132
133
                // if database was created, we have to apply rules
134
                if (!$dbFileExistBeforeAttachToConnection){
135
                    Util::addRegularWWWRights($dbDir);
136
                }
137
            }
138
        }
139
140
        // Register transactions events
141
        $mainConnection = $di->getShared('db');
142
143
        $eventsManager = $mainConnection->getEventsManager();
144
        if ($eventsManager === null) {
145
            $eventsManager = new Manager();
146
        }
147
        // Attach all created connections to one transaction manager
148
        $eventsManager->attach(
149
            'db',
150
            function ($event) use ($registeredDBServices, $di) {
151
                switch ($event->getType()) {
152
                    case 'beginTransaction':
153
                    {
154
                        foreach ($registeredDBServices as $service) {
155
                            $di->get($service)->begin();
156
                        }
157
                        break;
158
                    }
159
                    case 'commitTransaction':
160
                    {
161
                        foreach ($registeredDBServices as $service) {
162
                            $di->get($service)->commit();
163
                        }
164
                        break;
165
                    }
166
                    case 'rollbackTransaction':
167
                    {
168
                        foreach ($registeredDBServices as $service) {
169
                            $di->get($service)->rollback();
170
                        }
171
                        break;
172
                    }
173
                    default:
174
                }
175
            }
176
        );
177
        // Назначаем EventsManager экземпляру адаптера базы данных
178
        $mainConnection->setEventsManager($eventsManager);
179
    }
180
181
    /**
182
     * Recreate DB connections after table structure changes for additional modules
183
     */
184
    public static function recreateModulesDBConnections(): void
185
    {
186
        $di = Di::getDefault();
187
        $di->register(new self());
188
    }
189
}
190
191
192