In anticipation of mysql_query being deprecated PHP 5.5.0, I have been working on a class to handle all my DB queries :
class DataBaseClass {
//.....some other function and variables declared here....
function GetConnection() {
try {
$this->conn = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASS);
$this->conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
catch(PDOException $e) {
echo $e->getMessage();
}
return $this->conn;
}
function Query($str_sql, $arr_parameters = array()) {
try {
$this->str_mysql_error = $this->int_num_rows = $this->int_num_affected_rows = $this->int_mysql_insert_id = '';
if (count($arr_parameters) > 0) {
$obj_result = $this->conn->prepare($str_sql);
$obj_result->execute($arr_parameters);
} else {
$obj_result = $this->conn->query($str_sql);
}
}
catch(PDOException $e) {
$this->str_mysql_error = $e->getMessage() . $str_sql;
}
}
}
Then I have another class to create new user:
class AddNewUser {
//.....some other function and variables declared here....
function InsertUser() {
$str_sql = "INSERT INTO (uname, name, email, pass, user_regdate, theme) VALUES )";
$_SESSION['db_connection']->Query($str_sql, '');
}
}
Now on my main user creation page I have :
$_SESSION['db_connection'] = new DataBaseClass;
//Reason I used $_SESSION to store my DB object, is so that it can be accessible everywhere.
//Did not want to use "global" everywhere. Not sure if this is he best way???
$cls_new_user = new AddNewUser ();
$cls_new_user->InsertUser(); //Does not raise PDOExecption although SQL cleary wrong inside this method
if ( $_SESSION['db_connection']->str_mysql_error) {
//show error in error div
}
$str_sql = "SELECT some wrong SQL statment";
$_SESSION['db_connection']->Query($str_sql); // This does raise PDOExecption
if ( $_SESSION['db_connection']->str_mysql_error) {
//show error in error div
}
I'm not sure why the DB class function "Query" would not raise an exception on clearly wrong SQL when called from another class. But same function called from main page code (not inside function / class) raises and exception error.
Also, the "InsertUser" function does not execute / insert anything into DB even if SQL correct.
Could it be scope related, or the fact that I'm trying to enforce global scope of my DB object by putting it in $_SESSION ??
Am I going about this the wrong way? Reason for going class route to encapsulate all my DB calls was to avoid any deprecation issues in future - only having to update class.
Make your function this way.
function Query($str_sql, $arr_parameters = array()) {
$stmt = $this->conn->prepare($str_sql);
$stmt->execute($arr_parameters);
}
I am pretty sure that exception would be thrown
The only issue can be with catching exceptions, not throwing. And it could be caused by Namespace, not scope. To be certain, you can always prepend all PDO calls with a slash:
\PDO::FETCH_ASSOC
\PDOException
etc.
Related
I'm dealing with a PHP application with what seems to have a peculiarity: One of its files (helpers.php) has a couple of functions that includes another file, and the included file (db_connection.php) includes the file that originally included it.
helpers.php:
<?php
function lineBreak()
{
return "\n<br>\n";
}
function saveScoreToDB($score)
{
//session_start(); // Already started
$usuario_id = $_SESSION["usuario_id"];
$etapa = $_SESSION["etapa"];
try
{
$query_etapa = "SELECT id FROM etapas WHERE numero = $etapa";
require_once "db_connection.php";
// `$db_link` works perfectly fine here:
$etapa_id = $db_link->query($query_etapa)->fetchColumn();
$query_score = "INSERT INTO score
(
usuario_id,
etapa_id,
pontos
)
VALUES
(
$usuario_id,
$etapa_id,
$score
)";
$db_link->query($query_score);
}
catch (Exception $e)
{
$_SESSION["error_message"] = $e->getMessage();
header("Location: erro.php");
}
}
function completeTest($redirectTo)
{
unset($_SESSION["etapa"]);
$usuarioId = $_SESSION["usuario_id"];
// TODO: try/catch
try
{
$queryEmailUsuario = "SELECT email FROM usuarios WHERE id = $usuarioId";
$queryNomeUsuario = "SELECT nome FROM usuarios WHERE id = $usuarioId";
require_once "db_connection.php";
// `$db_link` does *not* work here. Why?
$emailUsuario = $db_link->query($queryEmailUsuario)->fetchColumn();
$nomeUsuario = $db_link->query($queryNomeUsuario)->fetchColumn();
// Routine to send email using the variables above
}
catch (Exception $ex)
{
// TODO
}
}
db_connection.php:
<?php
require_once "db_credentials.php";
require_once "helpers.php";
// Variables used here come from `db_credentials.php`
$dsn = "mysql:host=$host;dbname=$dbname;port=3307;charset=utf8;";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
];
try
{
$db_link = new PDO($dsn, $user, $pass, $options);
}
catch (PDOException $e)
{
echo "Error connecting to the database.";
echo lineBreak();
echo $e->getMessage();
echo lineBreak();
echo lineBreak();
}
Notice how in the first script variable $db_link is used in two different functions, both of which include the file where this variable is defined. Within the first function (saveScoreToDB), the variable is available and the function works fine; but within the second (completeTest) it is not available and I get an undefined variable error.
Why is that? How to make it work?
The first require_once() works because that's the "once", but it's only in-scope in that single function call, so $db_link gets tossed out at the end of the function call and is never seen again. You can change that to require(), but creating a new connection for every single function call is... not going to work out well in the long run.
Ideally you create the connection once and then pass it in via parameters where it is needed, eg:
require_once('db_credentials.php');
saveScoreToDB($score, $db_link);
completeTest($redirectTo, $db_link)
But that might get a bit tedious, right? Well this is where classes become useful.
class MyThing {
protected $db;
public function __construct(\PDO $db) {
$this->db = $db;
}
public function saveScoreToDB($score) {
$this->db->prepare(...);
}
public function completeTest($redirectTo) {
$this->db->prepare(...);
}
}
$thing = new Mything($db_link);
$thing->saveScoreToDB(42);
$thing->completeTest('yes');
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 define my own function in one of the php login libraries.
There some constants are defined which works perfectly fine until I call the databaseConnection() function on my own.
private function databaseConnection()
{
// if connection already exists
if ($this->db_connection != null) {
return true;
} else {
try {
// Generate a database connection, using the PDO connector
// #see http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
// Also important: We include the charset, as leaving it out seems to be a security issue:
// #see http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers#Connecting_to_MySQL says:
// "Adding the charset to the DSN is very important for security reasons,
// most examples you'll see around leave it out. MAKE SURE TO INCLUDE THE CHARSET!"
$this->db_connection = new PDO('mysql:host='. DB_HOST .';dbname='. DB_NAME . ';charset=utf8', DB_USER, DB_PASS);
return true;
} catch (PDOException $e) {
$this->errors[] = MESSAGE_DATABASE_ERROR . $e->getMessage();
}
}
// default return
return false;
}
this is the function I defined..
private function updateLastLoginDate($user_name)
{
if($this->databaseConnection())
{
$sth = $this->db_connection->prepare('UPDATE users '
. 'SET last_login_date = UTC_TIMESTAMP()'
. 'WHERE user_email =:user_name OR user_name =:user_name');
$sth->execute(array(':user_name' => $user_name));
}
}
when I call this function the error says that all the constants in the databaseConntion functions are not defined.. but they work perfectly fine other than the call I make to my defined function...
I am not good with pdo though..
I need to do continuous parsing of several external stomp data streams, inserts of relevant fields into a MySql db, and regular queries from the db. All of this is in a protected environment - ie I'm not dealing with web forms or user inputs
Because I'm implementing a range of inserts into + queries from different tables, I've decided to set up a PDO active record model - following the advice of Nicholas Huot and many SO contributors.
I've got a simple repeated insert working OK, but after several days of grief can't get a prepared insert to fly. I want to use prepared inserts given there are going to be a lot of these (ie for performance).
Relevant bits of the code are :
=========
Database class :
private function __construct()
{
try {
// Some extra bad whitespace removed around =
$this->dbo = new PDO('mysql:host=' . DBHOST . ';dbname=' . DBNAME, DBUSER, DBPSW, $options);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
}
public static function getInstance()
{
if(!self::$instance)
{
self::$instance = new self();
}
return self::$instance;
}
public function prepQuery($sql) // prepared statements
{
try {
$dbo = database::getInstance();
$stmt = new PDOStatement();
$dbo->stmt = $this->$dbo->prepare($sql);
var_dump($dbo);
}
catch (PDOException $e) {
echo "PDO prepare failed : ".$e->getMessage();
}
}
public function execQuery($sql) // nb uses PDO execute
{
try {
$this->results = $this->dbo->execute($sql);
}
catch (PDOException $e) {
echo "PDO prepared Execute failed : \n";
var_dump(PDOException);
}
}
=========
Table class :
function storeprep() // prepares write to db. NB prep returns PDOstatement
{
$dbo = database::getInstance();
$sql = $this->buildQuery('storeprep');
$dbo->prepQuery($sql);
return $sql;
}
function storexecute($paramstring) // finalises write to db :
{
echo "\nExecuting with string : " . $paramstring . "\n";
$dbo = database::getInstance(); // Second getInstance needed ?
$dbo->execQuery(array($paramstring));
}
//table class also includes buildQuery function which returns $sql string - tested ok
=======
Controller :
$dbo = database::getInstance();
$movements = new trainmovts();
$stmt = $movements->storeprep(); // set up prepared query
After these initial steps, the Controller runs through a continuous loop, selects the fields needed for storage into a parameter array $exec, then calls $movements->storexecute($exec);
My immediate problem is that I get the error message "Catchable fatal error: Object of class database could not be converted to string " at the Database prepquery function (which is called by the Table storeprep fn)
Can anyone advise on this immediate prob, whether the subsequent repeated executes should work in this way, and more widely should I change anything with the structure ?
I think your problem in this line $dbo->stmt = $this->$dbo->prepare($sql);, php want to translate $dbo to string and call function with this name from this. Actually you need to use $this->dbo.
And actually your functions not static, so i think you don't need to call getInstance each time, you can use $this.
I'm an inexperienced php programmer and only found out about PDO a few days ago. I'm now trying to port my website code over to using PDO, but I am getting an error when I try to use the PDO object that I create.
The error I'm getting is:
Fatal error: Call to a member function prepare() on a non-object in ... file2.php ...
The code looks like this:
index.php
class myClass
{
... variables ...
... functions ...
public function myFunction() // gets called on page load, outputs content to page
{
... stuff ...
require('file1.php');
... stuff ...
}
}
file1.php
require_once('mysql_connect.php'); // create pdo object if not created
... stuff ...
require_once('file2.php');
// I can use the PDO object in here to make queries
$output = function2(); // function2 is in file2.php
... stuff ...
file2.php
require_once('mysql_connect.php'); // create pdo object if not created
function function2()
{
... stuff ...
// PDO error occurs here
$stmt = $db->prepare(...);
makeQuery($stmt, array(...));
return $something;
}
mysql_connect.php
try
{
$db = new PDO("mysql:$dbhost=localhost;dbname=$dbname;charset=utf8", $dbuser, $dbpass, array(PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
}
catch (PDOException $e)
{
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
function makeQuery($stmt, $array = array())
{
try
{
$stmt->execute($array);
}
catch (PDOException $e)
{
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
If I understand your logic right, you're trying to use the PDO object inside myFunction2 - are you passing the PDO object in as a parameter, or declaring it as a global variable? Because if you're not, it's going to be out of scope, and you won't be able to use it.
You don't need to include mysql_coonect again.
Include it just once.
index.php
-class myClass defined
--method myFunction defined (it get's called on pageload & returns the page output)
---include file1.php
----require_once('mysql_connect.php') (creates pdo object)
----*I can use the pdo object here successfully*
----require_once('file2.php')
-----function myFunction2 defined
Are DSN, username and password correct? If so, you do something like that:
$pdo = new PDO("dsn");
/* Some code... at the moment something changes $pdo value */
$pdo->prepare("QUERY");