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.
Related
Hi I have the following code :
try {
$sth = $this->container->db->prepare("select x from table");
$sth->execute();
$result = $sth->fetchAll(PDO::FETCH_ASSOC);
return $result;
} catch (\PDOException $e) {
throw new ServerException("Could not get data");
} catch (\Exception $e) {
return false;
}
using slim 3 with wamp
the problem is when I point to the API(polling every 1 second) I got the following error:
Fatal error: Call to a member function prepare() on boolean in /
2 issues : it throws an error to the client
and it throws an error in php_error.log under wamp and the file becomes bigger
how can I prevent and catch those errors
PDO class:
public function getConnection($dsn, $username, $password) {
$conn = null;
try {
$conn = new PDO($dsn, $username, $password);
//Set common attributes
$conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
return $conn;
} catch (PDOException $e) {
return false;
//TODO: flag to disable errors?
throw $e;
}
catch(Exception $e) {
die();
//TODO: flag to disable errors?
throw $e;
}
}
Like many learners, you are taking this matter upside down
You should never actually catch an exception like this.
it throws an error to the client
Disable it for the whole site on the live server. There should be not a single PHP error message shown to the user, no matter if it's PHP exception or a filesystem error. Set display_errors to a negative value and forget this matter for all.
how can I prevent and catch those errors
Again, you should never do anything like this, bluntly catching every error and just dismissing it. It's like using the notorious # operator
it throws an error in php_error.log under wamp and the file becomes bigger
Ok, only this one makes sense. There are two possible solutions:
The best one: configure your mysql server properly so it wouldn't die under such a light load like 1 RPS.
Okay, what you actually want but I still don't recommend as it never pays to sweep the dirt under the rug: catch the exception, then verify if it's one you expect, then do something (i.e. try to reconnect after a short timeout), but re-throw the exception otherwise so you will have an idea when something else would go wrong. For this purpose you should add a condition in the try..catch block that should verify the error, handle it if it's one that you expect or just throw it again otherwise.
Of course, in order to catch a PDOException you have to enable it for PDO.
1) In Slim you should use the container (service factory) to build the PDO object. Example:
$container['db'] = function (Container $container) {
$settings = $container->get('settings');
$host = $settings['db']['host'];
$dbname = $settings['db']['database'];
$username = $settings['db']['username'];
$password = $settings['db']['password'];
$charset = $settings['db']['charset'];
$collate = $settings['db']['collate'];
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES $charset COLLATE $collate"
];
return new PDO($dsn, $username, $password, $options);
};
2) You must set the PDO options into the constructor to make it work. Example:
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
];
$pdo = new PDO($dsn, $username, $password, $options);
I have been looking in to this almost all day.. and can't seem to find the values returned anywhere. Can somebody tell me:
What values do PDO::getAttribute(PDO::ATTR_CONNECTION_STATUS); return?
Is it possible to rely on its result to determinate if the connection is still alive?(And eventually, what could I use to check if the connection is still alive?)
Finally! it turns out that the mysqli::ping() function could be implemented within PDO as follows:
class PDOExtended extends PDO {
public function __construct($dsn, $user, $pass, $options = array())
{
$this->link = parent::__construct($dsn, $user, $pass, $options);
$this->link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
}
// some methods
public function isConnected()
{
try {
return (bool) $this->link->query('SELECT 1+1');
} catch (PDOException $e) {
return false;
}
}
//some other methods
}
REASON:
PDO::query(); returns array containing the results or false, In the current case it won't return nothing, cuz the connection is dead and PDO should throw an exception at us. And that is what we are expecting. The catch block will return false and and will not stop the execution of our script. The query used
SELECT 1+1;
will return 2 always and it is good to rely on due to the fact that it is calculated on the DB side. No connection, no result! It is not an overkill because it is very simple query and most of the databases (on normal shared host) are on localhost it will not take more than 0.0000s which is not much of a performance issue. Have not tested it with transactions yet, but should do the trick still.
Here is Database.php:
<?php
/*Data Base Class
* MySQL - InnoDB
* PHP - PDO (PHP Data Object -So we could change databases if needed)
*/
class Database extends PDO{
private $DBH;
function __construct($host, $dbname, $user, $pass){
try {
$this->DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
} catch (PDOException $e) {
return $e->getMessage();
}
}
public function alteration_query($sql){
/* Begin a transaction, turning off autocommit */
$this->DBH->beginTransaction();
try{
$count = $this->DBH->exec($sql);
$this->DBH->commit();
return $count;
}catch (PDOException $e) {
$this->DBH->rollback();
return $e;
}
}
}
?>
Here is test.php:
<?php
require('Database.php');
$dbo = new Database('***.***.com','***','***','***');
echo $dbo->alteration_query('DELETE * from T_Table');
?>
For some reason, it won't give me an error or delete the contents of the T_table.
EDIT: The problem in your case is that the argument is called $sql, but you're using $query to execute it (in the alteration_query method). Next time, please enable error reporting, and/or use a decent IDE, that can show you these errors. Like so:
EDIT2: Set PDO's error mode to exception, this way any error would throw an exception. See updated code.
Don't catch your exceptions inside of the functions, do it outside:
<?php
/*Data Base Class
* MySQL - InnoDB
* PHP - PDO (PHP Data Object -So we could change databases if needed)
*/
class Database
{
private $DBH;
function __construct($host, $dbname, $user, $pass)
{
$this->DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
//Set PDO to throw exceptions on errors!
$this->DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function alteration_query($sql)
{
/* Begin a transaction, turning off autocommit */
$this->DBH->beginTransaction();
$count = $this->DBH->exec($query);
$this->DBH->commit();
return $count;
}
}
try {
$pdo = new Database("localhost", "dbname", "user", "pass");
$pdo->alteration_query("SELECT * FROM wrong_table");
}
catch (PDOException $e) {
die("An error has occured! " . $e->getMessage());
}
This way, you can catch the error exactly where you need it, and not force it inside of the function (which kinda beats the point of the exception).
Also, from the manual:
When the script ends or when a connection is about to be closed, if
you have an outstanding transaction, PDO will automatically roll it
back. This is a safety measure to help avoid inconsistency in the
cases where the script terminates unexpectedly--if you didn't
explicitly commit the transaction, then it is assumed that something
went awry, so the rollback is performed for the safety of your data.
Exceptions halt the execution of the function, meaning the commit() never happens, and it rolls back.
Try this line :
$this->DBH = new parent::__construct("mysql:host=$host;dbname=$dbname", $user, $pass);
Reason : __construct overwrite the main class function. I think that's why, really not sure.
If it's not working, try parrent::PDO. I am pretty sure it an overwrite problem.
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.
I built this class to work with PDO, to make SQL queries 'easier' and less to worry about.
Here are my thoughts
Should it be more like class DB extends PDO?
Is the query method too big? Should it be split into private methods which are called.. is this what is known as loose coupling?
Is my way for detecting a SELECT query too ugly for it's own good?
What other problems are evident? As I am sort of learning-as-I-go, I'm sure I could have overlooked a lot of potential problems.
Thank you
`
class Db
{
private static $_instance = NULL;
private function __construct() {
// can not call me
}
private function __clone() {
// no!
}
public static function getInstance() {
if (!self::$_instance)
{
try {
self::$_instance = new PDO('mysql:host=' . CONFIG_MYSQL_SERVER . ';dbname=' . CONFIG_MYSQL_DATABASE, CONFIG_MYSQL_USERNAME, CONFIG_MYSQL_PASSWORD);;
self::$_instance-> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
trigger_error($e->getMessage());
}
}
return self::$_instance;
}
public static function query($query /*string*/, $bindings = NULL)
{
$queryPortion = substr($query,0, 6);
try {
if ($bindings) {
$prepared = self::getInstance()->prepare($query);
foreach($bindings as $binding=>$data) { // defaults to string
if (!is_array($data)) {
$prepared->bindParam($binding, $data);
} else {
switch(count($data)) {
case 1:
$prepared->bindParam($binding, $data['value']);
break;
case 2:
$prepared->bindParam($binding, $data['value'], $data['dataType']);
break;
case 3:
$prepared->bindParam($binding, $data['value'], $data['dataType'], (int)$data['length']);
break;
default:
trigger_error('An error has occured with the prepared statement bindings.');
return false;
break;
}
}
}
$prepared->execute();
return $prepared->fetchAll(PDO::FETCH_ASSOC);
} else if (String::match($queryPortion, 'select')) { // if this is a select query
$rows = self::getInstance()->query($query);
return $rows->fetchAll(PDO::FETCH_ASSOC);
} else {
return self::getInstance()->exec($query);
}
}
catch(PDOException $e)
{
trigger_error($e->getMessage());
}
}
public static function getLastInsertId()
{
try {
self::getInstance()->lastInsertId();
}
catch(PDOException $e)
{
trigger_error($e->getMessage());
}
}
public static function disconnect()
{
// kill PDO object
self::$_instance = NULL;
}
}
It's not bad and as it's been said it might help for small applications although it's mostly a very thin abstraction on another abstraction. It's not bringing a lot of others functionalities.
Something you might want to consider, amongst other things:
As this is PHP5 code, use exceptions instead of trigger_error and set_exception_handler if necessary until exceptions are more widespread, but it's definitely cleaner and more future-proof.
You are using a singleton, it's not a bad thing necessarily but in this case, for example, one shortcoming will be that you'll only be able to handle one connection to one database.
I don't know if you make use of stored procedures, but a stored procedure might return a result set through the query() method too.
You have two semi-colons (;;) at the end of your new PDO line.
That being said, I don't think your query method is too big and there's not much that could be recalled from elsewhere in there at the moment. Though as soon as you see two or three lines that could be called from another function, split it. That's a good way to DRY.
Yes and No.
It is good code for a simple quick and dirty application.
The problem comes when you use this in a more complex structured application.
Where the error handling will vary depending on which sql you are executing.
Also any severe errors will show up as "problem at line 999" type errors
where 999 is in your super duper routine and you will have difficulty tracing it back
to a particular sql request.
Having said that I do this sort of thing myself all the time on small projects.
Here's what I've used (just replace the references to Zzz_Config with $GLOBALS['db_conf'] or something):
/**
* Extended PDO with databse connection (instance) storage by name.
*/
class Zzz_Db extends PDO
{
/**
* Named connection instances.
*
* #var array
*/
static private $_instances;
/**
* Retrieves (or instantiates) a connection by name.
*
* #param string $name Connection name (config item key).
* #return Zzz_Db Named connection.
*/
static public function getInstance($name = null)
{
$name = $name === null ? 'db' : "db.$name";
if (!isset(self::$_instances[$name])) {
if (!$config = Zzz_Config::get($name)) {
throw new RuntimeException("No such database config item: $name");
}
if (!isset($config['dsn'])) {
if (!isset($config['database'])) {
throw new RuntimeException('Invalid db config');
}
$config['dsn'] = sprintf('%s:host=%s;dbname=%s',
isset($config['adapter']) ? $config['adapter'] : 'mysql',
isset($config['host']) ? $config['host'] : 'localhost',
$config['database']);
}
$db = self::$_instances[$name] = new self(
$config['dsn'],
isset($config['username']) ? $config['username'] : null,
isset($config['password']) ? $config['password'] : null);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, 'Zzz_Db_Statement');
if ($db->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') {
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$db->exec('SET CHARACTER SET utf8');
}
}
return self::$_instances[$name];
}
}
Usage whould be:
$db = Zzz_Db::getInstance(); // or Zzz_Db::getInstance('some_named_db')
$stmt = $db->prepare('SELECT ...
The goal is to keep the db configuration in an *.ini file (editable by a non-coder).
I went the other way and made a class that extends PDO with a bunch of wrapper functions around prepare()/execute(), and it's much nicer than the built in functions (though that's a bit subjective...).
One other thing: you should set PDO::ATTR_EMULATE_PREPARES to false unless you're using a really old version of mysql (<=4.0). It defaults to true, which is a huge headache and causes things to break in obscure ways... which I'm guessing is the reason you've got a huge wrapper around bindParam() in the first place.
To answer your question, if it is a good code or not, ask yourself:
What is the added value of my code compared to using PDO directly?
If you find a good answer, go for using your code. If not, I would stick with PDO.
Also try considering implementing Zend Framework's DB class which works on its own and supports PDO.