0

So, I'm working on a project that requires a database connection. I've chosen to use PDO for its versatility and need to figure out how to set up the connection. Currently I'm going for something like this:

class Database {
  private static $db;
    static function initDB() {
      if(!is_object(self::$db) || get_class(self::$db) != 'PDO') {
        include('core/db.php');
        try {
            $db = new PDO($database, $username, $password);
        } catch(PDOException $e) {
            print("<br />Could not establish database connection. Error message: ".$e->getMessage()."<br />");
            die();
        }
    }
    //Try the transaction
    /*
    if($transaction = $db::query(PDO::quote($value)))
        $db::query(PDO::quote("INSERT INTO log VALUES ('".Authorization::$user."','".PDO::quote($value)."', 'Success')"));
    else
        $db::query(PDO::quote("INSERT INTO log VALUES ('".Authorization::$user."','".PDO::quote($value)."', 'Failure')"));*/
 }
}

So, this pretty much reveals one of the concepts I don't really know: singletons and static classes/objects. Any way to set up a database connection using OO best practices that initializes with the script via some kind of __construct method?

Yang
  • 8,580
  • 8
  • 33
  • 58

2 Answers2

1

A database connection should not be either static or a singleton. This merely introduces another form of global state, which is bad for unit-testing and does hide obvious dependencies.

The right way here would be is to inject an instance of PDO into the classes that need it. You adhere the Single-Responsibility Principle and Dependency Injection.

Note, you should never log errors and do include() inside PDOAdapter constructor because its masked violation of the Single-Responsibility Principle

So, this would look like this:

final class PDO_Provider extends PDO
{
    /**
     * Constructor. Inits PDO 
     * 
     * @param array $params
     * @return void
     */
    public function __construct(array $params)
    {
        try {

            extract($params);

            parent::__construct(sprintf('mysql: host=%s; dbname=%s', $host, $database), $user, $password);

            $this->setAttribute(parent::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES UTF8');
            $this->setAttribute(parent::ATTR_ERRMODE, parent::ERRMODE_EXCEPTION);
            $this->setAttribute(parent::ATTR_EMULATE_PREPARES, false);
            $this->setAttribute(parent::ATTR_DEFAULT_FETCH_MODE, parent::FETCH_ASSOC);

        } catch(PDOException $e) {

            die($e->getMessage());
        }
    }
}

And you would use it like this,

<?php


$sql_config = array(
    'host'          =>  'localhost',
    'user'          =>  'root',
    'password'      =>  '',
    'database'      =>  '_DB_NAME_',
);

// <- Or you can include that, like 
$sql_config = include(__DIR__ . '/core/db_params.php');

$pdoProvider = new PDO_Provider($sql_config);

$user = new User_Login($pdoProvider); // the point here is to inject an instance of $pdoProvider. User_Login is actually irrelevant
Yang
  • 8,580
  • 8
  • 33
  • 58
-1

if you want to use a normal object instead of sigleton, try something like this:

class PDOConnector{

    protected $connection;

    function __construct($host, $user, $pass, $db_name)
    {
        //create database connection
        try{
            $this->connection = new PDO('mysql:host='.$this->host.';dbname='.$this->db_name.';charset=utf8', $this->user, $this->pass,array(PDO::ATTR_EMULATE_PREPARES => false, 
                                                                                                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
        }
        catch(PDOException $ex) {
            echo "An Error occured : ".$ex->getMessage();               
        }

    }

}
Aris
  • 4,643
  • 1
  • 41
  • 38
  • -1: If you want to access db connection, you have two ways of doing it in your case 1) Either $pdoConnector->connection->prepare ($connection must be public) or 2) `class Foo extends PDOConnector {}` The problem here is that your 1-st approach is masked violation of the **Law of Demeter** and 2-nd breaks the `Liskov-Substitution Principle` and the `Single-Responsibility Principle` at the same time – Yang Jun 28 '13 at 04:26
  • i use a public method for accessing the connection. of course the connection is not public. – Aris Jun 28 '13 at 04:57
  • Its not that important, the core points here : it all breaks aforementioned principles (namely `SRP`, `LSP` and `LoD`). – Yang Jun 28 '13 at 05:00
  • I am just answering to your first point, that connection must be public. It is not. I will check those principles. – Aris Jun 28 '13 at 07:33