PHPでクロージャーを使用する別の例

Habréには、PHPでクロージャーを使用する例が記載された記事がすでにいくつかあります。 それらのいくつかは非常に抽象的であり、いくつかはそうではありません。 実際の条件でクロージャーを適用する別の方法を示します。



フレームワークなしでPHPの1つのプロジェクトに新しい機能を追加する場合、トランザクションを使用する必要が生じました(MySQLをInnoDBで、PHP 5.4をMYSQLiで使用)。



プロジェクトでは、 autocommit



デフォルトでtrue



設定されています。 プロジェクト全体でオフにすることはできません。 したがって、最初の考えは、SQLクエリを実行する前にautocommit



をオフにし、すべてのアクション(および最後にcommit



またはrollback



)の後、 autocommit



再びオンにすることでした。



しかし、このアプローチはすぐに受け入れられないことが判明しました。これは、通常、リクエストが行われる複数のメソッドを連続して実行する必要があり、メソッドの1つで例外が発生した場合、 rollback



です 各メソッドでcommit



、すべてのリクエストが実行される前に変更がコミットされます。



別のオプションは、関連するメソッドの各グループが完了した後にautocommit



を無効および有効にすることです。 条件付きコード(アクションはクラスで実行されます):



 public function save() { $result = $this->db->update(...); //     -  ,       . if (!$result) throw new Exception('Error while saving'); } public function append_log() { $result = $this->db->insert(...); if (!$result) throw new Exception('Error while append'); } public function add() { $this->db->autocommit(false); try { $this->save(); $this->append_log(); $this->db->commit(); } catch (Exception $e) { $this->db->rollback(); } $this->db->autocommit(true); }
      
      







しかし、ここでは2つの問題が発生します。

  1. 私は本当に各方法でこれを書きたくありません
  2. メソッド( save()



    またはappend_log()



    )のいずれかが、トランザクションに結合する必要のある複数の連続したリクエストもappend_log()



    どうなりますか? 次に、 commit



    を実行commit



    と親の変更も保存されるため、 autocommit



    かどうかを判断し、これに応じてcommit



    を実行commit



    必要があります。




変更を確認および修正するためのコードが、参加せずにメソッドの周囲で実行されることを確認する必要があります。



 public function transaction(callable $block) { $exception = null; if ($need_to_off = $this->isAutocommitOn()) $this->mysqli->autocommit(false); try { $block(); } catch (Exception $e) { $exception = $e; } if ($need_to_off) { if ($exception == null) { $this->db->mysqli->commit(); } else { $this->db->mysqli->rollback(); } $this->mysqli->autocommit(true); } if ($exception) throw $exception; } public function isAutocommitOn() { if ($result = $this->db->mysqli->query("SELECT @@autocommit")) { $row = $result->fetch_row(); $result->free(); } return isset($row[0]) && $row[0] == 1; }
      
      







無名関数内でtransaction()



メソッドを送信します。 autocommit



有効になっている場合、 transaction



はそれを無効にしてから、匿名機能を実行します。 結果に応じて、 commit



またはrollback



を行い、 autocommit



再度有効にします。 autocommit



すでにオフになっている場合、匿名関数が実行されるだけです-自動コミットは他の場所で処理されます。



使用例:



 public function save_all() { $this->transaction(function(){ $this->save(); $this->append_log(); }); }
      
      







PS:クロージャーの$this



は、PHPバージョン5.4以降で使用できます。



All Articles