Recover from fatal error in PHP due to failed MySQL query - php

I'm getting data back from an AJAX call, which I then try and INSERT into a database. I'm using Silverstripe 3.1 (using DB::query()), but this seems to be a PHP thing.
If the transaction works then everything works as planned, however, if the query fails I get a PHP fatal error.
Basically I'm looking for a way to continue despite a fatal error so that I can throw a failed notification to a user. I'd just like to know if the query worked or not.
*Edit to include Code
$sql = "UPDATE $table SET ,,"; // Syntax error added purposefully
foreach ($update_array as $key => $value) {
$name = $formHelpers->some_filter($key);
$content = $formHelpers->some_filter($value);
$sql .= " $name='$content',";
}
$sql = rtrim($sql, ",");
$sql .= "WHERE id=$id";
$result = DB::query($sql);

I'd need to see your code to tell you exactly what you need to do. However, the concept you are looking for is called try, catch, throw. When you make your database query, add a catch statement. Then if an error is tossed, you can catch it and still do something. Here is a tutorial about Try, Catch, Through in PHP
http://www.w3schools.com/php/php_exception.asp

Related

Doing two queries on a single php throws second to not being fetchable

I'm trying to fill various html selects with their respective information. For that I feel the right way is to make a query for each of them. I do the first query with this php tag to fill the first select like this:
<?php
$sql = "call mydb.getPositions();";
$result = $conn->query($sql);
while($row = $result->fetch_row()){
echo "<option>".$row[0]."</option>";
}
?>
That's it. Simple. It works. The function getPositions() is a select. But when I tried to fill the next select/combobox with it's own query, it started throwing this error:
Fatal error: Call to a member function fetch_row() on a non-object in file.php in line 200
I searched for a lot of reasons for a mistake like typos in the query or fetching rows from a delete query.
But then to discard any mistake of those, I decided to copy the exact same php tag one after the other. So, if the first works, why wouldn't the second work? So there I saw that I was receiving the exact same mistake. What am I missing? I tried to close the $result, with no success.
Thanks in advance.
EDIT.
When I said "I decided to copy the exact same php tag one after the other" I really meant that like:
<?php
$sql = "call mydb.getPositions();";
$result = $conn->query($sql);
while($row = $result->fetch_row()){
echo "<option>".$row[0]."</option>";
}
?>
<?php
$sql = "call mydb.getPositions();";
$result = $conn->query($sql);
//It throws the error down here
while($row = $result->fetch_row()){
echo "<option>".$row[0]."</option>";
}
?>
And this throws the same error described before.
EDIT2.
The procedure is this:
CREATE DEFINER=`mainSoccer`#`%` PROCEDURE `getPositions`()
BEGIN
Select namePosition from mydb.Position;
END
It looks like your question is actually: how do I run the same MySQL query twice in a php script? If so, here's the answer:
<?php
$sql = "Select namePosition from mydb.Position;";
$result = $conn->query($sql);
while($row = $result->fetch_row()){
echo "<option>".$row[0]."</option>";
}
?>
<!-- presumably you have other code in between here -->
<?php
$sql = "Select namePosition from mydb.Position;";
$result = $conn->query($sql);
while($row = $result->fetch_row()){
echo "<option>".$row[0]."</option>";
}
?>
And in your comment to my original answer below, you asked "Why shouldn't I be creating a MySQL procedure?" Two answers:
Your initial code is creating the same procedure twice. That is surely throwing an error at the MySQL level. Generally one creates the procedure once for permanent re-use directly in your server, so that many different scripts can them simply invoke (not create) that procedure at any time.
You should use MySQL procedures for complex, large MySQL operations that have performance issues that can gain efficiency as a pre-compiled procedure. Your query here is very simple and does not call for a MySQL procedure.
Hope this helps.
Since you only posted your code that works, but not the code that doesn't work, you can't get specific help.
But I can tell you that the error you quote "Fatal error: Call to a member function fetch_row() on a non-object in file.php in line 200" is typically caused when you write invalid SQL.
First confirm that the SQL you are attempting runs on its own as raw SQL, for example in phpMyAdmin.

PHP PDO is saving string from javascript object (ajax) as empty field

This is interesting and by that I mean incredibly frustrating.
I am passing this data to my php file via an ajax call: {"html":"<div>I'm a div!!</div>"}
I want to preface this next statement by saying that I do understand the reasons for not saving json to a database, but it does have a use here.
When I save this data to the database field, the field is empty. Now see this:
$in1 = file_get_contents('php://input'); //from ajax
var_dump($in1);
$in2 = '{"html":"<div>I\'m a div!!</div>"}';
var_dump($in2);
value of my ajax call:
string(33) "{"html":"<div>I'm a div!!</div>"}"
string(33) "{"html":"<div>I'm a div!!</div>"}"
Perfectly the same! Yet, $in2 will save to the database just fine!! While $in1 yields an empty field!!
To be certain, consider this:
if ($in1 === $in2) { echo "They're equal!"; }
Go figure...they're exactly equal, yet one will save correctly and the other won't. Amazing.
Further: mysqli does not have this issue, so that narrows it down to being a PDO issue.
$query = "UPDATE plugin_instances SET config=(?) WHERE id=2";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("s", $in1);
$stmt->execute(); //correct value in the db!!
I have now removed everything and this is the whole php file.
Non-working output
Working output
The only difference between these two is the result of $stmt->rowCount().
The sample that correctly updates the field says int(0) and the one that empties it says int(1).
$db = new PDO('mysql:host=localhost;dbname=disarray', 'root', 'temp');
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
var_dump($db);
$params = [':foo'=>'{"html":"<div>I\'m a div!!</div>"}'];
var_dump($params);
$params = [':foo'=>file_get_contents('php://input')];
var_dump($params);
$query = "UPDATE plugin_instances SET config=:foo WHERE id=2";
var_dump($query);
try {
$stmt = $db->prepare($query);
var_dump($stmt);
$stmt->execute($params);
var_dump($stmt);
var_dump($stmt->rowCount());
}
catch (PDOException $e) {
echo $e->getMessage();
}
Massive sigh. The issue is with my javascript, not php. I was testing this out in my api, which several things make calls to throughout my somewhat large app. There was an additional ajax call being made that I was unaware of (it was left in the code by error) and it wasn't sending any data, so my test script in the api was running with no data and thus emptying the database field right after I wrote to it. Of course, entering the data directly into the script worked out great because both calls were doing the same thing.
It is unfortunate that when I stripped down my php code, I used the same filename/location.
More unfortunate is that the success of the mysqli threw me even further off, solidifying in my mind that the issue was with PDO. It turns out that it was just changing which ajax call was responded to last.
And thus, I have hopefully learned not to have so much tunnel-vision when going through the debugging process.

How to skip non-object error in PHP mysqli?

When having an error in SQL syntax in classic PHP mysql, the query will not take place without any other effect. But in mysqli, it will kill the PHP script with Fatal error
mysql_query("SELECT title, misspelled_column FROM posts");
$mysqli->query("SELECT title, misspelled_column FROM posts");
In the first case, it will show the other queries and php output; but the second case kills the script by
Fatal error: Call to a member function fetch_assoc() on a non-object
The problem is related to non-object returned by false query. I can skip this error by
if($result){$row = $result->fetch_assoc();}
but my question is that why I did not need this check in classic mysql? With a more advanced system, one expects new features not missing what we had.
An error generated by MySQL should not be stopping execution. In fact, you can have your script show you any SQL errors by using $mysqli->error (assuming $mysqli is your database connection, like in your example). However, what may be happening is that your mysqli error causes a particular object not to be created, and then calling a method on that object will create a fatal PHP error. For example:
$dbconn = new mysqli("localhost", $username, $password, $dbname);
$stmt = $dbconn->prepare("bluh"); // not a valid statement. fails to create a mysqli statement object in $stmt.
echo($dbconn->error); // your script is still running, and this will show your MySQL syntax error.
$stmt->execute();
This will die not because you made an SQL error, but because $stmt was null and didn't have the expected execute() method. So like everyone else has said, check your logs and see what the actual error is.
Using # to ignore errors is going to be hit-or-miss until you figure out which specific command is creating the error.
update: If you know that the error is in the query, then you could check to see whether the query succeeded before you try to do anything with it. One way is to check the error parameter; another is to check to make sure that it actually returned the kind of object you want.
Here are examples of both:
$result = $db->query("select firstname, lastname from people where firstname = 'egbert';");
if($db->error == '') {
// the query worked, so fetch results from $result and do stuff with them.
}
else {
// the query didn't work, so don't try to do anything with $result
}
// alternately:
if(gettype($result) == "object") {
// the query worked.
}
else {
// it didn't.
}
A SQL error doesn't kill mysqli in my experience. I suspect you actually have a PHP error in the relevant statement. Check your error log.
In PHP, you can use # to suppress errors. It's a bad idea to use it here. But if that's what you really want, it's documented at http://php.net/manual/en/language.operators.errorcontrol.php.

Why is this database error occuring?

I am using the JQuery .post method to get data using an AJAX call. The PHP file that is rendering the data has the following code that inputs information into a database:
$query = "INSERT INTO questions(question, added_by) VALUES ('$question', '$user_id')";
$result = mysql_result($query);
//If db error
if(!$result )
{
$error = str_replace("'", "*", mysql_error());
$method = __METHOD__.'line: '.__LINE__;
return Error::db_error($method, $error, $this->ip, 'An internal error has occurred. Your question can not be added at this time.');
}
According to this code, a database error is occurring, yet mysql_error() is blank. When I use die($query), and copy and paste the literal query string into my mysql gui window (sqlyog) and run the query, it inserts just fine with no warnings or errors.
I have this general set-up for some other PHP functions and it works just fine.
I am really stumped by this. Any help would be greatly appreciated. Thanks.
Execute the query using mysql_query before Retrieving the contents of cells from a MySQL result set using mysql_result.
$result = mysql_query($query);
echo mysql_result($result);
Could it be an encoding issue? What is your contentType on the Ajax post?

PDO Unbuffered queries

I'm trying to get into PDO details. So I coded this:
$cn = getConnection();
// get table sequence
$comando = "call p_generate_seq('bitacora')";
$id = getValue($cn, $comando);
//$comando = 'INSERT INTO dsa_bitacora (id, estado, fch_creacion) VALUES (?, ?, ?)';
$comando = 'INSERT INTO dsa_bitacora (id, estado, fch_creacion) VALUES (:id, :estado, :fch_creacion)';
$parametros = array (
':id'=> (int)$id,
':estado'=>1,
':fch_creacion'=>date('Y-m-d H:i:s')
);
execWithParameters($cn, $comando, $parametros);
my getValue function works fine, and I get the next sequence for the table. But when I get into execWithParameters, i get this exception:
PDOException: SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. in D:\Servidor\xampp_1_7_1\htdocs\bitacora\func_db.php on line 77
I tried to modify the connection attributes but it doesn't work.
These are my core db functions:
function getConnection() {
try {
$cn = new PDO("mysql:host=$host;dbname=$bd", $usuario, $clave, array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
));
$cn->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
return $cn;
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
function getValue($cn, $comando) {
$resul = $cn->query($comando);
if (!$resul) return null;
while($res = $resul->fetch()) {
$retorno = $res[0][0];
break;
}
return $retorno;
}
function execWithParameters($cn, $comando, $parametros) {
$q = $cn->prepare($comando);
$q->execute($parametros);
if ($q->errorInfo() != null) {
$e = $q->errorInfo();
echo $e[0].':'.$e[1].':'.$e[2];
}
}
Somebody who can shed a light for this? PD. Please do not suggest doing autonumeric id, cause i am porting from another system.
The issue is that mysql only allows for one outstanding cursor at a given time. By using the fetch() method and not consuming all the pending data, you are leaving a cursor open.
The recommended approach is to consume all the data using the fetchAll() method.
An alternative is to use the closeCursor() method.
If you change this function, I think you will be happier:
<?php
function getValue($cn, $comando) {
$resul = $cn->query($comando);
if (!$resul) return null;
foreach ($resul->fetchAll() as $res) {
$retorno = $res[0];
break;
}
return $retorno;
}
?>
I don't think PDOStatement::closeCursor() would work if you're not doing a query that returns data (i.e. an UPDATE, INSERT, etc).
A better solution is to simply unset() your PDOStatement object after calling PDOStatement::execute():
$stmt = $pdo->prepare('UPDATE users SET active = 1');
$stmt->execute();
unset($stmt);
The problem seems to be---I'm not too familiar with PDO--- that after your getValue call returns, the query is still bound to the connection (You only ever ask for the first value, yet the connection returns several, or expects to do so).
Perhaps getValue can be fixed by adding
$resul->closeCursor();
before the return.
Otherwise, if queries to getValue will always return a single (or few enough) value, it seems that using fetchAll will be preferred.
I just spend 15 minutes googling all around the internet, and viewed at least 5 different Stackoverflow questions, some who claimed my bug apparently arose from the wrong version of PHP, wrong version of MySQL library or any other magical black-box stuff...
I changed all my code into using "fetchAll" and I even called closeCursor() and unset() on the query object after each and every query. I was honestly getting desperate! I also tried the MYSQL_ATTR_USE_BUFFERED_QUERY flag, but it did not work.
FINALLY I threw everything out the window and looked at the PHP error, and tracked the line of code where it happened.
SELECT AVG((original_bytes-new_bytes)/original_bytes) as saving
FROM (SELECT original_bytes, new_bytes FROM jobs ORDER BY id DESC LIMIT 100) AS t1
Anyway, the problem happened because my original_bytes and new_bytes both where unsigned bigints, and that meant that if I ever had a job where the new_bytes where actually LARGER than the original_bytes, then I would have a nasty MySQL "out of range" error. And that just happened randomly after running my minification service for a little while.
Why the hell I got this weird MySQL error instead of just giving me the plain error, is beyond me! It actually showed up in SQLBuddy (lightweight PHPMyAdmin) when I ran the raw query.
I had PDO exceptions on, so it should have just given me the MySQL error.
Never mind, the bottom line is:
If you ever get this error, be sure to check that your raw MySQL is actually correct and STILL working!!!
A friend of mine had very much the same problem with the xampp 1.7.1 build. After replacing xampp/php/* by the 5.2.9-2 php.net build and copying all necessary files to xampp/apache/bin it worked fine.
If you're using XAMPP 1.7.1, you just need to upgrade to 1.7.2.

Categories