I've browsed the web quite a bit before asking here; I noticed that some people have the same problem as me, but none of the answers that were given to the others didn't solve my problem, so....
I have a basic PDO Update Statement inside a public function:
public function editRank($name, $rank){
$query = "UPDATE `chat_mod` SET `chat_mod_rank` = :rank WHERE `chat_mod_ign` = :username";
$prepare = $this->_db->prepare($query);
$array = array(
':rank' => $rank,
':username' => $name
);
try {
$prepare->execute($array);
} catch (PDOException $e){
echo 'Error: ' . $e->getMessage();
return false;
}
return true; // If no PDO Exception is thrown..
}
No exception is thrown, so the function always returns true; but the rows are not being updated. Yes, I've checked that the rows are named properly and the values are not nulls.
Thanks,
Tom.
P.S Other queries like Select, Add & Delete work fine.
You are catching PDO exceptions but did you tell PDO to throw them?
To make PDO throw exceptions you have to configure PDO errmode. Note that setting this mode as a connection option will let PDO throw exceptions on connection errors too, which is very important.
So, here is an example for creating a PDO connection right way:
$dsn = "mysql:host=$host;dbname=$db;charset=utf8";
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// other options
);
$pdo = new PDO($dsn, $user, $pass, $opt);
Connecting this way, you will be always notified of all database errors, occurred during query execution. Note that you have to be able to see PHP errors in general. On a live site you have to peek into error logs, so, settings have to be
error_reporting(E_ALL);
ini_set('display_errors',0);
ini_set('log_errors',1);
while on a local development server it's ok to make errors on screen:
error_reporting(E_ALL);
ini_set('display_errors',1);
and of course you should never ever use error suppression operator (#) in front of your PDO statements.
Also, due to many bad examples telling you to wrap every PDO statement into try..catch block, I have to make a distinct note:
DO NOT use try..catch operator just to echo an error message. Uncaught exception is already excellent for this purpose, as it will act just the same way as other PHP errors - so, you can define the behavior using site-wide settings - so, you will have your error message without this useless code. While unconditionally echoed error message may reveal some sensitive information to a potential attacker, yet confuse a honest visitor.
A custom exception handler could be added later, but not required. Especially for new users, it is recommended to use unhandled exceptions, as they are extremely informative, helpful and secure.
Use try..catch only if you are going to handle the error itself - say, to rollback a transaction.
Related
I try to handle or catch possible errors with PHP/MySQL for security reasons and would like to know if I'm doing it right.
The first case: I use it as a function and call it always when I need a database connection.
The second case: I am not sure how to handle it. I put all prepared statements in if conditions but that's pretty awkward.
And what about $stmt->execute? This could also fail and to handle this also in an if condition can really get confusing.
I hope there is a better way to go.
First:
function pdo () {
try {
$pdo = new PDO('mysql:host=localhost;dbname=dbname', 'user', 'pw');
}
catch(PDOException $e) {
header("Location: handle_error.php");
exit;
}
return ($pdo);
}
Second:
if ($stmt = $pdo->prepare("SELECT a FROM b WHERE c = :c")) {
$stmt->execute(array(':c' => $c));
$result = $stmt->fetch();
echo 'All fine.';
}
else {
echo 'Now we have a problem.';
}
Your current code has some flaws. Let me give you few pointers.
You do not need the function you have created, at least not in the current form. Every time you call this function it creates a new PDO object, which can hinder your script's performance. Ideally you would want to have only one connection throughout the execution of your whole script.
When creating new PDO connection you need to remember 3 things: to set proper connection charset, enable error reporting, and disable emulated prepares.
Proper charset is important. Without it your data could get corrupted or even leave you vulnerable to SQL injection. The recommended charset is utf8mb4.
Enabling error reporting saves you the trouble of manually checking every single function call for failures. You just need to tell PHP to trigger exceptions when an error occurs and find a suitable way of logging the errors on your server (read more: My PDO Statement doesn't work)
Emulated prepares are enabled by default, but the truth is you are better off without them. If your database supports native prepared statements, and most do, then use them instead.
The full code should look something like this:
$options = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new \PDO('mysql:host=localhost;dbname=dbname;charset=utf8mb4', 'user', 'pw', $options);
Don't catch the exceptions unless you know what to do with them and are positively sure you need to do so. Exceptions are best left be; let PHP handle them together with the other exceptions/errors/warnings. After all why make an exception just for PHP exceptions? All PHP errors should be handled the same. Whatever you do never print (including die, exit, echo, var_dump) the error message manually on the screen. This is a huge security issue if such code ever makes its way into production.
If your PDO connection is set to throw exceptions on errors, you never need to use if statements to check return code of prepare() or execute().
Get info passed by POST method, and trim all space in the string, then start a new pdo instance, connect mysql, and insert info passed by POST into table.
$title = trim($_POST["title"]);
$content = trim($_POST["content"]);
$dsn = "mysql:host=localhost;dbname=blog";
$con = new PDO($dsn,"root","xxxx");
$title = $con->quote($title);
$content = $con->quote($content);
try
{
$sql = "insert into tmp (`title`,`content`) values('$title','$content')";
$stmt = $con->prepare($sql);
$stmt->execute();
}
catch(PDOException $e)
{
echo $e->getMessage();
}
The above is my PHP code to make the job done,the most import command is
insert into tmp (`title`,`content`) values('$title','$content')";
No error info is shown by running the above PHP code, and no error exists in /var/log/mysql/error.log, but info has not been inserted into the database.
I changed the
insert into tmp (`title`,`content`) values('$title','$content')";
into
insert into tmp (`title`,`content`) values($title,$content)";
The info passed by POST can be inserted into mysql now, the issue that confuses me is that:
echo $e->getMessage(); take no effect at all.
no error info in /var/log/mysql/error.log
How can I catch these errors?
The exception you are trying to catch will never be thrown, because you need to tell PDO how you want it to handle errors.
$con = new PDO($dsn,"root","xxxx");
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Otherwise, the default PDO::ERRMODE_SILENT will be used:
This is the default mode. PDO will simply set the error code for you to inspect using the PDO::errorCode() and PDO::errorInfo() methods on both the statement and database objects; if the error resulted from a call on a statement object, you would invoke the PDOStatement::errorCode() or PDOStatement::errorInfo() method on that object. If the error resulted from a call on the database object, you would invoke those methods on the database object instead.
Tangentially, you should be using prepared statements. You are using a prepare() call, but you are not parametrizing the query and binding the variables as you should. Using quote() is not secure enough.
2020 Update:
Interestingly, starting with PHP 8, the default behaviour for PDO will change and will throw exceptions by default. The change was voted on this RFC, which mentions:
The current default error mode for PDO is silent. This means that when an SQL error occurs, no errors or warnings may be emitted and no exceptions thrown unless the developer implements their own explicit error handling.
This causes issues for new developers because the only errors they often see from PDO code are knock-on errors such as “call to fetch() on non-object” - there's no indication that the SQL query (or other action) failed or why.
When PHP 8 is released on November 2020, the default error mode will be PDO::ERRMODE_EXCEPTION.
I'm tweaking a legacy database class written for PHP/5.2 that was designed to connect to MySQL and hide all errors. I've configured the PDO instance to throw exceptions:
new PDO($dsn, $user, $pass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION))
Now I need to adjust my code accordingly to handle the case where functions can throw an exception where they wouldn't before so I head to the manual.
In some cases the manual is explicit, e.g. PDO::prepare:
If the database server successfully prepares the statement,
PDO::prepare() returns a PDOStatement object. If the database server
cannot successfully prepare the statement, PDO::prepare() returns
FALSE or emits PDOException (depending on error handling).
In other cases it's kind of vague, e.g. PDO::commit:
Returns TRUE on success or FALSE on failure. Throws a PDOException
if there is no active transaction.
(Can it fail for some other reason and simply return false?)
And there're cases when exceptions are not even mentioned, e.g. PDO::bindValue:
Returns TRUE on success or FALSE on failure.
... even though it's clear to verify that it does throw PDOException on error (at least, on certain errors).
Do I still need to check the return value of methods that return false on error when the manual doesn't say otherwise?
No, when you set PDO::ERRMODE_EXCEPTION there will be always exception for any errors
When you have exceptions enabled and there is an error happening in your code, the code directly stops executing from that function, so it wont return anything.
So to be blund, you don't need to check the return values but if you don't you cannot error anything specific and you would rely fully on PDO to error correctly.
When I created my database system, I take advantage of both if I see an error upcoming, I throw it myself instead. For example $pdo->prepare('') is very much valid but will error upon binding.
Then there are other functions such as fetch that wont error if there are no results in the database, not checking the results from that would be silly.
Now for committing to fail, I believe there is 1 scenario that would cause it to return false without throwing an exception and that is when the connection to the server drops after connecting to the database and before calling PDO::commit, quite good to know if you have a remote database server.
So to answer your question, yes it can fail without throwing an exception, but its timing has to be very specific even more so if you have a local database.
Try this one
try {
$db = new PDO("mysql:host=localhost;dbname=dbname", "username", "password");
}catch( PDOException $Exception ) { $Exception->getMessage( ) ; }
A lot of tutorials and books I have been over and read have used the die() method to catch an exception when interacting with a local MySQL database
For example:
mysql_connect($dbhost, $dbuser, $dbpass)or die(mysql_error());
Would a try/catch block be more beneficial over the die() method or is that just the standard way that exception handling works with db connections?
or die() is an extremely primitive way to "handle" errors and only for examples or debugging at best. In practice, it depends on how you handle your errors. You may want to return false from a function call or you may want to throw your own exception instead; e.g.:
if (!$con = mysql_connect(..)) {
throw new DatabaseConnectionError(mysql_error());
}
try..catch will do exactly nothing with mysql, since mysql never throws any exceptions. It only ever returns false on failure.
You will have to have your own error handling strategy. You'll probably want to log errors and display a user friendly error page instead of cryptic error messages. mysql is not concerned with that part. It only gives you a way to check whether an operation was successful or not (check if it returns false); what you do with this information is up to you. die kills the entire application and at least doesn't allow the problem to propagate further; but it certainly does not display any user friendly error pages.
Having said all this, mysql is old and deprecated. If you'd use something newer like PDO instead, it can properly throw exceptions itself.
The mysql_connect method does not throw exceptions and thus die() is used by many applications to terminate when there is no connection available.
You can use the solution mentioned here: how to use throw exception in mysql database connect
Included for completeness:
try
{
if ($db = mysqli_connect($hostname_db, $username_db, $password_db))
{
//do something
}
else
{
throw new Exception('Unable to connect');
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
Alternatively use the new and more OOP styled database access: http://php.net/manual/en/book.pdo.php
The reason a lot of applications uses die is from the fact that they are so reliant on the database that continuing without a connection is utterly fruitless.
Edit As mentioned in the comments, the code example above is for illustrational purposes. Catching right after throwing is pointless.
I've recently changed all my mysql.* to PDO. I've been having a few issues and trying to get used to it.
My question is how to properly call an error when there is an issue in the sql statement. Normally I wouldn't use try, catch but is there another alternative?
Suppose I have the following:
private function init()
{
$query = $this->_PDO->prepare("SELECT * FROM here WHR name='john'");
if($query->execute())
{
$this->_sql_rows = $query->rowCount();
}
else
{
print_r($query->errorInfo());
}
}
This checks whether the execute method worked and if not, output errors. In this case it should as there is a spelling mistake. Normally when I do this, I never see any errors come out and must always use the method above to output an error. Is this a reliable and appropriate way of handling such errors?
Nope, it is not.
Just like almost every PHP user, you have quite vague and uncertain idea on error handling. And the code mostly looks like a dummy code from sandbox example. Speaking of your current approach, frankly - it's just terrible. On a live site it will only scare an innocent user, while webmaster would have no idea what's going on.
So, first of all you have to make your mind what is error handling you are looking for.
There are different possible scenarios, but most convenient is just to follow the way PHP handles all other errors. To do that just set PDO in exception mode. This way you'll be always notified of all the errors occurred. So - just add this line right after connect.
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
and thus your function become short and clean
private function checkUser($user)
{
$stmt = $this->_PDO->prepare("SELECT 1 FROM users WHERE name=?");
$stmt->execute(array($user));
return $stmt->fetchColumn();
}