Repeatedly try perform MySQL query with PHP - php

I have seen somewhere something like the code below. It is intended to make multiple attempts to perform a query if it has failed with anything else than deadlock error. I just want to ask you guys if is it a good practice or does it have any benefits?
$success = false;
$try_count = 5;
do{
try {
$this->_db->beginTransaction();
// PERFORM QUERY 1
// PERFORM QUERY 2
$this->_db->commit();
$success = true;
return true;
}catch(PDOException $e){
$this->_db->rollBack();
$try_count--;
$success = false;
// if not deadlock error, throw it
if($e->getCode()!=1213) throw $e;
if($try_count < 1) return false;
}
}while($try_count > 1 && !$success);

This is generally a bad practice unless you have a very good reason. I say this because you would have to add logic to see what caused the error and respond differently to each error before deciding to re-query the database. Cheers :)

Related

Get last inserted id after setting commit to false

I'm trying to insert data into two tables, if the insertion not work for one of them operation should ignored.(I think this called transaction)
try{
$dbcon=new mysqli($hn,$un,$pw,$dbn);
if($dbcon->connect_error)
throw new Exception($dbcon->connect_error);
$dbcon->autocommit(false);
$query="insert into users(id,email) values(null,'$email')";
$res_a=$dbcon->$query($query);
if($res_a){
$l_id=$res_a->insert_id;
$query="insert into profiles values($l_id,'$name','$birthday')";
$res_b=$dbcon->query($query);
}
if(!res_a || !res_b){
$dbcon->rollback();
throw new Exception("problem with database !!");
}
$dbcon->commit();
}catch(Exception $e){
echo $e->getMessage();
}finaly{
if(isset($dbcon))
$dbcon->close();
}
With this code PHP showing this Error : "Trying to get property of non-object" ...
In other words is there better way to do transaction without using autocommit methode (I'm working with mysqli)?
To answer the question and ignoring the issues with the error message this is how to run multiple updates within a transaction.
I would try retrieving the ->insert_id using the connection handle rather than the statement handle as the transaction/commit/rollback are all part of the connection handle and not the statement handle this appears to be more reliable and may be why you are not getting the error.
$dbcon=new mysqli($hn,$un,$pw,$dbn);
// if no connection can be made there is no point doing anything else
if($dbcon->connect_error) {
echo $dbcon->connect_error;
exit;
}
try{
//$dbcon->autocommit(false);
$dbcon->begin_transaction(); // this does the autocommit = false as well
$query = "insert into users(id,email) values(null,'$email')";
$res_a = $dbcon->$query($query);
if ( ! $res_a ) { // testing for FALSE is only safe way
throw new Exception($dbcon->error);
}
//$l_id = $res_a->insert_id;
$l_id = $dbcon->insert_id;
$query="insert into profiles values($l_id,'$name','$birthday')";
$res_b=$dbcon->query($query);
if( ! res_b) {
throw new Exception($dbcon->error);
}
$dbcon->commit();
}
catch(Exception $e){
echo $e->getMessage();
$dbcon->rollback();
}
finally{ // spelling correction
// not strictly necessary as PHP will close and cleanup automatically
if(isset($dbcon))
$dbcon->close();
}

Collect all downtime start times to reach total downtime at end of month - PHP

I have a cron job running every 10-15 minutes pinging minecraft servers in my database, and if one of them returns "offline", the database records the time this happened and sticks in it the lastDT (last downtime) table. My question is, how can I collect all of the times, and at the end of each month figure out the average downtime (if any). I assume this would be ((totalMinutesInMonth-totalMinutesOfDT)*100). Is there a way to do this? To get a better idea of what I'm dealing with, here is some code to reference:
<?php
require_once './inc/connectToDB.php';
date_default_timezone_set('UTC');
session_start();
try {
$collectServers = $database->prepare('SELECT * FROM servers');
$collectServers->execute();
$serversDat = $collectServers->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
error_log($e->getMessage());
exit();
}
foreach ($serversDat as $server) {
try {
$remoteCon = #fsockopen(#gethostbyname($server['address']),$server['port'],$errno,$errstr,1);
$connected = false;
if (#is_resource($remoteCon)) { $connected = true; #fclose($remoteCon); }
if ($connected) { $serverStat = 'Online'; } else { $serverStat = 'Offline'; }
if ($serverStat == 'Offline') {
$downTime=date('H:i:s',time());
} else {
$downTime=0;
}
$reinsert = $database->prepare('UPDATE servers SET status=:status,lastPing=:time,lastDT=:lastdt WHERE id=:id');
$reinsert->bindValue(':id',$server['id'],PDO::PARAM_INT);
$reinsert->bindValue(':status',$serverStat,PDO::PARAM_STR);
$reinsert->bindValue(':time',date('H:sa T',time()),PDO::PARAM_STR);
$reinsert->bindValue(':lastdt',$downTime,PDO::PARAM_STR);
$reinsert->execute();
} catch (PDOException $e) {
error_log($e->getMessage());
exit();
}
}
unset($database);
exit();
?>
The database table looks like:
(http://i.imgur.com/0ypc99g.png)
If I can provide more info, I'd be glad to do so. Not sure what to do from here, however. Thanks in advance.
EDIT: If anyone has any idea how to do this more efficiently, I'm all for rewriting. This is not working out for me.
It seems like finding the time the server went down is only half of the equation. You will also need to know when the server came back up.
Down at 10:00AM, Up at 10:15AM equals 15min downtime.
You will need to store that in the database somewhere. That isn't solution, but it does seem like the next step.

Statements in php

if($getstatus->num_rows != 0 && $getstatusarr = $getstatus->fetch_assoc() && $getstatusarr["Type"] != $data["type"])
echo "error"
else
...
first code will not work, to make works this way, see Nin's post
Is it possible to make the code easily?
Also I can do it like this:
if($getstatus->num_rows != 0)
$getstatusarr = $getstatus->fetch_assoc();
if($getstatusarr["Type"] != $data["type"]) {
echo "error"
$error = true;
}
if(!$error) {
...
}
by ellipsis I have too many lines of code :)
added:
also I can do in this way:
if($getstatus->num_rows != 0) {
$getstatusarr = $getstatus->fetch_assoc();
if($getstatusarr["Type"] != $data["type"]) {
echo "error";
goto skip;
}
}
... // some code which I need not to execute if $getstatusarr["Type"] != $data["type"] are true
skip:
// another code which will execute in all cases
Well, don't use goto: :)
Whether you put all the if's on one line or on several lines is mostly a personal preference.
Too many lines with if will make the code harder to read but putting it all on one line also makes it difficult to read and difficult to debug (error on line 12 can mean many things then). If you're using a debugger like xdebug or Zend debug then having multiple lines to step over is also easier.
So find a way in between this.
I would do it like this, since then you also check if fetch_assoc() returned a result:
if($getstatus->num_rows != 0 && $getstatusarr = $getstatus->fetch_assoc())
if($getstatusarr["Type"] != $data["type"]) {
echo "error"
$error = true;
}
if(!$error) {
...
}
From my point of view is always best to unwrap statements and clean the code as much as you can, maybe someone later on will have to read what you did and he will have a hard time doing that.
Also you cannot assign new variables in a if statement like that:
$error = false;
if($getstatus->num_rows)
$getstatusarr = $getstatus->fetch_assoc();
if($getstatusarr["Type"] != $data["type"]) {
$error = array('type' => 'invalid type');
}
}
if($error) {
// do something with $error array
}

PHP eval and capturing errors (as much as possible)

Disclaimer; I'm fully aware of the pitfalls and "evils" of eval, including but not limited to: performance issues, security, portability etc.
The problem
Reading the PHP manual on eval...
eval() returns NULL unless return is
called in the evaluated code, in which
case the value passed to return is
returned. If there is a parse error in
the evaluated code, eval() returns
FALSE and execution of the following
code continues normally. It is not
possible to catch a parse error in
eval() using set_error_handler().
In short, no error capture except returning false which is very helpful, but I'm sur eI could do way better!
The reason
A part of the site's functionality I'm working on relies on executing expressions. I'd like not to pass through the path of sandbox or execution modules, so I've ended using eval. Before you shout "what if the client turned bad?!" know that the client is pretty much trusted; he wouldn't want to break his own site, and anyone getting access to this functionality pretty much owns the server, regardless of eval.
The client knows about expressions like in Excel, and it isn't a problem explaining the little differences, however, having some form of warning is pretty much standard functionality.
This is what I have so far:
define('CR',chr(13));
define('LF',chr(10));
function test($cond=''){
$cond=trim($cond);
if($cond=='')return 'Success (condition was empty).'; $result=false;
$cond='$result = '.str_replace(array(CR,LF),' ',$cond).';';
try {
$success=eval($cond);
if($success===false)return 'Error: could not run expression.';
return 'Success (condition return '.($result?'true':'false').').';
}catch(Exception $e){
return 'Error: exception '.get_class($e).', '.$e->getMessage().'.';
}
}
Notes
The function returns a message string in any event
The code expression should be a single-line piece of PHP, without PHP tags and without an ending semicolon
New lines are converted to spaces
A variable is added to contain the result (expression should return either true or false, and in order not to conflict with eval's return, a temp variable is used.)
So, what would you add to further aide the user? Is there any further parsing functions which might better pinpoint possible errors/issues?
Chris.
Since PHP 7 eval() will generate a ParseError exception for syntax errors:
try {
$result = eval($code);
} catch (ParseError $e) {
// Report error somehow
}
In PHP 5 eval() will generate a parse error, which is special-cased to not abort execution (as parse errors would usually do). However, it also cannot be caught through an error handler. A possibility is to catch the printed error message, assuming that display_errors=1:
ob_start();
$result = eval($code);
if ('' !== $error = ob_get_clean()) {
// Report error somehow
}
I've found a good alternative/answer to my question.
First of, let me start by saying that nikic's suggestion works when I set error_reporting(E_ALL); notices are shown in PHP output, and thanks to OB, they can be captured.
Next, I've found this very useful code:
/**
* Check the syntax of some PHP code.
* #param string $code PHP code to check.
* #return boolean|array If false, then check was successful, otherwise an array(message,line) of errors is returned.
*/
function php_syntax_error($code){
if(!defined("CR"))
define("CR","\r");
if(!defined("LF"))
define("LF","\n") ;
if(!defined("CRLF"))
define("CRLF","\r\n") ;
$braces=0;
$inString=0;
foreach (token_get_all('<?php ' . $code) as $token) {
if (is_array($token)) {
switch ($token[0]) {
case T_CURLY_OPEN:
case T_DOLLAR_OPEN_CURLY_BRACES:
case T_START_HEREDOC: ++$inString; break;
case T_END_HEREDOC: --$inString; break;
}
} else if ($inString & 1) {
switch ($token) {
case '`': case '\'':
case '"': --$inString; break;
}
} else {
switch ($token) {
case '`': case '\'':
case '"': ++$inString; break;
case '{': ++$braces; break;
case '}':
if ($inString) {
--$inString;
} else {
--$braces;
if ($braces < 0) break 2;
}
break;
}
}
}
$inString = #ini_set('log_errors', false);
$token = #ini_set('display_errors', true);
ob_start();
$code = substr($code, strlen('<?php '));
$braces || $code = "if(0){{$code}\n}";
if (eval($code) === false) {
if ($braces) {
$braces = PHP_INT_MAX;
} else {
false !== strpos($code,CR) && $code = strtr(str_replace(CRLF,LF,$code),CR,LF);
$braces = substr_count($code,LF);
}
$code = ob_get_clean();
$code = strip_tags($code);
if (preg_match("'syntax error, (.+) in .+ on line (\d+)$'s", $code, $code)) {
$code[2] = (int) $code[2];
$code = $code[2] <= $braces
? array($code[1], $code[2])
: array('unexpected $end' . substr($code[1], 14), $braces);
} else $code = array('syntax error', 0);
} else {
ob_end_clean();
$code = false;
}
#ini_set('display_errors', $token);
#ini_set('log_errors', $inString);
return $code;
}
Seems it easily does exactly what I need (yay)!
How to test for parse errors inside eval():
$result = #eval($evalcode . "; return true;");
If $result == false, $evalcode has a parse error and does not execute the 'return true' part. Obviously $evalcode must not return itself something, but with this trick you can test for parse errors in expressions effectively...
Good news: As of PHP 7, eval() now* throws a ParseError exception if the evaluated code is invalid:
try
{
eval("Oops :-o");
}
catch (ParseError $err)
{
echo "YAY! ERROR CAPTURED: $err";
}
* Well, for quite a while then... ;)
I think that best solution is
try {
eval(/* ... */);
} catch (Throwable $t) {
//...
}
It catches every error and exception, including Call to undefined function etc
You can also try something like this:
$filePath = '/tmp/tmp_eval'.mt_rand();
file_put_contents($filePath, $evalCode);
register_shutdown_function('unlink', $filePath);
require($filePath);
So any errors in $evalCode will be handled by errors handler.

can you prevent a php while loop from erroring out?

I was wondering if there was a way to prevent a while loop from prematurely erroring out or terminating. I've thrown a try/catch in there and it seems to keep terminating. (As to the cause why it's terminating, I'm still debugging).
$stomp = $this->stomp;
if(isset($queue) && strlen($queue) > 0) {
error_log('Starting Monitor for: '.$queue);
$stomp->subscribe($queue);
while(true) {
$frame = $stomp->readFrame();
if ($frame != null) {
// Callback must be an array: array('Class','Method);
if(is_array($callback) && count($callback) == 2) {
try {
$body = $frame->body;
$callFunct = call_user_func_array($callback,array($body,$arguments));
$stomp->ack($frame);
} catch(StompException $e) {
$msg = 'Stomp Monitor readFrame() Callback Fail: '.$e->getMessage();
$this->context->reportError('STOMP',array('errorDetails'=>$msg));
}
} else {
error_log('Invalid Stomp Callback');
}
}
}
} `
Thanks,
Steve
There's nothing to break out of the loop, so while(true) will carry on until it hits a timeout or some form of error condition. As a fallback, it's worth setting either a break to break out of the loop on condition, or use a while condition that you can set to false;
while (true) {
// do some things
break;
}
or
$x = true;
while ($x) {
// do some things
$x = false;
}
that way, exit from the loop is under your control
However, timeouts and other fatal errors still terminate the script as normal
If your code is breaking out of the while loop, you should be seeing some error, unless you have an error handler suppressing it

Categories