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();
}
Related
private function _connect(){
try {
$this->con = new PDO(''.$this->dbdriver.':host='.$this->dbhost.';dbname='.$this->dbname.'', $this->dbuser, $this->dbpass);
$this->con->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$this->con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
return TRUE;
} catch (PDOException $e){
$reg = registry::_getInstance();
$reg->offsetSet('R_errors', $reg->offsetGet('R_errors').'</br>'.$e->getMessage());return false;
}
}
I am using the above code to connect to the database but am getting the following errors:
Fatal error: spl_autoload(): Class PDO could not be loaded in /home/tahidihomes/public_html/lib/core/pdo_mysql.core.php on line 72
What might be the problem?
Retry after PDO installation. If you get same error try again with the below code in top of index page
spl_autoload_extensions('.php, .class.php');
spl_autoload_register();
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'm using CodeIgniter 2.1.4, and I'm trying to use PDO instead of the internal database object.
I'd changed the "/config/database.php" to use pdo, and "/system/database/drivers/pdo/pdo_drivers.php" function db_connet(), pdo::attr_errmode to use pdo::errmode_exception mode instead of silent mode.
I ran my query like this:
try {
$query = "SELECT * FROM users";
$stmt = $this->db->conn_id->prepare($query);
$stmt->execute();
} catch (PDOException $e) {
$e->getMessage();
}
The code works fine except it's not outputting any error if it occurs. I'd tested it without the try/catch block but it's still the same.
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;
}
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.