How to share same connection with multiple instances of the object? - php

I was advised to rewrite how I handle connection, as it is now, my class creates new connection on every instance of the object. How would I change it to share connection between multiple objects created with this class?
/**
* This is Users class, deals with finding, updating, creating user
*/
class Users
{
private $host = DB_HOST;
private $user = DB_USERNAME;
private $pass = DB_PASSWORD;
private $dbname = DB_NAME;
private $conn;
private $stmt;
public $error;
function __construct()
{
$dsn = 'mysql:host='.$this->host.';dbname='.$this->dbname.';charset=utf8';
$options = array(
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
try {
$this->conn = new PDO($dsn,$this->user,$this->pass,$options);
} catch (PDOException $e) {
$this->error = $e->getMessage();
}
}
private function mysql_execute_query($sql,$params)
{
$this->stmt = $this->conn->prepare($sql);
$this->stmt->execute($params);
return $this->stmt;
}
public function find_user_by_provider_uid($provider,$provider_uid)
{
$sql = 'SELECT * FROM users WHERE provider = :provider AND provider_uid = :provider_uid LIMIT 1';
$params = array(
':provider' => $provider,
':provider_uid' => $provider_uid
);
$result = $this->mysql_execute_query($sql,$params);
return $result->fetch(PDO::FETCH_ASSOC);
}
}

If you really need that, define your property as static:
private static $conn;
Of course, make sure that accessing to that will be done right (i.e. not via $this, but via self (or class name) since it will be no longer belong to instance)
That, however, isn't a good practice: if you want manage connections - manage how many times you'll instantiate your class.

Related

Properly pass connection to connstructor PDO

I have spent several days reading different tutorials posts etc either info is outdated or appreciated.
I have a database connection class very simple
namespace App\Database;
use PDO;
use PDOException;
/**
* #desc Connection to the database
**/
class Database
{
protected string $dbhost = DATABASE_HOST;
protected string $dbuser = DATABASE_USER;
protected string $dbpass = DATABASE_PASS;
protected string $dbname = DATABASE_NAME;
protected PDO $conn;
public function __construct()
{
// Set DSN
$dsn = 'mysql:host=' . $this->dbhost . ';dbname=' . $this->dbname . ';charset=' . $this->charset;
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
// Create PDO instance
try {
$this->conn = new PDO($dsn, $this->dbuser, $this->dbpass, $options);
} catch (PDOException $e) {
echo 'Unable to establish a database connection: ' . $e->getMessage();
exit();
}
}
}
and in my user class I passed it to the constructor
protected Database $conn;
public function __construct()
{
$this->conn = new Database;
}
but when i write a statement like this
$stmt = $this->conn->prepare($sql);
prepare is high lighted saying Method 'prepare' not found in \App\Database\Database
I would prefer not to use static or singleton
Your variable $conn would probably better be named $db:
protected Database $db;
public function __construct()
{
$this->db = new Database;
}
Then, when you understand that the User class has a property called db which in turn has a property called conn, the proper use might make more sense:
$stmt = $this->db->conn->prepare($sql);
However, you've defined $conn as protected, so you can't do that. You could make it public, or make a getter method in Database:
public function getConn(): PDO
{
return $this->conn;
}
And then do:
$stmt = $this->db->getConn()->prepare($sql);
Better, I'd forget all that and instead have Database extend PDO, then just override the constructor to configure with your custom values:
class Database extends PDO
{
public function __construct()
{
$dsn = sprintf(
'mysql:host=%s;dbname=%s;charset=%s',
DATABASE_HOST,
DATABASE_NAME,
DATABASE_CHARSET
);
parent::__construct(
$dsn,
DATABASE_USER,
DATABASE_PASS,
[
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]
);
}
}
Now you have a PDO-compatible database object that is pre-configured, and you can just use it like a regular PDO object:
class User
{
protected Database $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function whatever()
{
$this->db->prepare($sql);
// ...
}
}
$db = new Database();
$user = new User($db);
$user->whatever();

PHP PDO close mysql connection in DatabaseFactory file

do you guys have an idea why I can't close the mysql connection?
Here is my code:
class DatabaseFactory {
private static $factory;
private $connection = array(
"DB_HOST" => "localhost",
"DB_PASS" => "*************",
"DB_USER" => "*************",
"DB_PORT" => "3306",
"DB_CHARSET" => "utf8"
);
public static
function getFactory() {
if(!self::$factory) {
self::$factory = new DatabaseFactory();
}
return self::$factory;
}
private $db;
public
function getconnection($name) {
echo "opened";
try {
$options = array(
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_OBJ,
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_WARNING
);
$this->db = new \PDO('mysql:host='.$this->connection["DB_HOST"].';dbname='.$name.';port=' .$this->connection["DB_PORT"].';charset='.$this->connection["DB_CHARSET"], $this->connection["DB_USER"], $this->connection["DB_PASS"], $options);
} catch (PDOException $e) {
exit($e->getMessage());
}
return $this->db;
}
public
function __destruct() {
echo "closed";
try {
$this->db = null;
unset($this->db);
} catch (PDOException $e) {
exit($e->getMessage());
}
}
}
$database = DatabaseFactory::getFactory()->getconnection("Admin");
$query = $database->prepare("SELECT * FROM tester WHERE t_u_id = :u_id");
$query->execute(array(":u_id" => "281123341528-D050C4-91A0BA-1CB0C8-8112334152855AC373"));
The connection is connecting but it is not closed.
The method __destruct will be called but the mysql total connections count doesn't decrease.
Why don't you implement and call a method (manually instead of relying on __destruct()) like Fil does in his response here?
Add the following static method to your DatabaseFactory class
static function closeConnection(&$conn) {
$conn=null;
}
So after your connections, you can NULL your connection reference (which is akin to calling $mysqli_conn->close();) later in your code like this:
$database = DatabaseFactory::getFactory()->getconnection("Admin");
$query = $database->prepare("SELECT * FROM tester WHERE t_u_id = :u_id");
$query->execute(array(":u_id" => "281123341528-D050C4-91A0BA-1CB0C8-8112334152855AC373"));
DatabaseFactory::closeConnection($database)
EDIT:
Also, I wanted to mention that by relying on __destruct(), you are subject to the following as per the PHP docs:
PHP 5 introduces a destructor concept similar to that of other
object-oriented languages, such as C++. The destructor method will be
called as soon as there are no other references to a particular
object, or in any order during the shutdown sequence.
In regards to your question, the method __destruct() might not be called because the static class var private static $factory is never being nulled. Therefore, the class always has a reference to "itself" preventing __destruct() from being called.
I hope this helps!

Retrieving a connection inside a page: PDO and PHP class

I'm new to classes and PDO as well. I'm trying to write a class with separate functions for connecting to the db and closing the connection so that, in a page, I can:
Open the connection with the $db->connOpen
Perform all the queries I need inside the page
Close the connection at the end of the script with $db->connClose
class database
{
private $host = '';
private $db_name = '';
private $charset = '';
private $username = '';
private $password = '';
public function setHost($host) {
$this->host = $host;
}
public function setDbName($db_name) {
$this->db_name = $db_name;
}
public function setUser($username, $password) {
$this->username = $username;
$this->password = $password;
}
public function connOpen() {
try {
$dsn = "mysql:host=$this->host;dbname=$this->db_name;charset=$this->charset";
$db = new PDO($dsn, $this->username, $this->password, array(PDO::ATTR_PERSISTENT => true));
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
public function connClose() {
$db = null;
}
}
I guess the problem is regarding more the classes syntax than the PDO, since to fire a query like the one below into the page I would need to instantiate again the class PDO, doubling the connection to the db.
$stmt = $dbo->prepare('SELECT * FROM products WHERE id=:id');
$stmt->execute(array(':id' => $id));
$res = $stmt->fetchAll(PDO::FETCH_ASSOC);
My questions are:
How can achieve what I want using a class like the one above?
Is it correct, in this case, using a persistent connection?
Thanks
For some reason, most people who wants to learn OOP starts with a database wrapper that implements singleton and interferes with error handling.
My tip is that you treat the database connection as any other variable:
class Post {
function getByUserId(PDO $connection, $user_id) {
}
}
$database = new PDO(...);
$post->getByUserId($database, 123);
This is called dependency injection.
And don't waste time and energy writing code to avoid useful features, such as:
Centralised error handling
Being able to connect to more than one database
Only care about writing a database wrapper when you really have real features to add on top of PDO. I can think of:
Convert dates to DateTime objects automatically
Pass all queries to a logger
Throw custom exceptions on interesting events, such as duplicate index violation
... and even then be extremely careful to not make PDO worse ;-)
I am using this with one singlton class which give me db ojbect and then I am using that object to query in other classes;
<?php
class db{
/*** Declare instance ***/
private static $instance = NULL;
/**
*
* the constructor is set to private so
* so nobody can create a new instance using new
*
*/
private function __construct() {
/*** maybe set the db name here later ***/
}
/**
*
* Return DB instance or create intitial connection
*
* #return object (PDO)
*
* #access public
*
*/
public static function getInstance() {
if (!self::$instance)
{
self::$instance = new \PDO("mysql:host=".databaseHost.";dbname=".databaseName."", databaseUserName,databasePassword);;
self::$instance-> setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
self::$instance-> setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
}
return self::$instance;
}
/**
*
* Like the constructor, we make __clone private
* so nobody can clone the instance
*
*/
private function __clone(){
}
} /*** end of class ***/
?>
class where i am querying using db object
<?php
class posts {
public function getPostOfUserId($userId,$offset=0,$limit=NULL){
$helperString=" ";
if(!empty($limit))
$helperString=" LIMIT $offset, $limit ";
$executor= db::getInstance()->prepare("SELECT posts.*,users.facebook_id, users.first_name as post_by FROM posts JOIN tagged_friends ON posts.id = tagged_friends.post_id JOIN users ON posts.user_id = users.id WHERE tagged_friends.user_id = :user_id ORDER BY posts.id DESC ".$helperString, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY));
$executor->execute(array(':user_id'=>$userId));
$posts=$executor->fetchAll(\PDO::FETCH_ASSOC);
if(!empty($posts))
return $posts;
else
return ;
}
}

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

Cannot access empty property php class

I'm trying to learn OOP, and some of its concept. I've following class for users:
class Users
{
private $host = DB_HOST;
private $user = DB_USERNAME;
private $pass = DB_PASSWORD;
private $dbname = DB_NAME;
private $conn;
private $stmt;
public $error;
function __construct()
{
$dsn = 'mysql:host='.$this->host.';dbname='.$this->dbname.';charset=utf8';
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
try {
$this->conn = new PDO($dsn,$this->user,$this->pass,$options);
} catch (PDOException $e) {
$this->error = $e->getMessage();
}
}
private function mysql_execute_query($sql,$params)
{
$this->stmt = $this->conn->prepare($sql);
$this->stmt->execute($params);
return $this->$stmt;
}
public function find_user_by_provider_uid($provider,$provider_uid)
{
$sql = 'SELECT * FROM users WHERE provider = :provider AND provider_uid = :provider_uid LIMIT 1';
$params = array(
':provider' => $provider,
':provider_uid' => $provider_uid
);
$result = $this->mysql_execute_query($sql,$params);
return $result->fetch();
}
}
First of all is there some tip that comes to mind for structuring this code better? or using more features of oop?
Second, it fails with following error:
PHP Notice: Undefined variable: stmt
PHP Fatal error: Cannot access empty property
Both of this lines refer to return $this->$stmt; inside mysql_execute_query
My hunch is that it has something to do with it being private function. But I cannot tell.
Any ideas?
Here the error:
return $this->$stmt;
But should be:
return $this->stmt;

Categories