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(...);
しかし、ここでは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以降で使用できます。