Created
March 24, 2021 22:26
-
-
Save IMSoP/a4e316684415413dca8ec5debf51a812 to your computer and use it in GitHub Desktop.
Python's with statement, but for PHP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class Transaction implements ContextManager | |
{ | |
public function __construct(private Database $db) {} | |
public function contextEnter() { | |
$this->db->beginTransaction(); | |
// Could return some other representation of the transaction if appropriate | |
return $this; | |
} | |
public function contextExit(?Throwable $e) { | |
if ( $e === null ) { | |
$this->db->commitTransaction(); | |
return true; | |
} | |
else { | |
$this->db->rollbackTransaction(); | |
return false; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
with ( new Transaction($db) as $transaction ) { | |
doSomething($to, $library, $thread); | |
andThenSomethingElse($author, $title, $library_name, $top_post); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// internal values not accessible to the user | |
$__context = new Transaction($db); | |
$__exited = false; | |
try { | |
// the transaction would be opened in contextEnter(), so you don't need complex logic in the constructor | |
// this also lets "with ( $existingObject )" work | |
// $transaction is optional in the with syntax and receives whatever contextEnter returns | |
$transaction = $__context->contextEnter(); | |
// user's code happens here; no capturing is needed, because we're still in the same scope! | |
doSomething($to, $library, $thread); | |
andThenSomethingElse($author, $title, $library_name, $top_post); | |
} | |
catch ( Throwable $e ) { | |
$__exited = true; | |
// Pass exception to Context Manager, which can indicate whether to re-throw it | |
if ( ! $__context->contextExit($e) ) { | |
throw $e; | |
} | |
} | |
finally { | |
// only call the contextExit() method if it wasn't already called in the catch block | |
if ( ! $__exited ) { | |
$__context->contextExit(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is based on my reading of https://www.python.org/dev/peps/pep-0343/ and may well have some details wrong.
See that document for plenty more examples of how it can be used.