MySQL database config in a separate class - php

Is it possible to keep all my database related configuration (hostnames, usernames, passwords, and databases) as well as the function to connect to and select the correct database in a separate class?
I tried something like this:
class Database
{
var $config = array(
'username' => 'someuser',
'password' => 'somepassword',
'hostname' => 'some_remote_host',
'database' => 'a_database'
);
function __construct() {
$this->connect();
}
function connect() {
$db = $this->config;
$conn = mysql_connect($db['hostname'], $db['username'], $db['password']);
if(!$conn) {
die("Cannot connect to database server");
}
if(!mysql_select_db($db['database'])) {
die("Cannot select database");
}
}
}
And then in another class I would use in the classes __construct function:
require_once('database.php');
var $db_conn = new Database();
But this doesnt save the connection, it ends up defaulting to the servers local db connection. Or do I have to do the database commands everytime before I execute some database commands?

I modified your class to work as you seem to be expecting it to:
<?php
class Database
{
var $conn = null;
var $config = array(
'username' => 'someuser',
'password' => 'somepassword',
'hostname' => 'some_remote_host',
'database' => 'a_database'
);
function __construct() {
$this->connect();
}
function connect() {
if (is_null($this->conn)) {
$db = $this->config;
$this->conn = mysql_connect($db['hostname'], $db['username'], $db['password']);
if(!$this->conn) {
die("Cannot connect to database server");
}
if(!mysql_select_db($db['database'])) {
die("Cannot select database");
}
}
return $this->conn;
}
}
Usage:
$db = new Database();
$conn = $db->connect();
Note that you can call connect() as many times as you like and it will use the current connection, or create one if it doesn't exist. This is a good thing.
Also, note that each time you instantiate a Database object (using new) you will be creating a new connection to the database. I suggest you look into implementing your Database class as a Singleton or storing it in a Registry for global access.
You can also do it the dirty way and shove it in $GLOBALS.
Edit
I took the liberty of modifying your class to implement the Singleton pattern, and follow the PHP5 OOP conventions.
<?php
class Database
{
protected static $_instance = null;
protected $_conn = null;
protected $_config = array(
'username' => 'someuser',
'password' => 'somepassword',
'hostname' => 'some_remote_host',
'database' => 'a_database'
);
protected function __construct() {
}
public static function getInstance()
{
if (null === self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
public function getConnection() {
if (is_null($this->_conn)) {
$db = $this->_config;
$this->_conn = mysql_connect($db['hostname'], $db['username'], $db['password']);
if(!$this->_conn) {
die("Cannot connect to database server");
}
if(!mysql_select_db($db['database'])) {
die("Cannot select database");
}
}
return $this->_conn;
}
public function query($query) {
$conn = $this->getConnection();
return mysql_query($query, $conn);
}
}
Usage:
$res = Database::getInstance()->query("SELECT * FROM foo;");
or
$db = Database::getInstance();
$db->query("UPDATE foo");
$db->query("DELETE FROM foo");

You can certainly keep your connection info in a separate file.
Just save your connection object - $conn in your connect() function - in a class variable. You'll then be able to reuse it across calls.

In your method connect() $conn is only a local variable that only exists in the scope of that method. As soon as the method returns there will be no (other) reference to the connection resource and it will be collected/disposed.
You'll need at least
$this->conn = mysql_connect(...)

Here comes the singleton with PDO example. Thanks to #hodobave
<?php
/**
* DB Connection class
* Singleton pattern
* An single instance of DB connection is created
**/
class Db{
protected static $_instance = null;
protected $_conn;
protected $_config = [
'username' => 'root',
'password' => '',
'hostname' => 'localhost',
'database' => 'news',
];
protected function __construct(){
}
public function getInstance(){
if(null === self::$_instance){
self::$_instance = new self();
}
return self::$_instance;
}
public function getConnection(){
if(is_null($this->_conn)){
//connect here
$db = $this->_config;
$dsn = "mysql:host={$db['hostname']};dbname={$db['database']}";
$this->_conn = new PDO($dsn, $db['username'], $db['password']);
}
return $this->_conn;
}
public function query($sql){
$args = func_get_args();
array_shift($args);
$statement = $this->getConnection()->prepare($sql);
$statement->execute($args);
return $statement->fetchAll(PDO::FETCH_OBJ);
}
}
$res = Db::getInstance();
$users = $res->query("SELECT * FROM `user` WHERE id=?",1);
print_r($users);
?>

Related

Properly pass connection to connstructor PDO

I have spent several days reading different tutorials posts etc either info is outdated or appreciated.
I have a database connection class very simple
namespace App\Database;
use PDO;
use PDOException;
/**
* #desc Connection to the database
**/
class Database
{
protected string $dbhost = DATABASE_HOST;
protected string $dbuser = DATABASE_USER;
protected string $dbpass = DATABASE_PASS;
protected string $dbname = DATABASE_NAME;
protected PDO $conn;
public function __construct()
{
// Set DSN
$dsn = 'mysql:host=' . $this->dbhost . ';dbname=' . $this->dbname . ';charset=' . $this->charset;
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
// Create PDO instance
try {
$this->conn = new PDO($dsn, $this->dbuser, $this->dbpass, $options);
} catch (PDOException $e) {
echo 'Unable to establish a database connection: ' . $e->getMessage();
exit();
}
}
}
and in my user class I passed it to the constructor
protected Database $conn;
public function __construct()
{
$this->conn = new Database;
}
but when i write a statement like this
$stmt = $this->conn->prepare($sql);
prepare is high lighted saying Method 'prepare' not found in \App\Database\Database
I would prefer not to use static or singleton
Your variable $conn would probably better be named $db:
protected Database $db;
public function __construct()
{
$this->db = new Database;
}
Then, when you understand that the User class has a property called db which in turn has a property called conn, the proper use might make more sense:
$stmt = $this->db->conn->prepare($sql);
However, you've defined $conn as protected, so you can't do that. You could make it public, or make a getter method in Database:
public function getConn(): PDO
{
return $this->conn;
}
And then do:
$stmt = $this->db->getConn()->prepare($sql);
Better, I'd forget all that and instead have Database extend PDO, then just override the constructor to configure with your custom values:
class Database extends PDO
{
public function __construct()
{
$dsn = sprintf(
'mysql:host=%s;dbname=%s;charset=%s',
DATABASE_HOST,
DATABASE_NAME,
DATABASE_CHARSET
);
parent::__construct(
$dsn,
DATABASE_USER,
DATABASE_PASS,
[
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]
);
}
}
Now you have a PDO-compatible database object that is pre-configured, and you can just use it like a regular PDO object:
class User
{
protected Database $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function whatever()
{
$this->db->prepare($sql);
// ...
}
}
$db = new Database();
$user = new User($db);
$user->whatever();

Why destroying a PDO is not needed for a singleton database class?

The discussion of using a Singleton or not is out of scope for this question.
That said, I've found online multiple database connection classes using PDO and following the singleton pattern.
Here is one for example :
<?php
define('DB_HOST', 'localhost');
define('DB_NAME', 'test');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_CHAR', 'utf8');
class DB
{
protected static $instance = null;
protected function __construct() {}
protected function __clone() {}
public static function instance()
{
if (self::$instance === null)
{
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => FALSE,
);
$dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset='.DB_CHAR;
self::$instance = new PDO($dsn, DB_USER, DB_PASS, $opt);
}
return self::$instance;
}
public static function __callStatic($method, $args)
{
return call_user_func_array(array(self::instance(), $method), $args);
}
public static function run($sql, $args = [])
{
if (!$args)
{
return self::instance()->query($sql);
}
$stmt = self::instance()->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
However, according to this Stackoverflow answer Is it necessary to close PDO connections
, some users suggest that it's a good practice to close all connections at the end of the script.
So:
1. Why does no one include a destroy/close method?
2. How would it be done right (destroying a PDO inside a singleton?)
My approach would have been:
class DB
{
// ... the whole singleton
public static function close()
{
self::$instance = null;
return null;
}
}
$pdo = DB::getInstance();
// do stuff
$pdo = DB::close($pdo);

Including file inside a class php

I am doing something using classes in php for very first time.
I am trying to fetch an return object array items in class.
This is my class
class User {
$dbconn = include("config.php");
private $dbHost = $dbconn->host;
private $dbUsername = $dbconn->username;
private $dbPassword = $dbconn->pass;
private $dbName = $dbconn->database;
private $userTbl = 'users';
function __construct(){
if(!isset($this->db)){
// Connect to the database
$conn = new mysqli($this->dbHost, $this->dbUsername, $this->dbPassword, $this->dbName);
if($conn->connect_error){
die("Failed to connect with MySQL: " . $conn->connect_error);
}else{
$this->db = $conn;
}
}
}
}
This is my config.php file
return (object) array(
'host' => 'localhost',
'username' => 'my_user',
'pass' => 'my_pass',
'database' => 'my_db'
);
How do i do it?
PHP Parse error: syntax error, unexpected '$dbconn' (T_VARIABLE)
You can't have executable code in a variable definition, only static values. So this sort of thing is not supported:
class foo {
public $var = result_of_some_function();
}
If you want to initialize a value, use the constructor. You're probably better off reading it as a config file:
class User {
public function __construct() {
$config = json_decode(file_get_contents('config.json'));
$conn = new mysqli($config->host, ...);
}
}
Or better, using dependency injection:
class User {
protected $db = null;
public function __construct($db) {
$this->db = $db;
}
}
Then in your code that creates a user object:
$db = new Db($config);
$user = new User($db);
The other way is to define constants in config file and use them in class.
in config.php file
define('HOST', 'localhost');
define('USERNAME', 'my_user');
define('PASS', 'my_pass');
define('DATABASE', 'my_db');
In class file
include("config.php")
class User {
private $dbHost = HOST;
private $dbUsername = USERNAME;
private $dbPassword = PASS;
private $dbName = DATABASE;
private $userTbl = 'users';
function __construct(){
if(!isset($this->db)){
// Connect to the database
$conn = new mysqli($this->dbHost, $this->dbUsername, $this->dbPassword, $this->dbName);
if($conn->connect_error){
die("Failed to connect with MySQL: " . $conn->connect_error);
}else{
$this->db = $conn;
}
}
}
}
Include this:
$dbconn = include("config.php");
in your construct function.
Maybe you should change in your code to
function __construct()
{
//included db file
include 'config.php';
if (!isset($this->db))
{
//code here
}
Try this way of using it.
<?php
class User
{
private $dbconn = null;
private $dbHost;
private $dbUsername;
private $dbPassword;
private $dbName;
private $userTbl = 'users';
function __construct()
{
include 'config.php'; //included file in constructor
if (!isset($this->db))
{
$this->dbHost= $this->dbconn->host;
$this->dbUsername= $this->dbconn->username;
$this->dbPassword= $this->dbconn->pass;
$this->dbName= $this->dbconn->database;
// Connect to the database
$conn = new mysqli($this->dbHost, $this->dbUsername, $this->dbPassword, $this->dbName);
if ($conn->connect_error)
{
die("Failed to connect with MySQL: " . $conn->connect_error);
} else
{
$this->db = $conn;
}
}
}
}
Config.php
<?php
$this->dbconn= (object) array(
'host' => 'localhost',
'username' => 'my_user',
'pass' => 'my_pass',
'database' => 'my_db'
);

Using Namespaces and Databases together

The way I used to use a database would be
class sitedb
{
public $db = '';
public $config;
public $settings = array(
'host' => "localhost",
'database' => "database",
'username' => "user",
'password' => "pass",
);
public function __construct(){
$this->db = new PDO(
"mysql:host={$this->settings['host']};" .
"dbname={$this->settings['database']};" .
"charset=utf8",
"{$this->settings['username']}",
"{$this->settings['password']}"
);
$this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
}
}
And then say I have a user script that uses this database
class User {
public function __construct() {
$this->db = new sitedb();
}
}
Now within my code I could use $resource = $this->db->db->prepare($query);
A friend of mine recently suggested I used namespaces instead and it would make things easier than the constructor every time, but not sure how to go about this.
I have my database class file
namespace database;
class site
{
public $db = '';
public $config;
public $settings = array(
'host' => "localhost",
'database' => "database",
'username' => "user",
'password' => "pass",
);
public function __construct(){
$this->db = new PDO(
"mysql:host={$this->settings['host']};" .
"dbname={$this->settings['database']};" .
"charset=utf8",
"{$this->settings['username']}",
"{$this->settings['password']}"
);
$this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
}
}
class auth
{
}
My thought process here was that I could use
$resource = \database\site::????
Now I'm at a loss as to how to actually connect. I'm hoping I drew up my issue well enough to show what I'm after. I'm just not sure how I can use this to actually connect to the database.

Mysqli query error outside class

I made a db connection class like this :
class db_connector{
const host = "localhost";
const user = "root";
const password = "";
const dbname = "test";
public $db;
public function __construct()
{
$database = $this::dbname;
$db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($db->connect_errno)
{
die (mysql_connect_error());
}
return $db;
}
}
When i create an object for the class it works fine enough, though then i want to make a query within the class i get the following error:
Fatal error: Call to undefined method db_connector::query()
The object is as follows (outside the class):
$con = new db_connector;
$con->query("SELECT * FROM test");
The connection gets established, just the query gives the error. I thought the object would inherit the mysqli methods since I returned it. Can anybody help me fix this one? Im fairly new in OOP so maybe my logic is not the best.
How about a little magic:
class db_connector{
protected $connectionData = array(
'host' => 'localhost',
'user' => 'root',
'password' => '',
'dbname' => 'test',
);
protected $db;
public function __construct($connectionData=array())
{
$cd = array_merge($this->connectionData, $connectionData);
$this->db = new mysqli($cd['host'], $cd['user'], $cd['password'], $cd['dbname']);
if($this->db->connect_errno)
{
die (mysql_connect_error());
}
}
public function foo()
{
return 'I am foo, i exist so i will be called even if mysqli::foo would exist, because i am defined in this class!';
}
public function __get($property)
{
if(property_exists($this->db, $property)){
return $this->db->$property;
}
}
public function __call($name, $args)
{
if(method_exists($this->db, $name))
return call_user_func_array(array($this->db, $name), $args);
}
}
And call it like:
$db = new db_connector();
$result = $db->query('SELECT foo FROM bar LIMIT 1');
// this should also work:
echo $result->num_rows;
The class constructor won't return the mysqli object as you expect.
$con = new db_connector;
//$con is a db_connector object not a mysqli object
$con->query("SELECT * FROM test");
Try:
$con = new db_connector;
$con->db->query("SELECT * FROM test");
instead. $con->db is a public field which holds the mysqli object.
Edit
Also change:
$db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($db->connect_errno)
{
die (mysql_connect_error());
}
return $db;
To:
$this->db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($this->db->connect_errno)
{
die (mysql_connect_error());
}

Categories