I'm trying to switch from building query strings and calling mysql_query() and embrace PDO. I've read a handful of tutorials, and understand the basic process of preparing a statement and assigning values to the placeholders.
What I don't understand is how to integrate this new technique into my existing web app.
Let me explain:
currently, I've got an init.php which is require_once'd on every page. It holds the database U/P and opens the database connection. With that loaded, I can sling queries left and right, willy AND nilly, anywhere I please without any further thought.
PDO strikes me as a much more deliberate approach. Do I still stuff all the U/P and connection handlers into my init file? Can I just work through my scripts and replace queries one-to-one with PDO statements?
Sorry for the simple question!
For what it is worth, I had the same issue and this is how I approached it. I divided the database functions into two different classes, Database and Query. I setup and open the database in my init function and then call instances of my Query class throughout the page, closing the queries each time I am done. Finally, I close the database.
The actual classes I use have some more error checking and other stuff I specifically, but here are the drafts I uses when putting the scheme together. Maybe it will be useful to you.
Database Class:
<?php
class Database {
private $db, $dbserver, $dbport, $dbname, $dbuser, $dbpassword;
//----------------------------------------------------------------------------------------------------------
//Function: __construct()
//----------------------------------------------------------------------------------------------------------
public function __construct($idbserver, $idbport, $idbname, $idbuser, $idbpassword)
{
$this->dbserver = $idbserver;
$this->dbport = $idbport;
$this->dbname = $idbname;
$this->dbuser = $idbuser;
$this->dbpassword = $idbpassword;
}
//----------------------------------------------------------------------------------------------------------
//Function: openDatabase()
//----------------------------------------------------------------------------------------------------------
public function openDatabase()
{
/* Create a new user db object with persistent database connection parameters */
try {
$this->db = new PDO("mysql:host=".$this->dbserver.";dbname=".$this->dbname.";port=".$this->dbport, $this->dbuser, $this->dbpassword, array(
PDO::ATTR_PERSISTENT => true
));
return true;
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
//----------------------------------------------------------------------------------------------------------
//Function: getDB
//----------------------------------------------------------------------------------------------------------
public function getDB()
{
//return the database
return $this->db;
}
//----------------------------------------------------------------------------------------------------------
//Function: getDBName
//----------------------------------------------------------------------------------------------------------
public function getDBName()
{
//return the database
return $this->dbname;
}
//----------------------------------------------------------------------------------------------------------
//Function: closeUserDatabase
//----------------------------------------------------------------------------------------------------------
public function closeDatabase()
{
//close the database
$this->db=null;
}
}
?>
Then my Query Class:
<?php
class Query {
private $db, $sql, $qresult;
//----------------------------------------------------------------------------------------------------------
//Function: __construct
//----------------------------------------------------------------------------------------------------------
public function __construct($idb, $isql)
{
$this->db = $idb;
$this->sql = $isql;
}
//----------------------------------------------------------------------------------------------------------
//Function: openQuery
//----------------------------------------------------------------------------------------------------------
public function openQuery($param)
{
/* assemble and run a query and return a result object */
//prepare the sql statement
//get the db object
$tempdb= $this->db->getDB();
$this->qresult = $tempdb->prepare($this->sql);
//bind parameters to the prepared statement
foreach ($param as $key => $value) {
$this->qresult->bindValue($key,$value);
}
return $this->qresult->execute();
}
//----------------------------------------------------------------------------------------------------------
//Function: fetchAssociative
//----------------------------------------------------------------------------------------------------------
public function fetchAssociative()
{
return $this->qresult->fetch(PDO::FETCH_ASSOC);
}
//----------------------------------------------------------------------------------------------------------
//Function: closeQuery
//----------------------------------------------------------------------------------------------------------
public function closeQuery()
{
$this->qresult = null;
}
}
?>
Then I set up the database in my init function:
//initialize the user database with buffered set true so we can use nested queries
$usrdb = new Database($udbserver, $udbport, $udbname, $udbuser, $udbpassword, array(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true));
//open the database
$usrdb->openDatabase();
Then I call my query in the page:
//set up the sql
$sql = "SELECT * FROM Blah WHERE blahid = :filter";
//create the parameter array
$params = array(':filter'=>$filter);
//craetea new query
$q = new Query($usrdb, $sql);
//run the query
if ($q->openQuery($params)) {
//get the results if there are any
while ($row=$q->fetchAssociative()) {
$viewData['blahid']=$row['blahid'];
}
}
//clean up the query
$q->closeQuery();
After that I work with the results just like I always used to.
PDO works same way as old school approach. You still need to define connection and initialise object in order to query database.
I store the PDO database connection and retrieve an instance of it as a singleton.
Any place you need your database, to run a query or execute prepared statements, you use the singleton instance.
$db_handle = PDO_DBConnect::getInstance();
$stmt = $db_handle->prepare("SELECT * FROM users WHERE name= ?");
$result = $stmt->execute(array($cleaned_username));
...
If a class has multiple queries, you can store the handle in an instance variable,
$this->dbh = PDO_DBConnect::getInstance();
for convenience.
One other note is unlike pg_close or mysql_close, there is no close method for PDO. You simply set the db handle to null.
Related
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.
Until recently all the database queries from my PHP app were going through a single function that put everything into an insecure "mysqli_query". I wanted to move away from this for obvious reasons and convert to PDO to prevent injection.
A helpful user provided this PDO class/function as an alternative:
class DatabaseConfig
{
private static $singleton;
public function __construct()
{
if(empty(self::$singleton))
self::$singleton = $this->connect();
return self::$singleton;
}
public function connect($host = "localhost", $username = "username", $password = "password", $database = "database")
{
// Create connection
$opts = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);
$conn = new PDO('mysql:host='.$host.';dbname='.$database, $username, $password,$opts);
return $conn;
}
}
// This is a query class that will run your sqls
class QueryEngine
{
private $results;
private static $singleton;
public function __construct()
{
if(empty(self::$singleton))
self::$singleton = $this;
return self::$singleton;
}
public function query($sql = false,$bind = false)
{
$this->results = 0;
$db = new DatabaseConfig();
try {
if(!empty($bind)) {
$query = $db ->connect()
->prepare($sql);
$query->execute($bind);
}
else {
$query = $db ->connect()
->query($sql);
}
$this->results = $query;
}
catch (PDOException $e)
{
die($e->getMessage());
}
return $this;
}
public function fetch()
{
while($row = $this->results->fetch())
$result[] = $row;
return (!empty($result))? $result : 0;
}
}
function dbcommand($req,$bind = false)
{
// Create query instance
$qEngine = new QueryEngine();
// Run the query
$qEngine->query($req,$bind);
// If select, fetch array
if(strpos(strtoupper($req), 'SELECT') === 0)
return $qEngine->fetch();
// The query already ran, so you can return whatever you want
// For ease I am just returning true
elseif(strpos($req, 'INSERT INTO') === 0)
return true;
}
The above code works great but the issue is that I can still SQL inject into statements that I'm not filtering prior to using dbcommand. I was under the impression PDO automatically resolves injection issues but I'm obviously adopting it incorrectly. Can anyone englighten me?
This "dbcommand" function is intended to be used this way:
$row = dbcommand("SELECT * FROM users WHERE id=?", [$id]);
Here you have to substitute all the variables in the query with question marks, while sending variables as a second parameter, in the form of array.
Or you can use named placeholders and associative array:
$update = [
'name' => $_POST['name'],
'id' => $_POST['id'],
]
dbcommand("UPDATE users SET name=:name WHERE id=:id", $update);
Only when variables are substituted with placeholders PDO will protect your queries.
On a side note, although this function is a good intention by itself, it severely reduces the usability of the vanilla PDO. For example, for the large datasets, it is not advised to fetch all the data at once, but this function offer no option for this. Or this function won't let you get the number of affected rows. To make a simple PDO wrapper usable, one should make it return a PDOStatement instance. It is essential, because there is way too much functionality in this class to be simply dismissed.
For a class that lets you use all the great power of PDO, refer to my Simple yet efficient PDO wrapper, especially "Examples" section. All examples can be run without any prior setup, save for setting database credentials.
Just for the clarification: a list of possible use cases from the link above which your class doesn't support:
Prepared statement multiple execution
Getting the insert id
Getting rows in a loop one by one
Getting one row
Getting single field value
Getting array of rows in all the dozens of predefined formats supported by PDO
Getting the number of affected rows
I am trying to return a new instance of my User class via a DAO, and am struggling to see if it is working, it certainly isn't working as I expected, as I am not defining User properties, yet I am still seeing all the User fields and values from the database.
class Database
class Database
{
private $dbh;
public function __construct()
{
$this->openConnection();
}
private function openConnection()
{
try {
$this->dbh = new PDO('mysql:host=localhost; dbname=stats', 'user', 'pass');
$this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
'There was an error connecting to the database, error: ' . $e->getMessage();
}
}
public function query($sql, $params)
{
$stmt = $this->dbh->prepare($sql, $params);
$stmt->execute($params);
return $stmt;
}
}
class UserDao
class UserDao
{
private $db;
public function __construct($db)
{
$this->db = $db;
}
public function findById($ward_code)
{
$record = $this->db->query("SELECT * from ward_statistics where WardCode = :WardCode", array(':WardCode' => $ward_code));
$record->setFetchMode(PDO::FETCH_CLASS, 'User')
$user = $record->fetch();
return $user;
}
}
class User
class User
{
// with no properties defined, I still get a
// full result set from UserDAO::findById()
}
Usage
$db = new Database();
$dao = new UserDao($db);
$user = $dao->findById('AMED');
var_dump($user);
Results in an object with a full result set from the DB (a single row that is - matched by the WardCode), with all fields populated with the correct values (from the DB).
Whilst this seems OK - I thought that PDO::FETCH_CLASS required the properties to available within the class.
My worry is, from a question I posted on Code Review a few days ago, I was also told that class methods such as the findById one should create new class instances, or update the existing one, yet all I seem to be doing is retrieving a row from the database.
Thanks
Replace fetchAll with fetch if you need just 1 object. According to documentation:
PDOStatement::fetchAll — Returns an array containing all of the result
set rows
I have just started learning the concept of Object oriented programming and I've written this class with functions.
It works fine, but I'm interested in knowing if I have done this correctly...
Here is my code:
class Database{
const DB_HOSTNAME = 'localhost';
const DB_USERNAME = 'root';
const DB_PASSWORD = 'password';
const DB_NAME = 'shop';
protected $_db_connect;
protected $_sql;
protected $_result;
protected $_row;
function db_connect(){
$this->_db_connect = mysql_connect(self::DB_HOSTNAME,self::DB_USERNAME,self::DB_PASSWORD) or die(mysql_error());
}
function slect_db(){
mysql_select_db(self::DB_NAME) or die(mysql_error());
}
function sql(){
$this->_sql = 'SELECT * FROM users';
}
function query(){
$this->_result = mysql_query($this->_sql);
}
function fetch_array(){
while($this->_row = mysql_fetch_array($this->_result)){
$username = $this->_row['user_USERNAME'];
echo "<ul>";
echo "<li>".$username."</li>";
echo "</ul>";
}
}
function db_close(){
mysql_close($this->_db_connect);
}
}
$database = new Database();
$database->db_connect();
$database->slect_db();
$database->sql();
$database->query();
$database->fetch_array();
$database->db_close();
Please use PDO or MySQLi as it's more secure and mysql_* functions are deprecated as stated above, I've provided some generic code using PDO to help you with that new venture. As stated in the comments, you really shouldn't be echoing out the data if you're looking for an object oriented design, what you should be doing is after you do the query, return the rows that you fetched and from there use something such as a foreach loop to then display your data. Doing your database class this way will also make sure that multiple database connections aren't open at once! Note, this code is just for you to reference, and should be tested before use in production or anything live.
config.php:
<?php
//Enter your database connection details here.
$host = 'localhost'; //HOST NAME.
$db_name = 'databasename'; //Database Name
$db_username = 'root'; //Database Username
$db_password = ''; //Database Password
try
{
$pdo = new PDO('mysql:host='. $host .';dbname='.$db_name, $db_username, $db_password);
}
catch (PDOException $e)
{
exit('Error Connecting To DataBase');
}
?>
database.class.php:
<?php
class database
{
function __construct($pdo)
{
$this->pdo = $pdo;
}
function getData()
{
$query = $this->pdo->prepare('SELECT * FROM database');
$query->execute();
return $query->fetchAll();
}
}
?>
index.php:
<?php
require_once 'config.php';
require_once 'database.class.php';
$db = new database($pdo);
$rows = $db->getData();
?>
It is possible to improve the way you connect to databases using autoloading and dependency injection containers. Here is a way of using Auryn to connect to your database while being sure there is only one connection opened and not having to manually require files throughout your application.
I will cover only PDO and Auryn here. There are other dependency injection containers and notably the mysqli extension to connect to database, but the content should help you using another container if you wish to do so.
The database class
Having a database class is superfluous. The \PDO class is already providing all necessary methods to query the database. Having a database class makes you repeat the functions it provides and limits your actions (or makes you create many functions) when you want to for example use multiple different fetch styles depending on your needs in a specific method.
Dependency Injection
If you haven't already, have a read on dependency injection. The point is that when a class needs to access the database, it should not have to bother constructing the \PDO object, it should be constructed with it:
class Mapper {
private $pdo;
public function __construct(\PDO $pdo) {
$this->pdo = $pdo;
}
public function createFromId($id) {
$stmt = $this->pdo->prepare("SELECT name FROM foo WHERE id=:id");
$stmt->execute([
":id" => $id,
]);
return $stmt->fetchObject();
}
}
Notice that I directly pass the \PDO object, not a wrapper class. That way, I always have access to all of its capabilities, not only a subset of user-defined functions.
Dependency Injection Container
A dependency injection container helps build your classes, giving them the objects they need, and giving you great flexibility on how to actually build those objects. Here I'll only focus on configuring and sharing a \PDO object through the use of Auryn.
I assume you have installed the required Auryn class, the easier way is using composer. This is out of the scope of this answer, there are multiple resources on how to use it.
Create the injector
$injector = new \Auryn\Injector();
Define the \PDO class parameters
$injector->define("PDO", [
":dsn" => "mysql:host=localhost;charset=utf8;dbname=dbname",
":username" => "user",
":passwd" => "passwd",
":options" => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
],
]);
You can either write the configuration parameters directly here, or get them from a config file. I like having a config.ini file and using parse_ini_file() to get my configuration options, as I can easily switch databases by editing a config file.
Share the \PDO object
$injector->share("PDO");
This part is really important. This lines makes the injector give the same \PDO object every time it is tasked with constructing a class that needs a connection. Note that the order of the lines is not important, you can share the class before defining it, only make sure to create you database needing classes after writing both those lines.
Create your objects
$mapper = $injector->make("Mapper");
That's it. The injector will create your mapper object, creating the \PDO object if it haven't already been, passing the existing instance if it has.
Autoloading
Assuming you have used composer, you can make use of its great autoloader. Otherwise you can also roll you own autoloader.
The point here is to stop having require() everywhere in your code, especially if you have complex class hierarchies, which you should have in a single responsibility compliant class system.
Wrapping up
With this set up, you now can use the \PDO object in your classes while being assured there will only be one instance per request, without the need to require files everywhere, and without using a singleton anti-pattern.
$database = new Connection();
class Connection
{
function __construct()
{
switch($_SERVER['DOCUMENT_ROOT'])
{
case 'path':
$this->host = 'hostname';
$this->user = 'username';
$this->passwd = 'password';
$this->database = 'dbname';
break;
default :
$this->host = 'localhost';
$this->user = 'root';
$this->passwd = 'root';
$this->database = 'dbname';
break;
}
$this->clink = #mysql_connect($this->host,$this->user,$this->passwd);
#mysql_select_db($this->database,$this->clink);
}
}
$objConn = new mysqlconnect();
$Conn = $objConn->setobjConnect("localhost","root","P#ssw0rd");
class Database{
var $last_query; //Saved result of the last query made
var $last_result; //Results of the last query made
var $func_call; //A textual description of the last query/get_row/get_var call
var $link; //database link
var $lastquery; //last query
var $result; //query result
// Connect to MySQL database
function database() {
$this->link=mysql_connect(DB_HOST, DB_USER, DB_PASS) or die('Server connexion not possible.');
//Set All Charsets to UTF8
mysql_query("SET character_set_results=utf8 , character_set_client=utf8 , character_set_connection=utf8 , character_set_database=utf8 , character_set_server=utf8");
mysql_select_db(DB_NAME) or die('Database connection not possible.');
}
/** Query the database.
* #param $query The query.
* #return The result of the query into $lastquery, to use with fetchNextObject().
*/
function query( $query ){
$this->lastquery=$query;
$this->result=#mysql_query( $query, $this->link );
return $this->result;
}
/** Do the same as query() but do not return nor store result.
* Should be used for INSERT, UPDATE, DELETE...
* #param $query The query.
* #param $debug If true, it output the query and the resulting table.
*/
function execute($query)
{
#mysql_query($query);
}
/** Convenient method for mysql_fetch_object().
* #param $result The ressource returned by query().
* #return An ARRAY representing a data row.
*/
function fetchArray($result){
if ($result == NULL)
$result = $this->result;
if ($result == NULL || mysql_num_rows($result) < 1)
return NULL;
else
return mysql_fetch_assoc($result);
}
/** Close the connecion with the database server.
* It's usually unneeded since PHP do it automatically at script end.
*/
function close()
{
mysql_close($this->link);
}
/** Get the number of rows of a query.
* #param $result The ressource returned by query(). If NULL, the last result returned by query() will be used.
* #return The number of rows of the query (0 or more).
*/
function numRows($result = NULL)
{
if ($result == NULL)
return #mysql_num_rows($this->result);
else
return mysql_num_rows($result);
}
}
![<?php
class mysqlconnect{
private $server=null;private $user=null;private $password=null;private $objConnect=null;
function setobjConnect($s,$u,$p){
$this->server = $s;
$this->user = $u;
$this->password = $p;
return $this->objConnect = mysql_connect($this->server,$this->user,$this->password);
}
}
$objConn = new mysqlconnect();
$Conn = $objConn->setobjConnect("localhost","root","P#ssw0rd");
if($Conn)
{
echo "Database Connect";
}
else
{
echo "Database Connect Failed.";
}
mysql_close($Conn);
?>]
1
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.)