This is in reference to a CentOS 7 server running PHP 5.4 and MariaDB 5.5.
I am somewhat new to OOP in PHP. In converting a bunch of scripts from MySQL to MySQLi and from procedural database functions to OOP, I set up this basic Database class:
class Database
{
private $host = "localhost";
private $username;
private $password;
private $database;
private $dbconnect;
function __construct()
{
// Load config file for database connection info
$ini = parse_ini_file("config.ini");
$this->username = $ini['db.user'];
$this->password = $ini['db.pword'];
$this->database = $ini['db'];
}
public function connect()
{
// Only make a new connection if one not already established.
if (empty($this->dbconnect)) {
$mysql = new mysqli($this->host, $this->username, $this->password, $this->database);
if ($mysql->connect_errno) {
throw new appError($mysql->connect_error);
}
$this->dbconnect = $mysql;
}
return $this->dbconnect;
}
public function query($query)
{
$db = $this->connect();
$result = $db->query($query);
if ($db->errno) return false;
return $result;
}
public function select($query)
{
$rows = array();
$result = $this->query($query);
if ($result === false) return false;
// Create array with results
while ($row = $result->fetch_assoc()) {
$rows[] = $row;
}
return $rows;
}
}
With the former procedural database functions that used mysql_*, a persistent database connection was made near the start of the script and the resource ID was stored in a global variable, then all queries were run through that one database connection by accessing the global resource variable. Importantly, this worked well from within other functions and objects. Here's how that function worked:
function db_connect() {
if (!empty($GLOBALS['DBCONNECT']) && is_resource($GLOBALS['DBCONNECT'])) {
return $GLOBALS['DBCONNECT'];
} else {
$result = mysql_connect("localhost", DB_USER, DB_PASSWORD);
$GLOBALS['DBCONNECT'] = $result;
return $result;
}
}
So at the start of each script I'd do this...
db_connect();
And then run my queries like this...
$result = mysql_query($query, db_connect());
This made sure one database connection was made and that all queries are run through that connection.
With the above new Database class, I instantiate it at the start of my script...
$db = new Database;
$db->connect();
But I don't understand how to make that Database object accessible to other objects that need to perform database queries so that the same database connection is used by the entire script. What I do now is essentially this...
class MyClass
{
public function myFunction()
{
$db = new Database;
$data = $db->select("SELECT * FROM mydata WHERE id = 888");
...
}
}
This instantiates a new Database object within the above class, which is creating a new and additional connection to the database, because it can't access the Database $db object created in the parent calling script (that I know of).
Is there a way to use an object to open a persistent MySLQi database connection that all functions and objects loaded by that script can use? Or is this just something that is better done with a procedural function rather than a class and object? Does the solution lie in making the Database properties and its methods static? If I do that, I'm not sure how I load the database username and password from the config.ini file which the connect() function needs and is only done upon instantiation of the Database object.
I guess the basic question here is how do I access the properties or methods in an instantiated object from another object? Or maybe that isn't the question and I'm completely missing something else. Thanks!
You can do something like that a static method with a only a single tone
<?php
/*
* Mysql database class - only one connection alowed
*/
class Database {
private $_connection;
private static $_instance; //The single instance
private $_host = "HOSTt";
private $_username = "USERNAME";
private $_password = "PASSWORd";
private $_database = "DATABASE";
/*
Get an instance of the Database
#return Instance
*/
public static function getInstance() {
if(!self::$_instance) { // If no instance then make one
self::$_instance = new self();
}
return self::$_instance;
}
// Constructor
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: " . mysql_connect_error(),
E_USER_ERROR);
}
}
// Magic method clone is empty to prevent duplication of connection
private function __clone() { }
// Get mysqli connection
public function getConnection() {
return $this->_connection;
}
}
and to make a connection to the database and make a query simple use the lines:
$db = Database::getInstance();
$mysqli = $db->getConnection();
$sql_query = "SELECT foo FROM .....";
$result = $mysqli->query($sql_query);
There are a few options, one simple one (and likely the preferred) would be to use a static factory method in your database class that returns a previously initialized instance of the database class.
class Database
{
//static instance of the class
private static $instance;
public static function getDB()
{
//if the instance is not set, set it
if(is_null(self::$instance))
{
self::$instance = new Database();
self::$instance->connect();
}
return self::$instance;
}
//rest of your class below
}
This static method can be called from anywhere in any context and would allow for getting the same connected instance of the database. Just call $db = Database::getDB() anywhere you need it. For example:
function TestFunction()
{
//get an instance to the database
$dbInstance = Database::getDB();
//then use the database object like you normally would.
$dbInstance->query("SELECT * FROM `someTable`");
}
class TestClass
{
public function doSomething()
{
//get an instance to the database
$dbInstance = Database::getDB();
//then use the database object like you normally would.
$dbInstance->query("SELECT * FROM `someTable`");
}
}
$tc = new TestClass();
$tc->doSomething();
Another option, and one that I use for simplicity, is declare your connection static and reference that everywhere in your database class for queries and such. This allows for creating $db = new Database() anywhere and will just use the same connection. I've had this frowned upon as if it were same as a global because you can really only connect to one database (subsequent connections would overwrite the connection variable) But it worked for me and I didn't need multiple connections. I also like it that I can have each instance remember whatever queries were run on that instance and have a private count of the queries run.
Related
Below is the solution to my question. I didn't have my class written wrong. I accidentally used a "die" function inside my master DB class that made all my queries fail. I was tracking down the wrong problem.
I hope you guys find this class example useful. It's clean, and very useful for wrapping SQL calls in a single location for multiple databases. By allowing the extension, you can track your calls much easier my var name.
class A extends DB {
protected $connection;
function __construct()
{
$this->db_host = "server.com:3327";
$this->db_name = "db_name_one";
$this->db_username = "root";
$this->db_password = 'pw';
// .. equals all the setup vars
parent::__construct();
}
}
class B extends DB {
function __construct()
{
$this->db_host = "server.com:3327";
$this->db_name = "db_name_two";
$this->db_username = "root";
$this->db_password = 'pw';
// .. equals all the setup vars
parent::__construct();
}
}
class DB {
function __construct()
{
$this->connect()
}
public function connect()
if (!$connection = # mysql_connect ($this->db_host,$this->db_username,$this->db_password,$this->second_flag))
die ('I cannot connect to the database because: ' . mysql_error());
if (!mysql_selectdb($this->db_name,$connection))
$this->showerror();
return $connection;
}
public function executeSQL($query)
{
$results = mysql_query($query,$this->connection);
if (!$results) {
die(...);
}
return $results;
}
}
$db1 = new A();
$db2 = new B();
$db1->executeSQL("select * from table");
I don't know if there's a legitimate reason to extend the DB class here. Unless those extending classes add something to the DB (e.g. they adapt it to a different database), then they have nothing to do with being a DB as such. Just because they use a DB doesn't mean they should extend one.
The simplest thing to do is: make one class that handles your database interaction. When you make a new instance of that class, it establishes a new connection to the database. This way you can have as many independent connections as you want. You then simply pass those instances to whoever needs them:
class DB {
protected $connection;
public function __construct() {
$this->connection = mysql_connect(..);
if (!$this->connection) {
throw new Exception(..);
}
}
..
}
class A {
protected $db;
public function __construct(DB $db) {
$this->db = $db;
}
..
}
$db = new DB;
$a = new A($db);
I have one class that encapsulate PDO defined as that example:
class db{
private $conn=null;
function __construct(){
getConn();
}
function getConn(){
$this->conn = new PDO("mysql:host=localhost;dbname=database", 'username', 'password');
}
}
I want reuse same db connection between different class instances so i can use transaction during complex operation like example:
function getDataFromAnotherTable(){
$db = new dbClass;
$data = execute('select * from table2');
return $data;
}
//main code start here...
$db = new db; //here i will instantiate class defined above
$db->beginTrans();
$db->execute(insert into table1);
//here transaction still active
$data = getDataFromAnotherTable()
//now transaction has been closed by previous call!
$db->execute('insert into table1');
//here i receive "There is no active transaction" error because transaction is closed inside
// getDataFromAnotherTable()
$db->endTransaction();
i want change my class like:
function __construct(){
if(empty($this->conn){
getConn();
}
}
So if a connection already exists, will be reused and no new connection will be created.
I've already tried this method but every instance of class obtain new connection so i can't reuse same connection to maintain my transaction over different classes and functions.
Please remember that this is only a simplified example! my situation is really more complex so i can't do a single insert statement using something like:
select x from table1 insert into table2
Thank you in advance for any support!
You could make $conn a static variable...
class myClass
{
private $conn = NULL;
function __construct()
{
static $nDB = false;
if ($nDB === false)
{
echo "INIT DB\n";
$nDB = true;
}
$this->conn = $nDB;
}
}
class myOne
{
private $oDB = NULL;
function __construct()
{
$this->oDB = new myClass();
}
}
class myTwo
{
private $oDB = NULL;
function __construct()
{
$this->oDB = new myClass();
}
}
$f1 = new myOne();
$f2 = new myTwo();
Should net you only one echo of
INIT DB
This is because the $conn is shared among all versions of myClass that are loaded.
You have 2 options basically.
First, if you have OOP exclusively all over the place, you have to pass PDO connection as a class property.
// having no class at hand I am passing $db into function as well
function getDataFromAnotherTable($db){
$data = $db->execute('select * from table2');
return $data;
}
$db = new db; //here i will instantiate class defined above
$db->beginTransaction();
$db->execute(insert into table1);
$data = getDataFromAnotherTable($db);
$db->execute('insert into table1');
$db->commit();
Second, you are mainly using old plain procedural style, then singleton would be the best answer. You'll only have to change -> to :: and get rid of this new dbClass; stuff:
function getDataFromAnotherTable(){
return DB::query('select * from table2')->fetchAll();
}
DB::beginTransaction();
DB::prepare("insert into table1 VALUES(?,?)")->execute([1,2]);
$data = getDataFromAnotherTable()
DB::prepare("insert into table1 VALUES(?,?)")->execute([3,4]);
DB::commit();
I'm trying to re-code a homepage I made. This time I want to use OOP style, but I always get following error:
Statistic::checkExistingCounter() [statistic.checkexistingcounter]: Couldn't fetch MySQL
What am I doing wrong? I know that the prepare statement is senseless, but even just a query instead of prepare statement is not working at all.
Same error:
Couldn't fetch MySQL
My Database class:
class MySQL extends MySQLi {
private static $_instance = null;
private $host, $username, $password, $db;
public static function getInstance() {
if (!(self::$_instance instanceof self)) {
self::$_instance = new self();
}
return self::$_instance;
}
public function __construct(){
$this->host = '...';
$this->username = '...';
$this->password = '...';
$this->database = '...';
$this->connect();
}
public function __destruct() {
$this->db->close();
}
private function __clone(){}
public function connect() {
$this->db = #new MySQLi($this->host, $this->username, $this->password, $this->database);
/* change character set to utf8 */
$this->db->set_charset("utf8");
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
return $this->db;
}
}
My statistic class:
class Statistic {
private $remote, $user_agent, $referer;
private $db;
/**
* Create Instance of MySQL
**/
function __construct($db) {
$this->db = MySQL::getInstance();
}
/**
* Check for counter today
*
* #param: string SQL
* #return: boolean (true = Counter exists, false = Counter doesnt exist)
**/
function checkExistingCounter($sql) {
$stmt = $this->db->prepare($sql);
$this->db->error;
if (!$stmt) {
echo 'Datenbankfehler';
exit;
}
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows) {
$stmt->close();
return true;
} else {
$stmt->close();
return false;
}
}
function counter() {
$sql = "SELECT ID FROM Counter WHERE Datum = CURDATE()";
$checkCounter = $this->checkExistingCounter($sql);
}
And this is a part of my index.php:
$db = new MySQL();
$statistic = new Statistic($db);
$statistic->counter();
You seem to be in a muddle here, implementing two sets of competing coding patterns:
your MySQL class both extends MySQLi (that is, any MySQL object is also a MySQLi object) and "delegates" to a MySQLi instance in its private variable $db
your Statistic class takes an instance of MySQL in its constructor ("dependency injection"), but then ignores it and asks the MySQL class for a "singleton" instance.
You need to read up more carefully on what each of these patterns is for, and decide on one or the other in each case (inheritance or delegation, dependency injection or singletons).
Currently, your code will do the following:
create a new MySQL object (which is also a MySQLi object, but hasn't been initialised to any particular database connection, because you haven't called parent::__construct())
in the MySQL constructor, set $this->host etc
in the connect() method, create a new MySQLi object, passing it the host etc
save this object as $this->db, which is only ever referenced in the destructor ($this->db->close())
return the MySQLi object from connect(), but nothing in __construct() is looking at that return value
back in the outer code, the MySQL object is passed to the constructor of the Statistic class
the constructor then ignores this, and calls the Singleton method MySQL::getInstance() instead
the getInstance() method (since this is the first time it has been called) will create a second MySQL object, repeating steps 1 to 5
this second MySQL object will be saved as $this->db on the Statistics object
the checkExistingCounter method attempts to use $this->db as a MySQLi connection, but the MySQL object was never connected to any database, so you get an error. (There is a connected connection, and if it wasn't private, you could access it as $this->db->db. There's another one kicking around as well, which was created at step 2, but you can't access that any more, because you ignored it at step 7.)
I want to use a class inside other class.
This is my first class:
class mydb {
public function connect($host="localhost",$user,$pass,$data,$persistency=False) {
$this->host = $host;
$this->user = $user;
$this->pass = $pass;
$this->data = $data;
$this->persistency = $persistency;
$this->link=#mysql_connect($this->host,$this->user,$this->pass)or die(mysql_error());
If(($this->link) AND ($this->data)) {
If(#mysql_select_db($this->data,$this->link) or die(mysql_error())) {
return True;
}
}
return False;
}
public function query($query="",$sup="") {
If($sup) {
$query=#mysql_query($query,$this->link)or die("<br /><br />".mysql_error()."<br /><br />");
} Else {
$query=#mysql_query($query,$this->link) or die("<br /><br />".mysql_error()."<br /><br />");
}
return($query);
}
}
This is my second class:
class articles {
function getpath($id) {
$sql=$db->query("select id,name from articles where id='$id'");
$row=mysql_fetch_array($sql);
return $row['name'];
}
}
I receive this error: ( Fatal error: Call to a member function query() )
You didn't create $db anywhere.
You need to create reference to the mydb class in the articles class. Assuming you create an instance of the mydb class and stored it in the variable $db, this is what you would do.
class articles {
public static $db = null;
...
self::$db->query(...);
}
articles::$db = $db;
The variable doesn't have to be static, it could be a regular class variable. You would then reference it like
$this->db->query(...);
As has been mentioned, you need an instance of the mydb class made available to articles.
Here's a simple example (an anti-pattern, perhaps):
class articles {
private $db = null; // add a class property to hold your database object
// each time we create an articles object, we'll connect to the database
public function __construct() {
$this->db = new mydb(); // add your connection params
}
public function getpath($id) {
// now we can reference the object via the class property $this->db
$sql = $this->db->query("select id,name from articles where id='$id'");
$row = mysql_fetch_array($sql);
return $row['name'];
}
// the rest of your code goes here
}
If you don't want to have to constantly create new mydb instances you can do something slightly different:
Dependency Injection
class articles {
private $db = null;
// Here we'll use an existing mydb object and pass it into our new articles
public function __construct(mydb $db) {
$this->db = $db;
}
// the rest of your code goes here
}
$db = new mydb(); // create mydb
$db->connect(); // add your params
$articles = new articles($db); // create articles using mydb from above
Singleton
class mydb {
// This is static, which is special
private static $instance = null;
public static function setInstance(mydb $db) {
self::$instance = $db;
}
public static function getInstance(mydb $db) {
return self::$instance;
}
// the rest of your code goes here
}
$db = new mydb(); // Create a mydb object
$db->connect(); // add your params
mydb::setInstance($db); // store the mydb object in the mydb class
// Later when you want to query we use the stored object:
// (and this will now work everywhere in the program--
// we don't need $db anymore)
mydb::getInstance()->query();
Analysis
Generally, you don't want to open or maintain many connections to your database. It's faster to use the same connection everywhere. Sometimes this is not appropriate, though.
The singleton is a cheap form of a Service Locator.. I.e. you create a resource (or "service") and then you have a means to locate that resource without passing a variable around with you. Singletons are a basic fix, but there are better ways to architect your applications.
You might want to read about two relevant design patterns: Service Locator and Dependency Injection. Both are described here: http://martinfowler.com/articles/injection.html
I now learn PHP OOP and I want to get yours tips. I create DB connection class, is this okay?
How use "Database" class in another class? Always use "extends"?
Thanks
<?php
//Config
$db_user = 'root';
$db_pass = '';
$db_host = 'localhost';
$db_name = 'test';
class Database
{
private $Database;
private static $instance;
public static function instance()
{
if ( !self::$instance )
self::$instance = new Database();
return self::$instance;
}
public function connect($host, $user, $password, $name)
{
$this->db_link = mysql_connect($host, $user, $password);
mysql_set_charset('utf8');
mysql_select_db($name, $this->db_link);
}
public function query($query)
{
$sql = mysql_query($query);
$row = mysql_fetch_array($sql);
return $row;
}
}
class Book extends Database
{
public function getData2()
{
$sql = $this->query('SELECT * FROM users WHERE price = "7"');
return $sql['name'];
}
}
$db = Database::instance();
$db->connect($db_host, $db_user, $db_pass, $db_name);
$b = new Book();
$res = $b->getData2();
print_r($res);
?>
You can extends the database class but it is not what I suggest.
You could also use the keyword global inside the function where you actually need database to let the function get the database instance from outside; but still some people get nervous about global keyword.
You could pass the instance of the database class as argument of constructor of the class where you need database, in this way you will be able to call db methods with a simple chain: $this->db->connect();
As björn states, a book is not a database, wouldn't recommend extend.
One approach would be to on object initialization of book, pass the reference to the db-layer/object..
$myDb = new dbLayer($settings);
$myBook = new book($myDb);
$a = $myBook->getAllData($someParameter);
in the constructor of book you save the reference to the dblayer...
class book {
var $dbTier;
__constructor($db) {
$this->dbTier = $db;
...
regards,
//t
You can do something like this, this should be easy to understand
class User{
private $db;
public function __construct($db){
$this->db = $db;
}
}
The $db in the constructor parameter would be your database connection and required fom another file. All any how you get to it but not in the class.
require 'file from where you have your database';
$user = new User($db);
Anytime you like to user the database to make a query, you just reference it like below
$this->db->query();
You would not use extends here ideally unless Book is a subclass of Database. You would do something like:
class Book
{
var $db;
function __construct() {
$this->db = Database::instance();
}
}
Then the book class would use the instance of the database object and you can always access it with $this->db inside Book.