Okay, it seems the word on the street is that global variables are bad.
Well, if I have:
$dbc = mysqli_connect(DB_HOST,DB_USER,DB_PASSWORD,DB_NAME);
and then I have several functions and such that all depend on that db connection, what is the most sound way to access $dbc in all my functions?
Is this a case where making it global is okay? Declaring it as a global variable inside each function seems redundant.
What is the best way to tackle this?
In this case, the global variables won't be a problem.
Another way to tackle this though could be to write a Database class which stores these variables. The plus side of this is that if you feel you need to switch to a new database handler, say PDO, it will be a rather simple task.
<?php
class Database
{
private $host;
private $user;
private $password;
private $dbname;
public function __construct()
{
$this->host = '';
$this->dbname = '';
$this->user = '';
$this->password = '';
}
public function connect() {
return mysqli_connect($this->host, $this->user, $this->password, $this->dbname);
}
}
?>
In the future you could easily:
switch to PDO without worrying about massive refactoring
you could inject connection details into the constructor
use the Singleton pattern to store your connection
Related
I've started learning PHP OOP a few days ago and while I know the main functionality about it, I decided it would be much more efficient and clean to make the class called DB. It would handle the connection between the database using PDO.
Well, I didn't find any information similar to what I'd like to achieve. That's the main purposes of this class:
Store all the instances of the database and the connection info
Be able to require a single file which contains all the created statements
Using different methods for each possible situation, so you wouldn't need to write the same stuff every time*
*What I mean by saying 'writing the same stuff' is that you could create a few different methods:
public function prepareExecFetch() { ... }
public function prepareExec() { ... }
public function fetch() { ... }
And somehow I'd like to connect these functions with the PDO methods, so I wouldn't need to write these lines again and again for every statement I created. DB class would take care of this stuff with the single method, depending what I'd like to achieve.
I try to avoid writing these lines by using DB:
$stmt1 = $pdo->prepare('...');
$stmt1->execute(array('...' => '...'));
$r1 = $stmt1->fetch(PDO::FETCH_ASSOC);
My project is getting bigger and bigger, so, in my opinion, that's a terrible idea to put these lines of code every time I need to access some information from the database.
Currently, I'm not interested in the PHP frameworks. Before using them, I'd like to understand how it works.
Also, could you give me some ideas about the written code of mine?
<?php
class DB {
private $host;
private $name;
private $charset;
private $user;
private $password;
// I wonder how could I store all the statements here, using the
// $stmt1 = ...; $stmt2 = ...; wouldn't work cause it has to be
// dymanic and I can't create new properties inside any method
private $stmts = [];
// Not using static modifier because if there was another instance of this
// class, then $stmtCounter variable should be resetted again to 0
private $stmtCounter = 0;
public function __construct(string $host, string $name, string $charset,
string $user, string $password) {
$this->host = $host;
$this->name = $name;
$this->charset = $charset;
$this->user = $user;
$this->password = $password;
$pdo = new PDO('mysql:host=' . $this->host . '; dbname=' . $this->name . ';
charset=' . $this->charset, $this->user, $this->password);
// While false, PDO will take advantage of the native prepared statement
// functionality
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// In exception mode, if there is an error in SQL, PDO will throw
// exceptions and the script will stop running
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function execPrepareFetch(string $stmt, array $execParams) {
$this->stmtCounter++;
array_push($this->stmts, $stmt . $this->stmtCounter);
}
}
// This way by instantiating multiple instances we can easily work with multiple
//databases
$db = new DB(DB_HOST, DB_NAME, DB_CHARSET, DB_USER, DB_PASSWORD);
$db->execPrepareFetch('$stmt', [':user' => 'some value']);
This code isn't completed, it's just my starting ideas how could I do this. I'd to get your ideas if it's worth doing that and is it even possible? Thank you :)
I am looking into oop to enhance my web dev knowledge. At the moment i am having a bit of an issue. I have created a Database class that contains all the queries etc. ( fetch, count, etc. ) This just allows the queries or updates to take up less space in the other classes etc. The issue i am having is passing this class along and making it globally accessible. I have used
global $db;
within a class function but i read that it is bad practice to use that. I also do not want to pass the $db variable as a parameter if i did i would have to change a lot of my current classes and it would just be easier if i can make $db globally available in a "good" practice way.
I can provide my Database class if necessary it is just a simple class with the variable that initiates the connection through construct.
( Second Question )
I was also reading about the singleton instance function, before implementing i read that it was also considered bad practice. Is there something that should take its place?
( I decided to place the class below )
class Database {
private $host = 'localhost';
private $user = 'xxx';
private $pass = 'xxxx';
private $dbname = 'xxxxx';
private $dbh;
private $error;
public function __construct(){
// Set DSN
$dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
// Set options
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING,
PDO::ATTR_EMULATE_PREPARES => false
);
// Create PDO Instance
$this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
}
public function fetch($sql, $param = "") {
$this->stmt = $this->dbh->prepare($sql);
if (empty($param)) {
$this->stmt->execute();
} else {
$this->stmt->execute($param);
}
return $this->stmt->fetch();
}
}
$db = new Database();
An example of what i am attempting to accomplish is as follows
( User Profile Page )
class User {
function set_user($input) {
if (is_numeric($input)) {
$this->user = $input;
} else {
$user = $db->fetch("SELECT userid FROM users WHERE url=:url", array(':url' => $input));
$this->user = $user['userid'];
}
}
}
AN issue with your approach is not the global var, but that the DB connection is permanently open. It is not always good. This is kind of service object and it is not partiularily bad to have it globally visible.
If you don't want it global and want to solve the open connection issue, you can easily close the connection in the destructor, remove the global var and create/use/delete the DB wherever required.
Another approach would be not to use singleton, but so called service class. Make DB class not instanciable, but rather define service methods (all static) - open, close, execute, or whatever. Than the DB clients should not create DB object, but only access the static methods. This approach fits the reality very nice, as DB-access is seen as a service.
I am wondering if I use mysql with php secure. This is my class:
class DB{
public $mysqli = null;
public $result = array();
private $_host = 'localhost';
private $_user = 'root';
private $_password = 'root';
private $_db_name = 'DBNAME';
public function __construct()
{
$this->mysqli = new mysqli($this->_host,$this->_user, $this->_password, $this->_db_name);
if ($this->mysqli->connect_errno){
echo "Error MySQLi: (" . $this->mysqli->connect_errno . ") " . $this->mysqli->connect_error;
exit();
}
$this->mysqli->set_charset("utf8");
}
public function __destruct(){
$this->mysqli->close();
}
public function query($query){
$this->result = $this->mysqli->query($query);
if($this->result){
return $this->result;
}else{
return false;
}
}
Is that way to comunicate with database good enough or should I use Doctrine ?
I am asking because something strange is in my code. If I vardump any object contains reference to DB object, I can see :
["_host":"DB":private]=> string(9) "localhost" ["_user":"DB":private]=> string(4) "root"
["_password":"DB":private]=> string(4) "root" ["_db_name":"DB":private]=> string(10) >"DBNAME" }
If you are worried about the DB settings shown on var_dump, I suggest you place those settings in a config file and send those configs as params.
Personally, I wouldn't worry about it. If someone has access to your object (maybe another shared object) they probably have the ability to read your settings. The only positive I see with having a config file is that you can update those configs without having to touch the php codebase.
Is there a reason why you think this would be an issue?
Also your code structure is nice, keep it extracted from mysqli, don't switch to an extend. This will give you flexibility in the future should you decide to switch to PDO or any other better extensions that come up.
Why reinvent the wheel? Your query function pretty much does what mysqli_query is already doing for you. I would just use the base mysqli class as you're already doing by making this class extend that one
class DB extends mysqli {
private $_host = 'localhost';
private $_user = 'root';
private $_password = 'root';
private $_db_name = 'DBNAME';
public function __construct()
{
parent::__construct($this->_host,$this->_user, $this->_password, $this->_db_name);
}
}
Your question is like "should I put a hat or a cap ?".
Actually, there is no best way to deal with a database. It depends of witch approach you have.
If you prefer write SQL code, then your solution is good and works well for your usage.
However, if you do not want to write SQL query and work only with objects, then you will prefer use doctrine.
I am using PHP's PDO to access my MySQL DB. I typically create a singleton class for my connection. I would like to set a MySQL session variable when initiating the connection, and later on as well. See below for my failed attempts to set MySession1 and MySession2. How can I do this? Thanks
EDIT. Just found out that it works if you only use one #. I was originally using two at-signs ## which I thought the documentation at http://dev.mysql.com/doc/refman/5.0/en/using-system-variables.html required. How am I misinterpreting the documentation?
class db {
private static $instance = NULL;
private function __construct() {} //Make private
private function __clone(){} //Make private
public static function db() //Get instance of DB
{
if (!self::$instance)
{
self::$instance = new PDO("mysql:host=localhost;dbname=myDB", 'myUsername', 'myPassword');
self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
self::$instance->exec('SET #MySession1=123');
}
return self::$instance;
}
}
db::db()->exec('SET #MySession2=321');
$stmt=db::db()->query('SELECT #MySession1 AS MySession1, #MySession2 AS MySession2');
$rs=$stmt->fetch(PDO::FETCH_ASSOC);
echo("MySession1={$rs['MySession1']} MySession2={$rs['MySession2']}");
There is a difference between server variables and user defined variables. See http://dev.mysql.com/doc/refman/5.0/en/user-variables.html for the description of user defined variables.
I'm new to OOP. Originally I was defining variables and assigning values to them within the class and outside of the constructor, but after an OOP lesson in Java today, I was told this is bad style and should be avoided.
Here is my original PHP database connection class that I mocked-up:
class DatabaseConnection {
private $dbHost = "localhost";
private $dbUser = "root";
private $dbPass = "";
private $dbName = "test";
function __construct() {
$connection = mysql_connect($this->dbHost, $this->dbUser, $this->dbPass)
or die("Could not connect to the database:<br />" . mysql_error());
mysql_select_db($this->dbName, $connection)
or die("Database error:<br />" . mysql_error());
}
}
Is the above considered okay? Or is the following a better way?
class DatabaseConnection {
private $dbHost;
private $dbUser;
private $dbPass;
private $dbName;
function __construct() {
$this->dbHost = "localhost";
$this->dbUser = "root";
$this->dbPass = "";
$this->dbName = "test";
$connection = mysql_connect($this->dbHost, $this->dbUser, $this->dbPass)
or die("Could not connect to the database:<br />" . mysql_error());
mysql_select_db($this->dbName, $connection)
or die("Database error:<br />" . mysql_error());
}
}
What should I be focusing on to make sure I am understanding OOP correctly?
First of all: this is pointless.
You are creating an object wrapper for the 10+ year old mysql_* function. This php extension is no longer maintained and the process of deprecation has already begun. You should not use this API for any new projects in 2012.
Instead you should learn how to use PDO or MySQLi and work with prepared statements.
That said .. lets take a look at your code:
Constructor should receive all the parameters required for creating new instance, parameters should not be hard-coded in the class definition. What if you need to work with two databases at the same time ?
When connection is created, it should be stored in object's scope variable. Something along the lines of $this->connection = mysql_conn.... Instead you store it in local variable, which you "loose" right after constructor is done.
You should not use private variables for everything. They are not visible to classes which would extend your original class. Unless it is intentional, you should choose protected for this.
The or die('..') bit most go. Do not stop the whole application if connection fails. Instead you should throw an exception, which then can be handled outside of the constructor.
Well, it's not going to run quite yet. You need to change your variables so that they match your connection params:
$dbHost = "localhost";
Should be
$this->dbHost = 'localhost';
I normally don't put my login params inside of the class at all. I would pass them into the constructor when the object is created. Use an outside config file so you can actually use this class on more than one build. :)
Update::
Okay, so here are a few little OOP configuration gold-nuggets that help you build a dynamic Database class.
Check out http://redbeanphp.com/ It will allow you to do a psuedo ORM style of data modelling. Super easy to install, and ridiculously easy to get your database up and running. http://redbeanphp.com/manual/installing
Create a configuration file that contains things like constants, template setups, common functions, and an AUTOLOADER Configuration files are key when working in version controlled environments. :)
Build your Database class as an abstract class http://php.net/manual/en/language.oop5.abstract.php
abstract class Database
{
public function update()
{
}
public function deactivate()
{
}
public function destroy()
{
}
//etc.
}
class MyAppObject extends Database
{
}
Put all of your class files into a library folder, and then put your configuration file into that library. Now, to make your life easier you can use an autoloader function to bring your classes to life whenever you need them, without having to include any specific class. See below:
//note: this is never explicitly instantiated
//note: name your files like this: MyAppObject.class.php
function my_fancypants_autoloader( $my_class_name )
{
if( preg_match( "%^_(Model_)%", $my_class_name ) ) return;
require_once( "$my_class_name.class.php" );
}
spl_autoload_register( 'my_fancypants_autoloader' );
Now all you have to do is include one configuration file in your .php files to access your classes.
Hope that points you in the right direction! Good luck!
Since your are only using them into the __construct method, you don't need them as class attributes. Only the $connection has to be kept for later use imho.
It might be much much better to not give any default values to those arguments but let them being set from the outside.
$db = new DatabaseConnection("localhost", "user", "password", "db");
They are plenty of PHP tools for that already, find them, read them and learn from that. First of all, use PDO and what is true in Java isn't always true in PHP.
The latter is probably better, but with an adjustment: pass some arguments to the constructor, namely the connection info.
Your first example is only useful if you've got one database connection and only if you're happy hard-coding the connection values (you shouldn't be). The second example, if you add say, a $name parameter as an argument, could be used to connect to multiple databases:
I'm new to OOP. Originally I was defining variables and assigning values to them within the class and outside of the constructor, but after an OOP lesson in Java today, I was told this is bad style and should be avoided.
class DatabaseConnection {
private $dbHost;
private $dbUser;
private $dbPass;
private $dbName;
function __construct($config) {
// Process the config file and dump the variables into $config
$this->dbHost = $config['host'];
$this->dbName = $config['name'];
$this->dbUser = $config['user'];
$this->dbPass = $config['pass'];
$connection = mysql_connect($this->dbHost, $this->dbUser, $this->dbPass)
or die("Could not connect to the database:<br />" . mysql_error());
mysql_select_db($this->dbName, $connection)
or die("Database error:<br />" . mysql_error());
}
}
So using this style, you now have a more useful class.
Here is mine and it works rather well:
class Database
{
private static $_dbUser = 'user';
private static $_dbPass = 'pwd';
private static $_dbDB = 'dbname';
private static $_dbHost = 'localhost';
private static $_connection = NULL;
/**
* Constructor
* prevents new Object creation
*/
private function __construct(){
}
/**
* Get Database connection
*
* #return Mysqli
*/
public static function getConnection() {
if (!self::$_connection) {
self::$_connection = #new mysqli(self::$_dbHost, self::$_dbUser, self::$_dbPass, self::$_dbDB);
if (self::$_connection -> connect_error) {
die('Connect Error: ' . self::$_connection->connect_error);
}
}
return self::$_connection;
}
}
By making the __construct empty, it prevents a new class being instantiated from anywhere. Then, make the function static so now all I need to do to get my connection is Database::getConnection() And this is all in an include file, in a password protected folder on the server and just included with each class file. This will also check to see if a connection is already open before attempting another one. If one is already established, it passes the open connection to the method.
<?php
class config
{
private $host='localhost';
private $username='root';
private $password='';
private $dbname='khandla';
function __construct()
{
if(mysql_connect($this->host,$this->username,$this->password))
{
echo "connection successfully";
}
}
function db()
{
mysql_select_db($this->$dbname);
}
}
$obj=new config();
?>