I've seen variations of this code all over the place, including many S.O. posts:
class db extends PDO {
public function __construct( $dbconf ) {
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => $dbconf['persist'] ? true : false
);
try {
parent::__construct('mysql:host='. $dbconf['dbhost'] .';port=3306;dbname='. $dbconf['dbname'] .';' , $dbconf['dbuser'],
$dbconf['dbpass'],
$options);
} catch (PDOException $e) {
$this->myerror( $e->getMessage() );
// echo 'Connection failed ... '. $e->getMessage();
}
}
...
private function myerror( $error ) {
echo 'Connection failed ... '. $error;
}
}
The class is instantiated with $db = new db( $config );, and it works great if the connection is valid, but it seems PDOException doesn't actually work if the connection fails. The catch totally fails to execute the $this->myerror(...) function! Instead of the useful $e->getMessage() that says "Connection failed ... Access denied for user blah", I get a PHP Fatal error: Call to a member function myerror() on a non-object in /.../lib/pdo.class.php on line 16.
If I comment out the first line in the catch and uncomment the echo, it works as expected, reporting the reason for the connection error. Why would the message be available in the catch but not in the simple myerror class function?
This post ... PHP, PDO, and Exceptions ... seems applicable, but doesn't explain much. Is catch (PDOException $e) obsolete, or dysfunctional under certain circumstances? How do I keep get this to work?
The reason being is PDO::__construct only creates the object upon successful connection. So when you call the parent constructor and it fails your object doesn't exist anymore. You should use self::myerror() to access the error function statically in this case.
There shouldn't be myerror() function at all. A database layer scarcely need it's own error handler.
And constructor have to be
public function __construct( $dbconf ) {
$dsn = "mysql:";
$dsn .= "host=". $dbconf['dbhost'].";";
$dsn .= "dbname=". $dbconf['dbname'].";";
$dsn .= "port=". $dbconf['dbport'].";";
$dsn .= "charset=".$dbconf['dbcharset'];
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES '.$dbconf['charset'],
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => (bool)$dbconf['persist'],
);
parent::__construct($dsn, $dbconf['dbuser'], $dbconf['dbpass'],$options);
}
Related
I'm completely new to PHP. I am creating a website.
In several pages, I start my code with :
<?php
$dsn = "mysql:host=localhost;dbname=db;";
$options = [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];
try {
$pdo = new PDO($dsn,'root','', $options);
} catch (Exception $e) {
die('Erreur : ' . $e->getMessage());
}
//rest of the php + html code here
I would like to unify this code in one php file and call $pdo in every page. But I want to do it in a safe way. Can anyone help.
Better using a singleton for this. This way you avoid opening multiple database connections:
<?php
class Database
{
public static $pdo;
public static function getPDO()
{
if (null === self::$pdo) {
$dsn = "mysql:host=localhost;dbname=db;";
$options = [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];
try {
self::$pdo = new PDO($dsn,'root','', $options);
} catch (Exception $e) {
die('Erreur : ' . $e->getMessage());
}
}
return self::$pdo;
}
//protected constructor
protected function __construct()
{
}
}
Then use
require_once('path/to/database.php'); //the file with Database class
$pdo = Database::getPDO();
Of course, you need to include this file/class in every page.
Also, isn't a good practice to hardcode your database credentials. Better load from some .env, getenv() or .ini file.
Save your connection code in a file. Let's call it db_connect.php. Then, using the proper path add the following to each file needing a database connection:
include('path/to/db_connect.php');
You may also use include_once(), require() or require_once() depending on what your needs are.
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've seen variations of this code all over the place, including many S.O. posts:
class db extends PDO {
public function __construct( $dbconf ) {
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => $dbconf['persist'] ? true : false
);
try {
parent::__construct('mysql:host='. $dbconf['dbhost'] .';port=3306;dbname='. $dbconf['dbname'] .';' , $dbconf['dbuser'],
$dbconf['dbpass'],
$options);
} catch (PDOException $e) {
$this->myerror( $e->getMessage() );
// echo 'Connection failed ... '. $e->getMessage();
}
}
...
private function myerror( $error ) {
echo 'Connection failed ... '. $error;
}
}
The class is instantiated with $db = new db( $config );, and it works great if the connection is valid, but it seems PDOException doesn't actually work if the connection fails. The catch totally fails to execute the $this->myerror(...) function! Instead of the useful $e->getMessage() that says "Connection failed ... Access denied for user blah", I get a PHP Fatal error: Call to a member function myerror() on a non-object in /.../lib/pdo.class.php on line 16.
If I comment out the first line in the catch and uncomment the echo, it works as expected, reporting the reason for the connection error. Why would the message be available in the catch but not in the simple myerror class function?
This post ... PHP, PDO, and Exceptions ... seems applicable, but doesn't explain much. Is catch (PDOException $e) obsolete, or dysfunctional under certain circumstances? How do I keep get this to work?
The reason being is PDO::__construct only creates the object upon successful connection. So when you call the parent constructor and it fails your object doesn't exist anymore. You should use self::myerror() to access the error function statically in this case.
There shouldn't be myerror() function at all. A database layer scarcely need it's own error handler.
And constructor have to be
public function __construct( $dbconf ) {
$dsn = "mysql:";
$dsn .= "host=". $dbconf['dbhost'].";";
$dsn .= "dbname=". $dbconf['dbname'].";";
$dsn .= "port=". $dbconf['dbport'].";";
$dsn .= "charset=".$dbconf['dbcharset'];
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES '.$dbconf['charset'],
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => (bool)$dbconf['persist'],
);
parent::__construct($dsn, $dbconf['dbuser'], $dbconf['dbpass'],$options);
}
I'm using this function to connect to my MySQL db when needed, and also to re-use the same connection object for any further query I might need in the same php script.
function cnn() {
static $pdo;
if(!isset($pdo)) {
try {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
$pdo->setAttribute(PDO::ATTR_TIMEOUT, 30);
$pdo->setAttribute(PDO::ATTR_PERSISTENT, true);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
return $pdo;
} catch(PDOException $e) {
http_response_code(503);
echo $e->getCode.': '.$e->getMessage();
die(); //or whatever error handler you use
}
} else {
return $pdo;
}
}
First query (object is created)
echo cnn()->query('SELECT firstname FROM user WHERE id=4;')->fetch(PDO::FETCH_COLUMN)
Second query (object is reused)
echo cnn()->query('SELECT title FROM news WHERE id=516;')->fetch(PDO::FETCH_COLUMN)
Do you agree on this approach? Do you think it can be optimized? Thanks for your opinions.
I agree with the method, though many people will tell you that this "singleton" approach is bad, bad.
However, I disagree with your implementation. It should be:
function cnn() {
static $pdo;
if(!$pdo) {
$conf = array(PDO::ATTR_TIMEOUT => 30,
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
);
$dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME;
$pdo = new PDO($dsn, DB_USER, DB_PASS, $conf);
}
return $pdo;
}
Also, it looks sensible to move handler code into handler (and of course without echoing the error unconditionally!)
function my_exceptionHandler($exception) {
http_response_code(503);
if (ini_get('display_errors')) {
echo $e->getMessage().$e->getTrace();
} else {
log_error($e->getMessage().$e->getTrace());
}
die(); //or whatever error handler you use
}
set_exception_handler("my_exceptionHandler");
Also, I'd extend it to accept a parameter to make several connections possible.
I'm using a slight variation on the PHP PDO Wrapper Class found here: http://www.imavex.com/php-pdo-wrapper-class/
This is the new constructor I'm using:
public function __construct($dsn, $user='', $passwd='') {
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
try {
parent::__construct($dsn, $user, $passwd, $options);
} catch (PDOException $e) {
$this->error = $e->getMessage();
}
}
error is a private variable of this class, defined at the begin of the class, above the constructor.strong text
And when I use it like this
$database = new Database(
'mysql:host=' . $config['mysql_host'] . ';port=' . $config['mysql_port'] . ';dbname=' . $config['mysql_database'],
$config['mysql_username'],
$config['mysql_password']
);
It shows me this error: Creating default object from empty value, pointing to the database class file, at the line $this->error = $e->getMessage();.
The reason an exception is thrown is because I haven't set the mysql username and password yet, so don't worry about that. I'm trying to figure out why it gives this error and how to fix it.
It seems that since the db class extends PDO, when the PDO constructor throws an exception $this becomes NULL.
class db extends PDO {
public function __construct($dsn, $user="", $passwd="") {
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
try {
parent::__construct($dsn, $user, $passwd, $options);
} catch (PDOException $e) {
echo "Got PDOException\n";
var_dump($this);
}
}
}
new db('mysql:host=localhost;port=3306;dbname=something', 'invaliduser', 'invalidpass');
If you run that code you will get this output:
Got PDOException
NULL
To solve that you could remove the offending line and check if the db object is null or remove the try-catch and do the exception handling when you instantiate a new db.
Alternatively, you could rewrite the class to store the PDO object in a variable instead of extending PDO.