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.
Related
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.
So, I'm working on a project that requires a database connection. I've chosen to use PDO for its versatility and need to figure out how to set up the connection. Currently I'm going for something like this:
class Database {
private static $db;
static function initDB() {
if(!is_object(self::$db) || get_class(self::$db) != 'PDO') {
include('core/db.php');
try {
$db = new PDO($database, $username, $password);
} catch(PDOException $e) {
print("<br />Could not establish database connection. Error message: ".$e->getMessage()."<br />");
die();
}
}
//Try the transaction
/*
if($transaction = $db::query(PDO::quote($value)))
$db::query(PDO::quote("INSERT INTO log VALUES ('".Authorization::$user."','".PDO::quote($value)."', 'Success')"));
else
$db::query(PDO::quote("INSERT INTO log VALUES ('".Authorization::$user."','".PDO::quote($value)."', 'Failure')"));*/
}
}
So, this pretty much reveals one of the concepts I don't really know: singletons and static classes/objects. Any way to set up a database connection using OO best practices that initializes with the script via some kind of __construct method?
A database connection should not be either static or a singleton. This merely introduces another form of global state, which is bad for unit-testing and does hide obvious dependencies.
The right way here would be is to inject an instance of PDO into the classes that need it. You adhere the Single-Responsibility Principle and Dependency Injection.
Note, you should never log errors and do include() inside PDOAdapter constructor because its masked violation of the Single-Responsibility Principle
So, this would look like this:
final class PDO_Provider extends PDO
{
/**
* Constructor. Inits PDO
*
* #param array $params
* #return void
*/
public function __construct(array $params)
{
try {
extract($params);
parent::__construct(sprintf('mysql: host=%s; dbname=%s', $host, $database), $user, $password);
$this->setAttribute(parent::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES UTF8');
$this->setAttribute(parent::ATTR_ERRMODE, parent::ERRMODE_EXCEPTION);
$this->setAttribute(parent::ATTR_EMULATE_PREPARES, false);
$this->setAttribute(parent::ATTR_DEFAULT_FETCH_MODE, parent::FETCH_ASSOC);
} catch(PDOException $e) {
die($e->getMessage());
}
}
}
And you would use it like this,
<?php
$sql_config = array(
'host' => 'localhost',
'user' => 'root',
'password' => '',
'database' => '_DB_NAME_',
);
// <- Or you can include that, like
$sql_config = include(__DIR__ . '/core/db_params.php');
$pdoProvider = new PDO_Provider($sql_config);
$user = new User_Login($pdoProvider); // the point here is to inject an instance of $pdoProvider. User_Login is actually irrelevant
if you want to use a normal object instead of sigleton, try something like this:
class PDOConnector{
protected $connection;
function __construct($host, $user, $pass, $db_name)
{
//create database connection
try{
$this->connection = new PDO('mysql:host='.$this->host.';dbname='.$this->db_name.';charset=utf8', $this->user, $this->pass,array(PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
}
catch(PDOException $ex) {
echo "An Error occured : ".$ex->getMessage();
}
}
}
I am trying to migrate a PHP app from MySQL to SQLite, and some things that used to work, simply stopped working now. I am using PDO through a custom database wrapper class (the class is a singleton, seems logical to do it like that).
The problem:
When trying to execute a query on a prepared statement, it throws a "fatal error: Call to a member function execute() on a non-object ...".
Relevant code (narrowed it down to this, after some hours of var_dumps and try-catch):
Connection string:
$this->connection = new PDO("sqlite:"._ROOT."/Storage/_sqlite/satori.sdb");
Obviously, the $connection variable here is a private variable from the class.
The error happens here (inside a function that is supposed to perform database insert):
try{
$statement = self::getInstance()->connection->prepare($sql);
}catch (PDOException $e){
print $e->getMessage;
}
try{
var_dump($statement);
$statement->execute($input);
}catch (Exception $e){
print $e->getMessage();
}
More accurately, it happens when I try to $statement->execute($input).
Any help appreciated. Thanks.
You are probably getting a mySQL error when the statement is being prepared, but you don't have PDO configured to throw an exception in case of an error.
<?php
$dbh = new PDO( /* your connection string */ );
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
// . . .
?>
try declaring the $statement variable outside the first try block. e.g.
$statement = null;
try{
$statement = self::getInstance()->connection->prepare($sql);
}catch (PDOException $e){
print $e->getMessage;
}
try{
var_dump($statement);
$statement->execute($input);
}catch (Exception $e){
print $e->getMessage();
}
I have a script that does a lot of legwork nightly.
It uses a PDO prepared statement that executes in a loop.
The first few are running fine, but then I get to a point where they all fail with the error:
"MySQL server has gone away".
We run MySQL 5.0.77.
PHP Version 5.2.12
The rest of the site runs fine.
Most likely you sent a packet to the server that is longer than the maximum allowed packet.
When you try to insert a BLOB that exceeds your server's maximum packet size, even on a local server you will see the following error message on clientside:
MySQL server has gone away
And the following error message in the server log: (if error logging is enabled)
Error 1153 Got a packet bigger than 'max_allowed_packet' bytes
To fix this, you need to decide what is the size of the largest BLOB that you will ever insert, and set max_allowed_packet in my.ini accordingly, for example:
[mysqld]
...
max_allowed_packet = 200M
...
The B.5.2.9. MySQL server has gone away section of the MySQL manual has a list of possible causes for this error.
Maybe you are in one of those situations ? -- Especially considering you are running a long operation, the point about wait_timeout might be interesting...
I had the same problem where the hosting server administration kills connection if there is a timeout.
Since I have used the query in major part I wrote a code which instead of using PDO class we can include the below class and replace the classname to "ConnectionManagerPDO". I just wrapped the PDO class.
final class ConnectionManagerPDO
{
private $dsn;
private $username;
private $passwd;
private $options;
private $db;
private $shouldReconnect;
const RETRY_ATTEMPTS = 3;
public function __construct($dsn, $username, $passwd, $options = array())
{
$this->dsn = $dsn;
$this->username = $username;
$this->passwd = $passwd;
$this->options = $options;
$this->shouldReconnect = true;
try {
$this->connect();
} catch (PDOException $e) {
throw $e;
}
}
/**
* #param $method
* #param $args
* #return mixed
* #throws Exception
* #throws PDOException
*/
public function __call($method, $args)
{
$has_gone_away = false;
$retry_attempt = 0;
try_again:
try {
if (is_callable(array($this->db, $method))) {
return call_user_func_array(array($this->db, $method), $args);
} else {
trigger_error("Call to undefined method '{$method}'");
/*
* or
*
* throw new Exception("Call to undefined method.");
*
*/
}
} catch (\PDOException $e) {
$exception_message = $e->getMessage();
if (
($this->shouldReconnect)
&& strpos($exception_message, 'server has gone away') !== false
&& $retry_attempt <= self::RETRY_ATTEMPTS
) {
$has_gone_away = true;
} else {
/*
* What are you going to do with it... Throw it back.. FIRE IN THE HOLE
*/
throw $e;
}
}
if ($has_gone_away) {
$retry_attempt++;
$this->reconnect();
goto try_again;
}
}
/**
* Connects to DB
*/
private function connect()
{
$this->db = new PDO($this->dsn, $this->username, $this->passwd, $this->options);
/*
* I am manually setting to catch error as exception so that the connection lost can be handled.
*/
$this->db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
/**
* Reconnects to DB
*/
private function reconnect()
{
$this->db = null;
$this->connect();
}
}
Then use can start using the above class as you do in PDO.
try {
$db = new ConnectionManagerPDO("mysql:host=localhost;dbname=dummy_test", "root", "");
$query = $db->query("select * from test");
$query->setFetchMode(PDO::FETCH_ASSOC);
}
catch(PDOException $e){
/*
handle the exception throw in ConnectionManagerPDO
*/
}
Try using PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true) on your pod instance(s). Dont know that it will help but with no log data its all i got.
It's likely that either your connection has been killed (e.g. by wait_timeout or another thread issuing a KILL command), the server has crashed or you've violated the mysql protocol in some way.
The latter is likely to be a bug in PDO, which is extremely likely if you're using server-side prepared statements or multi-results (hint: Don't)
A server crash will need to be investigated; look at the server logs.
If you still don't know what's going on, use a network packet dumper (e.g. tcpdump) to dump out the contents of the connection.
You can also enable the general query log - but do it very carefully in production.
Nathan H, below is php class for pdo reconnection + code usage sample.
Screenshot is attached.
<?php
# set errors reporting level
error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
# set pdo connection
include('db.connection.pdo.php');
/* # this is "db.connection.pdo.php" content
define('DB_HOST', 'localhost');
define('DB_NAME', '');
define('DB_USER', '');
define('DB_PWD', '');
define('DB_PREFIX', '');
define('DB_SHOW_ERRORS', 1);
# connect to db
try {
$dbh = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PWD);
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e) {
# echo $e->getMessage()."<br />";
# exit;
exit("Site is temporary unavailable."); #
}
*/
$reconnection = new PDOReconnection($dbh);
$reconnection->getTimeout();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
echo 'sleep 10 seconds..'.PHP_EOL;
sleep(10);
$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
echo 'sleep 35 seconds..'.PHP_EOL;
sleep(35);
$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
echo 'sleep 55 seconds..'.PHP_EOL;
sleep(55);
$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
echo 'sleep 300 seconds..'.PHP_EOL;
sleep(300);
$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
# *************************************************************************************************
# Class for PDO reconnection
class PDOReconnection
{
private $dbh;
# constructor
public function __construct($dbh)
{
$this->dbh = $dbh;
}
# *************************************************************************************************
# get mysql variable "wait_timeout" value
public function getTimeout()
{
$timeout = $this->dbh->query('show variables like "wait_timeout"')->fetch(); # print_r($timeout);
echo '========================'.PHP_EOL.'mysql variable "wait_timeout": '.$timeout['Value'].' seconds.'.PHP_EOL.'========================'.PHP_EOL;
}
# *************************************************************************************************
# check mysql connection
public function checkConnection()
{
try {
$this->dbh->query('select 1')->fetchColumn();
echo 'old connection works..'.PHP_EOL.'========================'.PHP_EOL;
} catch (PDOException $Exception) {
# echo 'there is no connection.'.PHP_EOL;
$this->dbh = $this->reconnect();
echo 'connection was lost, reconnect..'.PHP_EOL.'========================'.PHP_EOL;
}
return $this->dbh;
}
# *************************************************************************************************
# reconnect to mysql
public function reconnect()
{
$dbh = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PWD);
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $dbh;
}
}
# /Class for PDO reconnection
# *************************************************************************************************
I got this same error this morning after changing my DB properties in Laravel. I'd commented out the old settings and pasted in new ones. The problem was that the new settings where missing the DB_CONNECTION variable:
DB_CONNECTION=pgsql
Obviously you need to add whatever connection type you are using: sqlite, mysql, ...
I had the exact same problem.
I resolved this issue by doing unset on the PDO object instead of seting it to NULL.
For example:
function connectdb($dsn,$username,$password,$driver_options) {
try {
$dbh = new PDO($dsn,$username,$password,$driver_options);
return $dbh;
}
catch(PDOException $e)
{
print "DB Error: ".$e->getMessage()."<br />";
die();
}
}
function closedb(&$dbh) {
unset($dbh); // use this line instead of $dbh = NULL;
}
Also, it is highly recommended to unset all your PDO objects. That includes variables that contain prepared statements.
$pdo = new PDO(
$dsn,
$config['username'],
$config['password'],
array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
)
);
try this. It may work