I'm working on a simple login with mysql using pdo. Everything works fine so now I'm focusing on the error handling. But there's a problem. In my dbconfig I voluntarily saved the wrong psw/email/dbname to create an error.
<?php
session_start();
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'Captive');
define('DB_PASSWORD', 'arancione');
define('DB_DATABASE', 'geochimie');
function get_db() {
$dbhost=DB_SERVER;
$dbuser=DB_USERNAME;
$dbpass=DB_PASSWORD;
$dbname=DB_DATABASE;
try {
$dbConnection = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
$dbConnection->exec("set names utf8");
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $dbConnection;
}
catch (PDOException $e) {
header("../pages/errore.php?errore=dbconfig");
}
}
?>
However, no error can be caught so when I prepare a statement using the db connection returned by get_db() I get this:
Uncaught Error: Call to a member function prepare() on null
Here is the php script where I prepare the stmt:
<?php
class Admin {
public function userLogin( $email,$password)
{
try{
$db = get_db();
$stmt = $db->prepare('SELECT id FROM admin WHERE email=:email AND psw=:hash_password');
$stmt->bindParam("email", $email,PDO::PARAM_STR) ;
$stmt->bindParam("hash_password", $password,PDO::PARAM_STR) ;
$stmt->execute();
$count=$stmt->rowCount();
$data=$stmt->fetch(PDO::FETCH_OBJ);
$db = null;
if($count)
{
$_SESSION['uid']=$data->id;
return true;
}
else
{
return false;
}
}
catch(PDOException $e) {
header("Location: ../pages/errore.php?errore=db");
}
}
}
?>
As you can see there is another try-catch but It doesn't work at the same way. Many thanks for your attention!
Ok guys, It seems that I needed to add die() or exit() after the header in the catch blocks. Now I can handle the errors properly.
Related
Fatal error: Call to a member function prepare() on null in C:\xampp\htdocs\af\functions\indexdatasummary.php on line 6
dbconnect.php
global $dbh;
//Server Variables========-------------->
$af_host="localhost";
$af_root="root";
$af_password="";
//Database Variables========------------>
$af_cbms_database="af_cbms";
try
{
$dbh = new PDO("mysql:host=$af_host", $af_root, $af_password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
$af_cbms_database = "`" . str_replace("`", "``", $af_cbms_database) . "`";
$dbh->query("CREATE DATABASE IF NOT EXISTS $af_database");
$dbh->query("SET CHARACTER SET utf8");
$dbh->query("USE $af_database");
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
}
catch(PDOException $e)
{
echo $e->getMessage();
}
the above code I use is working for almost all of my pages but in this page it's having an error. the way I call this is just the same way for the other file and this is the only page that returns with error.
indexsummary.php
global $dbh;
require_once '../functions/dbconnect.php';
$stmt = $dbh->prepare("SELECT * FROM `city_tbl`");
$stmt->execute();
and soon.....
what do you think is causing this error? any help!
1) Your problem with creating connection and creating database.
Cuz You define:
$af_cbms_database="af_cbms";
and then You call:
$dbh->query("CREATE DATABASE IF NOT EXISTS $af_database");
so where in Your code You've defined $af_database variable?
2) it's too unprofessional to make this (seems like You're new to programming):
$af_cbms_database = "`" . str_replace("`", "``", $af_cbms_database) . "`";
You've already defined Your variable and then replacing it, funny, like You don't trust Yourself that You've defined variable? (:
or You cannot do it like this? :
$dbh->query("CREATE DATABASE IF NOT EXISTS `".$af_cbms_database."`");
$dbh->query("USE `".$af_cbms_database."`");
3) Don't complicate Your code wit too much of variables like $af_, be simple as in this fixed code of dbconnect.php:
<?php
global $dbh;
$host = "localhost";
$user = "root";
$password = "";
$db_name = "af_cbms";
try {
$dbh = new PDO("mysql:host=$host", $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->query("CREATE DATABASE IF NOT EXISTS ".$db_name);
$dbh->query("SET CHARACTER SET utf8");
$dbh->query("USE ".$db_name);
}
catch(PDOException $e) {
die($e->getMessage());
}
4) BONUS: Don't use global $dbh, because may happen that some process, some code can replace $dbh variable. Also using global vars is not in fashion (:
so have some Object that will keep shared stuff :
class Objs {
private $data = [];
final public static function set($key, $instance, $preventReset = false) {
if($preventReset === true AND isset(self::$data[$key])) {
return self::$data[$key];
}
return self::$data[$key] = $instance;
}
final public static function get($key, $instance) {
return self::$data[$key];
}
}
and in Your db connection file:
require_once('classes/Objs.php');
Objs::set('db', $dbh, true);
and in Your another files:
$stmt = Objs::get('db')->prepare('SELECT * FROM city_tbl');
I got this problem too. The error is I call the function before the function is declared. So I changed the sequence so that I call the function after it is declared.
I am trying to use properties inside try catch blocks.
The try catch blocks are inside classes.
I want to extend a specific class, making it the subclass of the class which handles exceptions.
The problem is, that when I try to use those variables from the subclass, it always says undefined. I have to delete both of classes in order to catch the properties. After reading some other answers here by adding a return statement (I added return 1) outside, inside of the try catch block, it doesn't seem to work and it always says undefined variable.
Any help?
The language is php
The source code without classes works perfect:
try
{
//$pdo variable to insert PDO object information
$pdo = new PDO('mysql:host=localhost;dbname=studenti', 'root', '');
//Set php to catch exceptions
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//Set UTF-8 for character encodings
$pdo->exec('SET NAMES "utf8"');
}
//Catch error if unable to connect
catch(PDOException $e)
{
//error variable
$error = 'Unable to connect with database. ' . $e->getMessage();
//include file once and show on screen error message
include_once $_SERVER['DOCUMENT_ROOT'] . '/inc/error.inc.php';
//Exit and don't process further
exit();
}
//Another Exception handling
try
{
//Select statement
$sql = 'SELECT * FROM dega';
$select = $pdo->query($sql);
}
catch(PDOException $e)
{
//error variable
$error = 'Unable to select table. ' . $e->getMessage();
//include file once and show on screen error message
include_once $_SERVER['DOCUMENT_ROOT'] . '/inc/error.inc.php';
//Exit and don't process further
exit();
}
The source code with classes doesn't work:
<?php
//PDO class, connection with MySQL database
class Connect
{
function connection()
{
$pdo = null;
try
{
//$pdo variable to insert PDO object information
$pdo = new PDO('mysql:host=localhost;dbname=studenti', 'root', '');
//Set php to catch exceptions
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//Set UTF-8 for character encodings
$pdo->exec('SET NAMES "utf8"');
}
//Catch error if unable to connect
catch(PDOException $e)
{
//error variable
$error = 'Unable to connect with database. ' . $e->getMessage();
//include file once and show on screen error message
include_once $_SERVER['DOCUMENT_ROOT'] . '/inc/error.inc.php';
//Exit and don't process further
exit();
}
}
}
class Select extends Connect
{
function selection()
{
//Another Exception handling
try
{
//Select statement
$sql = 'SELECT * FROM dega';
$select = $pdo->query($sql);
}
catch(PDOException $e)
{
//error variable
$error = 'Unable to select table. ' . $e->getMessage();
//include file once and show on screen error message
include_once $_SERVER['DOCUMENT_ROOT'] . '/inc/error.inc.php';
//Exit and don't process further
exit();
}
}
}
//Output if successful
$error = 'Database connection established.';
include $_SERVER['DOCUMENT_ROOT'] . '/inc/error.inc.php';
?>
You have no subclass. Object Inheritance
You have no properties. Properties
Read about Classes and Objects.
class Connect
{
protected $pdo = null;
public function connection()
{
$pdo = null;
try
{
$this->pdo = new PDO('mysql:host=localhost;dbname=studenti', 'root', '');
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pdo->exec('SET NAMES "utf8"');
} catch (PDOException $e)
{
$error = 'Unable to connect with database. ' . $e->getMessage();
include_once $_SERVER['DOCUMENT_ROOT'] . '/inc/error.inc.php';
exit();
}
}
}
class Select extends Connect
{
function selection()
{
try
{
$sql = 'SELECT * FROM dega';
$select = $this->pdo->query($sql);
} catch (PDOException $e)
{
$error = 'Unable to select table. ' . $e->getMessage();
include_once $_SERVER['DOCUMENT_ROOT'] . '/inc/error.inc.php';
exit();
}
}
}
Put $pdo just before connection():
class Connect
{
protected $pdo = null; // or var $pdo = null;
function connection()
{
...
Inside the function it's a local variable not a class level property.
EDIT:
PHP requires $this-><class-variable> to access class properties inside functions. (Check out #Sectus's answer below.) Just using $pdo is creating a local variable on the fly (in both the methods) but gives an error in only selection() because that's where query() is being invoked without initializing a PDO() object first.
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 have a db.php file that has the following in it:
// db.php file
// creates connection to database
// DATABASE CONNECTION FUNCTION
function sql_con(){
try{
$dbh = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASS,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
// log error to file and display friendly error to user
ExceptionErrorHandler($e);
exit;
}
}
I have a signup page and i want to check if username already exists, so i call my sql_con(); function beforehand to connect to database then do the below query
// connect to database
sql_con();
$stmt = $dbh->prepare("SELECT `user_login` FROM `users` WHERE `user_login` = ? LIMIT 1");
$stmt->execute(array($username));
if ( $stmt->rowCount() > 0 ) {
$error[] = 'Username already taken';
}
I'm very new to PDO and with the above i get the following errors:
Notice: Undefined variable: dbh in C:\wamp\www\signup.php on line 64
Fatal error: Call to a member function prepare() on a non-object in
C:\wamp\www\signup.php on line 64
Probably something very silly and I seem to confuse myself with PDO as I'm at the beginner stages. Could anyone tell me what I am doing wrong? Also I am not sure if this is the correct way as I'm new to PDO so if there's a more efficient way to do the username query check then please let me know.
This is because the $dbh object is limited to inside the try catch block and your sql_con() function due to scope.
The correct solution would be to remove the try catch block, and return the $dbh variable at the end of the sql_con() function.
Then:
try {
$dbh = sql_con();
$stmt = $dbh->prepare("SELECT `user_login` FROM `users` WHERE `user_login` = ? LIMIT 1");
$stmt->execute(array($username));
if ( $stmt->rowCount() > 0 ) {
$error[] = 'Username already taken';
}
}
catch (PDOException $e) {
//Do stuff with $e
}
The $dbh variable is destroyed once the function is done executing.
You could try returning the handle:
function sql_con() {
// your connection code
return $dbh;
}
Then in your signup page:
$dbh = sql_con();
Depending on your needs, a better alternative would be to employ a DI container.
You are defining pdo in function an ist not visible out it.
function sql_con(){
try{
$dbh = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASS,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $dbh; //this
} catch (PDOException $e) {
// log error to file and display friendly error to user
ExceptionErrorHandler($e);
exit;
}
}
$dbh = sql_con();
It will be better if you create a class for pdo abstraction.
$dbh = DB::getInstance();
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