Reputation: 455
I have a very common mysqli class to provide database connections for the rest of the application:
class IabDB extends mysqli {
private static $instance = null;
// db connection config vars
private $user = "xxx-lit";
private $pass = "xxx";
private $dbName = "xxx";
private $dbHost = "xxx";
private function __construct() {
parent::__construct($this->dbHost, $this->user, $this->pass, $this->dbName);
if (mysqli_connect_error()) {
exit('Connect Error (' . mysqli_connect_errno() . ') '
. mysqli_connect_error());
}
mysqli_set_charset($this, "utf8");
}
public static function getInstance() {
if (!self::$instance instanceof self) {
self::$instance = new self;
}
return self::$instance;
}
In my model I retrive data like this which works nicely:
$db = IabDB::getInstance();
$query = $db->query("SELECT status, username, email, id FROM " . $table);
$items = $query->fetch_all();
EDIT But when I try to get another instance for another query I get the error message: Call to a member function fetch_all() on null.
Believe me: I tried it back and forth. Both queries are valid and both return a result if only either one of them is used. Just for the sake of completeness here is the second one with the model around:
class Model {
public static function getItems($table) {
$db = IabDB::getInstance();
$query = $db->query("SELECT * FROM lit_veranstaltungen");
$items = $query->fetch_all();
$db->close();
var_dump($items);
} }
So it seems the second connection is disturbed somehow!?
Bonus question: from my knowledge the obviously now mostly laughed upon singleton pattern seems to be of good use for this purpose?
Any help and optionions are greatly appreciated!
Upvotes: 4
Views: 1463
Reputation: 6447
You cannot make singleton from class which extends mysqli (or PDO) since mysqli (and PDO) class constructor is public. You cannot override public parent's constructor and make it private in inherited child class! Try it and you will get the following error:
Fatal error: Access level to DataBase::__construct() must be public (as in class mysqli)
Upvotes: 1
Reputation: 407
Try to use $query->close();
after you executed the query.
For this case, a singleton is not a bad idea. But more commonly, you have something like a "connection manager", which creates a mysqli object on first use, and then returns that object on consecutive calls, with the ability to have multiple connections to multiple databases or with differen users.
Something like this:
class DBConnection {
protected static $connections = array();
public static function getConnection($connection_name = 'default') {
if (!isset(static::$connections[$connection_name])) {
static::$connections[$connection_name] = // setup connection
// ... error handling and stuff
}
return static::$connections[$connection_name];
}
}
Btw.: In your constructor, I would use the mysqli object's properties for errorhandling:
http://php.net/manual/de/mysqli.connect-error.php
http://php.net/manual/de/mysqli.connect-errno.php
and http://php.net/mysqli_set_charset
Upvotes: 0
Reputation: 157839
Your code works for me, whatever number of instances I get:
$db = IabDB::getInstance();
$query = $db->query("SELECT name FROM users limit 1");
$items = $query->fetch_all();
var_export($items);
$db = IabDB::getInstance();
$query = $db->query("SELECT name FROM users limit 1,1");
$items = $query->fetch_all();
var_export($items);
returns
array ( 0 => array ( 0 => 'John', ), )
array ( 0 => array ( 0 => 'Mike', ), )
So I suppose there is an error with particular query you run. Therefore you need just better error reporting for your queries. Change your constructor to this
private function __construct() {
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
parent::__construct($this->dbHost, $this->user, $this->pass, $this->dbName);
$this->set_charset("utf8");
}
Then make sure your PHP is configured like this
error_reporting(E_ALL);
ini_set('display_errors',1);
and then run your code again.
It will either report an error or return an empty array. In the latter case it means that there are no rows in the database to match your second query conditions. E.g., for a code like this
$db = IabDB::getInstance();
$query = $db->query("SELECT name FROM users WHERE 1=0");
$items = $query->fetch_all();
var_export($items);
$db = IabDB::getInstance();
$query = $db->query("SELECT name FOrM users");
$items = $query->fetch_all();
var_export($items);
it will return an empty array for the first snippet and error for the second:
array ()
Fatal error: Uncaught exception 'mysqli_sql_exception' with message 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'users' at line 1'
Also, let me suggest you to add a function like this
public function query_params($query, $params, $types = NULL)
{
$statement = $this->prepare($query);
$types = $types ?: str_repeat('s', count($params));
$statement->bind_param($types, ...$params);
$statement->execute();
return $statement->get_result();
}
that will let you to use prepared statements as easily as with PDO:
$user = $db->query_params("SELECT * FROM users WHERE name = ?", ['Mike'])->fetch_assoc();
Regarding singleton, if you're using old style procedural PHP, then singleton is all right. If you're using OOP, then better implement a Dependency Injection based approach.
Upvotes: 2