Problem connecting with PDO - php

It's the first time i'm using PDO just for testing purpose.
But a strange error occurred and googling it, it seems to be weird.
Here's my database testing class
class db extends PDO
{
# Our instance.
private static $db = NULL;
# Calling the connector.
public static function connect()
{
if (self::$db === NULL)
{
$class = __CLASS__;
self::$db = new $class();
}
return self::$db;
}
# Connector.
public function __construct()
{
$dns = 'mysql:dbname='.reg::get('db-name').';host='.reg::get('db-host');
self::$db = new PDO($dns, reg::get('db-username'), reg::get('db-password'));
reg::delete('db-password');
}
# Quick reporting
public function reportError($array)
{
if ($this->db != NULL) { echo 'Myself getting horny'; } // Just for testing, i'm not getting horny because of a mysql database connection!
}
}
Then executing the following code:
$db = new db();
$row = $db->prepare('SELECT * FROM test WHERE id = :id')->execute(array('id' => 1));
echo $row['value'];
It shows me the following error:
Warning: PDO::prepare() [pdo.prepare]: SQLSTATE[00000]: No error: PDO constructor was not called in myfile.php on line 39
Considering line 39 as
$row = $db->prepare('SELECT * FROM test WHERE id = :id')->execute(array('id' => 1));

You code is a mess, it's probably because you're so horny...
connect() method - why is it there?:
if ($db === NULL)
should be:
if (self::$db === NULL)
self::$db = new $class();
So, if $class == __CLASS__ == db, you are doing self::$db = new db();, doesn't seem right.
You can't use PDO to prepare an identifier, like a table or a column.
$db->prepare('SELECT * FROM :table WHERE id = :id')->execute(array('table' => 'test', 'id' => 1));
Should be:
$db->prepare('SELECT * FROM `test` WHERE id = :id')->execute(array('id' => 1));
Try this:
class db extends PDO
{
private static $db = null;
public static function singleton()
{
if (is_null(self::$db) === true)
{
self::$db = new PDO('mysql:dbname='.reg::get('db-name').';host='.reg::get('db-host'), reg::get('db-username'), reg::get('db-password'));
}
return self::$db;
}
}
Like this:
$result = db::singleton()->prepare('SELECT * FROM `test` WHERE id = :id')->execute(array('id' => 1));
var_dump($result);

I'm not entirely sure if this is your answer, as it seems to be unrelated to the error message, but I do not think you can pass in a table name as a bound parameter. What happens if you put a hard-coded table name in the place of :table?

You have a static 'connect' constructor, that makes a db-object in (static), and returns it. But you also make a new db() yourself.
The prepare statement uses self::$db, so tries to call the static made variable. I'm not really sure how your code is supposed to work, combining some sort of singleton/static form with an object form.
But that seems to be the trouble

Your singleton pattern is all wrong. Your constructor should be private and you should use static instances of $db. Refer to this singleton example.

Related

Several mysqli queries with php singleton

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!
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.
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)
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

PHP/PDO how reuse connection started in a class to resolve "There is no active transaction" error

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();

PHP DataMapper pattern: My class needs an instance of PDO, I want to wrap it inside a Db class

here's what I have:
class Entry
{
public $id;
public $name;
public $seoName;
public $timeCreated;
public function someFunction()
{
}
}
class EntryMapper
{
protected $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function saveEntry(Entry &$entry)
{
if($entry->id){
$sql = "";
}
else {
$sql = "INSERT INTO tbl_entry (name, seo_name, time_created) VALUES (:name, :seo_name, :time_created)";
$stmt = $this->db->prepare($sql);
$stmt->bindParam("name", $entry->name);
$stmt->bindParam("seo_name", $entry->seoName);
$stmt->bindParam("time_created", $entry->timeCreated);
$stmt->execute();
$entry->id = $this->db->lastInsertId();
}
}
}
Now, here's how I use it in my view file (currently just testing insert command):
$entry = new Entry();
$entry->name = "Some Company LLC";
$entry->seoName = "some-company-llc";
$entry->timeCreated = date("Y-m-d H:i:s");
$entryMapper = new EntryMapper(new PDO("mysql:host=....."));
$entryMapper->saveEntry($entry);
I want to have the $entryMapper line like this:
$entryMapper = new EntryMapper(new Database());
meaning I should have a separate class Database.php where I would establish the connection.
I tried that, but since my class EntryMapper.php needs an instance of PDO directly, i'm getting an error. I have tried Database extend from PDO but that also raises error saying that PDO constructor was not called in EntryMapper
Any thoughts?
EDIT: if you see any signs of code coupling or similar, let me know because I want to learn to code properly. Thank you very much!
You can use Factory pattern and create the PDO object within a function in the Database class.
class Database {
private const connStr = 'mysql:host=.....';
public static function createPDODatabase() {
return new PDO(connStr);
}
}
So you may call your EntryMapper constructor as:
$entryMapper = new EntryMapper(Database::createPDODatabase());
EDIT: If you want to do it by instantiating the Database object, you should call the PDO constructor in the constructor of the Database class.
class Database extends PDO {
public function __construct($dbname='db_name', $server='localhost', $username='db_user', $password='db_password') {
parent::__construct("mysql:host=$server;dbname=$dbname", $username, $password);
parent::setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
}
}
Then you may just instantiate the Database object.
$entryMapper = new EntryMapper(new Database());
This is how I finally solved it (if a better implementation arises, I will for sure recode). It is an implementation of solution under the accepted answer here: Global or Singleton for database connection?
My ConnFactory.php
include('config/config.php');
class ConnFactory
{
private static $factory;
public static function getFactory()
{
if(!self::$factory){
self::$factory = new ConnFactory();
return self::$factory;
}
}
private $db;
public function pdo()
{
if(!$this->db){
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
);
$this->db = new PDO("mysql:host=".DB_HOST.";port=".DB_PORT.";dbname=".DB_SCHEMA."", DB_USER, DB_PASS, $options);
}
return $this->db;
}
}
Usage in my view/html file (just a test of insert functionalty):
$entry = new Entry();
$entry->name = "Kartonaža ad Gradačac";
$entry->seoName = "kartonaza-ad-gradacac";
$entry->timeCreated = date("Y-m-d H:i:s");
$entryMapper = new EntryMapper(ConnFactory::getFactory()->pdo());
$entryMapper->saveEntry($entry);

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());
}

PHP Singleton Database Query

In PHP, I have following Singleton Database Class:
class Database
{
private static $instance;
private function __construct()
{
self::$instance = new mysqli('localhost', 'root', 'Matthias', 'financecontrol', '3307');
if (!self::$instance) {
throw new Exception('Could not connect to database in function __construct.');
}
}
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new Database();
}
return self::$instance;
}
}
Whenever I try to perform a query on the database in another PHP file, for example to check whether a user already exists:
function userExists($username)
{
try {
$connection = Database::getInstance();
$result = $connection->query("select * from user where username='$username'");
if (!$result) {
throw new Exception("Connection to database failed in function userExists.");
}
if ($result->num_rows > 0) {
return true;
} else {
return false;
}
} catch (Exception $ex) {
$errorPager = new ErrorpageGenerator();
$errorPager->generateErrorPage($ex->getMessage());
return false;
}
}
I get an error message "PHP Fatal error: Call to undefined method Database::query() in User.php on line 44"
I've tried adding a query function in the Database class, but that did not seem to fix the problem. Any ideas? Thanks
You have to add this method of course. But you cannot assign Database() and the mySQLi object to m_pInstance
so do:
class Database
{
private static $conn;
// ...
public function __construct()
{
self::$conn = new mysqli('localhost', 'root', 'root', 'database', '3307');
//...
and then
public function query($sql)
{
return self::$conn->query($sql);
// or
return mysqli_query(self::$conn, $sql);
}
EDIT
Working code:
class Database
{
private static $instance = null;
private static $conn;
private function __construct()
{
self::$conn = new mysqli('localhost', 'root', 'root', 'database', '3307');
}
public static function getInstance()
{
if (self::$instance == null) {
self::$instance = new Database();
}
return self::$instance;
}
public function query($sql)
{
return self::$conn->query($sql);
}
}
You get this error, because Database::$m_pInstance is contains an instance of Database class and not instance of MySQLi. You have created a "conflict" between to parts of the code:
public static function getInstance()
{
if (!self::$m_pInstance) {
self::$m_pInstance = new Database(); // << PROBLEM
}
return self::$m_pInstance;
}
Which overrides what your constructor does:
private function __construct()
{
self::$m_pInstance = new mysqli( /* .. */ ); // PROBLEM
if (!self::$m_pInstance) {
throw new Exception('Could not .. blah');
}
else {
return self::$m_pInstance;
}
}
Even though the constructor assigns self::$m_pInstance the instance of MySQLi object, it gets overridden by self::$instance = new Database(); right after.
Also, in php __constuct() method should not return, ever.
That said, i think is should warn you that singleton is considered to be an anti-patterns, and should be avoided. Your code also has the unintended side-effect, forcing you to have only one database (not connection, the database) available per application.
You might benefit from watching few lectures:
Advanced OO Patterns (slides)
Global State and Singletons
Don't Look For Things!
Your code does not look right.
first, you assign $m_pInstance a new Database instance. But then, in the constructor, you assign it a new mysqli instance. I am unsure how php handles this case, but it seems that it treats it as Database object as indicated by your error message. The Database class however does not have a query method.
So the solution would be to save the mysqli object in a different field and add getters and setters for it or delegate the methods to it.

Categories