CoreModel::beginTransaction()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 1
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
namespace WebStream\Core;
3
4
use WebStream\Annotation\Attributes\Filter;
5
use WebStream\Annotation\Base\IAnnotatable;
6
use WebStream\Container\Container;
7
use WebStream\Database\DatabaseManager;
8
use WebStream\Database\Result;
9
use WebStream\DI\Injector;
10
use WebStream\Exception\Extend\DatabaseException;
11
use WebStream\Exception\Extend\MethodNotFoundException;
12
use WebStream\Util\CommonUtils;
13
use WebStream\Util\ApplicationUtils;
14
use Doctrine\DBAL\Connection;
0 ignored issues
show
Bug introduced by
The type Doctrine\DBAL\Connection 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...
15
16
/**
17
 * CoreModel
18
 * @author Ryuichi TANAKA.
19
 * @since 2012/09/01
20
 * @version 0.7
21
 */
22
class CoreModel implements CoreInterface, IAnnotatable
23
{
24
    use Injector, CommonUtils, ApplicationUtils;
25
26
    /**
27
     * @var Container コンテナ
28
     */
29
    private $container;
30
31
    /**
32
     * @var DatabaseManager データベースマネージャ
33
     */
34
    private $manager;
35
36
    /**
37
     * @var boolean オートコミットフラグ
38
     */
39
    private $isAutoCommit;
40
41
    /**
42
     * @var array<mixed> カスタムアノテーション
43
     */
44
    protected $annotation;
45
46
    /**
47
     * @var LoggerAdapter ロガー
0 ignored issues
show
Bug introduced by
The type WebStream\Core\LoggerAdapter 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...
48
     */
49
    protected $logger;
50
51
    /**
52
     * destructor
53
     */
54
    public function __destruct()
55
    {
56
        $this->logger->debug("Model end.");
57
        $this->__clear();
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     * @Filter(type="initialize")
63
     */
64
    public function __initialize(Container $container)
65
    {
66
        $this->container = $container;
67
        $this->manager = new DatabaseManager($container);
68
        $this->isAutoCommit = true;
69
    }
70
71
    /**
72
    * {@inheritdoc}
73
     */
74
    public function __customAnnotation(array $annotation)
75
    {
76
        $this->annotation = $annotation;
77
    }
78
79
    /**
80
     * method missing
81
     * @param string メソッド名
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...
82
     * @param array sql/bindパラメータ
83
     */
84
    final public function __call($method, $arguments)
85
    {
86
        // DBコネクションが取得できなければエラー
87
        $filepath = debug_backtrace()[0]["file"];
88
89
        if (!$this->manager->loadConnection($filepath)) {
90
            throw new MethodNotFoundException("Undefined method called: $method");
91
        }
92
93
        if ($this->manager->isConnected() === false) {
94
            $this->manager->connect();
95
        }
96
97
        $result = $this->__execute($method, $arguments, $filepath);
98
99
        if ($this->isAutoCommit) {
100
            if (is_int($result) && $result > 0) {
101
                $this->manager->commit();
102
            }
103
            $this->manager->disconnect();
104
        }
105
106
        return $result;
107
    }
108
109
    /**
110
     * DB処理を実行する
111
     * @param string メソッド名
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...
112
     * @param array sql/bindパラメータ
113
     * @param string 現在実行中のクラスのファイルパス
114
     */
115
    final public function __execute($method, $arguments, $filepath)
116
    {
117
        $result = null;
118
119
        try {
120
121
122
            // TODO xmlのidにselect,update等が指定されると、メソッド実行の判定からのmethod_missingループになるバグ
123
            //
124
            if (preg_match('/^(?:select|(?:dele|upda)te|insert)$/', $method)) {
125
                $sql = $arguments[0];
126
                $bind = null;
127
                if (array_key_exists(1, $arguments)) {
128
                    $bind = $arguments[1];
129
                }
130
131
                if (is_string($sql)) {
132
                    if ($method !== 'select' && $this->isAutoCommit) {
133
                        $this->manager->beginTransaction(Connection::TRANSACTION_READ_COMMITTED);
134
                    }
135
                    if (is_array($bind)) {
136
                        $result = $this->manager->query($sql, $bind)->{$method}();
137
                    } else {
138
                        $result = $this->manager->query($sql)->{$method}();
139
                    }
140
                } else {
141
                    throw new DatabaseException("Invalid SQL or bind parameters: " . $sql .", " . strval($bind));
142
                }
143
            } else {
144
                $bind = null;
145
                if (array_key_exists(0, $arguments)) {
146
                    $bind = $arguments[0];
147
                }
148
149
                $trace = debug_backtrace();
150
                $modelMethod = null;
151
                for ($i = 0; $i < count($trace); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
152
                    if ($this->inArray($trace[$i]["function"], ["__call", "__execute"])) {
153
                        continue;
154
                    }
155
156
                    if ($trace[$i]["function"] !== null) {
157
                        $modelMethod = $trace[$i]["function"];
158
                        break;
159
                    }
160
                }
161
162
                $namespace = substr($this->getNamespace($filepath), 1);
163
                $queryKey = $namespace . "\\" . basename($filepath, ".php") . "#" . $modelMethod;
164
                $xpath = "//mapper[@namespace='$namespace']/*[@id='$method']";
165
166
                $queryInfo = $this->container->queryInfo;
0 ignored issues
show
Bug Best Practice introduced by
The property queryInfo does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
167
                $queryList = $queryInfo($queryKey, $xpath);
168
                $query = array_shift($queryList);
169
170
                if ($query === null) {
171
                    throw new DatabaseException("SQL statement can't getting from xml file: " . $modelMethod);
172
                }
173
174
                $sql = $query["sql"];
175
                $method = $query["method"];
176
                $entityClassPath = $query["entity"];
177
178
                if ($entityClassPath !== null) {
179
                    if (!class_exists($entityClassPath)) {
180
                        throw new DatabaseException("Entity classpath is not found: " . $entityClassPath);
181
                    }
182
183
                    switch ($method) {
184
                        case "select":
185
                            if (is_string($sql)) {
186
                                if (is_array($bind)) {
187
                                    $result = $this->manager->query($sql, $bind)->select()->toEntity($entityClassPath);
188
                                } else {
189
                                    $result = $this->manager->query($sql)->select()->toEntity($entityClassPath);
190
                                }
191
                            } else {
192
                                $errorMessage = "Invalid SQL or bind parameters: " . $sql;
193
                                if (is_array($bind)) {
194
                                    $errorMessage .= ", " . strval($bind);
195
                                }
196
197
                                throw new DatabaseException($errorMessage);
198
                            }
199
200
                            break;
201
                        case "insert":
202
                        case "update":
203
                        case "delete":
204
                            // Not implement
205
                            throw new DatabaseException("Entity mapping is select only.");
206
                    }
207
                } else {
208
                    if (is_string($sql)) {
209
                        if ($method !== 'select' && $this->isAutoCommit) {
210
                            $this->manager->beginTransaction(Connection::TRANSACTION_READ_COMMITTED);
211
                        }
212
                        if (is_array($bind)) {
213
                            $result = $this->manager->query($sql, $bind)->{$method}();
214
                        } else {
215
                            $result = $this->manager->query($sql)->{$method}();
216
                        }
217
                    } else {
218
                        $errorMessage = "Invalid SQL or bind parameters: " . $sql;
219
                        if (is_array($bind)) {
220
                            $errorMessage .= ", " . strval($bind);
221
                        }
222
223
                        throw new DatabaseException($errorMessage);
224
                    }
225
                }
226
            }
227
228
            return $result;
229
        } catch (DatabaseException $e) {
230
            $this->manager->rollback();
231
            $this->manager->disconnect();
232
            throw $e;
233
        }
234
    }
235
236
    /**
237
     * トランザクション開始
238
     * @param int $isolationLevel トランザクション分離レベル
239
     */
240
    final public function beginTransaction(int $isolationLevel = Connection::TRANSACTION_READ_COMMITTED)
241
    {
242
        $filepath = debug_backtrace()[0]["file"];
243
        if (!$this->manager->loadConnection($filepath)) {
244
            throw new MethodNotFoundException("Undefined method called: $method");
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $method seems to be never defined.
Loading history...
245
        }
246
247
        if ($this->manager->isConnected() === false) {
248
            $this->manager->connect();
249
        }
250
251
        $this->manager->beginTransaction($isolationLevel);
252
        $this->isAutoCommit = false;
253
    }
254
255
    /**
256
     * コミットする
257
     */
258
    final public function commit()
259
    {
260
        if ($this->isAutoCommit === false) {
261
            $this->manager->commit();
262
        }
263
    }
264
265
    /**
266
     * ロールバックする
267
     */
268
    final public function rollback()
269
    {
270
        $this->manager->rollback();
271
    }
272
}
273