How do you create a function with code blocks that wrap around commonly used code?
I have a do...while loop and a try...catch block that wraps around database centric code. The do...while and try...catch serves the purpose of preventing transaction deadlocks, and it does so very well from extensive testing. My problem now is code redundancy. I cannot figure out a way to deploy a function or method that performs the do...while and try...catch around a generic set of database centric code without using the "evil" eval command. I imagine this comes up under other software design pattern scenarios.
This illustrates how I imagine the method with eval:
class Database {
public static function deadlockSafeTransaction($code) {
do {
// setup a condition
try {
// start transaction
eval($code); //evil
// commit transaction
}
catch(Exception $ex) {
// rollback transaction
// analyze exception for deadlock and adjust condition or rethrow exception
}
} while(condition);
}
}
This illustrates an undesired coding practice if I were to use the method above:
// code
Database::deadlockSafeTransaction("
// code - sometimes a lot of complicated code with other method calls and loops
");
// code
I am looking for a solution that does not include the use of the eval command.
After writing all of this up, I just thought of the possibility of flanking my code with include statements:
include "do-try.php";
// database code
include "catch-while.php";
But I am looking for something more OOPish and l开发者_运维百科ess hackish. I don't even know if the above would work and the includes may carry an unnecessary performance expense. So the issue still warrants inquiry and discussion for the best solution.
You could pass in an anonymous function:
$myFunction = function() {
// do something
};
Database::deadlockSafeTransaction($myFunction);
Call the function in your deadlockSafeTransaction code:
public static function deadlockSafeTransaction($myFunction) {
...
$return = $myFunction();
...
}
More information is available in the PHP docs. If you're using PHP < 5.3.x you can also use the call_user_func
method.
Use callbacks:
function doInTransaction($fn) {
// stuff
$fn();
// more stuff
}
and then
doInTransaction(function() {
// whatever
});
As to the question how to pass the context into the function, I don't think using globals is a good idea. Much better is to pass what you need explicitly to the function:
function doInTransaction($fn) {
// stuff
$fn($this->databaseConnection, $this->logFile);
// more stuff
}
and the function should be prepared to accept what's gets passed to it:
doInTransaction(function($connection, $logFile) {
$x = $connection->xxxx
})
Even better option is to make function a Delegate and just give it the whole "parent" object:
function doInTransaction($delegate) {
// stuff
$delegate($this);
// more stuff
}
doInTransaction(function($dbObject) {
$dbObject->doSomething(...);
})
In C# you coulduse a lambda expression to do this in a type-safe way. Not sure if PHP has anything similar.
DeadlockSafeTransaction(() => DoSomething());
You can use an interface class to implement an OOP callback.
http://php.net/manual/en/language.oop5.interfaces.php
Your code would be something like:
interface databaseAction(){
public function DbActions();
}
class someClass implements databaseAction{
//regular class stuff
public function DbActions(){
//your code to be executed
}
}
Your Db class would then be this:
class Database {
public static function deadlockSafeTransaction($obj) {
do {
// setup a condition
try {
// start transaction
$obj->DbActions(); //not evil
// commit transaction
}
catch(Exception $ex) {
// rollback transaction
// analyze exception for deadlock and adjust condition or rethrow exception
}
} while(condition);
}
}
精彩评论