In my program I want update some information, so I used:
return ($result->rowCount() == 1)? true: false;
this way, if you save information without any change, false is returned and this is not the result usually we expect.
I change my function to this one
try{
$result=$db->prepare($sql);
$result->execute($arr);
return true;
}catch(Exception $e){
return false;
}
Is it the best way? Does this way guarantee that the update statement worked or not?
Assuming your $db is instance of PDO
When you run PDO::prepare you can detect sql syntax error and when you run PDO::execute you will be sure that syntax is correct. Then application is resposible for knowing if update should have updated something or not.
So here is some sample code:
function update($db, $sql)
{
$preparedSql = $db->prepare($sql);
if(!$preparedSql) {
// syntax error occured
$errorInfo = $db->errorInfo();
// handle error
return false;
}
$db->execute($preparedSql);
// note that rowCount returns 0 when none updated
return $db->rowCount();
}
In your application
$result = update($db, $sql);
if($result === false)
{
// error occured
} elseif($result === 0) {
// zero rows updated
} else {
// some rows were updated
}
Note that it's not tested and for learning purposes only, update function should accept to bind parameters for real world application
Related
I'm maintaining a project based on legacy code that was written by an external company. We are in the process of fixing a lot of PHP errors in this code base and one of them comes from a call to mysqli::reap_async_query() :
mysqli::reap_async_query(): Connection not opened, clear or has been
closed
This happens only on the first call to this function:
function mysqlQuery($sql, $getLastId = false, $die = true, $alwaysDie = false, $async = false)
{
#$GLOBALS['con']->reap_async_query(); // This is what is triggering the error
if ($async) {
$result = $GLOBALS['con']->query($sql, MYSQLI_ASYNC);
} else {
$result = $GLOBALS['con']->query($sql);
}
if (!$result) {
if ($alwaysDie or ($_ENV['ENV_MODE'] === 'dev' and $die)) {
die('Etwas stimmte mit dem Query nicht: ' . $GLOBALS['con']->error . '<br/>Ganzes Query: ' . $sql);
}
return false;
} else {
$lastId = mysqlLastId();
if ($getLastId == true) {
return $lastId;
} else {
return $result;
}
}
}
Note that $GLOBALS['con'] is an instance of mysqli.
The code is calling mysqlQuery() with no specific parameter:
$result = mysqlQuery("SELECT * FROM someTable");
According to the Git history, the call to #$GLOBALS['con']->reap_async_query(); was added to support async SQL queries. But reading the PHP doc, it doesn't seem to be useful here at all since we are not storing its return value.
So my question is: is there a reason for it to be here, does calling it even without reading its return value have any important side effect ?
I might just remove this call completely if it is useless.
Also, why is it triggering this error ? I understand that trying to read a result before any query has been executed could trigger an error but the error indicates that the connection is not active, which does not seem to be the case.
Is there a reason for it [mysqli::reap_async_query()] to be here, does calling it even without reading its return value has any important side effect ?
The return value is not assigned to a local variable, however it is still returned.
So the original interest was in calling that function. And for what for, has been written about in the commit message.
According to the Git history, the call to #$GLOBALS['con']->reap_async_query(); was added to support async SQL queries.
Let's consider this example:
$con->query('SELECT SLEEP(5) as `zzzZZZ...Schnarch...Schmatz..zzz`', MYSQLI_ASYNC);
$con->reap_async_query();
How long does it take to execute this code?
This is the reason how that call supports async queries. If an async query would still run on the connection (it has not been reaped yet), every new query would fail.
So add of the call in-so-far supports async SQL queries as it allows to fire one on the same (shared) connection that might be in use for other queries, too.
Additionally you ask:
Also, why is it triggering this error ? I understand that trying to read a result before any query has been executed could trigger an error but the error indicates that the connection is not active, which does not seem to be the case.
Let's take a look at the error, actually a message on the diagnostic channel:
PHP Warning: mysqli::reap_async_query(): Connection not opened, clear or has been closed in ...
As we know the connection has been opened and has not been closed, the last point might be it:
[...] Connection [...] clear [...]
Now I have not programmed that error message, but my reading of it is that there is no async query running yet on the (open) connection - the connection is clear.
It produces a warning as this might not be intended (there is no need to reap a clear connection normally) and henceforth as with your function this is intended, the call is prefixed with the error suppression operator (#).
Thanks to #hakre's answer i understand that this call is there to get rid of the queue of async queries and i plan to fix it with a static flag to check if one is pending before calling #$GLOBALS['con']->reap_async_query():
function mysqlQuery($sql, $getLastId = false, $die = true, $alwaysDie = false, $async = false)
{
static $asyncPending = false;
if (true === $asyncPending) {
#$GLOBALS['con']->reap_async_query();
$asyncPending = false;
}
if ($async) {
$result = $GLOBALS['con']->query($sql, MYSQLI_ASYNC);
$asyncPending = true;
} else {
$result = $GLOBALS['con']->query($sql);
}
if (!$result) {
if ($alwaysDie or ($_ENV['ENV_MODE'] === 'dev' and $die)) {
die('Etwas stimmte mit dem Query nicht: ' . $GLOBALS['con']->error . '<br/>Ganzes Query: ' . $sql);
}
return false;
} else {
$lastId = mysqlLastId();
if ($getLastId == true) {
return $lastId;
} else {
return $result;
}
}
}
I am working on my first OO PHP website.
When I am calling an update function, for example to unblock an user, I will check if this was successful. If not I will throw an exception like this:
Business layer code:
if($DB->unblockUserByUsername($username) === FALSE){
throw new DBExpectedRowUpdate("Expected row update.");
}
Called function in DB Class:
public function unblockUserByUsername($input){
$sql = "UPDATE user SET ......"; //<- imagine valid update query
return $this->updateQuery($sql) === 1; // Return bool
}
I thought, I don't have update functions where I don't expect a row to be updated. So I will end up doing this check every time I call an update function in my upper layer.
So I thought isn't it better to throw this exception in the DB class update function itself? Like:
private function updateQuery($sql, $dontThrowError = FALSE){
$this->CheckConn(); try {
$affectedRows = $this->DBH->exec($sql.";");
$this->DBH->commit();
//Check affected rows count
if($affectedRows === 0 && $dontThrowError !== TRUE){
throw new DBExpectedRowUpdate("Expected row update.");
}
return $affectedRows;
} catch(PDOException $e) {
$this->DBH->rollback();
return -1; // Also throw error here
}
}
I am asking this because I can't seem to find a lot about this subject or best practice examples for a similar case.
I am currently developing a website which uses MySQLi for the database access. Currently the code that performs a database query looks something like this:
$query = "SELECT height, color FROM llamas WHERE name = ?";
if(!$stmt = $connection->prepare($query)) {
// Error handling here.
}
$stmt->bind_param("s", $name);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($height, $color);
$stmt->fetch();
$stmt->close();
The return values for most of these calls return FALSE to indicate a problem. From the PHP docs:
Returns TRUE on success or FALSE on failure.
If I attempt to check all of these function calls for false, I get something like this:
$query = "SELECT height, color FROM llamas WHERE name = ?";
if(!$stmt = $connection->prepare($query)) {
// Error handling here.
}
if(!$stmt->bind_param("s", $name)) {
// Error handling here.
}
if(!$stmt->execute()) {
// Error handling here.
}
if(!$stmt->store_result()) {
// Error handling here.
}
if(!$stmt->bind_result($height, $color)) {
// Error handling here.
}
if(!$stmt->fetch()) {
// Error handling here.
}
if(!$stmt->close()) {
// Error handling here.
}
My question is: Which of these function calls do I actually need to check for a return value of FALSE? And is there a tidy way of doing this?
I would use PDO and try - catch
try {
// your code here
} catch(PDOException $e){
error_log('ERROR: ' . $e->getMessage());
}
Edit: Here is the MySQLi version
try {
} catch (mysqli_sql_exception $e) {
}
The following code updates the table correctly, but it also returns an Exception. Any idea what might be happening here?
public function updateThis($aaa){
try
{
$success = false;
$query = "
UPDATE this_table
SET thing = '0'
WHERE aaa = :aaa";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':aaa', $aaa);
$stmt->execute();
if($this->conn->commit())
$success = true;
return $success;
}
catch(Exception $e)
{
return $e;
}
}
When you are using PDO, Auto-Commit is on by default, unless you specifically turn it off using Begin Transaction. I can't see it in your connection, so are you perhaps trying to commit a transaction that has already been auto-commited?
I am trying to capture database (MYSQL) errors in my PHP web application. Currently, I see that there are functions like mysqli_error(), mysqli_errno() for capturing the last occurred error. However, this still requires me to check for error occurrence using repeated if/else statements in my php code. You may check my code below to see what I mean. Is there a better approach to doing this? (or) Should I write my own code to raise exceptions and catch them in one single place? Also, does PDO raise exceptions? Thanks.
function db_userexists($name, $pwd, &$dbErr)
{
$bUserExists = false;
$uid = 0;
$dbErr = '';
$db = new mysqli(SERVER, USER, PASSWORD, DB);
if (!mysqli_connect_errno())
{
$query = "select uid from user where uname = ? and pwd = ?";
$stmt = $db->prepare($query);
if ($stmt)
{
if ($stmt->bind_param("ss", $name, $pwd))
{
if ($stmt->bind_result($uid))
{
if ($stmt->execute())
{
if ($stmt->fetch())
{
if ($uid)
$bUserExists = true;
}
}
}
}
if (!$bUserExists)
$dbErr = $db->error();
$stmt->close();
}
if (!$bUserExists)
$dbErr = $db->error();
$db->close();
}
else
{
$dbErr = mysqli_connect_error();
}
return $bUserExists;
}
I have created my own code to execute MySQL statements and raise exceptions if they fail, including specific exceptions for different causes of failure. One of the most helpful of these is when collisions occur, allowing me to use try..catch blocks and catch DatabaseCollisionExceptions, and handle those differently from other database exceptions.
What I found easiest for doing this was an MVC design pattern where every table was represented by a PHP class (models) which I could just assign member variables to and call a save method to save to the database, similar to:
try
{
$user = new User();
$user->username = 'bob';
$user->setPassword($_POST['password'); // Could do some SHA1 functions or whatever
$user->save
}
catch(DatabaseCollisionException $ex)
{
displayMessage("Sorry, that username is in use");
}
catch(DatabaseException $ex)
{
displayMessage("Sorry, a database error occured: ".$ex->message());
}
catch(Exception $ex)
{
displayMessage("Sorry, an error occured: ".$ex->message());
}
For more information on similar design patterns, see:
http://www.phpactiverecord.org/
http://book.cakephp.org/view/17/Model-Extensions-Behaviors
http://www.derivante.com/2009/05/14/php-activerecord-with-php-53/
Of course this isn't the only answer, it's just some ideas you might find helpful.
I think exceptions are the best approach. PDO does throw exceptions you just need to set PDO::ERRORMODE_EXCEPTION when you create the object.
this still requires me to check for
error occurrence using repeated
if/else statements
How's that?
You don't need to check for every possible error.
I'd make just final check for the returned value and nothing else.
For what purpose do you use these nested conditions?
You may rewrite it in the following manner:
$bUserExists = false;
$uid = false;
if (!mysqli_connect_errno())
{
if($stmt = $db->prepare("select uid from user where uname = ? and pwd = ?")
->bind_param("ss", $name, $pwd))
{
$stmt->execute();
$stmt->bind_result($uid);
$stmt->fetch();
$stmt->close();
}
if ($uid)
$bUserExists = true;
$db->close();
}
else
{
$dbErr = mysqli_connect_error();
}