Passed
Push — feature/0.7.0 ( 0c7d59...ae5b22 )
by Ryuichi
78:01 queued 33:04
created

CoreModel::__execute()   D

Complexity

Conditions 34
Paths 36

Size

Total Lines 135
Code Lines 88

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 34
eloc 88
nc 36
nop 3
dl 0
loc 135
rs 4.3215
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace WebStream\Core;
3
4
use WebStream\DI\Injector;
5
use WebStream\Container\Container;
6
use WebStream\Module\Utility\CommonUtils;
7
use WebStream\Module\Utility\ApplicationUtils;
8
use WebStream\Annotation\Filter;
0 ignored issues
show
Bug introduced by
The type WebStream\Annotation\Filter 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...
9
use WebStream\Annotation\Base\IAnnotatable;
10
use WebStream\Database\DatabaseManager;
11
use WebStream\Database\Result;
12
use WebStream\Exception\Extend\DatabaseException;
13
use WebStream\Exception\Extend\MethodNotFoundException;
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 array<AnnotationContainer> クエリアノテーションリスト
38
     */
39
    private $queryAnnotations;
40
41
    /**
42
     * @var boolean オートコミットフラグ
43
     */
44
    private $isAutoCommit;
45
46
    /**
47
     * @var array<mixed> カスタムアノテーション
48
     */
49
    protected $annotation;
50
51
    /**
52
     * @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...
53
     */
54
    protected $logger;
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    public function __destruct()
60
    {
61
        $this->logger->debug("Model end.");
62
        $this->__clear();
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     * @Filter(type="initialize")
68
     */
69
    public function __initialize(Container $container)
70
    {
71
        if ($container->connectionContainerList === null) {
0 ignored issues
show
Bug Best Practice introduced by
The property connectionContainerList does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
72
            $this->logger->warn("Can't use database in Model Layer.");
73
74
            return;
75
        }
76
77
        $this->queryAnnotations = $container->queryAnnotations;
0 ignored issues
show
Bug Best Practice introduced by
The property queryAnnotations does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
Documentation Bug introduced by
It seems like $container->queryAnnotations can also be of type string. However, the property $queryAnnotations is declared as type WebStream\Core\AnnotationContainer[]. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
78
        $container->logger = $this->logger;
0 ignored issues
show
Bug Best Practice introduced by
The property logger does not exist on WebStream\Container\Container. Since you implemented __set, consider adding a @property annotation.
Loading history...
79
        $this->manager = new DatabaseManager($container);
80
        $this->isAutoCommit = true;
81
        $this->container = $container;
82
    }
83
84
    /**
85
    * {@inheritdoc}
86
     */
87
    public function __customAnnotation(array $annotation)
88
    {
89
        $this->annotation = $annotation;
90
    }
91
92
    /**
93
     * method missing
94
     * @param string メソッド名
95
     * @param array sql/bindパラメータ
96
     */
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...
97
    final public function __call($method, $arguments)
98
    {
99
        // DBコネクションが取得できなければエラー
100
        $filepath = debug_backtrace()[0]["file"];
101
        if (!$this->manager->loadConnection($filepath)) {
102
            throw new MethodNotFoundException("Undefined method called: $method");
103
        }
104
105
        if ($this->manager->isConnected() === false) {
106
            $this->manager->connect();
107
        }
108
109
        $result = $this->__execute($method, $arguments, $filepath);
110
111
        if ($this->isAutoCommit) {
112
            if (is_int($result) && $result > 0) {
113
                $this->manager->commit();
114
            }
115
            $this->manager->disconnect();
116
        }
117
118
        return $result;
119
    }
120
121
    /**
122
     * DB処理を実行する
123
     * @param string メソッド名
124
     * @param array sql/bindパラメータ
125
     * @param string 現在実行中のクラスのファイルパス
126
     */
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...
127
    final public function __execute($method, $arguments, $filepath)
128
    {
129
        $result = null;
130
131
        try {
132
            if (preg_match('/^(?:select|(?:dele|upda)te|insert)$/', $method)) {
133
                $sql = $arguments[0];
134
                $bind = null;
135
                if (array_key_exists(1, $arguments)) {
136
                    $bind = $arguments[1];
137
                }
138
139
                if (is_string($sql)) {
140
                    if ($method !== 'select' && $this->isAutoCommit) {
141
                        $this->manager->beginTransaction(Connection::TRANSACTION_READ_COMMITTED);
142
                    }
143
                    if (is_array($bind)) {
144
                        $result = $this->manager->query($sql, $bind)->{$method}();
145
                    } else {
146
                        $result = $this->manager->query($sql)->{$method}();
147
                    }
148
                } else {
149
                    throw new DatabaseException("Invalid SQL or bind parameters: " . $sql .", " . strval($bind));
150
                }
151
            } else {
152
                $bind = null;
153
                if (array_key_exists(0, $arguments)) {
154
                    $bind = $arguments[0];
155
                }
156
157
                $trace = debug_backtrace();
158
                $modelMethod = null;
159
                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...
160
                    if ($this->inArray($trace[$i]["function"], ["__call", "__execute"])) {
161
                        continue;
162
                    }
163
164
                    if ($trace[$i]["function"] !== null) {
165
                        $modelMethod = $trace[$i]["function"];
166
                        break;
167
                    }
168
                }
169
170
                $namespace = substr($this->getNamespace($filepath), 1);
171
                $queryKey = $namespace . "\\" . basename($filepath, ".php") . "#" . $modelMethod;
172
173
                $query = null;
174
                foreach ($this->queryAnnotations as $queryAnnotation) {
175
                    $queryFunctions = $queryAnnotation->get($queryKey);
176
177
                    if ($queryFunctions === null) {
178
                        continue;
179
                    }
180
181
                    foreach ($queryFunctions as $queryFunction) {
182
                        $xmlObjectList = $queryFunction->fetch();
183
                        foreach ($xmlObjectList as $xmlObject) {
184
                            if ($xmlObject !== null) {
185
                                $xmlElement = $xmlObject->xpath("//mapper[@namespace='$namespace']/*[@id='$method']");
186
187
                                if (!empty($xmlElement)) {
188
                                    $query = ["sql" => trim($xmlElement[0]->__toString()), "method" => $xmlElement[0]->getName()];
189
                                    $entity = $xmlElement[0]->attributes()["entity"];
190
                                    $query["entity"] = $entity !== null ? $entity->__toString() : null;
191
                                    break;
192
                                }
193
                            }
194
                        }
195
                    }
196
                }
197
198
                if ($query === null) {
199
                    throw new DatabaseException("SQL statement can't getting from xml file: " . $modelMethod);
200
                }
201
202
                $sql = $query["sql"];
203
                $method = $query["method"];
204
                $entityClassPath = $query["entity"];
205
206
                if ($entityClassPath !== null) {
207
                    if (!class_exists($entityClassPath)) {
208
                        throw new DatabaseException("Entity classpath is not found: " . $entityClassPath);
209
                    }
210
211
                    switch ($method) {
212
                        case "select":
213
                            if (is_string($sql)) {
214
                                if (is_array($bind)) {
215
                                    $result = $this->manager->query($sql, $bind)->select()->toEntity($entityClassPath);
216
                                } else {
217
                                    $result = $this->manager->query($sql)->select()->toEntity($entityClassPath);
218
                                }
219
                            } else {
220
                                $errorMessage = "Invalid SQL or bind parameters: " . $sql;
221
                                if (is_array($bind)) {
222
                                    $errorMessage .= ", " . strval($bind);
223
                                }
224
225
                                throw new DatabaseException($errorMessage);
226
                            }
227
228
                            break;
229
                        case "insert":
230
                        case "update":
231
                        case "delete":
232
                            // Not implement
233
                            throw new DatabaseException("Entity mapping is select only.");
234
                    }
235
                } else {
236
                    if (is_string($sql)) {
237
                        if ($method !== 'select' && $this->isAutoCommit) {
238
                            $this->manager->beginTransaction(Connection::TRANSACTION_READ_COMMITTED);
239
                        }
240
                        if (is_array($bind)) {
241
                            $result = $this->manager->query($sql, $bind)->{$method}();
242
                        } else {
243
                            $result = $this->manager->query($sql)->{$method}();
244
                        }
245
                    } else {
246
                        $errorMessage = "Invalid SQL or bind parameters: " . $sql;
247
                        if (is_array($bind)) {
248
                            $errorMessage .= ", " . strval($bind);
249
                        }
250
251
                        throw new DatabaseException($errorMessage);
252
                    }
253
                }
254
            }
255
        } catch (DatabaseException $e) {
256
            $this->manager->rollback();
257
            $this->manager->disconnect();
258
            throw $e;
259
        }
260
261
        return $result;
262
    }
263
264
    /**
265
     * トランザクション開始
266
     * @param int $isolationLevel トランザクション分離レベル
267
     */
268
    final public function beginTransaction(int $isolationLevel = Connection::TRANSACTION_READ_COMMITTED)
269
    {
270
        $filepath = debug_backtrace()[0]["file"];
271
        if (!$this->manager->loadConnection($filepath)) {
272
            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...
273
        }
274
275
        if ($this->manager->isConnected() === false) {
276
            $this->manager->connect();
277
        }
278
279
        $this->manager->beginTransaction($isolationLevel);
280
        $this->isAutoCommit = false;
281
    }
282
283
    /**
284
     * コミットする
285
     */
286
    final public function commit()
287
    {
288
        if ($this->isAutoCommit === false) {
289
            $this->manager->commit();
290
        }
291
    }
292
293
    /**
294
     * ロールバックする
295
     */
296
    final public function rollback()
297
    {
298
        $this->manager->rollback();
299
    }
300
}
301