I am creating a model for a small PHP application. This will utilize PDO to communicate with a MySQL-server. I have understood that the recommended error mode is the one which throws exceptions, as this allows for graceful error handling. But I don't understand how I should handle these exceptions?
Technically, it is easy, but let me give you an example:
class Model()
{
private $host = "localhost",
$user = "",
$pass = "",
$DBH;
function __construct()
{
try
{
$DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
catch(PDOException $e)
{
error_log($e->getMessage());
}
}
}
If I create an object Model in my controller, and it fails, I have no way of handling this in my controller, right? Or what happens when I create that object, "new Model" returns false?
Excuse me for being a newbie, but I want to be able to handle any exceptions also from other functions in the model. How should I go about this? I need to be able to know if something went wrong in my controller and be able to do the appropriate thing there.
If you want your controller to catch the exception as well, you can always rethrow it after logging.
class Model()
{
...
function __construct()
{
try
{
...
}
catch(PDOException $e)
{
error_log($e->getMessage());
throw $e;
}
}
}
Depends entirely on what the error_log function does.
You can return the exception.
You can simply die after logging the error (presumably if your application can't hit the DB then theres no graceful recovery).
You can return a custom exception via throw();
It's really just your preference.
Related
I am currently writing a web app in PHP and have decided to use exceptions (duh!).
I could not find an answer to whether putting try and catch blocks in all functions would be considered bad code.
I am currently using Exceptions to handle Database errors (Application errors are handled via a simple function which just adds them to an array and then they are displayed to the user). The try blocks are placed on all functions which require a database connection.
The code in question is:
public function db_conn_verify()
{
if(!isset($this->_mysqli)){
throw new Exception("Network Error: Database connection could not be established.");
} else {
return Null;
}
}
And an example function using this code:
public function get_users() {
try {
$this->db_conn_verify();
//Rest of function code
return True;
} Catch(Exception $e) {
Core::system_error('function get_users()', $e->getMessage());
return False;
}
}
Also would it be better to extend the Exception class and then use that new Exception class to handle application errors?
Thanks
I suggest to you to use something like this:
public function get_users() {
try {
if( !isset($this->_mysqli) ) {
throw new Exception("Network Error: Database connection could not be established.");
}
//Rest of function code
} Catch(Exception $e) {
Core::system_error('function get_users()', $e->getMessage());
}
}
I prefer to use my exends of Exception, but is the same. For exdending exception you can see the PHP documentation to Example #5
EDIT: For an immediate use of try-catch on database connection error you can try this:
try{
$mysqli = new mysqli("localhost", "user", "password", "database");
if ($mysqli->connect_errno) {
throw new Exception("Network Error: Database connection could not be established.");
}
} Catch(Exception $e) {
Core::system_error('function get_users()', $e->getMessage());
}
I have a database class dbconnect.php, and processform.php. Inside dbconnect.php there is a method for connecting to the database.
If there's an error, how do I throw an exception? Where do I put the try catch block, in the processform.php? People say I shouldn't echo an error directly from inside the class. Here's an example:
<?php
// dbconnect.php
class DbConnect
{
public function open_connection()
{
/* Should I do it like this? */
$this->conn = PDO($dsn, $this->username, $this->password);
if (!$this->conn) {
throw new Exception('Error connecting to the database.');
}
/* Or like this */
try {
$this->conn = PDO($dsn, $this->username, $this->password);
} catch (PDOException $e) {
echo 'Error: ', $e->getMessage(), '<br>';
}
}
?>
// processform.php
<?php
require_once 'dbconnect.php';
$pdo = new DbConnect($host, $username, $password);
try {
$pdo->open_connection();
} catch (PDOException $e) {
echo 'Error connecting to the database.');
}
?>
I really want to learn the correct way of implementing the try catch in my code.
You don't have to throw an exception manually, especially on a successful connect :-)
Instead you need to tell PDO that it needs to throw exceptions when something goes wrong and you can do that when you open your database connection:
$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
$this->conn = new PDO($dsn, $this->username, $this->password, $options);
Now you can put everything in try / catch blocks but that is not even necessary; if you don't do that, php will show you unhandled exceptions complete with a stack trace when you don't catch them manually.
And when you decide you want to fine-tune your error handling for your visitors, you can set your own exception handler using set_exception_handler(). That way you can handle everything at one place instead of wrapping different sections in try / catch blocks. Should you prefer that of course.
In my practice, I prefer to catch exception in bottom. I mean, second way in your DbConnect.
You can output error message to error log. And return an error code to front-end. So the front-end knows how to tell users an error occours in a friendly way.
What's more, you can use global error handler such as set_error_handler/set_exception_handler to do this. Redirect to an error page when error occours.
I have an abstract classes for update, select, delete and insert statements. in my database connection function, config.php I have
function dbconnect()
{
$dbh; // database handler
$host = 'localhost';
$user = 'root';
$pass = '';
$dbname = '';
$error;
// Set DSN
$dsn = 'mysql:host=' . $host . ';dbname=' . $dbname;
// Set options
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
);
// Create a new PDO instanace
try{
$dbh = new PDO($dsn, $user, $pass, $options);
}
// Catch any errors
catch(PDOException $e){
$error = $e->getMessage();
}
return $dbh;
}
notice try catch an error
in my abstract class.
public function __construct() {
$this->dbh = dbconnect();
}
public function query($query) {
$this->stmt = $this->dbh->prepare($query);
}
public function bind($param, $value, $type = null) {
if(is_null($type)) {
switch (true) {
case is_int($value):
$type = PD0::PARAM_INT;
break;
case is_bool($value);
$type = PDO::PARAM_BOOL;
break;
case is_null($value);
$type = PDO::PARAM_NULL;
break;
default :
$type = PDO::PARAM_STR;
}
}
$this->stmt->bindValue($param, $value, $type);
}
}
My question
In the construct I connect to the database which also means I am catching errors here and in the other function I have not error handling and I am not returning any errors in it. Is they any point in me having error handle in other functions I have and if yes how do you suggest I handle this? I am still need to PDO and OOP in general but I do try to do things the right way. Please advice
Basically, if you cannot connect to the database, there's no point in having any of the other code, right? Then simply do not catch the exception. The exception will bubble up and even prevent your class from being instantiated, which means you do not need to worry about error handling in any of the other code because it can never be executed. That's the point of throwing exceptions in constructors.
Exceptions are for signalling exceptional circumstances. If you cannot connect to the database, that's quite an exceptional circumstance. That renders your entire database layer non-functional. Throwing an exception from the database layer is the correct thing to do here, because the database layer is non-operational and cannot handle this problem itself. It needs to signal to the rest of the system that it cannot work and the rest of the system needs to decide what to do in this case. Catch the exception higher up in your app, if at all, and react to it by displaying an error page there.
For other exceptions which may happen during regular queries, the same rule applies. Decide whether the problem is recoverable and whether it makes sense to catch and handle it inside your database class, or whether an exception (which, again, is something which should never ever happen) means it's time to give up.
The only time you might need to handle an Exception in a constructor for a database wrapper is to ensure a particular type of Exception is thrown and bubbled up to the application - though I can only see this being useful if you're building a wrapper that can switch database connection objects.
If you're using a Factory pattern for instance that can create either a mysqli or a PDO object you might want the constructor to catch mysqli::connect_error (not an Excepton) AND PDOException and always throw a common AppException (which extends Exception).
So in the mysqli constructor you might simply have:
$dbConn = new mysqli($sHost, $sUsername, $sPassword, $sSchema, $iPort);
if($dbConn->connect_error) {
throw new AppExecption(AppException::DB_CONNECT, $dbConn->connect_error);
}
Whilst in the PDO wrapper constructor you might have:
try {
$dbConn = new PDO($sDSN, $sUsername, $sPassword);
}
catch(PDOException $e) {
throw new AppException(AppException::DB_CONNECT, $e->getMessage());
}
So you're not really handling the PDOException in the constructor - merely converting it into a common type of Exception which could be handled in a specific way when it's bubbled up to the application irrespective as to whether you're constructing a mysqli object or a PDO one.
I use try/catch block in my classes methods, If a get an exception, I log the error. But I would like to tell the "User" that a database query/etc failed - and the problem should be fixed soon.
I could use a die() on the Exception in my methods, but that wouldn't be DRY, as I would have to retype it a lot, so any suggestions on how I can do this.
Example method:
public function login($username, $password) {
try {
$this->STH = $this->DBH->prepare("SELECT id, baned, activated FROM users WHERE username = ? AND password = ?");
$this->STH->setFetchMode(PDO::FETCH_OBJ);
$this->STH->execute(array($username, $password));
if (($row = $this->STH->fetch()) !== false)
return $row;
} catch (PDOException $e) {
//Log $e->getMessage();
die('A database error occoured, we are working on the problem, and it should work in a few...');
}
}
If you need a quick fix, you can set a global exception handler, like this:
function pdo_exception_handler($exception) {
if ($exception instanceof PDOException) {
// do something specific for PDO exceptions
} else {
// since the normal exception handler won't be called anymore, you
// should handle normal exceptions yourself too
}
}
set_exception_handler('pdo_exception_handler');
It's OK to repeat yourself in this case because as each instance of die() passes a unique message.
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();