Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
Select
n/a
0 / 0
n/a
0 / 0
39
n/a
0 / 0
 sanitizeStackTrace
n/a
0 / 0
n/a
0 / 0
1
 createPdoConnection
n/a
0 / 0
n/a
0 / 0
2
 setReadConnection
n/a
0 / 0
n/a
0 / 0
2
 getReadConnection
n/a
0 / 0
n/a
0 / 0
5
 hasReadConnection
n/a
0 / 0
n/a
0 / 0
2
 closeReadConnection
n/a
0 / 0
n/a
0 / 0
1
 setWriteConnection
n/a
0 / 0
n/a
0 / 0
2
 getWriteConnection
n/a
0 / 0
n/a
0 / 0
8
 hasWriteConnection
n/a
0 / 0
n/a
0 / 0
2
 closeWriteConnection
n/a
0 / 0
n/a
0 / 0
1
 setQueryCache
n/a
0 / 0
n/a
0 / 0
1
 setProfiling
n/a
0 / 0
n/a
0 / 0
1
 setCriticalReadSession
n/a
0 / 0
n/a
0 / 0
1
 setTransaction
n/a
0 / 0
n/a
0 / 0
1
 writeRollback
n/a
0 / 0
n/a
0 / 0
3
 writeCommit
n/a
0 / 0
n/a
0 / 0
4
 writeCommitWithStartLock
n/a
0 / 0
n/a
0 / 0
2
1<?php
2
3namespace BO\Zmsbackend\Connection;
4
5use BO\Slim\Helper\Sanitizer;
6
7/**
8 *
9 * @codeCoverageIgnore
10 *
11 * @SuppressWarnings(TooManyFields)
12 * Handle read and write connections
13 */
14class Select
15{
16    /**
17     * @var Bool $enableProfiling
18     */
19    public static $enableProfiling = false;
20
21    /**
22     * @var String $readSourceName PDO connection string
23     */
24    public static $readSourceName = null;
25
26    /**
27     * @var String $writeSourceName PDO connection string
28     */
29    public static $writeSourceName = null;
30
31    /**
32     * @var String $dbname_zms
33     */
34    public static $dbname_zms = 'zmsbo';
35
36    /**
37     * @var String $username Login
38     */
39    public static $username = null;
40
41    /**
42     * @var String $password Credential
43     */
44    public static $password = null;
45
46    /**
47     * @var Array $pdoOptions compatible to the 4th PDO::__construct parameter
48     */
49    public static $pdoOptions = [];
50
51    /**
52     * @var String $connectionTimezone
53     *
54     */
55    public static $connectionTimezone = ' UTC';
56
57    /**
58     * @var Bool $enableWsrepSyncWait
59     */
60    public static $enableWsrepSyncWait = false;
61
62    /**
63     * @var Bool $enableWsrepSyncWait
64     */
65    public static $galeraConnection = false;
66
67    /**
68     * @var Pdo|null $readConnection for read only requests
69     */
70    protected static $readConnection = null;
71
72    /**
73     * @var Pdo|null $writeConnection for write only requests
74     */
75    protected static $writeConnection = null;
76
77    /**
78     * @var object|null $readProfiler for read only requests
79     */
80    protected static $readProfiler = null;
81
82    /**
83     * @var object|null $writeProfiler for write only requests
84     */
85    protected static $writeProfiler = null;
86
87    /**
88     * @var Bool $useTransaction
89     *
90     */
91    protected static $useTransaction = false;
92
93    /**
94     * @var Bool $useProfiling
95     *
96     */
97    protected static $useProfiling = false;
98
99    /**
100     * @var Bool $useQueryCache
101     *
102     */
103    protected static $useQueryCache = true;
104
105    protected static function sanitizeStackTrace($trace)
106    {
107        return Sanitizer::sanitizeStackTrace($trace);
108    }
109
110    /**
111     * Create a PDO compatible object
112     *
113     * @param  String $dataSourceName compatible with PDO
114     */
115    protected static function createPdoConnection($dataSourceName): Pdo
116    {
117        try {
118            $pdoOptions = array_merge([
119                ], self::$pdoOptions);
120            $pdo = new Pdo($dataSourceName, self::$username, self::$password, $pdoOptions);
121            $pdo->exec('SET NAMES "UTF8";');
122            //$timezone = date_default_timezone_get();
123            //$pdo->prepare('SET time_zone = ?;')->execute([$timezone]);
124            $pdo->exec('SET SESSION sql_mode = "STRICT_ALL_TABLES";');
125            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
126        } catch (\Exception $exception) {
127            $sanitizedDsn     = self::sanitizeStackTrace($dataSourceName);
128            $sanitizedMessage = self::sanitizeStackTrace($exception->getMessage());
129            throw new \BO\Zmsbackend\Exception\Pdo\PDOFailed(
130                $sanitizedDsn . ': ' . $sanitizedMessage,
131            );
132        }
133        return $pdo;
134    }
135
136    /**
137     * Set the read connection.
138     * Usually this function is only required to set mockups for testing
139     *
140     * @param PdoInterface $connection
141     */
142    public static function setReadConnection(PdoInterface $connection)
143    {
144        if (!$connection instanceof Pdo) {
145            throw new \InvalidArgumentException('Expected ' . Pdo::class);
146        }
147        self::$readConnection = $connection;
148    }
149
150    /**
151     * Create or return a connection for reading data
152     */
153    public static function getReadConnection(): Pdo
154    {
155        if (null === self::$readConnection) {
156            self::$readConnection = self::createPdoConnection(self::$readSourceName);
157            self::$readProfiler = new \Aura\Sql\Profiler\Profiler();
158            self::$readProfiler->setActive(self::$enableProfiling);
159            self::$readConnection->setProfiler(self::$readProfiler);
160            //self::$readConnection->exec('SET SESSION TRANSACTION READ ONLY');
161            if (!self::$useQueryCache) {
162                try {
163                    self::$readConnection->exec('SET SESSION query_cache_type = 0;');
164                } catch (\Exception $exception) {
165                    // ignore, query cache might be disabled
166                }
167            }
168            if (self::$useProfiling) {
169                self::$readConnection->exec('SET profiling = 1;');
170            }
171        }
172        return self::$readConnection;
173    }
174
175    /**
176     * Test if a read connection is established
177     *
178     */
179    public static function hasReadConnection()
180    {
181        return (null === self::$readConnection) ? false : true;
182    }
183
184    /**
185     * Close a connection for reading data
186     *
187     */
188    public static function closeReadConnection()
189    {
190        self::$readConnection = null;
191    }
192
193    /**
194     * Set the write connection.
195     * Usually this function is only required to set mockups for testing
196     *
197     * @param  PdoInterface $connection
198     * @return self
199     */
200    public static function setWriteConnection(PdoInterface $connection)
201    {
202        if (!$connection instanceof Pdo) {
203            throw new \InvalidArgumentException('Expected ' . Pdo::class);
204        }
205        self::$writeConnection = $connection;
206    }
207
208    /**
209     * Create or return a connection for writing data
210     */
211    public static function getWriteConnection(): Pdo
212    {
213        if (null === self::$writeConnection) {
214            self::$writeConnection = self::createPdoConnection(self::$writeSourceName);
215            self::$writeProfiler = new \Aura\Sql\Profiler\Profiler();
216            self::$writeProfiler->setActive(self::$enableProfiling);
217            self::$writeConnection->setProfiler(self::$writeProfiler);
218            if (self::$useTransaction) {
219                self::$writeConnection->exec('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED');
220                self::$writeConnection->exec('SET SESSION innodb_lock_wait_timeout=15');
221                self::$writeConnection->beginTransaction();
222            }
223            if (!self::$useQueryCache) {
224                try {
225                    self::$writeConnection->exec('SET SESSION query_cache_type = 0;');
226                } catch (\Exception $exception) {
227                    // ignore, query cache might be disabled
228                }
229            }
230            if (self::$useProfiling) {
231                self::$writeConnection->exec('SET profiling = 1;');
232            }
233            if (self::$galeraConnection && self::$enableWsrepSyncWait) {
234                self::$writeConnection->exec(
235                    'SET SESSION wsrep_sync_wait = (
236                        SELECT CAST(value AS INT) FROM config WHERE name = "setting__wsrepsync"
237                    );'
238                );
239            }
240            // On writing, use the same host to avoid racing/transaction conditions
241            self::$readConnection = self::$writeConnection;
242        }
243        return self::$writeConnection;
244    }
245
246    /**
247     * Test if a write connection is established
248     *
249     */
250    public static function hasWriteConnection()
251    {
252        return (null === self::$writeConnection) ? false : true;
253    }
254
255    /**
256     * Close a connection for writing data
257     *
258     */
259    public static function closeWriteConnection()
260    {
261        self::$writeConnection = null;
262    }
263
264    /**
265     * Set query cache
266     *
267     * @param Bool $useQueryCache
268     *
269     */
270    public static function setQueryCache($useQueryCache = true)
271    {
272        static::$useQueryCache = $useQueryCache;
273    }
274
275    /**
276     * Set profiling
277     *
278     * @param Bool $useProfiling
279     *
280     */
281    public static function setProfiling($useProfiling = true)
282    {
283        static::$useProfiling = $useProfiling;
284    }
285
286    /**
287     * Set cluster wide causality checks, needed for critical reads across different nodes
288     * @param Bool $wsrepStatus Set to true for critical reads
289     */
290    public static function setCriticalReadSession($wsrepStatus = true)
291    {
292        static::$enableWsrepSyncWait = $wsrepStatus;
293        static::getWriteConnection();
294    }
295
296    /**
297     * Set transaction
298     *
299     * @param Bool $useTransaction
300     *
301     */
302    public static function setTransaction($useTransaction = true)
303    {
304        static::$useTransaction = $useTransaction;
305    }
306
307    /**
308     * Rollback transaction if started
309     *
310     */
311    public static function writeRollback()
312    {
313        if (self::$useTransaction && self::getWriteConnection()->inTransaction()) {
314            return self::getWriteConnection()->rollBack();
315        }
316        return null;
317    }
318
319    /**
320     * Commit transaction if started
321     *
322     */
323    public static function writeCommit()
324    {
325        if (self::$useTransaction && null !== self::$writeConnection && self::getWriteConnection()->inTransaction()) {
326            $status = self::getWriteConnection()->commit();
327            self::$writeConnection->beginTransaction();
328            return $status;
329        }
330        return null;
331    }
332
333    public static function writeCommitWithStartLock()
334    {
335        return self::writeCommit() && (new \BO\Zmsbackend\Config\Service\Config())->readProperty('status__calculateSlotsLastRun', true);
336    }
337}