Thanks for your input. I did not try-catch because it doesn't really work, you either duplicate rollback code or get excessive nesting. Real code will obviously be more complicated than the examples. Also in both cases the handlers are many lines apart from the actual transaction call.
Duplicate rollback code: try { transactionOne(); } catch (\Throwable $e) { rollbackTransactionOne(); throw $e; } try { transactionTwo(); } catch (\Throwable $e) { rollbackTransactionOne(); rollbackTransactionTwo(); throw $e; } finally { cleanupTransactionTwo(); } try { transactionThree(); } catch (\Throwable $e) { rollbackTransactionOne(); rollbackTransactionTwo(); rollbackTransactionThree(); throw $e; } logTransactionOne(); logTransactionTwo(); logTransactionThree(); Excessive nesting: try { transactionOne() try { transactionTwo(); try { transactionThree(); } catch (\Throwable $e) { rollbackTransactionThree() throw $e; } } catch (\Throwable $e) { rollbackTransactionTwo(); throw $e; } finally { cleanupTransactionTwo(); } } catch (\Throwable $e) { rollbackTransactionOne(); throw $e; } But you did help me very well with your second example because I can put a single try-catch over the entire thing and indeed do the optional callbacks at the end: try { $scope = new ScopeGuard; $scope->onSucces(function() { logTransactionOne(); }); $scope->onFailure(function() { rollbackTransationOne(); }); doTransactionOne(); $scope->onSuccess(function() { logTransactionTwo(); }); $scope->onFailure(function() { rollbackTransactionTwo(); }); $scope->onExit(function() { cleanupTransactionTwo(); }); doTransactionTwo(); } catch (\Throwable $e) { $scope->callFailureHandlers(); throw $e; } finally { $scope->callExitHandlers(); } $scope->callSuccessHandlers(); This suffices as far as duplicate code and nesting go. But the last few lines are always the same, and this is a common pattern. Perhaps this can be encapsulated somehow (IoC)? Destructors seem like a good fit but it seems the engine does not have the necessary hook. Maybe we can let the ScopeGuard object itself execute the code? Like this: class ScopeGuard { public static function execute(closure $code) { try { $scope = new self; $result = $code($scope); $scope->executeSuccessHandlers(); } catch(\Throwable $e) { $scope->executeFailureHandlers(); throw $e; } finally { $scope->executeExitHandlers(); } return $result; } public function onFailure($callable) {} public function onSuccess($callable) {} public function onExit($callable) {} } ScopeGuard::execute(function(ScopeGuard $scope) { /* same linear transaction code here as above, but without try-catch */ }); Not sure on this one. It restricts you to use a single ScopeGuard at a time. I might make a library for it though. 2015-10-28 12:24 GMT+01:00 Leigh <lei...@gmail.com>: > > > Why don't you catch the exception? (You can always re-throw it if you want > to do something when there is an exception, but don't want to handle it > yourself) > > try { > doTransactionOne(); > logTransactionOne(); > } > catch (\Throwable $e) { > rollbackTransationOne(); > throw $e; > } > > Or if you really want to use your scope thingy. > > try { > doTransactionOne(); > } > catch (\Throwable $e) { > $scope->abortedDueToException(); > throw $e; > } > -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php