Passed
Pull Request — master (#4)
by Ryuichi
59:44 queued 14:45
created

DatabaseManager::isRollback()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
3
namespace WebStream\Database;
4
5
use Closure;
6
use Doctrine\DBAL\TransactionIsolationLevel;
7
use WebStream\Container\Container;
0 ignored issues
show
Bug introduced by
The type WebStream\Container\Container was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use WebStream\Database\Driver\DatabaseDriver;
9
use WebStream\DI\Injector;
0 ignored issues
show
Bug introduced by
The type WebStream\DI\Injector was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use WebStream\Exception\Extend\DatabaseException;
0 ignored issues
show
Bug introduced by
The type WebStream\Exception\Extend\DatabaseException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
12
/**
13
 * DatabaseManager
14
 * @author Ryuichi TANAKA.
15
 * @since 2013/12/07
16
 * @version 0.4
17
 */
18
class DatabaseManager
19
{
20
    use Injector;
21
22
    /**
23
     * @var ConnectionManager コネクションマネージャ
24
     */
25
    private ConnectionManager $connectionManager;
26
27
    /**
28
     * @var DatabaseDriver データベースコネクション
29
     */
30
    private DatabaseDriver $connection;
31
32
    /**
33
     * @var bool 自動コミットフラグ
34
     */
35
    private bool $isAutoCommit;
36
37
    /**
38
     * @var Query クエリオブジェクト
39
     */
40
    private Query $query;
41
42
    /**
43
     * @var Logger ロガー
0 ignored issues
show
Bug introduced by
The type WebStream\Database\Logger was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
44
     */
45
    private $logger;
46
47
    /**
48
     * constructor
49
     * @param Container $container
50 52
     */
51
    public function __construct(Container $container)
52 52
    {
53 52
        $this->connectionManager = new ConnectionManager($container);
54 52
        $this->logger = $container->logger;
55 52
        $this->isAutoCommit = false;
56
    }
57
58
    /**
59
     * destructor
60 52
     */
61
    public function __destruct()
62 52
    {
63 52
        $this->disconnect();
64 52
        unset($this->query);
65
    }
66
67
    /**
68
     * データベース接続する
69
     * すでに接続中であれば再接続はしない
70 52
     */
71
    public function connect()
72
    {
73 52
        try {
74 52
            $this->connection->connect();
75 52
            $this->query = new Query($this->connection);
76
            $this->query->inject('logger', $this->logger);
77
        } catch (\PDOException $e) {
78
            throw new DatabaseException($e);
79 52
        }
80
    }
81
82
    /**
83
     * データベース切断する
84 52
     */
85
    public function disconnect()
86 52
    {
87 52
        if (!isset($this->connection)) {
88
            return;
89
        }
90 52
91
        try {
92
            if ($this->inTransaction()) {
93 16
                // トランザクションが明示的に開始された状態でcommit/rollbackが行われていない場合
94 16
                // ログに警告を書き込み、強制的にロールバックする
95
                $this->connection->rollback();
96
                $this->logger->warn("Not has been executed commit or rollback after the transaction started.");
97 52
            }
98 52
99 52
            $this->connection->disconnect();
100
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
101
        } catch (\Exception $e) {
102
            throw new DatabaseException($e);
103
        }
104
    }
105 16
106
    /**
107
     * トランザクションを開始する
108
     * @param int $isolationLevel トランザクション分離レベル
109 16
     */
110
    public function beginTransaction(int $isolationLevel)
111
    {
112
        // 既にトランザクションが開始されている場合、継続しているトランザクションを有効のままにする
113
        // トランザクションを破棄して再度開始する場合は明示的に破棄してから再呼び出しする
114
        if ($this->inTransaction()) {
115 16
            $this->logger->debug("Transaction already started.");
116
117
            return;
118
        }
119 16
120
        if (!$this->connection->beginTransaction()) {
121
            throw new DatabaseException("Failed to start transaction.");
122 16
        }
123 16
124
        $this->connection->setAutoCommit($this->isAutoCommit);
125 16
126
        if (
127 16
            $isolationLevel === TransactionIsolationLevel::READ_UNCOMMITTED ||
128
            $isolationLevel === TransactionIsolationLevel::READ_COMMITTED ||
129
            $isolationLevel === TransactionIsolationLevel::REPEATABLE_READ ||
130
            $isolationLevel === TransactionIsolationLevel::SERIALIZABLE
131
        ) {
132 16
            $this->connection->setTransactionIsolation($isolationLevel);
133 16
        } else {
134
            throw new DatabaseException("Invalid transaction isolation level: " . $isolationLevel);
135
        }
136
137
        $this->logger->debug("Transaction start.");
138 8
    }
139
140
    /**
141 8
     * コミットする
142 8
     */
143 8
    public function commit()
144 8
    {
145
        try {
146 8
            if ($this->connection !== null) {
147
                if ($this->inTransaction()) {
148
                    $this->connection->commit();
149 8
                    $this->logger->debug("Execute commit.");
150
                } else {
151
                    $this->logger->warn("Not executed commit because the transaction is not started.");
152
                }
153
            } else {
154
                throw new DatabaseException("Can't execute commit.");
155 8
            }
156
        } catch (\Exception $e) {
157
            $this->query = null;
158
            throw new DatabaseException($e);
159
        }
160 8
    }
161
162
    /**
163 8
     * ロールバックする
164 8
     */
165 8
    public function rollback()
166 8
    {
167
        try {
168 8
            if ($this->connection !== null) {
169
                if ($this->inTransaction()) {
170
                    $this->connection->rollback();
171 8
                    $this->logger->debug("Execute rollback.");
172
                } else {
173
                    $this->logger->warn("Not executed rollback because the transaction is not started.");
174
                }
175
            } else {
176
                throw new DatabaseException("Can't execute rollback.");
177 8
            }
178
        } catch (\Exception $e) {
179
            $this->query = null;
180
            throw new DatabaseException($e);
181
        }
182
    }
183
184 8
    /**
185
     * トランザクションスコープを使用する
186 8
     * @param Closure $closure クロージャ
187 4
     * @param array $config
188
     * @return object 処理結果
189 8
     */
190 4
    public function transactional(\Closure $closure, $config = [])
191
    {
192
        if (!array_key_exists('isolationLevel', $config)) {
193 8
            $config['isolationLevel'] = TransactionIsolationLevel::READ_COMMITTED;
194 8
        }
195
        if (!array_key_exists('autoCommit', $config)) {
196 8
            $config['autoCommit'] = false;
197 4
        }
198 4
199 4
        $this->isAutoCommit = $config['autoCommit'];
200
        $this->beginTransaction($config['isolationLevel']);
201
        try {
202 4
            $result = $closure($this);
203 4
            $this->commit();
204 4
            return $result;
205
        } catch (DatabaseException $e) {
206
            $this->rollback();
207
            throw $e;
208
        } catch (\Throwable $e) {
209
            $this->rollback();
210
            throw new DatabaseException($e);
211 16
        }
212
    }
213 16
214 16
    /**
215
     * 自動コミットを有効化
216
     */
217
    public function enableAutoCommit()
218
    {
219 4
        $this->isAutoCommit = true;
220
    }
221 4
222 4
    /**
223
     * 自動コミットを無効化
224
     */
225
    public function disableAutoCommit()
226
    {
227
        $this->isAutoCommit = false;
228
    }
229
230
    /**
231
     * トランザクション内かどうか
232
     * @return bool トランザクション内かどうか
233
     */
234
    public function inTransaction()
235
    {
236
        if ($this->connection->inTransaction()) {
237 52
            return true;
238
        }
239 52
240
        return false;
241
    }
242
243
    /**
244
     * DB接続されているか
245
     * @param boolean 接続有無
0 ignored issues
show
Documentation Bug introduced by
The doc comment 接続有無 at position 0 could not be parsed: Unknown type name '接続有無' at position 0 in 接続有無.
Loading history...
246
     * @return bool
247
     */
248
    public function isConnected()
249
    {
250
        return $this->connection->isConnected();
251
    }
252
253
    /**
254
     * トランザクション分離レベルを返却する
255
     * @return int トランザクション分離レベル
256
     */
257
    public function getTransactionIsolation()
258
    {
259
        return $this->connection->getTransactionIsolation();
260
    }
261
262
    /**
263
     * データベース接続が可能かどうか
264
     * @param string Modelファイルパス
0 ignored issues
show
Documentation Bug introduced by
The doc comment Modelファイルパス at position 0 could not be parsed: Unknown type name 'Modelファイルパス' at position 0 in Modelファイルパス.
Loading history...
265 52
     * @return bool 接続可否
266
     */
267 52
    public function loadConnection($filepath)
268 52
    {
269 52
        $connection = $this->connectionManager->getConnection($filepath);
270
        if ($connection !== null) {
271
            $this->connection = $connection;
272 52
        }
273
274
        return $this->connection !== null;
275
    }
276
277
    /**
278
     * クエリを設定する
279
     * @param string $sql SQL
280 52
     * @param array $bind パラメータ
281
     * @return Query
282 52
     */
283
    public function query(string $sql, array $bind = [])
284
    {
285 52
        if ($this->query === null) {
286 52
            throw new DatabaseException("Query does not set because database connection failed.");
287
        }
288 52
        $this->query->setSql($sql);
289
        $this->query->setBind($bind);
290
291
        return $this->query;
292
    }
293
}
294