PHP try/catch and fatal error - php

I'm using the following script to use a database using PHP:
try{
$db = new PDO('mysql:host='.$host.';port='.$port.';dbname='.$db, $user, $pass, $options);
}
catch(Exception $e){
$GLOBALS['errors'][] = $e;
}
Now, I want to use this database handle to do a request using this code:
try{
$query = $db->prepare("INSERT INTO users (...) VALUES (...);");
$query->execute(array(
'...' => $...,
'...' => $...
));
}
catch(Exception $e){
$GLOBALS['errors'][] = $e;
}
Here is the problem:
When the connection to the DB is OK, everything works,
When the connection fails but I don't use the DB, I have the $GLOBALS['errors'][] array and the script is still running afterwards,
When the connection to the DB has failed, I get the following fatal error:
Notice: Undefined variable: db in C:\xampp\htdocs[...]\test.php on line 32
Fatal error: Call to a member function prepare() on a non-object in C:\xampp\htdocs[...]\test.php on line 32
Note: Line 32 is the $query = $db->prepare(...) instruction.
That is to say, the script crashes, and the try/catch seems to be useless. Do you know why this second try/catch don't works and how to solve it?
Thanks for the help!
EDIT: There are some really good replies. I've validated one which is not exactly what I wanted to do, but which is probably the best approach.

try/catch blocks only work for thrown exceptions (throw Exception or a subclass of Exception must be called). You cannot catch fatal errors using try/catch.
If your DB connection cannot be established, I would consider it fatal since you probably need your DB to do anything meaningful on the page.
PDO will throw an exception if the connection cannot be established. Your specific problem is that $db is not defined when you try to call a method with it so you get a null pointer (sort of) which is fatal. Rather than jump through if ($db == null) hoops as others are suggesting, you should just fix your code to make sure that $db is either always defined when you need it or have a less fragile way of making sure a DB connection is available in the code that uses it.
If you really want to "catch" fatal errors, use set_error_handler, but this still stops script execution on fatal errors.

In PHP7, we now can using try catch fatal error with simple work
try {
do some thing evil
} catch (Error $e) {
echo 'Now you can catch me!';
}
But usualy, we should avoid using catch Error, because it involve to miss code which is belong to programmer's reponsibility :-)

I will not report what has already been written about testing if $db is empty. Just add that a "clean" solution is to artificially create an exception if the connection to the database failed:
if ($db == NULL) throw new Exception('Connection failed.');
Insert the previous line in the try - catch as follow:
try{
// This line create an exception if $db is empty
if ($db == NULL) throw new Exception('Connection failed.');
$query = $db->prepare("INSERT INTO users (...) VALUES (...);");
$query->execute(array(
'...' => $...,
'...' => $...
));
}
catch(Exception $e){
$GLOBALS['errors'][] = $e;
}
Hope this will help others!

If database connection fails, $db from your first try .. catch block will be null. That's why later you cannot use a member of non-object, in your case $db->prepare(...). Before using this add
if ($db) {
// other try catch statement
}
This will ensure that you have db instance to work with it.

Try adding the following if statement :
if ($db) {
$query = $db->prepare("INSERT INTO users (...) VALUES (...);");
$query->execute(....);
}
else die('Connection lost');

try{
if(!is_null($db))
{
$query = $db->prepare("INSERT INTO users (...) VALUES (...);");
$query->execute(array(
'...' => $...,
'...' => $...
));
}
}
catch(Exception $e){
$GLOBALS['errors'][] = $e;
}

Related

try{...}catch does not throw exception in PHP/Symfony2/PHPImap

I am using the ImapMail Library and try to initiate a new Mailbox:
try{
$mailbox = new ImapMailbox($url, $username, $password);
}catch (\Exception $e){
return new Response("fail");
}
But the above try/catch does not work, although symfony gives me this:
Connection error: (is empty)
500 Internal Server Error - Exception
the Stacktrace
in vendor/php-imap/php-imap/src/PhpImap/Mailbox.php at line 67:
protected function initImapStream() {
$imapStream = #imap_open($this->imapPath, $this->imapLogin, $this->imapPassword, $this->imapOptions, $this->imapRetriesNum, $this->imapParams);
if(!$imapStream) {
throw new Exception('Connection error: ' . imap_last_error());
}
return $imapStream;
}
(Direct link to function on github)
It does throw a new Exception, why can't I catch it?
Any hint appreciated!
The reason is very simple. You call only class constructor in try block. If you look into the class source code, you will see that constructor does call nothing.
https://github.com/barbushin/php-imap/blob/master/src/PhpImap/Mailbox.php#L20-L31
It means code throwing exception must be under you try/catch. You have to move it inside try block if you want to catch the exception. I am talking about $mailbox->checkMailbox(). This command throws exception.

fatfree SQL error handling

If, for whatever reason, there is an error in creation of an entry using the mapper I get an error.
I'd like to do a custom notification and fail gracefully like so...
try {
$request->save();
} catch (Exception $e) {
$this->utils->errorNotify($f3,'could not create a request entry',http_build_query($_POST));
return null;
}
is this possible with F3?
\DB\SQL is a subclass of PDO so it can throw catchable PDO exceptions. Since these are disabled by default, you need to enable them first. This can be done in 2 different ways:
at instantiation time, for all transactions:
$db = new \DB\SQL($dsn, $user, $pwd, array( \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION ));
later on in the code, on a per-transaction basis:
$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
Once PDO exceptions are enabled, just catch them as other exceptions:
try {
$db->exec('INSERT INTO mytable(id) VALUES(?)','duplicate_id');
} catch(\PDOException $e) {
$err=$e->errorInfo;
//$err[0] contains the error code (23000)
//$err[2] contains the driver specific error message (PRIMARY KEY must be unique)
}
This also works with DB mappers, since they rely on the same DB\SQL class:
$db=new \DB\SQL($dsn,$user,$pwd,array(\PDO::ATTR_ERRMODE=>\PDO::ERRMODE_EXCEPTION));
$mytable=new \DB\SQL\Mapper($db,'mytable');
try {
$mytable->id='duplicate_id';
$mytable->save();//this will throw an exception
} catch(\PDOException $e) {
$err=$e->errorInfo;
echo $err[2];//PRIMARY KEY must be unique
}

ZF2: How to get error info from Zend\Db

I am connecting to MySQL through PDO with Zend\Db from ZF2. How can I report the last errorInfo()?
Here's what I have:
$sqlWriter = new Sql($this->getAdapter());
$insert = $sqlWriter->insert('table_name')->columns(array_keys($data))->values($data);
$stmt = $sqlWriter->prepareStatementForSqlObject($insert);
try {
$stmt->execute();
$object->id = $this->getAdapter()->driver->getLastGeneratedValue();
} catch (\Exception $e) {
//
// HOW CAN I display errorInfo() here?
//
throw new Exception\Exception('Unable to insert record...');
}
I have tried calling methods on the adapter, driver, statement, platform, result, etc... But all to no avail...
EDIT: I found that I can get the info I am looking for by posting the following at the top of the catch block:
$pdoException = $e->getPrevious();
var_dump($pdoException);
I'll leave the question open however since it would be good to know how to execute PDO::errorInfo() directly.

How do I raise PDOException?

This code works fine, but I'll want to handle exception if any thing goes wrong, so I deliberately made a syntax error in the query but nothing happens. Below is the code
try {
$sql = "INSERT INTO journals (topic, author, ) VALUES ('$topic', '$authors', ')";
echo "1st";
$lecturers_db->query($sql);
echo "second";
} catch(PDOException $e) {
echo $e->getMessage();
echo $msg = "Error!";
}
Without the obvious syntax error, the code works fine but with the syntax error, nothing happens, all the code in the try block executes and the code in the catch block never executes.
I want to raise an exception, please how do I do it here, thanks for any help.
Be sure to set the attribute PDO::ATTR_ERRMODE to PDO::ERRMODE_EXCEPTION, as soon as you init your pdo object:
$lecturers_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
After that, any failed queries will raise an exception
Exceptions are thrown. Syntax errors in code != Exceptions.
<?php
try {
$code = 12;
throw new PDOException('Message', $code );
} catch (PDOException $e) {
}
?>
However, from the maual:
You should not throw a PDOException from your own code. See Exceptions
for more information about Exceptions in PHP.
My advice is to throw either a general exception, or to write your own custom exception to handle your error.

PHP SQLite3 error?

How do I know if there's an error if I did $db = new SQLite3("somedb.db"); in PHP? Right now the $db doesn't really give me any sort of error?
I can check for file existance, but I'm unsure if there could be any other errors when I open a connection.
You should enable exceptions and instantiate in a try-catch block.
It is not obvious from the documentation but if you use the constructor to open the database it will throw an exception on error.
Further if you set the flag SQLITE3_OPEN_READWRITE in the second argument then it will also throw an exception when the database does not exist (rather than creating it).
class Database extends SQLite3
{
function __construct($dbName)
{
$this->enableExceptions(true);
try
{
parent::__construct($dbName, SQLITE3_OPEN_READWRITE );
}
catch(Exception $ex) { die( $ex->getMessage() ); }
}
Try:
echo $db->lastErrorMsg();

Categories