Calling single db connection using static method - php

I tried to follow the structure factory in creating a single db connection as per below:
try {
$provider = function() {
$instance = new PDO("dsn", "username", "password");
return $instance;
};
} catch (PDOException $exception) {
error_log($exception->getMessage());
}
$factory = new StructureFactory($provider);
and StructureFactory as below:
class StructureFactory {
protected $provider = null;
protected $connection = null;
public function __construct(callable $provider) {
$this->provider = $provider;
}
public function create($class_name) {
if ($this->connection === null) {
$this->connection = call_user_func($this->provider);
}
return new $class_name($this->connection);
}
}
Then when I need to create a book instance, say "Book", I do this:
$factory->create("Book");
But if I don't need to create a book instance yet but just want to check if that book is existing, I'd like to call a static method on my "Book" class like:
Book::isBookExisting($bookname);
But inside this static method, how can I have the single database connection if I can only have it when I create the "Book" instance?
Or should I just use a Singleton for my db connection instead of doing this factory?
I am kinda at lost cause I'm not sure if I am doing it correctly. Please advise.

First
The way you've written the try catch block is mostly useless and certainly not doing as you expect.
try {
$provider = function() {
$instance = new PDO("dsn", "username", "password");
return $instance;
};
} catch (PDOException $exception) {
error_log($exception->getMessage());
}
Remember, $provider = function() { ... is an anonymous function. The code inside it only runs when the function is called. In other words, the code inside the try block, in your example, will never throw an exception. It will only ever throw an exception when is is called e.g. $provider().
Refactored
$provider = function() {
try {
$instance = new PDO("dsn", "username", "password");
} catch (PDOException $exception) {
error_log($exception->getMessage());
}
return $instance;
};
The database connection
There are too many ways to solve the database dependency issue, many of which are opinionated. There is no one solution.
You could:
Use a singleton
Use a argument dep. Book::isBookExisting($provider, $bookname);
Use a static dep. Book::setProvider($provider); Book::isBookExisting($bookname);
...
Further Reading:
Dependency Injection

Related

Can't instantiate same class twice in PHP

I have the following code and I want to connect to 2 servers by passing $redisdb param.
Unfortunatelly the second instance get first connection.
$redis = new RedisHandler();
$redis2 = new RedisHandler('redis2'); //this one gets first $redis connection
What am I doing wrong?
class RedisHandler
{
static $db = null;
public function __construct($redisdb = 'redis') {
// opening db connection
return self::connect($redisdb);
}
static public function connect($redisdb)
{
global $config;
if (self::$db === null)
{
try {
$redisClient = new Redis();
$redisClient -> connect($config[$redisdb]['host'], $config[$redisdb]['port'], $config[$redisdb]['timeout'], null, $config[$redisdb]['reservedInterval'] );
$redisClient->setOption(Redis::OPT_READ_TIMEOUT, 100);
if (!$redisClient) { throw new Exception("Can't connect to Redis"); }
} catch (Exception $e) {
die('Failed to connect to Redis '.$e->getMessage());
}
self::$db = $redisClient;
return self::$db;
//return $m;
}
else
{
// return self::$db;
return self::$db;
}
}
}
The RedisHandler class is developed on Singleton pattern i.e. if a connection already exists then existing connection is returned, thus you are facing the issue.
You may use and close the first connection and then use the second one for resoling the issue.
a static keyword is used whenever your variable or your function member is indifferent from the current instance of your object.
Here your
static $db
is the same for each instance of RedisHandler.
First instanciation $db is null, but at the second instanciation, because $db is static, the variable won't be null, so you can't instanciate another Redis because of the condition if (self::$db === null)
I use PHP with java to instantiate excel class that generates excel file.
$excel = new Excel (...)
then i wanted to generate second file.
unset and $excel = null didn't work.
It worked with:
$excel = $excel->Excel (...)

PDO setAttribute on a per transaction basis

I would like to know if it is possible to set the PDO::ATTR_ERRMODE on a per transaction basis instead of a per connection basis.
I open a database connection in class Database and am currently setting the attribute there:
namespace Core {
class Database {
private $pdo;
public function __construct() {
$this->pdo = new \PDO(/*Connection arguments*/);
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}
public function getConnection() {
return $this->pdo;
}
}
}
All errors will now throw exceptions, however, I would only like this behaviour when dealing with manual transactions.
Ideally, I would be able to do something like:
namespace Repositories {
class Something {
private $pdo;
public function __construct(\Core\Database $database) {
$this->pdo = $database->getConnection();
}
public function doSomething() {
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); // Set the attribute here
$this->pdo->beginTransaction();
try {
// Execute some queries and statements
$this->pdo->commit();
}
catch (\PDOException $e) {
$this->pdo->rollback();
// Do something with $e
}
}
}
}
And for the error mode to be returned to its default at the end of the method (I know my example wouldn't work)
Thanks in advance
It may be useful for you to know that I do not create a new PDO connection in each method that uses one, but the Database class is stored in an object pool when it is first created.
You can do the following:
$pdo = $this->pdo;
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

Having issue with understanding two singleton Php code for conecting to mongoDb

I have these two code to use to connect to mongodb.
First i used this code but this doesn't seem to work.I dont know why.
class DbConnection
{
static $db = NULL;
static function getMongoCon()
{
if (self::$db === null)
{
try {
$m = new Mongo("mongodb://username:password#localhost:27017");
} catch (MongoConnectionException $e) {
die('Failed to connect to MongoDB '.$e->getMessage());
}
self::$db = $m;
}
else
{
return self::$db;
}
}
}
After this i used this way to connect mongo in another class
$db=DbConnection::getMongoCon();
$database=$db->databasename;
$collection=$db->users;
But this doesn't seem to work always . i always get error $db not defined or some other undefined error.
Second Code is this . which i used to connect to mongodb without having to create multiple connection. This works fine without having problem.
class DbConnection{
static protected $_instance;
protected $db = null;
final protected function __construct() {
$m = new Mongo("mongodb://username:password#localhost:27017");
$this->db = $m->selectDB( "databasename" );
}
static public function getInstance() {
if (!(self::$_instance instanceof self)) {
self::$_instance = new self();
}
return self::$_instance;
}
public function getConnection() {
return $this->db;
}
final protected function __clone() { }
}
To use this code in another class i used
$db=DbConnection::getInstance()->getConnection();
$collection=$db->users;
I dont know why second one worked but not the first code. if i use both in mysql both works fine.
Also can this be issue than in second code i have create connection to mongodatabase and kept it open and directly used in another class.
please describe simply why the second code worked fine and first didn't worked.
In the first piece of code, when the $db variable is null and you create a new connection, your getMongoCon function doesn't return anything, hence when you try to use it on the example, $db=DbConnection::getMongoCon(); end ups asigning null to the $db variable.
To make it work correctly, you should do something like this:
...
static function getMongoCon()
{
if (self::$db === null)
{
try {
$m = new Mongo("mongodb://username:password#localhost:27017");
} catch (MongoConnectionException $e) {
die('Failed to connect to MongoDB '.$e->getMessage());
}
self::$db = $m;
}
return self::$db;
}
...

mysqli constructor returns null

I am trying to write a db util class using the singleton pattern. My problem is that the "connection" object is always null. The connection settings are correct. What could i be doing wrong ? Also, i am relatively new to php development. What method should i use to figure out what's wrong ? Code follows.
class DBUtil {
public $connection = NULL; //mysqli_connection object
private static $instance = NULL;
private function _constructor($conn){
//$this->connection = mysqli_connect(TagMetroConfiguration::getConfigurationValueFor("db_servser_name"), TagMetroConfiguration::getConfigurationValueFor("db_username"), TagMetroConfiguration::getConfigurationValueFor("db_password"), TagMetroConfiguration::getConfigurationValueFor("db_name"));
$this->connection = new mysqli("localhost", "root", "toor", "testdb");
}
public static function getInstance(){
if(DBUtil::$instance == NULL){
try{
DBUtil::$instance = new DBUtil();
}catch(Exception $ex){
throw new Exception("Unable to create DB Instance");
}
}
return DBUtil::$instance;
}
}
Your constructor function should be named __construct (notice two underscores).
Also, in your constructor, you have one parameter, $conn. When you call new DBUtil(), you are not providing that input parameter, so perhaps it's calling the default contructor, not your custom one.
If you want the input parameter $conn to be optional, try __construct($conn = null).
Or try calling it as new DBUtil(null).
private function _constructor($conn) ??
should this be
private function __construct($conn)
There should be two underscores __ (__construct).
You should do like this :
class DBUtil {
private static $instance;
private function _construct(){
$this->$instance = new mysqli("localhost", "root", "toor", "testdb");
}
public static function getInstance(){
if(!isset(self::$instance){
try{
self::$instance = new DBUtil();
}catch(Exception $ex){
throw new Exception("Unable to create DB Instance");
}
}
return self::$instance;
}

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