質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

CodeIgniter

CodeIgniterは、PHP向けオープンソースのWebアプリケーションフレームワークです。CodeIgniterは覚える構文が少なく、自由度も高いため、PHPを理解していれば構築が簡単です。

Q&A

解決済

2回答

6384閲覧

CodeIgniterでのトランザクション管理について

hukinotou

総合スコア13

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

CodeIgniter

CodeIgniterは、PHP向けオープンソースのWebアプリケーションフレームワークです。CodeIgniterは覚える構文が少なく、自由度も高いため、PHPを理解していれば構築が簡単です。

0グッド

1クリップ

投稿2017/07/18 14:51

CodeIgniter、MySQLのトランザクション管理について教えてください。

###実現したいこと
コントローラーの開始でトランザクションを開始し、コントローラーの終わりでトランザクションをコミット / ロールバック。
チームで開発をする際にメンバーのスキルによって、漏れが発生してしまうことを防ぐために、上位で制御を行いたいです。

###回答いただきたいこと
下記に示すコード(__destruct()でのトランザクション処理)で問題はないか。

__destruct()のタイミングでコネクションがなくなる(closeされる)ことがあり得るのかが気になっています。

###該当のソースコード

lang

1class MY_Controller extends CI_Controller { 2 public $trans_failure = FALSE; 3 private $_trans_status = FALSE; 4 5 public function __construct() 6 { 7 parent::__construct(); 8 $this->_trans_start(); 9 } 10 11 public function __destruct() 12 { 13 parent::__destruct(); 14 $last_error = error_get_last(); 15 if (isset($last_error) && 16 ($last_error['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING))) 17 { 18 // Exceptionのためロールバック 19 $this->trans_failure = TRUE; 20 } 21 22 $this->_trans_complete(); 23 } 24 25 private function _trans_start() 26 { 27 if ($this->db->trans_begin()) 28 { 29 $this->_trans_status = TRUE; 30 } 31 } 32 33 private function _trans_complete() 34 { 35 if ($this->_trans_status === FALSE) 36 { 37 return; 38 } 39 40 if ($this->trans_failure === TRUE) 41 { 42 $this->db->trans_rollback(); 43 $this->_trans_status = FALSE; 44 return; 45 } 46 47 $this->db->trans_commit(); 48 $this->_trans_status = FALSE; 49 } 50}

基本的にはコントローラーの最後でコミットとし、排他チェックなどによる意図的なロールバックは$this->trans_failure = FALSE;をすることを想定しています。

###試したこと
hookの機能を利用し、post_controller_constructorでトランザクションの開始、post_controllerでトランザクションの完了処理を試しました。
url_helperのredirect()などでexitした場合に、トランザクションの完了処理が呼ばれず、結果としてロールバックされてしまいました。

###補足情報(言語/FW/ツール等のバージョンなど)
CodeIgniter 3.1.4
MySQL

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

下記の方法で試してみることにしました。

  1. _trans_complete() のアクセス修飾子をpublicへ変更
  2. register_shutdown_function([&$this, '_trans_complete']); を追加
  3. Exceptionの発生判定を_trans_complete()へ移動

lang

1class MY_Controller extends CI_Controller { 2 public $trans_failure = FALSE; 3 private $_trans_status = FALSE; 4 5 public function __construct() 6 { 7 parent::__construct(); 8 $this->_trans_start(); 9 } 10 11 public function __destruct() 12 { 13 parent::__destruct(); 14 15 $this->_trans_complete(); 16 } 17 18 private function _trans_start() 19 { 20 register_shutdown_function([&$this, '_trans_complete']); 21 if ($this->db->trans_begin()) 22 { 23 $this->_trans_status = TRUE; 24 } 25 } 26 27 public function _trans_complete() 28 { 29 $last_error = error_get_last(); 30 if (isset($last_error) && 31 ($last_error['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING))) 32 { 33 // Exceptionのためロールバック 34 $this->trans_failure = TRUE; 35 } 36 37 if ($this->_trans_status === FALSE) 38 { 39 return; 40 } 41 42 if ($this->trans_failure === TRUE) 43 { 44 $this->db->trans_rollback(); 45 $this->_trans_status = FALSE; 46 return; 47 } 48 49 $this->db->trans_commit(); 50 $this->_trans_status = FALSE; 51 } 52}

投稿2017/07/22 08:59

hukinotou

総合スコア13

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

がると申します。
CodeIgniterに長けているわけではないので、「PHPの一般論として」程度のお話、になるのですが。

デストラクタは「特定のオブジェクトを参照するリファレンスがひとつもなくなったとき」または「スクリプトの終了時」にcallされますが。
特にスクリプトの終了時には「順不同で」callされるので。

また、「$this->db->」とあるので、「$this->db」には何がしかのDB接続系インスタンス(PDOとかですかねぇ?)が入っている、と予見されるので。

上述から

  • 順不同なので「$this->dbのデストラクタ」が先にcallされる可能性

が十分に予見されるので、そうすると

  • $this->dbのデストラクタが動く(=DB接続が切られる:トランザクション中であれば、rollbackされる)
  • MY_Controllerクラスのデストラクタがcallされる時には「DB接続が切れている」ので、commitはできない

といった流れが、可能性として予見されるように思われます。

少々斜めにぐぐってみたのですが……ほかのフレームワークだと定期的にみられる、例えば「afterFilter」的なメソッドが、ざっくり検索した限りだと、見当たらないようなので。
そのあたりを自作するか、或いはサポートされている機能として実はあるのであれば、その辺を使うか、といった感じの箇所にcommit系の処理を書くと、比較的穏当に動くのではないか、と思うのですが如何でしょうか?

投稿2017/07/18 15:15

gallu

総合スコア506

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hukinotou

2017/07/19 12:22

がるさん回答ありがとうございます。 デストラクタではやはり、正常にトランザクションのコントロールができる保証がないということですね。 私もこのあたりが気になっておりました。 「試したこと」に記載しておりますが、hookの機能ではexit時に意図したようにできませんでした。 `register_shutdown_function()`を利用したら制御できるかどうかなどの知見をお持ちでしたら教示ください。
gallu

2017/07/20 15:21

そのあたり、大分と「予想込み」になってしまうのですが。 確か、先日斜めに見た限りですと、CI_Controller、シングルトンのような作りになっていたように見受けられます。 で、もし「シングルトン」だとしますと、register_shutdown_function()関数の中で、先に明示的にMY_Controllerのインスタンスを削除してやると、ある程度、意図通りに動く、かも、しれません(元々、デストラクタって「適当なタイミングでcallされる」ので、どこまで期待できるか、は微妙ですが)。 もし自分が実装するのであれば…を想定してみたのですが。 自分であればなんとなく ・内部に「トランザクションの完了処理」用のフラグを持っておく(初手はfalse) ・一度デストラクタが走ったら「トランザクションの完了処理フラグをtrue」にして、二重には走らないようにしておく ・exitする前に、出来るだけ「自力でデストラクタ(相当の、トラン完了処理)はcallするようにしておく」 ・register_shutdown_function()で、デストラクタを走らせるか、或いはインスタンスを明示的にunsetして、GCに送り込む といった作りにするか……とか、妄想しました。 上述、結構アバウトだったり雑だったりする箇所が含まれているので「話半分」程度に見ていただければ、と思いますが、参考の足しにでもなれば幸いです。
hukinotou

2017/07/22 08:53

回答ありがとうございます。 がるさんの回答を踏まえ、考えたのですが、デストラクタだけでなく、register_shutdown_function() でも_trans_complete()を実行するようにして試してみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問