Db::inTransaction()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * Db
4
 *
5
 * コンフィグ操作のためのファンクション群
6
 *
7
 * @package           risoluto
8
 * @author            Risoluto Developers
9
 * @license           http://opensource.org/licenses/bsd-license.php new BSD license
10
 * @copyright     (C) 2008-2015 Risoluto Developers / All Rights Reserved.
11
 */
12
13
//------------------------------------------------------//
14
// 名前空間の定義
15
//------------------------------------------------------//
16
namespace Risoluto;
17
18
//------------------------------------------------------//
19
// クラス定義
20
//------------------------------------------------------//
21
class Db
22
{ //------------------------------------------------------//
23
    // クラス変数定義
24
    //------------------------------------------------------//
25
    /**
26
     * $pdo_instance
27
     * @access private
28
     * @var    object    PDOクラスインスタンスを保持
29
     * @instanceof \PDO
30
     */
31
    private $pdo_instance = '';
32
33
    /**
34
     * $pdostatement_instance
35
     * @access private
36
     * @var    object    PDOStatementクラスインスタンスを保持
37
     * @instanceof \PDOStatement
38
     */
39
    private $pdostatement_instance = '';
40
41
    /**
42
     * $dbinfo
43
     * @access private
44
     * @var    array    DB接続に必要な情報
45
     */
46
    private $dbinfo = [ ];
47
48
    //------------------------------------------------------//
49
    // クラスメソッド定義
50
    //------------------------------------------------------//
51
    use RisolutoErrorLogTrait;
52
53
    /**
54
     * connect(array $param)
55
     *
56
     * DBへの接続を開始する
57
     *
58
     * @param     array $param DB接続に必要となる情報を含んだ配列
59
     * @param     array $option DB接続に指定するオプション情報を含んだ配列
60
     *
61
     * @return    boolean 実行結果(true: 成功 / false: 失敗)
62
     *
63
     */
64
    public function connect( array $param, array $option = [ ] )
65
    {
66
        // 戻り値を初期化
67
        $retval = true;
68
69
        // 接続情報をクラス変数へセット
70
        $this->dbinfo = $param;
71
72
        // DSNを生成しクラス変数へセット
73
        if (!array_key_exists( 'dsn', $this->dbinfo ) or empty( $this->dbinfo[ 'dsn' ] )) {
74
            $this->dbinfo[ 'dsn' ] = $this->genDSN();
75
        }
76
77
        // DBへの接続を試みる
78
        try {
79
            if (empty( $option )) {
80
                // オプションが指定されていなければそのまま指定する
81
                $this->pdo_instance = new \PDO(
82
                    $this->dbinfo[ 'dsn' ], $this->dbinfo[ 'user' ], $this->dbinfo[ 'pass' ],
83
                    [
84
                        \PDO::ATTR_PERSISTENT => ( $this->dbinfo[ 'persistent' ] ? true : false ),
85
                        \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
86
                    ]
87
                );
88
            } else {
89
                // オプションが指定されていればそれを指定して接続する
90
                $this->pdo_instance = new \PDO( $this->dbinfo[ 'dsn' ], $this->dbinfo[ 'user' ],
91
                    $this->dbinfo[ 'pass' ],
92
                    $option );
93
            }
94
        } catch ( \PDOException $e ) {
95
            // 接続に失敗したらエラーメッセージを生成
96
            $this->genErrorMsg( 'pdo', $e->getMessage() );
97
            $retval = false;
98
        }
99
100
        return $retval;
101
    }
102
103
    /**
104
     * disConnect($force = false)
105
     *
106
     * DBへの接続を終了する
107
     *
108
     * @param     boolean $force 持続的接続時に切断するか(true: 切断する / false: 切断しない(デフォルト))
109
     *
110
     * @return    boolean 常にtrue
111
     *
112
     */
113
    public function disConnect( $force = false )
114
    {
115
        // 持続的接続が無効か強制切断が有効なときのみ接続を解除
116
        if (!$this->dbinfo[ 'persistent' ] or $force) {
117
            $this->pdo_instance = null;
118
        }
119
120
        return true;
121
    }
122
123
    /**
124
     * getAttribute($attribute)
125
     *
126
     * DB接続に関する属性値を取得する
127
     *
128
     * @param     string $attribute 取得対象となるアトリビュート(PDO::ATTR_*から「PDO::ATTR_*」を除いたもの、または「ALL」)
129
     *
130
     * @return    array 属性値が格納された連想配列
131
     *
132
     */
133
    public function getAttribute( $attribute = 'ALL' )
134
    {
135
        // 接頭語をセット
136
        $prefix = 'PDO::ATTR_';
137
138
        // 引数値をすべて大文字に変更
139
        $attribute = strtoupper( $attribute );
140
141
        // 属性値を取得
142
        switch ($attribute) {
143
            case 'AUTOCOMMIT': // FALL THRU
144
            case 'PREFETCH': // FALL THRU
145
            case 'TIMEOUT': // FALL THRU
146
            case 'ERRMODE': // FALL THRU
147
            case 'SERVER_VERSION': // FALL THRU
148
            case 'CLIENT_VERSION': // FALL THRU
149
            case 'SERVER_INFO': // FALL THRU
150
            case 'CONNECTION_STATUS': // FALL THRU
151
            case 'CASE': // FALL THRU
152
            case 'DRIVER_NAME': // FALL THRU
153
            case 'ORACLE_NULLS': // FALL THRU
154
            case 'PERSISTENT': // FALL THRU
155
            case 'STATEMENT_CLASS': // FALL THRU
156
            case 'DEFAULT_FETCH_MODE': // FALL THRU
157
                if ($this->dbinfo[ 'driver' ] == 'mysql' and ( $attribute == 'PREFETCH' or $attribute == 'TIMEOUT' )) {
158
                    $retval = [ $attribute => 'Not Supported' ];
159
                } else {
160
                    $retval = [
161
                        $attribute => $this->pdo_instance->getAttribute( constant( $prefix . $attribute ) )
162
                    ];
163
                }
164
                break;
165
166
            case 'ALL': // FALL THRU
167
            default:
168
                $retval = [
169
                    'AUTOCOMMIT' => $this->pdo_instance->getAttribute( constant( $prefix . 'AUTOCOMMIT' ) ),
170
                    'PREFETCH' => ( $this->dbinfo[ 'driver' ] == 'mysql' ) ? 'Not Supported' : $this->pdo_instance->getAttribute( constant( $prefix . 'PREFETCH' ) ),
171
                    'TIMEOUT' => ( $this->dbinfo[ 'driver' ] == 'mysql' ) ? 'Not Supported' : $this->pdo_instance->getAttribute( constant( $prefix . 'TIMEOUT' ) ),
172
                    'ERRMODE' => $this->pdo_instance->getAttribute( constant( $prefix . 'ERRMODE' ) ),
173
                    'SERVER_VERSION' => $this->pdo_instance->getAttribute( constant( $prefix . 'SERVER_VERSION' ) ),
174
                    'CLIENT_VERSION' => $this->pdo_instance->getAttribute( constant( $prefix . 'CLIENT_VERSION' ) ),
175
                    'SERVER_INFO' => $this->pdo_instance->getAttribute( constant( $prefix . 'SERVER_INFO' ) ),
176
                    'CONNECTION_STATUS' => $this->pdo_instance->getAttribute( constant( $prefix . 'CONNECTION_STATUS' ) ),
177
                    'CASE' => $this->pdo_instance->getAttribute( constant( $prefix . 'CASE' ) ),
178
                    'DRIVER_NAME' => $this->pdo_instance->getAttribute( constant( $prefix . 'DRIVER_NAME' ) ),
179
                    'ORACLE_NULLS' => $this->pdo_instance->getAttribute( constant( $prefix . 'ORACLE_NULLS' ) ),
180
                    'PERSISTENT' => $this->pdo_instance->getAttribute( constant( $prefix . 'PERSISTENT' ) ),
181
                    'STATEMENT_CLASS' => $this->pdo_instance->getAttribute( constant( $prefix . 'STATEMENT_CLASS' ) ),
182
                    'DEFAULT_FETCH_MODE' => $this->pdo_instance->getAttribute( constant( $prefix . 'DEFAULT_FETCH_MODE' ) ),
183
                ];
184
                break;
185
        }
186
187
        return $retval;
188
    }
189
190
    /**
191
     * setAttribute($attribute, $value)
192
     *
193
     * DB接続に関する属性値をセットする
194
     *
195
     * @param     integer $attribute 設定対象となるアトリビュート
196
     * @param     mixed   $value 設定する値
197
     *
198
     * @return    boolean true:正常終了/false:異常終了
199
     *
200
     */
201
    public function setAttribute( $attribute, $value )
202
    {
203
        if (!empty( $attribute ) and !empty( $value )) {
204
            return $this->pdo_instance->setAttribute( $attribute, $value );
205
        } else {
206
            return false;
207
        }
208
    }
209
210
    /**
211
     * beginTransaction()
212
     *
213
     * トランザクションを開始する
214
     *
215
     * @param     void
216
     *
217
     * @return    boolean true:正常終了/false:異常終了
218
     *
219
     */
220
    public function beginTransaction()
221
    {
222
        return $this->pdo_instance->beginTransaction();
223
    }
224
225
    /**
226
     * inTransaction()
227
     *
228
     * トランザクションが開始しているかを判定する
229
     *
230
     * @param     void
231
     *
232
     * @return    boolean true:トランザクションが開始している/false:トランザクションが開始していない
233
     *
234
     */
235
    public function inTransaction()
236
    {
237
        return $this->pdo_instance->InTransaction();
238
    }
239
240
    /**
241
     * commit()
242
     *
243
     * トランザクションをコミットする
244
     *
245
     * @param     void
246
     *
247
     * @return    boolean true:正常終了/false:異常終了
248
     *
249
     */
250
    public function commit()
251
    {
252
        return $this->pdo_instance->commit();
253
    }
254
255
    /**
256
     * rollBack()
257
     *
258
     * トランザクションをロールバックする
259
     *
260
     * @param     void
261
     *
262
     * @return    boolean true:正常終了/false:異常終了
263
     *
264
     */
265
    public function rollBack()
266
    {
267
        try {
268
            return $this->pdo_instance->rollBack();
269
        } catch ( \PDOException $e ) {
270
            // ロールバックに失敗したらエラーメッセージを生成
271
            $this->genErrorMsg( 'pdo', $e->getMessage() );
272
273
            return false;
274
        }
275
    }
276
277
    /**
278
     * lastInsertId($name)
279
     *
280
     * 最後に挿入されたID値を取得する
281
     *
282
     * @param     string $name 取得対象となるID値のカラム名
283
     *
284
     * @return    string 取得したID値
285
     *
286
     */
287
    public function lastInsertId( $name = null )
288
    {
289
        try {
290
            return $this->pdo_instance->lastInsertId( $name );
291
        } catch ( \PDOException $e ) {
292
            // 取得に失敗したらエラーメッセージを生成
293
            $this->genErrorMsg( 'pdo', $e->getMessage() );
294
295
            return false;
0 ignored issues
show
Bug Best Practice introduced by Yuta Hayakawa
The return type of return false; (false) is incompatible with the return type documented by Risoluto\Db::lastInsertId of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
296
        }
297
    }
298
299
    /**
300
     * exec($sql)
301
     *
302
     * SQLを実行する
303
     *
304
     * @param     string $sql 実行するSQL
305
     *
306
     * @return    boolean true:正常終了/false:異常終了
307
     *
308
     */
309
    public function exec( $sql )
310
    {
311
        if (!empty( $sql )) {
312
            return ( $this->pdo_instance->exec( $sql ) === false ? false : true );
313
        } else {
314
            return false;
315
        }
316
    }
317
318
    /**
319
     * doQuery($sql = '', array $param = [], array $query_options = [], $fetch_style = \PDO::FETCH_ASSOC)
320
     *
321
     * SQLを実行する
322
     *
323
     * @param     string  $sql 実行するSQL("clear"が指定された場合はPDOStatementのインスタンスをクリア)
324
     * @param     array   $param PDOStatement::bindParam()へ渡すバインドする値(array(id, value, type[, length]))
325
     * @param     array   $query_options PDO::prepare()へ渡すオプション
326
     * @param     integer $fetch_style PDOStatement::fetchAll()へ渡すオプション(\PDO::FETCH_**のいずれか)
327
     *
328
     * @return    mixed   SQLの実行結果/false:異常終了
329
     *
330
     */
331
    public function doQuery(
332
        $sql = '',
333
        array $param = [ ],
334
        array $query_options = [ ],
335
        $fetch_style = \PDO::FETCH_ASSOC
336
    ) {
337
        // SQLの前後についている余分な空白を排除
338
        $sql = trim( $sql );
339
340
        // SQLを判定し、SELECTだったときのみデータ取得モードにする
341
        $tmp_mode = false;
342
        if (!empty( $sql ) AND preg_match( '/^SELECT/i', $sql )) {
343
            $tmp_mode = true;
344
        } elseif (
345
            empty( $sql ) AND
346
            !empty( $this->pdostatement_instance->queryString ) AND
347
            preg_match( '/^SELECT/i', $this->pdostatement_instance->queryString )
348
        ) {
349
            $tmp_mode = true;
350
        }
351
352
        // SQLが渡されたときはPDOStatementのインスタンスを更新する(既存のインスタンスがなくSQL未指定の場合はfalseを返す)
353
        try {
354
            if (strtolower( $sql ) == 'clear') {
355
                $this->pdostatement_instance = null;
356
            } elseif (!empty( $sql )) {
357
                $this->pdostatement_instance = null;
358
                $this->pdostatement_instance = $this->pdo_instance->prepare( $sql, $query_options );
359
            }
360
361
            // PDOStatementクラスのインスタンスが生成済みの時だけSQLを実行
362
            if (!empty( $this->pdostatement_instance )) {
363
                // パラメタが指定されている時はバインドする
364
                if (!empty( $param ) and is_array( $param )) {
365
                    foreach ($param as $dat) {
366
                        // lengthがセットされているかどうかでbindParam()のコールを変更する
367
                        if (isset( $dat[ 'length' ] ) and !empty( $dat[ 'length' ] )) {
368
                            /** @noinspection PhpUndefinedMethodInspection */
369
                            $this->pdostatement_instance->bindParam( $dat[ 'id' ], $dat[ 'value' ], $dat[ 'type' ],
370
                                $dat[ 'length' ] );
371
                        } else {
372
                            /** @noinspection PhpUndefinedMethodInspection */
373
                            $this->pdostatement_instance->bindParam( $dat[ 'id' ], $dat[ 'value' ], $dat[ 'type' ] );
374
                        }
375
                    }
376
                }
377
378
                // SQLを実行し結果を取得する
379
                /** @noinspection PhpUndefinedMethodInspection */
380
                $retval_execute = $this->pdostatement_instance->execute();
381
                // データ取得モードの時のみfetchする
382
                if ($tmp_mode) {
383
                    /** @noinspection PhpUndefinedMethodInspection */
384
                    $retval_fetch = ( ( $fetch_dat = $this->pdostatement_instance->fetchAll( $fetch_style ) ) === false ? false : true );
385
                } else {
386
                    $retval_fetch = true;
387
                    $fetch_dat = true;
388
                }
389
                $retval = ( ( $retval_execute and $retval_fetch ) ? $fetch_dat : false );
390
391
                return $retval;
392
            } else {
393
                $retval = false;
394
            }
395
        } catch ( \PDOException $e ) {
396
            // 取得に失敗したらエラーメッセージを生成
397
            $this->genErrorMsg( 'pdo', $e->getMessage() );
398
399
            return false;
400
        }
401
402
        return $retval;
403
    }
404
405
    /**
406
     * genDSN()
407
     *
408
     * DSN文字列を生成する
409
     *
410
     * @access    private
411
     *
412
     * @param     void
413
     *
414
     * @return    string DSN文字列
415
     */
416
    private function genDSN()
417
    {
418
        // 変数の初期化
419
        $retval = '';
420
421
        // ドライバ名のセット
422
        if (isset( $this->dbinfo[ 'driver' ] ) and !empty( $this->dbinfo[ 'driver' ] ) and in_array( $this->dbinfo[ 'driver' ],
423
                \PDO::getAvailableDrivers() )
424
        ) {
425
            $retval .= $this->dbinfo[ 'driver' ] . ':';
426
        } else {
427
            $this->genErrorMsg( "undefined", 'driver' );
428
        }
429
430
        // DB名のセット
431 View Code Duplication
        if (isset( $this->dbinfo[ 'dbname' ] ) and !empty( $this->dbinfo[ 'dbname' ] )) {
0 ignored issues
show
Duplication introduced by Yuta Hayakawa
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
432
            $retval .= 'dbname=' . $this->dbinfo[ 'dbname' ] . ';';
433
        } else {
434
            $this->genErrorMsg( "undefined", 'dbname' );
435
        }
436
437
        // ホスト名のセット
438 View Code Duplication
        if (isset( $this->dbinfo[ 'host' ] ) and !empty( $this->dbinfo[ 'host' ] )) {
0 ignored issues
show
Duplication introduced by Yuta Hayakawa
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
439
            $retval .= 'hostname=' . $this->dbinfo[ 'host' ] . ';';
440
        } else {
441
            $this->genErrorMsg( "undefined", 'host' );
442
        }
443
444
        // キャラクタセットのセット
445 View Code Duplication
        if (isset( $this->dbinfo[ 'charset' ] ) and !empty( $this->dbinfo[ 'charset' ] )) {
0 ignored issues
show
Duplication introduced by Yuta Hayakawa
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
446
            $retval .= 'charset=' . $this->dbinfo[ 'charset' ];
447
        } else {
448
            $this->genErrorMsg( "undefined", 'charset' );
449
        }
450
451
        return $retval;
452
    }
453
454
    /**
455
     * genErrorMsg($key = '')
456
     *
457
     * クラス内で発生したエラーに対するエラーメッセージを生成する
458
     *
459
     * @access    private
460
     *
461
     * @param     string $key エラーを示すキー文字列
462
     * @param     string $optional_text オプションの文字列
463
     *
464
     * @return    string    エラーメッセージ
465
     */
466
    private function genErrorMsg( $key = '', $optional_text = '' )
467
    {
468
        // 引数の値に応じてエラーメッセージをセットする
469
        switch ($key) {
470
            // 未定義エラーの場合
471 View Code Duplication
            case 'undefined':
0 ignored issues
show
Duplication introduced by Yuta Hayakawa
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
472
                $msg = 'Value not set - ' . ( ( isset( $optional_text ) and !empty( $optional_text ) ) ? $optional_text : 'unknown' );
473
                break;
474
475
            // PDO関連エラーの場合
476 View Code Duplication
            case 'pdo':
0 ignored issues
show
Duplication introduced by Yuta Hayakawa
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
477
                $msg = 'PDO error happened - ' . ( ( isset( $optional_text ) and !empty( $optional_text ) ) ? $optional_text : 'unknown' );
478
                break;
479
480
481
            // 未定義のエラーの場合
482
            default:
483
                $msg = 'Unknown Error occurred';
484
                break;
485
        }
486
487
        // ログ出力しエラーメッセージを返却
488
        $this->risolutoErrorLog( 'error', $msg );
489
490
        return $msg;
491
    }
492
}
493