Passed
Branch master (b2f909)
by Andreas
04:01
created

connection   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 215
Duplicated Lines 0 %

Test Coverage

Coverage 78.05%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 86
c 4
b 0
f 0
dl 0
loc 215
ccs 64
cts 82
cp 0.7805
rs 10
wmc 28

12 Methods

Rating   Name   Duplication   Size   Complexity  
A set_user() 0 6 2
A get_user() 0 3 1
A get_em() 0 6 2
A __construct() 0 4 1
A get_parameter() 0 6 2
A log() 0 17 5
A invalidate_cache() 0 4 2
A generate_guid() 0 3 1
A initialize() 0 18 2
A set_autostart() 0 3 1
A get_fqcn() 0 6 2
B startup() 0 45 7
1
<?php
2
/**
3
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
4
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
6
 */
7
8
namespace midgard\portable\storage;
9
10
use midgard\portable\driver;
11
use midgard\portable\classgenerator;
12
use midgard\portable\api\user;
13
use midgard\portable\api\config;
14
use midgard\portable\api\error\exception;
15
use midgard\portable\storage\type\datetime;
16
use Doctrine\ORM\EntityManager;
17
use Doctrine\DBAL\Types\Type;
18
use Monolog\Logger;
19
use Monolog\Handler\StreamHandler;
20
use midgard_connection;
21
use midgard\portable\storage\filter\softdelete;
22
use midgard\portable\mapping\factory;
23
use Doctrine\DBAL\Types\Types;
24
25
class connection
26
{
27
    /**
28
     * @var Logger
29
     */
30
    private static $logger;
31
32
    /**
33
     * Loglevel translation table.
34
     *
35
     * The semantics of info and notice/message are unfortunately reversed between Monolog
36
     * and Midgard, so it looks a bit confusing..
37
     *
38
     * @var array
39
     */
40
    private static $loglevels = [
41
        'error' => Logger::ERROR,
42
        'warn' => Logger::WARNING,
43
        'warning' => Logger::WARNING,
44
        'info' => Logger::NOTICE,
45
        'message' => Logger::INFO,
46
        'debug' => Logger::DEBUG
47
    ];
48
49
    /**
50
     * Flag for automatically starting up during initialize
51
     *
52
     * @var boolean
53
     */
54
    private static $autostart = true;
55
56
    /**
57
     * Initialization parameters
58
     *
59
     * @param array
60
     */
61
    private static $parameters = [];
62
63
    private $user;
64
65
    private $namespace;
66
67
    /**
68
     * @var \Doctrine\ORM\EntityManager
69
     */
70
    protected $em;
71
72
    /**
73
     * @var \midgard\portable\storage\connection
74
     */
75
    protected static $instance;
76
77 9
    public function __construct(EntityManager $em, string $namespace)
78
    {
79 9
        $this->em = $em;
80 9
        $this->namespace = $namespace;
81
    }
82
83 167
    public static function get_em() : EntityManager
84
    {
85 167
        if (self::$instance === null) {
86
            throw new \Exception('Not initialized');
87
        }
88 167
        return self::$instance->em;
89
    }
90
91 101
    public static function get_user() : ?user
92
    {
93 101
        return self::$instance->user;
94
    }
95
96 8
    public static function set_user(user $user = null)
97
    {
98 8
        if (self::$instance === null) {
99
            throw new \Exception('Not initialized');
100
        }
101 8
        self::$instance->user = $user;
102
    }
103
104 171
    public static function get_fqcn(string $classname) : string
105
    {
106 171
        if (self::$instance->namespace) {
107 171
            return self::$instance->namespace . '\\' . $classname;
108
        }
109
        return $classname;
110
    }
111
112
    /**
113
     * Generate a new GUID
114
     */
115 117
    public static function generate_guid() : string
116
    {
117 117
        return bin2hex(random_bytes(16));
118
    }
119
120
    /**
121
     * Invalidate ClassMetadata cache
122
     */
123
    public static function invalidate_cache()
124
    {
125
        if ($cache = self::get_em()->getConfiguration()->getMetadataCache()) {
126
            $cache->clear();
127
        }
128
    }
129
130
    /**
131
     * Toggle autostart
132
     */
133
    public static function set_autostart(bool $autostart)
134
    {
135
        self::$autostart = $autostart;
136
    }
137
138
    /**
139
     * Initialize Midgard connection
140
     */
141 9
    public static function initialize(driver $driver, array $db_config, bool $dev_mode = false)
142
    {
143 9
        $vardir = $driver->get_vardir();
144
145 9
        $mgd_config = new config;
146 9
        $mgd_config->vardir = $vardir;
147 9
        $mgd_config->cachedir = $vardir . '/cache';
148 9
        $mgd_config->blobdir = $vardir . '/blobs';
149 9
        $mgd_config->sharedir = $vardir . '/schemas';
150 9
        $mgd_config->logfilename = $vardir . '/log/midgard-portable.log';
151
        // TODO: Set rest of config values from $config and $driver
152
153
        // we open the config before startup to have logfile available
154 9
        midgard_connection::get_instance()->open_config($mgd_config);
155
156 9
        self::$parameters = ['driver' => $driver, 'db_config' => $db_config, 'dev_mode' => $dev_mode];
157 9
        if (self::$autostart) {
158 9
            static::startup();
159
        }
160
    }
161
162
    public static function get_parameter(string $name)
163
    {
164
        if (!array_key_exists($name, self::$parameters)) {
165
            throw new \RuntimeException('Parameter "' . $name . '" is not available');
166
        }
167
        return self::$parameters[$name];
168
    }
169
170
    /**
171
     * Start the API emulation layer
172
     */
173 9
    public static function startup()
174
    {
175 9
        if (empty(self::$parameters)) {
176
            throw new \RuntimeException('Not initialized');
177
        }
178 9
        $driver = self::$parameters['driver'];
179 9
        $db_config = self::$parameters['db_config'];
180 9
        $dev_mode = self::$parameters['dev_mode'];
181 9
        $vardir = $driver->get_vardir();
182
        // generate and include entities file if its a fresh namespace
183
        // otherwise it should be included already
184 9
        if ($driver->is_fresh_namespace()) {
185 9
            $entityfile = $vardir . '/mgdschema_classes.php';
186 9
            if ($dev_mode || !file_exists($entityfile)) {
187 9
                $classgenerator = new classgenerator($driver->get_manager(), $entityfile, $dev_mode);
188 9
                $classgenerator->write($driver->get_namespace());
189
            }
190 9
            require $entityfile;
191
        }
192
193 9
        $config = \Doctrine\ORM\Tools\Setup::createConfiguration($dev_mode, $vardir . '/cache');
194 9
        $config->addFilter('softdelete', softdelete::class);
195 9
        $config->setMetadataDriverImpl($driver);
196 9
        $config->setClassMetadataFactoryName(factory::class);
197
198 9
        if (!array_key_exists('charset', $db_config)) {
199 9
            $db_config['charset'] = 'utf8';
200
        }
201
202 9
        $em = \Doctrine\ORM\EntityManager::create($db_config, $config);
203 9
        $em->getFilters()->enable('softdelete');
204 9
        $em->getEventManager()->addEventSubscriber(new subscriber);
205
206 9
        Type::overrideType(Types::DATETIME_MUTABLE, datetime::class);
207
208 9
        $midgard = midgard_connection::get_instance();
209 9
        $level = self::$loglevels[$midgard->get_loglevel()];
210 9
        if ($level === Logger::DEBUG) {
211
            $logger = new Logger('doctrine');
212
            $logger->pushHandler(new StreamHandler($midgard->config->logfilename, $level));
213
214
            $em->getConnection()->getConfiguration()->setSQLLogger(new sqllogger($logger));
215
        }
216
217 9
        self::$instance = new static($em, $driver->get_namespace());
218
    }
219
220
    /**
221
     * Get Logger instance
222
     */
223 3
    public static function log() : Logger
224
    {
225 3
        if (self::$logger === null) {
226 1
            $midgard = midgard_connection::get_instance();
227 1
            if ($midgard->config->logfilename) {
228 1
                $logdir = dirname($midgard->config->logfilename);
229 1
                if (   !is_dir($logdir)
230 1
                    && !mkdir($logdir, 0777, true)) {
231
                    throw exception::user_data('Log directory could not be created');
232
                }
233 1
                self::$logger = new Logger('midgard-portable');
234 1
                self::$logger->pushHandler(new StreamHandler($midgard->config->logfilename, self::$loglevels[$midgard->get_loglevel()]));
235
            } else {
236
                throw exception::user_data('log filename not set in config');
237
            }
238
        }
239 3
        return self::$logger;
240
    }
241
}
242