I have been recently toying with dynamic PDO connection methods. However I run into some problems when I started to use other classes.
Why can't I connect to database in Admin class with method that has been constructed in Server class?
I have tried many solutions. This one seemed most logical to me ...
How do I make it work so that I wouldn't have to construct connection in every class?
class Server
{
private $hostdb = 'blah';
private $namedb = 'blah';
private $userdb = 'blah';
private $passdb = 'blah';
public static $conn;
public $errorMessage = 'If you read this text, contact web administrator and tell him about your problem.';
public function __construct()
{
$this->connect();
}
public function connect()
{
try {
$this->conn = new PDO("mysql:host=$this->hostdb; dbname=$this->namedb", $this->userdb, $this->passdb, array(PDO::ATTR_PERSISTENT => true));
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->conn->exec("SET CHARACTER SET utf8");
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
}
}
And the Admin class:
class Admin extends User
{
function someFunction($table)
{
try {
$sql = "SELECT * FROM $table";
//I want to change this line so that my connection would work
$result = Server::$conn->query($sql);
while ($row = $result->fetch(PDO::FETCH_NUM)) {
//Do something
}
} catch (PDOException $e) {
//Show when debugging
//echo $e->getMessage();
echo Server::errorMessage;
}
}
}
I have instantiated both Server, User and Admin class in config.req.php file.
When I changed "Server::$conn->" to "static::$conn->" it still gave me an error.
Make sure you have instantiated Server at least once in the Request. Otherwise, connect() will never be called. Also, $this->conn will create a new public instance property. Static properties need to be set with static::$conn or self::$conn.
So change your connect() method to
public function connect()
{
try {
self::$conn = new PDO("arguments …"));
self::$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
self::$conn->exec("SET CHARACTER SET utf8");
// … shortened for brevity
On a side note, why not just use Dependency Injection? That makes for more maintainable and testable designs, e.g. keep the $this->conn and remove all the static stuff instead (in addition to the constructor) for something like
class Server
{
private $hostdb = 'blah';
private $namedb = 'blah';
private $userdb = 'blah';
private $passdb = 'blah';
private $conn;
public function getConnection()
{
if (!isset($this->conn)) {
$this->connect();
}
return $this->connection;
}
And then for your Admin class:
class Admin extends User
{
private $server;
public function __construct(Server $server)
{
$this->server = $server;
}
function someFunction($table)
{
try {
$sql = "SELECT * FROM $table";
$result = $this->server->getConnection()->query($sql);
On another side note:
$sql = "SELECT * FROM $table"
Interpolating strings into a query makes you open to SQL Injection attacks. Use prepared statements instead.
Related
I can not make the PDO connection outside the class. Browser gives 500 error. I want to write PDO codes in out class. I am doing global change, but it is not.
try {
$pas = new PDO('mysql:host=localhost; dbname=tets', 'root', '123');
$pas->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$pas->exec("SET CHARACTER SET utf8"); // return all sql requests as UTF-8
} catch (Exception $e) {
echo $e->getMessage();
}
/**
User Avatar Check
**/
class Usercheck
{
public $sql , $paso;
public function __construct()
{
global $pas;
$this->paso =& $pas;
}
public function smf_members($whatid)
{
$this->sql = $this->paso->query("SELECT * FROM smf_members WHERE id_member = $whatid");
}
public function fetchcheck(){
if ($this->sql) {
return $this->sql->fetch(PDO::FETCH_BOTH);
}
}
}
$memberuser = new Usercheck();
$memberuser->smf_members('1');
It is not a good practice to use $global. There are many solutions to achieve what you want to do, and here is my approach.
1) Create a Connection Class to handle the PDO connectivity.
use PDO;
/**
* PDO Connection Classes
* Used by Database wrapper class to ensure that there is only one connection
*/
class Connection
{
private $dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME. ';charset=' . DB_CHARSET;
private $conn;
private $error;
public function __construct()
{
try
{
$this->conn = new PDO($this->dsn, DB_USER, DB_PASSWORD);
}
catch(PDOException $e)
{
$this->error = $e->getMessage();
}
}
public function getConnection()
{
return $this->conn;
}
}
2) Establish the PDO connection via your wrapper class __construct(), the __construct() also ensure that there is only one PDO connection is instantiated.
use PDO;
class UserCheck
{
private $conn;
private $db;
public function __construct()
{
if (!$this->db) {
$conn = new Connection();
$this->db = $conn->getConnection();
}
}
}
I'm highly recommend that you read this PDO tutorial on how to use PDO and many best practice tips.
My first real foray into using PHP OOP and PDO extensively. I have finally gotten the script to work, but as you notice in order to do it I had to move the PDO connect into the login function - originally it was just in the __construct(). I do not want to have to open a new PDO connect and this is sloppy. How can I maintain the same connection throughout the whole class?
<?php
class user{
public $id;
public $name;
public $email;
private $password;
public function __construct() {
$DBH = new PDO("mysql:host=HOST;dbname=DB", "USER", "PASS");
$DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function login($email,$password,$type){
$DBH = new PDO("mysql:host=HOST;dbname=DB", "USER", "PASS");
$DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$password_hash = sha1($password);
try{
if ($type != "actives") {
throw new Exception("Type Handling Error");
}
$STH = $DBH->query("SELECT id, email, password FROM $type WHERE email='$email' AND password='$password_hash'");
$STH->setFetchMode(PDO::FETCH_ASSOC);
$row_count = $STH->rowCount();
$row = $STH->fetch();
if($row_count == 1){
session_start();
session_regenerate_id();
$_SESSION['id'] == $row[id];
return true;
}
else{
return false;
}
}
catch (Exception $e) {
echo $e->getMessage();
}
}
public function loggout(){
session_destroy();
setcookie(session_name(), session_id(), 1, '/');
}
Make the database handle a private member within the class:
class user
{
public $id;
public $name;
public $email;
private $password;
private $dbh;
public function __construct(PDO $dbh)
{
$this->dbh = $dbh;
}
public function login($email, $password, $type)
{
$dbh = $this->dbh;
...
}
Usage:
$pdo = new PDO("mysql:host=HOST;dbname=DB", "USER", "PASS");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$user = new User($pdo);
Sooner or later, you may need the PDO connection object somewhere else in your code(outside your user object). Therefore,
I would suggest to use one class which will provide static method as below to get the PDO connection object everywhere you want.
class Database{
private static $datasource='mysql:host=HOST dbname=DB';
private static $username='USER';
private static $password='PASS';
private static $db;
//make the constructor private and empty so that no code will create an object of this class.
private function __construct(){}
//the main public function which will return the required PDO object
public static function getDB(){
if(!isset(self::$db)){
try{
self::$db=new PDO(self::$datasoure,self::$username,self::$password);
}
catch(PDOExceptin $e)
{
$error=$e->getMessage(); //variable $error can be used in the database_error.php file
//display database error file.
include('database_error.php');
exit();
}
}
return self::$db;
}
}
Then you can use the static method like below any time you want PDO connection
$conn=Database::getDB();
Use ceejayoz' proposal or add a global function which is responsible for establishing the database connection. The function is written as to connect to the database at most 1 time per HTTP request:
function getPDOConnection() {
// Thanks to the static-specifier, this variable will be initialized only once.
static $conn = new PDO("mysql:host=HOST;dbname=DB", "USER", "PASS");
return $conn;
}
This enables you to maintain the same connection in your whole application (but of course you don't have to use the function everywhere). Basically, this is a very simple application of the Singleton pattern. See the documentation of the static specifier/keyword and try to get familiar with the Singleton pattern.
Add your database connection as a parameter in the constructor, So you when you create a new instance from the user class it automatically runs across the instantiated object
The variable $DBH needs to be a member variable of your class.
Below private $password;, add private $DBH; and you're good to go with deleting the new-connection code from your login function.
I need to use $GLOBALS['db'] in my classes ($db is defined in my index.php), but I don't want to use $GLOBALS['db'] when I have to call it.
I wrote this code at the beginning of my classes :
class ClassName
{
var $db;
public function __construct()
{
$this->db = $GLOBALS['db'];
}
public function test()
{
$val = $this->db->oneValue('SELECT first_name FROM users LIMIT 0, 1');
echo $val->first_name;
}
}
But I'm not enjoying this; I prefer to use directly $db in my code. Is there a solution to be able to call $GLOBALS['db'] by $db?
Simples, just inject in the constructor or a setter method: (I'm assuming $db is an object here, not an array of connection parameters etc)
class ClassName
{
protected $db;
public function __construct($db)
{
$this->setConnection($db);
//Any other constructor things you want to happen...
}
/*
* This is just here for convenience, this could be protected if you only want to set
* the db connection via the constructor
*/
public function setConnection($db)
{
$this->db = $db;
}
public function test()
{
$val = $this->db->oneValue('SELECT first_name FROM users LIMIT 0, 1');
echo $val->first_name;
}
}
As mentioned in some comments above, this is a form of dependency injection which will give you more ability to re-use code inside your project (A Good Thing TM).
I prefer using singleton pattern for databases.
this is the DB class i am using for my app.
class Database {
protected static $_dbh;
const HOST = 'localhost';
const DATABASE = 'dbname';
const USERNAME = 'username';
const PASSWORD = 'password';
private function __construct() { }
public static function getInstance() {
if(!isset($_dbh)) {
try {
#Connection String.
self::$_dbh = new PDO('mysql:host='.self::HOST.';dbname='.self::DATABASE,self::USERNAME,self::PASSWORD);
self::$_dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
#Print Errors.
echo $e->getMessage();
}
}
return self::$_dbh;
}
}
as i am using singleton pattern the connection will be re-used. you can now use the connection everywhere in your app by calling static connection method i.e
class ClassName
{
protected static $_dbh;
public function __construct() {
self::$_dbh = Database::getInstance();
}
public function test() {
$sth = self::$_dbh->query('SELECT first_name FROM users LIMIT 0, 1');
$row = $sth->fetchAll(PDO::FETCH_ASSOC);
echo $row['first_name'];
}
}
I've spent the last several hours trying to find an answer to the "best", most logical, etc way to write a php database class to simultaneously connect to one postgresql db and one mysql db. Also, I'd like to adopt a Dependency Injection design but am new to that whole concept.
So far I've come up with...
class Database {
public function PgSqlConnect() {
/* Connect to database */
$host = 'localhost';
$dbname = '---';
$user = '---';
$pass = '---';
$timeout = 5; /* seconds */
try {
$pgsql_dbh = new PDO("pgsql:host=$host; dbname=$dbname", $user, $pass);
$pgsql_dbh->setAttribute( PDO::ATTR_TIMEOUT, $timeout );
$pgsql_dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
return $pgsql_dbh;
} catch( PDOException $e ) {
echo 'Unable to connect to database: ' . $e->getMessage();
}
}
public function MySqlConnect() {
/* Connect to database */
$host = 'localhost';
$dbname = '---';
$user = '---';
$pass = '---';
$timeout = 5; /* seconds */
try {
$mysql_dbh = new PDO("mysql:host=$host; dbname=$dbname", $user, $pass);
$mysql_dbh->setAttribute( PDO::ATTR_TIMEOUT, $timeout );
$mysql_dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
return $mysql_dbh;
} catch( PDOException $e ) {
echo 'Unable to connect to database: ' . $e->getMessage();
}
}
}
Obviously the duplicated code violates the DRY approach. I know and have seen many examples of multiple db connections, but most deal with same driver and don't provide DI capability.
I should also add that I've considered placing the connection details into the Database class constructor as...
$driver = 'mysql';
...
$mysqldb = new Database($driver,$un,$pw,...);
$driver = 'pgsql';
...
$pgsqldb = new Database($driver,$un,$pw,...);
but I don't know if that is really a good idea nor how well it would work with DI.
Many thanks!
You should create an interface first for all the DB operations.
interface IDatabase
{
function connect();
function query();
...
}
Then have different driver classes implementing this interface
class MySQLDB implements IDatabase
{
}
class PGSQLDB implements IDatabase
{
}
This way you can easily use dependency injection.
class Test
{
private $db;
function __construct(IDatabase $db)
{
$this->db = $db;
}
}
You can call it as:
$mysqldb = new MySQLDB();
$test = new Test($mysqldb);
or
$pgsqldb = new PGSQLDB();
$test = new Test($pgsqldb);
To avoid duplicated code you can extend an abstract class
abstract class AbstractDb {
public function connect() {
// common code to avoid duplication
echo 'connected!';
}
abstract public function escapeField();
abstract public function escapeValue();
}
class MySQL extends AbstractDb {
public function escapeField() {
// Db-specific method
}
public function escapeValue() {
// Db-specific method
}
}
$db = new MySQL;
$db->connect();
Or use composition, and let the Db class use a different driver for db-specific methods
interface IDriver {
public function escapeField();
public function escapeValue();
}
class MySQLDriver implements IDriver {
public function escapeField() {
// Db-specific method
}
public function escapeValue() {
// Db-specific method
}
}
class Db {
public function __construct($driver) {
$this->driver = $driver;
}
public function connect() {
// common code here ? idk, it's just an example
echo 'connect!';
}
// this method is db-specific, so we call the driver
public function escapeField($field) {
return $this->driver->escapeField($field);
}
public function escapeValue() {
// same here
}
}
$db = new Db(new MySQLDriver);
$db->connect();
In php 5.4 there will be traits, so there will be more approaches to avoid code duplication.
Can anybody please guide me with a sample code to establish a database connection in php using singleton class.
class DatabaseSingleton
{
// [Singleton]
private static $instance = null;
public static function getInstance()
{
if (!self::$instance)
{
self::$instance = new self();
}
return self::$instance;
}
private function __clone(){}
// [/Singleton]
private $connection = null;
private function __construct()
{
$this->connection = mysql_connect('localhost','root','admin');
if ($this->connection)
{
mysql_select_db('my_database');
}
}
//
// crud operations go here.
//
}
$db = DatabaseSingleton::getInstance();
$db->SomeCRUDOperation();
Something like that perhaps? Very basic, but should give you a starting point.
That's how a singleton-pattern looks like:
<?php
class SingletonClass
{
static private $instance = null;
static public function getInstance()
{
if (null === self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
private function __construct(){}
private function __clone(){}
}
$singletonClass = SingletonClass::getInstance();
Now you can put random functions and parameters in there that handle your DB-stuff. I hope that answers your question.
See the manual for an example on how to implement the Singleton pattern: http://www.php.net/manual/en/language.oop5.patterns.php
Then just establish the database connection in your class constructor.
I use something like this:
class DBConn
{
static private $_db = null; // The same PDO will persist from one call to the next
private function __construct() {} // disallow calling the class via new DBConn
private function __clone() {} // disallow cloning the class
/**
* Establishes a PDO connection if one doesn't exist,
* or simply returns the already existing connection.
* #return PDO A working PDO connection
*/
static public function getConnection()
{
if (self::$_db == null) { // No PDO exists yet, so make one and send it back.
try {
self::$_db = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PASS);
} catch (PDOException $e) {
// Use next line for debugging only, remove or comment out before going live.
// echo 'PDO says: ' . $e->getMessage() . '<br />';
// This is all the end user should see if the connection fails.
die('<h1>Sorry. The Database connection is temporarily unavailable.</h1>');
} // end PDO connection try/catch
return self::$_db;
} else { // There is already a PDO, so just send it back.
return self::$_db;
} // end PDO exists if/else
} // end function getConnection
} // end class DBConn
/**
* And you can use it as such in a class
* */
class Post {
public function __construct(){
$this->db = DBConn::getConnection();
}
public function getPosts()
{
try {
/*** The SQL SELECT statement ***/
$sql = "SELECT * FROM posts";
foreach ($this->_dbh->query($sql) as $row) {
var_dump($row);
}
/*** close the database connection ***/
$this->_dbh = null;
} catch (PDOException $e) {
echo $e->getMessage();
}
}
}
I am using something like below
class Database
{
private $_connection;
private static $_instance; //The single instance
private $_host = "HOST";
private $_username = "USERNAME";
private $_password = "PASSWORd";
private $_database = "DATABASE";
public static function getInstance() {
if(!self::$_instance) { // If no instance then make one
self::$_instance = new self();
}
return self::$_instance;
}
private function __construct() {
$this->_connection = new mysqli($this->_host, $this->_username,
$this->_password, $this->_database);
// Error handling
if(mysqli_connect_error()) {
trigger_error("Failed to conencto to MySQL: " . mysqli_connect_error(),
E_USER_ERROR);
}
}
// Magic method clone is for prevent duplication of connection
private function __clone() { }
public function getConnection() {
return $this->_connection;
}
}
$db = Database::getInstance();
$mysqli = $db->getConnection();
$sql_query = "SELECT foo FROM etc";
$result = $mysqli->query($sql_query);